我是靠谱客的博主 重要啤酒,这篇文章主要介绍Spring5底层原理 学习笔记(二)AOP篇AOP实现之ajc编译器AOP实现之agent类加载AOP实现之动态代理Spring选择代理从@Aspect到Advisor,现在分享给大家,希望可以做个参考。

文章目录

  • AOP实现之ajc编译器
  • AOP实现之agent类加载
  • AOP实现之动态代理
    • jdk动态代理
      • 演示
      • 模拟实现动态代理
      • 动态生成代理类需要使用到asm的api,这里就不展开了
      • Jdk对于反射调用的优化
    • cglib动态代理
      • 演示
      • 模拟实现cglib动态代理
      • cglib如果通过MethodProxy使用非反射的方式调用方法
  • Spring选择代理
    • Spring选择动态代理规则
    • 切点匹配
  • 从@Aspect到Advisor
    • AnnotationAwareAspectJAutoProxyCreator
      • 方法findEligibleAdvisors
      • 方法wrapIfNecessary
    • 代理对象的创建时机
    • @Order
    • 从高级切面到低级切面
    • 将切面统一转换成环绕通知
    • 调用执行链
    • 总结静态通知调用过程
    • 动态通知调用

参考 糖果墙的 黑马程序员Spring视频教程,全面深度讲解spring5底层原理 学习笔记


AOP 底层实现方式之一是代理,由代理结合通知和目标,提供增强功能
除此以外,aspectj 提供了两种另外的 AOP 底层实现:

  • 第一种是通过 ajc 编译器在编译 class 类文件时,就把通知的增强功能,织入到目标类的字节码中
  • 第二种是通过 agent 在加载目标类时,修改目标类的字节码,织入增强功能
  • 作为对比,之前学习的代理是运行时生成新的字节码

简单比较的话:

  • aspectj 在编译和加载时,修改目标字节码,性能较高
  • aspectj 因为不用代理,能突破一些技术上的限制,例如对构造、对静态方法、对 final 也能增强
  • 但 aspectj 侵入性较强,且需要学习新的 aspectj 特有语法,因此没有广泛流行

AOP实现之ajc编译器

使用ajc编译器进行代码增强,首先需要在pom.xml文件中加入ajc编译器插件依赖

复制代码
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
<build> <plugins> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>aspectj-maven-plugin</artifactId> <version>1.11</version> <configuration> <complianceLevel>1.8</complianceLevel> <source>8</source> <target>8</target> <showWeaveInfo>true</showWeaveInfo> <verbose>true</verbose> <Xlint>ignore</Xlint> <encoding>UTF-8</encoding> </configuration> <executions> <execution> <goals> <!-- use this goal to weave all your main classes --> <goal>compile</goal> <!-- use this goal to weave all your test classes --> <goal>test-compile</goal> </goals> </execution> </executions> </plugin> </plugins> </build>

加入aspectjweaver的依赖

复制代码
1
2
3
4
5
6
7
<dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.7</version> </dependency>

需要增强的类MyService

复制代码
1
2
3
4
5
6
7
8
@Slf4j public class MyService { public void foo() { log.debug("foo()"); } }

切面类MyAspect,编写execution表达式,对MyService类的foo()方法进行增强

复制代码
1
2
3
4
5
6
7
8
9
10
@Aspect // ⬅️注意此切面并未被 Spring 管理,本项目pom文件中根本没有引入spring的相关类 public class MyAspect { private static final Logger log = LoggerFactory.getLogger(MyAspect.class); @Before("execution(* top.jacktgq.service.MyService.foo())") public void before() { log.debug("before()"); } }

测试代码

复制代码
1
2
3
4
5
6
7
8
public class Aop_Aspectj_Test { @Test public void testAopAjc() { new MyService().foo(); } }
  • 可以看到没有引入任何跟spring框架相关的包,MyService类是通过直接new()的方式获得的,所以也就不存在使用了动态代理的说法了

  • 打开编译后的MyService.class文件,双击以后idea会反编译该字节码文件,可以看到foo()方法体的开头加了一行代码,这就是增强的代码,这是ajc编译器在编译MyService类的时候为我们添加的代码,这是一种编译时的增强。
    在这里插入图片描述

AOP实现之agent类加载

需要依赖

复制代码
1
2
3
4
5
6
7
<dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.7</version> </dependency>

需要增加的类MyService

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
@Slf4j public class MyService { public void foo() { log.debug("foo()"); bar(); } public void bar() { log.debug("bar()"); } }

切面类MyAspect,编写execution表达式,对MyService类的foo()方法进行增强

复制代码
1
2
3
4
5
6
7
8
9
10
@Aspect // ⬅️注意此切面并未被 Spring 管理,本项目pom文件中根本没有引入spring的相关类 @Slf4j public class MyAspect { @Before("execution(* top.jacktgq.service.MyService.*())") public void before() { log.debug("before()"); } }

测试代码

复制代码
1
2
3
4
5
6
7
8
9
10
public class Aop_agent_Test { @Test public void testAopAgent() throws Exception { MyService myService = new MyService(); myService.foo(); System.in.read(); } }

运行时需要在 VM options 里加入 -javaagent:D:同步空间repositoryorgaspectjaspectjweaver1.9.7aspectjweaver-1.9.7.jar把其中 D:同步空间repository 改为你自己 maven 仓库起始地址
注:还需要在resources/META-INF目录下建一个aop.xml配置文件,内容如下,aspectj会自动扫描到这个配置文件,不加这个配置文件不会出效果。

复制代码
1
2
3
4
5
6
7
8
9
10
11
<aspectj> <aspects> <aspect name="top.jacktgq.aop.MyAspect"/> <weaver options="-verbose -showWeaveInfo"> <include within="top.jacktgq.service.MyService"/> <include within="top.jacktgq.aop.MyAspect"/> </weaver> </aspects> </aspectj>

总结:

  • 可以看到没有引入任何跟spring框架相关的包,MyService类是通过直接new()的方式获得的,所以也就不存在使用了动态代理的说法了

  • 打开编译后的MyService.class文件,双击以后idea会反编译该字节码文件,可以看到foo()方法体中并没有添加多余的代码,所以就不是编译时增强了,而是类加载的时候增强的,这里可以借助阿里巴巴的Arthas工具,下载地址:https://arthas.aliyun.com/doc/en/download.html,解压以后进入到arthas的bin目录下,启动黑窗口,输入java -jar .arthas-boot.jar,在输出的java进程列表里面找到我们要连接的进程,输入对应进程的序号,我这里是4,连接上以后会打印ARTHAS的logo
    在这里插入图片描述
    再输入jad top.jacktgq.service.MyService反编译内存中的MyService类
    在这里插入图片描述
    可以看到foo()和bar()方法体的第一行都加了一行代码,这就说明通过添加虚拟机参数-javaagent的方式可以在类加载的时候对代码进行增强。

AOP实现之动态代理

jdk动态代理

演示

复制代码
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
public class ProxyJdkTest { public static void main(String[] args) { Dog dog = new Dog(); // 用于加载在运行期间生成的字节码 ClassLoader classLoader = ProxyJdkTest.class.getClassLoader(); Animal proxyDog = (Animal) Proxy.newProxyInstance(classLoader, new Class[]{Animal.class}, (proxy, method, args1) -> { System.out.println("Before"); Object result = method.invoke(dog, args1); System.out.println("After"); return result; }); proxyDog.run(); } interface Animal { void run(); } static class Dog implements Animal { @Override public void run() { System.out.println("Dog is running."); } } }

运行结果
在这里插入图片描述
Jdk动态代理要求目标必须实现接口,代理类与被代理类是平级兄弟关系

模拟实现动态代理

准备好一个接口Animal和一个目标代理类Dog

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public interface Animal { void run(); int eat(); } public class Dog implements Animal { @Override public void run() { System.out.println("Dog is running."); } @Override public int eat() { System.out.println("Dog is eating"); return 0; } }

手动创建一个类叫做 P r o x y 0 ,假装这是编译时期生成的并且定义一个 I n v o c a t i o n H a n d l e r 置于 Proxy0,假装这是编译时期生成的 并且定义一个InvocationHandler置于 Proxy0,假装这是编译时期生成的并且定义一个InvocationHandler置于Proxy0内部,用于提供给使用者使用以灵活编写增强逻辑

复制代码
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
40
41
42
43
44
public class $Proxy0 implements Animal { static Method runMethod; static Method eatMethod; static { try { runMethod = Animal.class.getMethod("run"); eatMethod = Animal.class.getMethod("eat"); } catch (NoSuchMethodException e) { throw new RuntimeException(e); } } public $Proxy0(InvocationHandler invocationHandler) { this.invocationHandler = invocationHandler; } private final InvocationHandler invocationHandler; @Override public void run() { try { invocationHandler.invoke(this, runMethod, new Object[0]); } catch (Exception e) { throw new RuntimeException(e); } } @Override public int eat() { Object result; try { result = invocationHandler.invoke(this, eatMethod, new Object[0]); } catch (Exception e) { throw new RuntimeException(e); } return (int) result; } } public interface InvocationHandler { Object invoke(Object proxy, Method method, Object[] args) throws Exception; }

测试代码

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class ProxyJdkSimulation { public static void main(String[] args) { Dog dog = new Dog(); // 模拟Jdk动态代理 Animal proxyDog = new $Proxy0((proxy, method, args1) -> { System.out.println("Before"); Object result = method.invoke(dog, args1); System.out.println("After"); return result; }); proxyDog.eat(); proxyDog.run(); } }

测试结果
在这里插入图片描述

动态生成代理类需要使用到asm的api,这里就不展开了

Jdk对于反射调用的优化

在jdk代理中调用方法使用到的method.invoke()是通过反射实现的,我们知道反射的效率比较低
循环调用method.invoke17次,能够发现前16次都是通过NativeMethodAccessorImpl实现的,第17次通过一个动态生成的GeneratedMethodAccessor2,优化为非反射调用

cglib动态代理

演示

cglib动态代理是通过生成被代理类的子类,子类重写被代理类的方法实现的
因此要求被代理类不能被final修饰,待增强方法也不能被final修饰

cglib动态代理的增强过程有method和methodProxy两个参数,他们可以做到相同的效果,但是method.invoke()是通过反射的方式调用,methodProxy不是通过反射,所以效率要比method高

methodProxy有invoke和invokeSuper两个方法
methodProxy.invoke(targe, args)方法是Spring使用的
methodProxy.invokeSuper(proxy, args)方法不需要被代理类作为参数

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class ProxyCglibTest { public static void main(String[] args) { Dog dog = new Dog(); Dog proxyDog = (Dog) Enhancer.create(Dog.class, (MethodInterceptor) (proxy, method, args1, methodProxy) -> { System.out.println("Before"); // Object result = method.invoke(dog, args1); // Object result = methodProxy.invoke(dog, args1); Object result = methodProxy.invokeSuper(proxy, args1); System.out.println("After"); return result; }); proxyDog.run(); } static class Dog { public void run() { System.out.println("Dog is running."); } } }

控制台输出
在这里插入图片描述

模拟实现cglib动态代理

cglib动态代理其实跟jdk的很相似
不同在于代理类中除了带有增强功能的方法之外,还需要带有原始功能的方法,以用于创建methodProxy对象

复制代码
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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
public class Target { public void save() { System.out.println("save()"); } public void save(int i) { System.out.println("save(int i)"); } public void save(long j) { System.out.println("save(long j)"); } } public class Proxy extends Target { private static Method save0Method; private static Method save1Method; private static Method save2Method; private static MethodProxy save0MethodProxy; private static MethodProxy save1MethodProxy; private static MethodProxy save2MethodProxy; static { try { save0Method = Target.class.getMethod("save"); save1Method = Target.class.getMethod("save", int.class); save2Method = Target.class.getMethod("save", long.class); save0MethodProxy = MethodProxy.create(Target.class, Proxy.class, "()V", "save", "saveSuper"); save1MethodProxy = MethodProxy.create(Target.class, Proxy.class, "(I)V", "save", "saveSuper"); save2MethodProxy = MethodProxy.create(Target.class, Proxy.class, "(J)V", "save", "saveSuper"); } catch (NoSuchMethodException e) { throw new RuntimeException(e); } } private MethodInterceptor methodInterceptor; public Proxy(MethodInterceptor methodInterceptor) { this.methodInterceptor = methodInterceptor; } /** * 原始功能的方法 */ public void saveSuper() { super.save(); } public void saveSuper(int i) { super.save(i); } public void saveSuper(long j) { super.save(j); } /** * 以下是带有增强功能的方法 */ @Override public void save() { try { methodInterceptor.intercept(this, save0Method, null, save0MethodProxy); } catch (Throwable e) { throw new RuntimeException(e); } } @Override public void save(int i) { try { methodInterceptor.intercept(this, save1Method, new Object[]{i}, save1MethodProxy); } catch (Throwable e) { throw new RuntimeException(e); } } @Override public void save(long j) { try { methodInterceptor.intercept(this, save2Method, new Object[]{j}, save2MethodProxy); } catch (Throwable e) { throw new RuntimeException(e); } } }

测试代码

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class CglibProxySimulation { public static void main(String[] args) { Target target = new Target(); Proxy targetProxy = new Proxy((p, method, objects, methodProxy) -> { System.out.println("Before"); // return method.invoke(target, objects); // return methodProxy.invoke(target, objects); return methodProxy.invokeSuper(p, objects); }); targetProxy.save(); targetProxy.save(1); targetProxy.save(2L); } }

cglib如果通过MethodProxy使用非反射的方式调用方法

在MethodInterceptor中如果调用了methodProxy.invoke()或methodProxy.invokeSuper()方法,都会生成一个FastClass子类的动态代理类来使用非反射的方式调用

  • TargetFastClass
    假设methodProxy.invoke()时生成的代理类叫做TargetFastClass,那么这个类内部大概是这样的
复制代码
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
public class TargetFastClass { static Signature signature0 = new Signature("save", "()V"); static Signature signature1 = new Signature("save", "(I)V"); static Signature signature2 = new Signature("save", "(J)V"); public Object invoke(int index, Object target, Object[] args) throws InvocationTargetException { if (index == 0) { ((Target) target).save(); return null; } if (index == 1) { ((Target) target).save((int) args[0]); return null; } if (index == 2) { ((Target) target).save((long) args[0]); return null; } throw new RuntimeException("No such method"); } public int getIndex(Signature signature) { if (signature.equals(signature0)) { return 0; } if (signature.equals(signature1)) { return 1; } if (signature.equals(signature2)) { return 2; } return -1; } }
复制代码
1
2
3
* getIndex方法根据方法的标签判断得到当前方法在内部定义的序号 * 然后fastClass内部会将getIndex拿到的序号传递到方法invoke中,根据这个序号就可以使用非反射的方式调用方法了。
  • ProxyFastClass
    这个类是针对代理类使用的,内部同TargetFastClass逻辑类似,不同之处在于标签记录的是原始功能的方法(即前面的saveSuper()那几个方法),invoke方法也是调用代理类的原始功能方法,因为如果调用增强功能的方法,就会不断进入MethodInterceptor.intercept开始死循环

Spring选择代理

Spring在使用动态代理的时候会在不同情况下使用jdk亦或是cglib的动态代理

准备一个切面(一个通知+一个切点),使用Spring提供的ProxyFactory生成一个代理类,打印查看这个代理类是什么方式生成的
导入依赖

复制代码
1
2
3
4
5
6
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> </dependency>
复制代码
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
40
41
42
43
44
45
46
47
48
public class SpringChooseProxyDemo { public static void main(String[] args) { // 2. 准备一个切点 AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut(); pointcut.setExpression("execution(* foo())"); // 1. 准备一个通知 MethodInterceptor methodInterceptor = invocation -> { System.out.println("Before"); Object result = invocation.proceed(); System.out.println("After"); return result; }; // 3. 将通知和切点整合成一个切面 DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, methodInterceptor); // 4. 通过ProxyFactory生成代理类 ProxyFactory proxyFactory = new ProxyFactory(); Target target = new Target(); proxyFactory.setTarget(target); proxyFactory.addAdvisor(advisor); I1 proxy = (I1) proxyFactory.getProxy(); System.out.println("proxy.class ======== " + proxy.getClass()); proxy.foo(); System.out.println("-----------"); proxy.bar(); } static class Target implements I1 { @Override public void foo() { System.out.println("foo"); } @Override public void bar() { System.out.println("bar"); } } interface I1 { void foo(); void bar(); } }

执行结果
在这里插入图片描述可以看出这里是通过cglib实现的动态代理

Spring选择动态代理规则

ProxyFactory有一个属性叫做proxyTargetClass,默认情况下proxyTargetClass = false;

  • 当proxyTargetClass = false, 若目标类实现了接口,使用jdk动态代理
  • 当proxyTargetClass = false, 若目标类没有实现接口,使用cglib动态代理
  • 当proxyTargetClass = true,则总是使用cglib动态代理

但是前面并没有把proxyTargetClass改成true,并且Target类也实现了接口I1,为什么还是使用了cglib动态代理呢,那是因为我们需要主动告诉proxyFactory目标类实现了什么接口
proxyFactory.setInterfaces();

切点匹配

前面提到的AspectJExpressionPointcut能够匹配切点,其实是实现了MethodMatcher接口的matches方法,根据matches的返回值是否为true来判断是否匹配上
现在就可以通过实现一个MethodMatcher接口,定义@Transactional注解的切点规则

复制代码
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
40
41
42
43
44
45
46
47
48
public class MethodMatch{ public static void main(String[] args) throws NoSuchMethodException { StaticMethodMatcher methodMatcher = new StaticMethodMatcher() { @Override public boolean matches(Method method, Class<?> targetClass) { MergedAnnotations annotations = MergedAnnotations.from(method); if (annotations.isPresent(Transactional.class)) { return true; } annotations = MergedAnnotations.from(targetClass, MergedAnnotations.SearchStrategy.TYPE_HIERARCHY); return annotations.isPresent(Transactional.class); } }; System.out.println(methodMatcher.matches(Target1.class.getMethod("foo"), Target1.class)); System.out.println(methodMatcher.matches(Target1.class.getMethod("bar"), Target1.class)); System.out.println(methodMatcher.matches(Target2.class.getMethod("foo"), Target2.class)); System.out.println(methodMatcher.matches(Target3.class.getMethod("foo"), Target3.class)); } static class Target1 { @Transactional public void foo() { } public void bar() { } } @Transactional static class Target2 { public void foo() { } } @Transactional interface T { } static class Target3 implements T { public void foo() { } } }

控制台输出
在这里插入图片描述

从@Aspect到Advisor

AnnotationAwareAspectJAutoProxyCreator

AnnotationAwareAspectJAutoProxyCreator是一个Bean后处理器,Spring容器能够获取到配置的切面信息,跟它的存在息息相关
AnnotationAwareAspectJAutoProxyCreator的发挥作用的时机
类:创建 -> (发挥作用)依赖注入 -> 初始化(发挥作用)

方法findEligibleAdvisors

AnnotationAwareAspectJAutoProxyCreator中有两个很重要的方法,一个是它的父类AbstractAdvisorAutoProxyCreator的findEligibleAdvisors,这个方法可以查询容器中有哪些切面是与指定的类有关系的。
下面模拟使用AnnotationAwareAspectJAutoProxyCreator,通过反射的方式调用findEligibleAdvisors方法,查看哪些切面与Target1或Target2有关系。

复制代码
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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
public class Demo { public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { GenericApplicationContext context = new GenericApplicationContext(); context.registerBean("aspect1", Aspect1.class); context.registerBean("config", Config.class); context.registerBean(ConfigurationClassPostProcessor.class); context.registerBean(AnnotationAwareAspectJAutoProxyCreator.class); context.refresh(); AnnotationAwareAspectJAutoProxyCreator autoProxyCreator = context.getBean(AnnotationAwareAspectJAutoProxyCreator.class); Method findEligibleAdvisors = AbstractAdvisorAutoProxyCreator.class.getDeclaredMethod("findEligibleAdvisors", Class.class, String.class); findEligibleAdvisors.setAccessible(true); List<Advisor> advisors = (List<Advisor>) findEligibleAdvisors.invoke(autoProxyCreator, Target1.class, "target1"); advisors.forEach(System.out::println); } static class Target1 { public void foo() { System.out.println("Target1 foo"); } } static class Target2 { public void bar() { System.out.println("Target2 bar"); } } @Aspect static class Aspect1 { @Before("execution(* foo())") public void before() { System.out.println("Aspect1 before"); } @After("execution(* foo())") public void after() { System.out.println("Aspect1 after"); } } @Configuration static class Config { @Bean public DefaultPointcutAdvisor pointcutAdvisor(MethodInterceptor methodInterceptor) { AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut(); pointcut.setExpression("execution(* foo())"); return new DefaultPointcutAdvisor(pointcut, methodInterceptor); } @Bean public MethodInterceptor methodInterceptor() { return invocation -> { System.out.println("Config before"); return invocation.proceed(); }; } } }

当findEligibleAdvisors的目标是Target1的时候,控制台打印

复制代码
1
2
3
4
5
org.springframework.aop.interceptor.ExposeInvocationInterceptor.ADVISOR org.springframework.aop.support.DefaultPointcutAdvisor: pointcut [AspectJExpressionPointcut: () execution(* foo())]; advice [com.ysatnaf.spring_learning.aop.from_aspect_to_advisor.Demo$Config$$Lambda$56/580871917@7d8704ef] InstantiationModelAwarePointcutAdvisor: expression [execution(* foo())]; advice method [public void com.ysatnaf.spring_learning.aop.from_aspect_to_advisor.Demo$Aspect1.before()]; perClauseKind=SINGLETON InstantiationModelAwarePointcutAdvisor: expression [execution(* foo())]; advice method [public void com.ysatnaf.spring_learning.aop.from_aspect_to_advisor.Demo$Aspect1.after()]; perClauseKind=SINGLETON

是Target2的时候,控制台打印为空

方法wrapIfNecessary

该方法是AnnotationAwareAspectJAutoProxyCreator的父类AbstractAutoProxyCreator的方法。
该方法内部会调用方法findEligibleAdvisors,根据返回的advisor数量,来判断是否需要为目标对象生成代理对象。如果没有切面与目标对象相关,那么就返回目标对象,如果有切面,那么生成代理类

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { GenericApplicationContext context = new GenericApplicationContext(); context.registerBean("aspect1", Aspect1.class); context.registerBean("config", Config.class); context.registerBean(ConfigurationClassPostProcessor.class); context.registerBean(AnnotationAwareAspectJAutoProxyCreator.class); context.refresh(); AnnotationAwareAspectJAutoProxyCreator autoProxyCreator = context.getBean(AnnotationAwareAspectJAutoProxyCreator.class); Method wrapIfNecessary = AbstractAutoProxyCreator.class.getDeclaredMethod("wrapIfNecessary", Object.class, String.class, Object.class); wrapIfNecessary.setAccessible(true); Object target1 = wrapIfNecessary.invoke(autoProxyCreator, new Target1(), "target1", "target1"); Object target2 = wrapIfNecessary.invoke(autoProxyCreator, new Target2(), "target2", "target2"); System.out.println("target1:" + target1.getClass()); System.out.println("target2:" + target2.getClass()); }

输出台打印,在这里插入图片描述
因为Target1有被切面切到,所以生成了代理类,而Target2没有被切面切到,所以没有生成代理类。

代理对象的创建时机

正常执行下来代理对象是在对象初始化之后才被创建
但是如果有循环依赖,就会在依赖注入之前创建
假设A依赖B,B又依赖A,在A的创建过程中 依赖注入B的时候会先去创建对象B,但是因为B也依赖了A,这时候就会先把A的代理对象创建出来放到二级缓存中,然后B创建完成,A才能拿到B的对象进行依赖注入
也就是说依赖注入的是代理对象
注意:依赖注入和初始化过程不应该被增强,仍应该被施加于原始对象上

@Order

可以通过将@Order注解标注在高级切面(被@Aspect标注的类)上控制切面的顺序
低级切面是通过setOrder()进行控制
order数值越大优先级越低,默认是最大的

从高级切面到低级切面

在查找切面的过程中,如果遇到了高级切面,那么会通过解析把高级切面转换成低级切面,最后放在一个List里
以解析@Before为例
在这里插入图片描述

将切面统一转换成环绕通知

拿到所有切面之后会将他们都统一转换成环绕通知MethodInvocation
在这里插入图片描述
打印,查看转换前后的类型
在这里插入图片描述
可以看到AspectJMethodBeforeAdvice转成了MethodBeforeAdviceInterceptor
AspectJAfterReturningAdvice转化成AfterReturningAdviceInterceptor
AspectJAroundAdvice没有转换,因为它本身就是环绕通知
转换过程采用了适配器模式

调用执行链

调用链List的执行过程是一个执行链
在最前面需要添加一个环绕通知叫做ExposeInvocationInterceptor,它的作用就是把一个MethodInvocation放入当前线程以供后续的切面使用。
通过ReflectiveMethodInvocation执行

最后执行每一个环绕通知,其实是采用了递归的方式

  1. before1
  2. before2
  3. target.method.invoke
  4. after2
  5. after1
  • 模拟调用链
    在这里插入图片描述

总结静态通知调用过程

  1. 分析执行类是否需要生成代理类
  2. 获取所有的切面
  3. 把高级切面转换成低级切面
  4. 把所有的通知转换成环绕通知
  5. 调用执行链,在这之前需要把一个MethodInvocation放入当前线程

动态通知调用

动态通知调用指的是需要传递参数的通知,没有参数的通知因为可以直接执行增强逻辑所以叫做静态通知,相对于没有参数的通知来说,需要记录切点的信息以便于后续解析参数
下图中就是一个动态通知
在这里插入图片描述
动态通知调用过程和静态的基本一样
唯一不同的地方就在于factory.getInterceptorsAndDynamicInterceptionAdvice(转换成环绕通知)时,会在List中新增一个叫做InterceptorAndDynamicMethodMatcher,这个动作其实在方法名中DynamicInterception体现了。
在这里插入图片描述
InterceptorAndDynamicMethodMatcher包含了两个成员变量,一个是环绕通知,一个是MethodMatcher
在这里插入图片描述
通过反射的方式打印InterceptorAndDynamicMethodMatcher内部包含的信息
在这里插入图片描述
可以看到包含了切点信息和通知类型,切点信息就可以用于解析动态参数

最后

以上就是重要啤酒最近收集整理的关于Spring5底层原理 学习笔记(二)AOP篇AOP实现之ajc编译器AOP实现之agent类加载AOP实现之动态代理Spring选择代理从@Aspect到Advisor的全部内容,更多相关Spring5底层原理内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(75)

评论列表共有 0 条评论

立即
投稿
返回
顶部