当前位置: 首页 > news >正文

Spring AOP面向切面编程

Spring AOP面向切面编程

  • 面向切面编程
    • AOP作用
    • AOP功能
    • AOP总结
  • AOP核心概念
  • AOP的实现方式
  • Spring 对AOP支持
    • 支持@Aspect
    • 声明一个切面
    • 声明一个切入点
      • AspectJ描述符如下
      • AspectJ类型匹配的通配符
      • 常用的匹配规则
    • 声明增强
  • 用AOP实现日志拦截
    • 一般的实现
    • 仅拦截需要的方法
      • 先定义一个日志注解Log
      • 用@annotation定义切入点
      • 在想做日志输出的方法上使用注解Log
    • requestId传递
  • 关于增强执行顺序

面向切面编程

面向切面编程(AOP,Aspect Oriented Programming)是通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。

AOP作用

利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

AOP功能

主要功能:日志记录、性能统计、安全控制、事务处理、异常处理等。

AOP总结

面向切面编程是希望能够将通用需求功能从不相关的类当中分离出来,能够使得很多类共享一个行为,一旦发生变化,不必修改很多类,而只是修改这个行为即可。

AOP通过提供另一种思考程序结构的方式来补充了面向对象编程(OOP)。OOP中模块化的基本单元是类(class),而AOP中模块化的基本单元是切面(aspect)。可以这么理解,OOP是解决了纵向的代码复用问题,AOP是解决了横向的代码复用问题。

Spring的关键组件之一是AOP框架。虽然Spring IOC容器不依赖于AOP,意味着如果你不想使用AOP,则可以不使用AOP,但AOP补充了Spring IOC以提供一个非常强大的中间件解决方案。

在这里插入图片描述

AOP核心概念

  • 切面(aspect): 在AOP中,切面一般使用@Aspect注解来标识。
  • 连接点(Join Point): 在Spring AOP,一个连接点总是代表一次方法的执行。
  • 增强(Advice): 在连接点执行的动作。
  • 切入点(Pointcout): 说明如何匹配到连接点。
  • 引介(Introduction): 为现有类型声明额外的方法和属性。
  • 目标对象(Target Object): 由一个或者多个切面建议的对象,也被称为建议对象,由于Spring AOP是通过动态代理来实现的,这个对象永远是一个代理对象。
  • AOP代理(AOP proxy): 一个被AOP框架创建的对象,用于实现切面约定(增强方法的执行等)。在Spring Framework中,一个AOP代理是一个JDK动态代理或者CGLIB代理
  • 织入(Weaving): 连接切面和目标对象或类型创建代理对象的过程。它能在编译时(例如使用AspectJ编译器)、加载时或者运行时完成。Spring AOP与其他的纯Java AOP框架一样是在运行时进行织入的。

在这里插入图片描述
Spring AOP包括以下类型的增强:

  • 前置增强(Before advice):在连接点之前运行,但不能阻止到连接点的流程继续执行(除非抛出异常)
  • 返回增强(After returning advice):在连接点正常完成后运行的增强(例如,方法返回没有抛出异常)
  • 异常增强(After thorwing advice):如果方法抛出异常退出需要执行的增强
  • 后置增强(After (finally) Advice):无论连接点是正常或者异常退出,都会执行该增强
  • 环绕增强(Around advice):围绕连接点的增强,例如方法的调用。环绕增强能在方法的调用之前和调用之后自定义行为。它还可以选择方法是继续执行或者去缩短方法的执行通过返回自己的值或者抛出异常。

AOP的实现方式

AOP的两种实现方式:静态织入(以AspectJ为代表)和动态代理(Spring AOP实现)

  • AspectJ是一个采用Java实现的AOP框架,它能够对代码进行编译(在编译期进行),让代码具有AspectJAOP功能,当然它也可支持动态代理的方式;
  • Spring AOP实现:通过动态代理技术来实现,Spring2.0集成了AspectJ,主要用于PointCut的解析和匹配,底层的技术还是使用的Spring1.x中的动态代理来实现。

在这里插入图片描述
Spring AOP实现采用了两种混合的实现方式:JDK动态代理和CGLib动态代理。

  • JDK动态代理:Spring AOP的首选方法。每当目标对象实现一个接口时,就会使用JDK动态代理。目标对象必须实现接口。
  • CGLIB:如果目标对象没有实现接口,则可以使用CGLIB代理。

在这里插入图片描述

Spring 对AOP支持

Spring可以使用两种方式来实现AOP:基于注解式配置和基于XML配置。

下面介绍基于注解配置的形式

支持@Aspect

如果是Spring Framework,需要使用aspectjweaver.jar包,然后创建我们自己的AppConfig,如下,并加上@EnableAspectJAutoProxy注解开启AOP代理自动配置(Spring Boot默认是开启的,则不需要增加配置),如下:

@Configuration
@EnableAspectJAutoProxy
public class AppConfig {}

声明一个切面

@Aspect //告诉Spring 这是一个切面
@Component  //交给Spring容器管理
public class MyAspect {}

可以使用@Aspect来定义一个类作为切面,但是这样,该类并不会自动被Spring加载,还是需要加上@Component注解。

声明一个切入点

一个切入点的生命包含两个部分:一个包含名称和任何参数的签名和一个切入点的表达式,这个表达式确定了我们对哪些方法的执行感兴趣。

我们以拦截Controller层中的MyController中的test方法为例子,代码如下:

@RestController
@RequestMapping("/my")
public class MyController {@GetMapping("/test")public void test() {System.out.println("test 方法");}
}

下面定义一个名为controller的切入点,该切入点与上述的test方法相匹配,切入点需要用@Pointcut注解来标注,如下:

//表达式
@Pointcut("execution (public * com.yc.springboot.controller.MyController.test())")
public void controller(){}; //签名

切入点表达式的格式如下:

execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)
execution(方法修饰符(可选) 返回类型 类路径 方法名 参数 异常模式(可选))

AspectJ描述符如下

AspectJ描述符描述
arg()限制连接点匹配参数为指定类型的执行方法
@args()限制连接点匹配参数由指定注解标注的执行方法
execution()用于匹配是连接点的执行方法
this()限制连接点匹配的AOP代理的bean引用为指定类型的类
target限制连接点匹配目标对象为指定类型的类
@target()限制连接点匹配特定的执行对象,这些对象对应的类要具有指定类型的注解
within()限制连接点匹配指定的类型
@within()限制连接点匹配指定注解所标注的类型
@annotationn限定匹配带有指定注解的连接点

常用的主要是:execution()

AspectJ类型匹配的通配符

通配符含义
*匹配任何数量字符
..匹配任何数量字符的重复
+ 匹配指定类型的子类型;仅能用于后缀放在类型模式后面

常用的匹配规则

表达式内容
execution(public * *(..))匹配所有public方法
execution(* set*(..))匹配所有方法名开头为set的方法
execution(* com.xyz.service.AccountService.*(..))匹配AccountService下的所有方法
execution(* com.xyz.service.*.*(..))匹配service包下的所有方法
execution(* com.xyz.service..*.*(..))匹配service包或其子包下的所有方法
@annotation(org.springframework.transaction.annotation.Transactional)匹配所有打了@Transactional注解的方法
bean(*Service)匹配命名后缀为Service的类的方法

声明增强

增强与切点表达式相关联,并且在与切点匹配的方法之前、之后或者前后执行。

下面直接罗列了各种增强的声明,用于拦截MyController中的各个方法

@Aspect //告诉Spring 这是一个切面
@Component  //告诉Spring容器需要管理该对象
public class MyAspect {//通过规则确定哪些方法是需要增强的@Pointcut("execution (public * com.yc.springboot.controller.MyController.*(..))")public void controller() {}//前置增强@Before("controller()")public void before(JoinPoint joinPoint) {System.out.println("before advice");}//返回增强@AfterReturning(pointcut = "controller()",returning = "retVal")public void afterReturning(Object retVal) {System.out.println("after returning advice, 返回结果 retVal:" + retVal);}//异常增强@AfterThrowing(pointcut = "controller()",throwing = "ex")public void afterThrowing(Exception ex) {System.out.println("after throwing advice, 异常 ex:" + ex.getMessage());}//后置增强@After("controller()")public void after(JoinPoint joinPoint) {System.out.println("after advice");}//环绕增强@Around("controller()")public Object around(ProceedingJoinPoint joinPoint) throws Throwable {System.out.println("before advice");//相当于是before adviceObject reVal = null;try {reVal = joinPoint.proceed();} catch (Exception e) {//相当于afterthrowing adviceSystem.out.println("afterthrowing advice");}//相当于是after adviceSystem.out.println("after advice");return reVal;}
}

需要注意的是:

  • 在返回增强中,我们需要给@AfterReturing设置returning的值,且需要与方法的参数名一致,用于表示业务方法的返回值
  • 在异常增强中,需要给@AfterThrowing设置throwing的值,且需要与方法的参数名一致,用于表示业务方法产生的异常
  • 在环绕增强中,参数为ProceedingJoinPoint类型,它是JoinPoint的子接口,我们需要在这个方法中手动调用其proceed方法来触发业务方法
  • 在所有的增强方法中都可以申明第一个参数为JoinPoint(注意的是,环绕增强是使用ProceedingJoinPoint来进行申明,它实现了JoinPoint接口)
  • JoinPoint接口提供了几个有用的方法
    • getArgs():返回这个方法的参数
    • getThis():返回这个代理对象
    • getTarget():返回目标对象(被代理的对象)
    • getSignature():返回被增强方法的描述
    • toString():打印被增强方法的有用描述

下面为Mycontroller测试类:

@RestController
@RequestMapping("/my")
public class MyController {@GetMapping("/testBefore")public void testBefore() {System.out.println("testBefore 业务方法");}@GetMapping("/testAfterReturning")public String testAfterReturning() {System.out.println("testAfterReturning 业务方法");return "我是一个返回值";}@GetMapping("/testAfterThrowing")public void testAfterThrowing() {System.out.println("testAfterThrowing 业务方法");int a = 0;int b = 1 / a;}@GetMapping("/testAfter")public void testAfter() {System.out.println("testAfter 业务方法");}@GetMapping("/around")public void around() {System.out.println("around 业务方法");}
}

用AOP实现日志拦截

一般的实现

打印日志是AOP的一个常见应用场景,我们可以对Controller层向外提供的接口做统一的日志拦截,用日志记录请求参数、返回参数、请求时长以及异常信息,方便我们线上排查问题,下面是核心类LogAspect的实现

/*** 日志的切面*/
@Aspect
@Component
public class LogAspect {@Resourceprivate IdWorker idWorker;@Pointcut("execution (public * com.yc.core.controller.*.*(..))")public void log(){}/*** 使用环绕增强实现日志打印* @param joinPoint* @return* @throws Throwable*/@Around("log()")public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {//获得执行方法的类和名称String className = joinPoint.getTarget().getClass().getName();//获得方法名MethodSignature signature = (MethodSignature) joinPoint.getSignature();String methodName = signature.getMethod().getName();//获得参数Object[] args = joinPoint.getArgs();long requestId = idWorker.nextId();//打印参数LogHelper.writeInfoLog(className, methodName, "requestId:" + requestId + ",params:" + JSONObject.toJSONString(args));long startTime = System.currentTimeMillis();//执行业务方法Object result = null;try {result = joinPoint.proceed();} catch (Exception e) {LogHelper.writeErrLog(className, methodName, "requestId:" + requestId + ",异常啦:" + LogAspect.getStackTrace(e));}long endTime = System.currentTimeMillis();//打印结果LogHelper.writeInfoLog(className, methodName, "requestId:" + requestId + ",耗时:" + (endTime - startTime) +  "ms,result:" + JSONObject.toJSONString(result));//返回return result;}/*** 获取异常的堆栈信息* @param throwable* @return*/public static String getStackTrace(Throwable throwable){StringWriter sw = new StringWriter();PrintWriter pw = new PrintWriter(sw);try{throwable.printStackTrace(pw);return sw.toString();} finally{pw.close();}}
}
  • proceed()方法之前,相当于前置增强,收集类名、方法名、参数,记录开始时间,生成requestId
  • proceed()方法之后,相当于后置增强,并能获取到返回值,计算耗时
  • 在前置增强时,生成requestId,用于串联多条日志
  • 使用try、catch包裹proceed()方法,在catch中记录异常日志
  • 提供了getStackTrace方法获取异常的堆栈信息,便于排查报错详细情况

仅拦截需要的方法

但是上面的日志是针对所有controller层中的方法进行了日志拦截,如果我们有些方法不想进行日志输出,比如文件上传的接口、大量数据返回的接口,这个时候定义切入点的时候可以使用@annotation描述符来匹配加了特定注解的方法,步骤如下:

先定义一个日志注解Log

/*** 自定义日志注解*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {
}

用@annotation定义切入点

@Pointcut("@annotation(com.yc.core.annotation.Log)")
public void logAnnotation(){}

在想做日志输出的方法上使用注解Log

@Log
@PostMapping(value = "testannotation")
public AOPTestVO testannotation(@RequestBody AOPTestDTO aopTestDTO) {AOPTestVO aopTestVO = new AOPTestVO();aopTestVO.setCode(1);aopTestVO.setMsg("哈哈哈");return aopTestVO;
}

这样,我们就可以自定义哪些方法需要日志输出了。

requestId传递

后来有同事提到,如果这是针对Controller层的拦截,但是Service层也有自定义的日志输出,怎么在Service层获取到上述的requestId呢?

其实就是我们拦截之后,是否可以针对方法的参数进行修改呢?其实注意是看

result = joinPoint.proceed();

我们发现ProceedingJoinPoint还有另外一个带有参数的proceed方法,定义如下:

public Object proceed(Object[] args) throws Throwable;

我们可以利用这个方法,在环绕增强中去增加requestId,这样后面的增强方法或业务方法中就能获取到这个requestId了。

首先,我们先定义一个基类AOPBaseDTO,只有一个属性requestId

@Data
@ApiModel("aop参数基类")
public class AOPBaseDTO {@ApiModelProperty(value = "请求id", hidden = true)private long requestId;
}

然后我们让Controller层接口的参数AOPTestDTO继承上述AOPBaseDTO,如下:

@Data
@ApiModel("aop测试类")
public class AOPTestDTO extends AOPBaseDTO{@ApiModelProperty(value = "姓名")private String name;@ApiModelProperty(value = "年龄")private int age;
}

最后在环绕的增强中添加上requestId,如下:

/*** 使用环绕增强实现日志打印* @param joinPoint* @return* @throws Throwable*/
@Around("logAnnotation()")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {//获得执行方法的类和名称String className = joinPoint.getTarget().getClass().getName();//获得方法名MethodSignature signature = (MethodSignature) joinPoint.getSignature();String methodName = signature.getMethod().getName();//获得参数Object[] args = joinPoint.getArgs();long requestId = idWorker.nextId();for(int i = 0; i < args.length; i++) {if (args[i] instanceof AOPBaseDTO) {//增加requestId((AOPBaseDTO) args[i]).setRequestId(requestId);}}//打印参数LogHelper.writeInfoLog(className, methodName, "requestId:" + requestId + ",params:" + JSONObject.toJSONString(args));long startTime = System.currentTimeMillis();//执行业务方法Object result = null;try {result = joinPoint.proceed(args);} catch (Exception e) {LogHelper.writeErrLog(className, methodName, "requestId:" + requestId + ",异常啦:" + LogAspect.getStackTrace(e));}long endTime = System.currentTimeMillis();//打印结果LogHelper.writeInfoLog(className, methodName, "requestId:" + requestId + ",耗时:" + (endTime - startTime) +  "ms,result:" + JSONObject.toJSONString(result));//返回return result;
}

我们运行起代码,访问一下,下面是运行结果:
在这里插入图片描述
可以看到,我们的业务方法中已经能获取到requestId,如果Service层需要,可以通过传递AOPTestDTO,从中获取。

关于增强执行顺序

  1. 针对不同类型的增强,顺序固定的,比如before在after前面
  2. 针对同一切面的相同类型的增强,根据定义先后顺序依次执行
  3. 针对不同切面的相同增强,可以通过使我们的切面实现Ordered接口,重写getOrder方法,返回值最小,优先级越高。注意的是before和after的相反的,before的优先级越高越早执行,after的优先级越高,越晚执行。

相关文章:

Spring AOP面向切面编程

Spring AOP面向切面编程 面向切面编程AOP作用AOP功能AOP总结 AOP核心概念AOP的实现方式Spring 对AOP支持支持Aspect声明一个切面声明一个切入点AspectJ描述符如下AspectJ类型匹配的通配符常用的匹配规则 声明增强 用AOP实现日志拦截一般的实现仅拦截需要的方法先定义一个日志注…...

Visual Studio 中增加的AI功能

前言&#xff1a; 人工智能的发展&#xff0c;在现在&#xff0c;编程技术的IDE里面也融合了AI的基本操做。本例&#xff0c;以微软的Visual Studio中的人工智能的功能介绍例子。 本例的环境&#xff1a; Visual Studio 17.12 1 AI 智能变量检测&#xff1a; 上图展示了一…...

15. 接雨水

接雨水 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。 示例 1: 输入:height = [0,1,0,2,1,0,1,3,2,1,2,1] 输出:6 解释:上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(…...

从索尼爱立信手机打印短信的简单方法

昨天&#xff0c;我买了一部新手机来代替我的旧索尼爱立信Xperia&#xff0c;但手机上有很多珍贵的短信&#xff0c;是我男朋友发来的&#xff0c;我不想失去它们。然后我尝试打印它们&#xff0c;但我无法从我的索尼爱立信手机中取出它们。您有什么从索尼爱立信手机打印短信的…...

Java-list均分分割到多个子列表

在Java中,如果你有一个List并且想要将其均分到多个子列表中,可以使用以下方法。假设你有一 个List<T>,并且想要将其分成n个子列表。 import java.util.ArrayList; import java.util.List;public class ListSplitter {public static <T> List<List<T>…...

kettle合并表数据

总体执行图&#xff1a;以两个数据表作为输入&#xff0c;根据关键栏位进行合并后&#xff0c;以excel表输出。 两表数据输入 需要确定查询的表名 2. 根据关键栏位进行排序。在记录集连接之前需要进行排序操作 3. 记录连接与合并 此方式表示select EQP_ID, ID FROM T_EQP_C…...

蓝耘平台使用InstantMesh‌生成高质量的三维网格模型!3D内容创作!小白入门必看!!!

目录 引言 InstantMesh应用介绍 蓝耘平台与InstantMesh结合使用 如何部署&#xff08;超简单&#xff09; 第一步登录蓝耘平台 第二步点击应用商城 ​编辑 第三步选择InstantMesh 第四步点击部署 第五步点击快速启动应用 第六步即可体验该产品 总结 注册链接 引言…...

关于IDE的相关知识之二【插件推荐】

成长路上不孤单&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a; 【14后&#x1f60a;///计算机爱好者&#x1f60a;///持续分享所学&#x1f60a;///如有需要欢迎收藏转发///&#x1f60a;】 今日分享关于ide插件推荐的相关内容&#xff01…...

oceanbase集群访问异常问题处理

1.报错现象 2.问题排查 检查obproxy状态发现为不可用状态 重启obproxy 依次重启Obproxy集群 观察任务状态 重启完成 Obproxy状态正常 3.验证登录 登录成功...

Linux(centos)安装 MySQL 8 数据库(图文详细教程)

前言 前几天写了个window系统下安装Mysql的博客&#xff0c;收到很多小伙伴私信需要Linux下安装Mysql的教程&#xff0c;今天这边和大家分享一下&#xff0c;话不多说&#xff0c;看教程。 一、删除以前安装的MySQL服务 一般安装程序第一步都需要清除之前的安装痕迹&#xff…...

C++之map和set的模拟实现

目录 引言 红黑树迭代器实现 红黑树元素的插入 map模拟实现 set模拟实现 之前我们已经学习了map和set的基本使用&#xff0c;但是因为map和set的底层都是用红黑树进行封装实现的&#xff0c;上期我们已经学习了红黑树的模拟实现&#xff0c;所以本期我们在红黑树模拟实现…...

判断一个单链表是否是回文结构 要求O(N)时间复杂度 O(1)空间复杂度

没做出来 看了解析 但是思路想到了 就是只能调整链表顺序&#xff0c;正确答案是 把链表变成两条单链表&#xff0c;分别从两侧走向中间拿两个指针 分别指向两头 &#xff0c;往中间走 中途有不一样的就返回false, private static boolean handle(Node head){int size size…...

Kafka 快速实战及基本原理详解解析-01

一、Kafka 介绍 1. MQ 的作用 消息队列&#xff08;Message Queue&#xff0c;简称 MQ&#xff09;是一种用于跨进程通信的技术&#xff0c;核心功能是通过异步消息的方式实现系统之间的解耦。它在现代分布式系统中有着广泛的应用&#xff0c;主要作用体现在以下三个方面&…...

wujie无界微前端框架初使用

先说一下项目需求&#xff1a;将单独的四套系统的登录操作统一放在一个入口页面进行登录&#xff0c;所有系统都使用的是vue3&#xff0c;&#xff08;不要问我为啥会这样设计&#xff0c;产品说的客户要求&#xff09; 1.主系统下载wujie 我全套都是vue3&#xff0c;所以直接…...

C++ 设计模式:职责链模式(Chain of Responsibility)

链接&#xff1a;C 设计模式 链接&#xff1a;C 设计模式 - 组合模式 链接&#xff1a;C 设计模式 - 迭代器模式 职责链模式&#xff08;Chain of Responsibility Pattern&#xff09;是一种行为型设计模式&#xff0c;它允许多个对象都有机会处理请求&#xff0c;从而避免请求…...

Yocto项目 - 详解PACKAGECONFIG机制

引言 Yocto项目是一个强大的嵌入式Linux开发工具&#xff0c;广泛应用于创建定制的嵌入式Linux发行版。在Yocto中&#xff0c;配置和定制化构建系统、软件包、以及生成适用于特定硬件的平台镜像是非常重要的。PACKAGECONFIG是Yocto项目中用于灵活启用或禁用软件包特性的强大工…...

Linux下部署ElasticSearch集群

Elasticsearch7.17.8集群的搭建 节点host名称节点ip节点部署内容k8s-m192.168.40.142主节点 数据节点k8s-w1192.168.40.141主节点 数据节点k8s-w2192.168.40.140数据节点 一、准备安装环境 1.下载安装包 官网 www.elastic.co 下载所有版本地址 点击跳转 下载elasticsearch-7.…...

超高分辨率 图像 分割处理

文章大纲 制造业半导体领域高分辨率图像半导体数据集开源的高分辨率晶圆图像数据集1. WM-811K数据集2. Kaggle上的WM-811K Clean Subset数据集医疗 病理领域高分辨率图像1. Camelyon+2. CAMELYON173. CPIA Dataset4. UCF-WSI-Dataset航拍 遥感中的高分辨率 图像航拍遥感领域高分…...

【含文档+PPT+源码】基于springboot的农贸菜市场租位管理系统的设计与实现

开题报告 本文旨在探讨基于SpringBoot框架构建的农贸菜市场租位管理系统的设计与实现。系统结合了现代化信息技术与农贸市场管理需求&#xff0c;为用户提供了注册登录、查看系统公告、分类搜索店铺、查看店铺详情、填写租赁信息、在线租赁、我的订单管理以及用户信息和密码修…...

信息科技伦理与道德1:绪论

1 问题描述 1.1 信息科技的进步给人类生活带来的是什么呢&#xff1f; 功能&#xff1f;智能&#xff1f;陪伴&#xff1f;乐趣&#xff1f;幸福&#xff1f; 基于GPT-3的对话Demo DeepFake 深伪技术&#xff1a;通过神经网络技术进行大样本学习&#xff0c;将个人的声音、面…...

Linux实验报告15-添加系统调用

目录 一&#xff1a;实验目的 二&#xff1a;实验内容 &#xff08;1&#xff09;查看系统内核版本 &#xff08;2&#xff09;安装内核版本源码 &#xff08;3&#xff09;修改注册表 &#xff08;4&#xff09;添加系统调用头文件 &#xff08;5&#xff09;实现系统调…...

logback之配置文件使用详解

目录 &#xff08;一&#xff09;配置文件的加载 &#xff08;二&#xff09;使用介绍 1、configuration&#xff1a;配置文件的跟元素 2、contextName&#xff1a;设置日志上下文名称 3、contextListener&#xff1a;设置上下文监听事件 4、property/variable/substituti…...

壁纸样机神器,这个工具适合专业设计师用吗?

壁纸样机神器在一定程度上适合专业设计师使用&#xff0c;但是否适合具体取决于设计师的需求和使用场景&#xff1a; 适合专业设计师的方面 快速实现设计想法&#xff1a;专业设计师在创作过程中&#xff0c;有时需要快速将设计想法变为可视化的效果图&#xff0c;以便进行初…...

MySQL秘籍之索引与查询优化实战指南

MySQL秘籍之索引与查询优化实战指南 目录 MySQL秘籍之索引与查询优化实战指南相关阅读索引相关EXPLAIN 版本 1. 初级篇1.1 【练体术】基础1.1.1 库操作1.1.1 表操作创建一个表增加表字段 1.1.2 增删改插入一条数据删除一条数据更新一条数据库 1.1.3 查询查询所有数据条件查询&a…...

【AI日记】25.01.03 kaggle 比赛 3-2 未来的命运

【AI论文解读】【AI知识点】【AI小项目】【AI战略思考】【AI日记】 工作 参加&#xff1a;kaggle 比赛 Forecasting Sticker Sales时间&#xff1a;8 小时 读书 书名&#xff1a;秦制两千年时间&#xff1a;1.5 小时评估&#xff1a;读完&#xff0c;非常不错&#xff0c;很…...

Linux(Centos 7.6)命令详解:ls

1.命令作用 列出目录内容(list directory contents) 2.命令语法 Usage: ls [OPTION]... [FILE]... 3.参数详解 OPTION: -l&#xff0c;long list 使用长列表格式-a&#xff0c;all 不忽略.开头的条目&#xff08;打印所有条目&#xff0c;包括.开头的隐藏条目&#xff09…...

【Unity3D】UGUI Canvas画布渲染流程

参考文档&#xff1a;画布 - Unity 手册 Canvas组件&#xff1a;画布组件是进行 UI 布局和渲染的抽象空间。所有 UI 元素都必须是附加了画布组件的游戏对象的子对象。 参数&#xff1a; Render Mode 渲染模式&#xff1a;Screen Space - Overlay、Screen Spa…...

minikube安装k8s

一、安装k8s版本 export REGISTRY_MIRRORhttps://registry.cn-hangzhou.aliyuncs.com curl -sSL https://kuboard.cn/install-script/v1.30.x/install_kubelet.sh | sh -s 1.30.0 二、安装docker及minikube useradd docker passwd docker 密码也设置为docker #创建docker组…...

Docker图形化界面工具Portainer最佳实践

前言 安装Portainer 实践-基于Portainer安装redis-sentinel部署 Spring Boot集成Redis Sentinel 前言 本篇文章笔者推荐一个笔者最常用的docker图形化管理工具——Portainer。 安装Portainer 编写docker-compose文件 Portainer部署的步骤比较简单&#xff0c;我们还是以…...

借助 FinClip 跨端技术探索鸿蒙原生应用开发之旅

在当今数字化浪潮汹涌澎湃的时代&#xff0c;移动应用开发领域正经历着深刻的变革与创新。鸿蒙操作系统的崛起&#xff0c;以其独特的分布式架构和强大的性能表现&#xff0c;吸引了众多开发者的目光。而FinClip 跨端技术的出现&#xff0c;为开发者涉足鸿蒙原生应用开发提供了…...