【spring】 spring aop 加载期织入

描述如何使用加载期织入

前言

织入是把切面应用到目标对象来创建新的代理对象的过程,织入有 3 种方式

  1. 编译期织入,在编译的时候将增强的代码加入到代理对象里,需要特殊的编译器
  2. 加载期织入(load-time weaving),在类加载的时候将增强的代码加入到代理对象里,需要使用 -javaagent 参数指定 agent 用于修改类的字节码
  3. 运行期织入,在运行期将增强的代码加入到代理对象里

在 spring 中, AOP 默认是使用运行期织入,通过 CGLIB 或 JDK 实现代理,此外也可以用 aspectj 实现加载期织入

使用 load-time weaving

在 spring 使用 load-time weaving 有两种情况。一种是 spring 的内置功能,例如 Caching 和 Async,一种是用户用 @Aspect 或 aop.xml 定义的切面

spring 的内置功能

这种情况需要将 AdviceMode 设置为 ASPECTJ,再将 javaagent 设置为 aspectjweaver.jar

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class AsyncApp {
public static void main(String[] args) {
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class);
HelloService hello = ctx.getBean("helloService", HelloService.class);
hello.hello();
}

@Configuration
@EnableAsync(mode = AdviceMode.ASPECTJ) // 使用 aspectj 增强
static class Config {
@Bean
public HelloService helloService() {
return new HelloService();
}

@Bean
public ConcurrentTaskExecutor executor() {
return new ConcurrentTaskExecutor();
}
}
}

public class HelloService {
@Async
public void hello() {
System.out.println("hello");
}
}

运行命令

1
java -javaagent:aspectjweaver-1.9.4.jar -jar myapp.jar

使用 @Aspect 的情况

这种情况可以将 javaagent 设置为 aspectjweaver.jar , 也可以设置为 spring-instrument.jar,如果设置为 spring-instrument.jar 就需要有 @EnableLoadTimeWeaving

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public class AspectjApp {
public static void main(String[] args) {
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class);
HelloService hello = ctx.getBean("helloService", HelloService.class);
hello.hello();
}

@Configuration
@EnableAspectJAutoProxy
@EnableLoadTimeWeaving // 打开 load-time weaving 功能
static class Config {
@Bean
public HelloService helloService() {
return new HelloService();
}

@Bean
public Interceptor myInterceptor() {
return new Interceptor();
}
}
}

public class HelloService {
public void hello() {
System.out.println("hello");
}
}

@Aspect
public class Interceptor {
@Pointcut("execution(* HelloService.hello(..))")
public void PointCut() {}

@Before("PointCut()")
public void before() {
System.out.println("before");
}
}

这种情况需要有 aop.xml, 即使内容为空

1
2
3
4
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE aspectj PUBLIC "-//AspectJ//DTD//EN" "http://www.eclipse.org/aspectj/dtd/aspectj.dtd">
<aspectj>
</aspectj>
1
java -javaagent:spring-instrument-5.1.9.RELEASE.jar -jar myapp.jar

如果两种情况都需要使用,则可以设置两个 javaagent

1
java -javaagent:aspectjweaver-1.9.4.jar -javaagent:spring-instrument-5.1.9.RELEASE.jar -jar myapp.jar