- 👏作者简介:大家好,我是爱吃芝士的土豆倪,24届校招生Java选手,很高兴认识大家
- 📕系列专栏:Spring原理、JUC原理、Kafka原理、分布式技术原理、数据库技术
- 🔥如果感觉博主的文章还不错的话,请👍三连支持👍一下博主哦
- 🍂博主正在努力完成2023计划中:源码溯源,一探究竟
- 📝联系方式:nhs19990716,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬👀
文章目录
- 切点匹配
- 根据名字进行匹配
- 根据注解进行匹配
- @Transactional注解
- 总结
- 从 @Aspect 到 Advisor
- 注册bean
- 代理创建时机
- 总结
- 循环依赖
- 为什么要三级缓存?⼆级不⾏吗?
- 切面加载顺序
- 高级切面转换过程
切点匹配
根据名字进行匹配
static class T1 {@Transactionalpublic void foo() {}public void bar() {}}
AspectJExpressionPointcut pt1 = new AspectJExpressionPointcut();pt1.setExpression("execution(* bar())");System.out.println(pt1.matches(T1.class.getMethod("foo"), T1.class));// falseSystem.out.println(pt1.matches(T1.class.getMethod("bar"), T1.class));// true
根据注解进行匹配
AspectJExpressionPointcut pt2 = new AspectJExpressionPointcut();pt2.setExpression("@annotation(org.springframework.transaction.annotation.Transactional)");System.out.println(pt2.matches(T1.class.getMethod("foo"), T1.class));// trueSystem.out.println(pt2.matches(T1.class.getMethod("bar"), T1.class));// false
@Transactional注解
前面提到的,无论是execution 还是 annotation只能去匹配方法上的,都不能解决如特定情况
static class T1 {@Transactionalpublic void foo() {}public void bar() {}}@Transactionalstatic class T2 {public void foo() {}}@Transactionalinterface I3 {void foo();}static class T3 implements I3 {public void foo() {}}
因此需要换一个别的实现
StaticMethodMatcherPointcut pt3 = new StaticMethodMatcherPointcut() {@Overridepublic boolean matches(Method method, Class<?> targetClass) {// 检查方法上是否加了 Transactional 注解MergedAnnotations annotations = MergedAnnotations.from(method);if (annotations.isPresent(Transactional.class)) {return true;}// 查看类上是否加了 Transactional 注解annotations = MergedAnnotations.from(targetClass, MergedAnnotations.SearchStrategy.TYPE_HIERARCHY);// 设置查询搜索策略if (annotations.isPresent(Transactional.class)) {return true;}return false;}};System.out.println(pt3.matches(T1.class.getMethod("foo"), T1.class));System.out.println(pt3.matches(T1.class.getMethod("bar"), T1.class));System.out.println(pt3.matches(T2.class.getMethod("foo"), T2.class));System.out.println(pt3.matches(T3.class.getMethod("foo"), T3.class));
总结
底层切点实现是如何匹配的: 调用了 aspectj 的匹配方法
如果aspectj不能满足的话,那么就使用MethodMatcher 接口, 用来执行方法的匹配,通过这个我们完全可以自行的去定义匹配规则。
从 @Aspect 到 Advisor
static class Target1 {public void foo() {System.out.println("target1 foo");}}static class Target2 {public void bar() {System.out.println("target2 bar");}}
高级切面类@Aspect
@Aspect // 高级切面类@Order(1)static class Aspect1 {@Before("execution(* foo())")public void before1() {System.out.println("aspect1 before1...");}@After("execution(* foo())")public void before2() {System.out.println("aspect1 before2...");}}
低级切面类
@Configurationstatic class Config {@Bean // 低级切面public Advisor advisor3(MethodInterceptor advice3) {AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();pointcut.setExpression("execution(* foo())");DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advice3);return advisor;}@Beanpublic MethodInterceptor advice3() {return invocation -> {System.out.println("advice3 before...");Object result = invocation.proceed();System.out.println("advice3 after...");return result;};}}
注册bean
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有两个比较重要的方法
第一个重要方法 findEligibleAdvisors 找到有【资格】的 Advisors
a. 有【资格】的 Advisor 一部分是低级的, 可以由自己编写, 如下例中的 advisor3
b. 有【资格】的 Advisor 另一部分是高级的, 由本章的主角解析 @Aspect 后获得
AnnotationAwareAspectJAutoProxyCreator creator = context.getBean(AnnotationAwareAspectJAutoProxyCreator.class);List<Advisor> advisors = creator.findEligibleAdvisors(Target1.class, "target1");for (Advisor advisor : advisors) {System.out.println(advisor);}
返回三个
InstantiationModelAwarePointcutAdvisor: expression [execution(* foo())]; advice method [public void org.springframework.aop.framework.autoproxy.A17$Aspect1.before1()];
perClauseKind=SINGLETONInstantiationModelAwarePointcutAdvisor: expression [execution(* foo())]; advice method [public void org.springframework.aop.framework.autoproxy.A17$Aspect1.before2()];
perClauseKind=SINGLETONorg.springframework.aop.support.DefaultPointcutAdvisor: pointcut [AspectJExpressionPointcut: () execution(* foo())]; advice [org.springframework.aop.framework.autoproxy.A17$Config$$Lambda$117/0x00000008001ff630@5c530d1e]
其实也就是将高级切面转换成低级切面
第二个重要方法 wrapIfNecessary
a. 它内部调用 findEligibleAdvisors, 只要返回集合不空, 则表示需要创建代理
Object o1 = creator.wrapIfNecessary(new Target1(), "target1", "target1");System.out.println(o1.getClass());Object o2 = creator.wrapIfNecessary(new Target2(), "target2", "target2");System.out.println(o2.getClass());((Target1) o1).foo();
当最后执行的时候,就能看到增强的效果
代理创建时机
GenericApplicationContext context = new GenericApplicationContext();context.registerBean(ConfigurationClassPostProcessor.class);context.registerBean(Config.class);context.refresh();context.close();
@Configurationstatic class Config {@Bean // 解析 @Aspect、产生代理public AnnotationAwareAspectJAutoProxyCreator annotationAwareAspectJAutoProxyCreator() {return new AnnotationAwareAspectJAutoProxyCreator();}@Bean // 解析 @Autowiredpublic AutowiredAnnotationBeanPostProcessor autowiredAnnotationBeanPostProcessor() {return new AutowiredAnnotationBeanPostProcessor();}@Bean // 解析 @PostConstructpublic CommonAnnotationBeanPostProcessor commonAnnotationBeanPostProcessor() {return new CommonAnnotationBeanPostProcessor();}@Beanpublic Advisor advisor(MethodInterceptor advice) {AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();pointcut.setExpression("execution(* foo())");return new DefaultPointcutAdvisor(pointcut, advice);}@Beanpublic MethodInterceptor advice() {return (MethodInvocation invocation) -> {System.out.println("before...");return invocation.proceed();};}@Beanpublic Bean1 bean1() {return new Bean1();}@Beanpublic Bean2 bean2() {return new Bean2();}}static class Bean1 {public void foo() {}public Bean1() {System.out.println("Bean1()");}@PostConstruct public void init() {System.out.println("Bean1 init()");}}static class Bean2 {public Bean2() {System.out.println("Bean2()");}@Autowired public void setBean1(Bean1 bean1) {System.out.println("Bean2 setBean1(bean1) class is: " + bean1.getClass());}@PostConstruct public void init() {System.out.println("Bean2 init()");}}
当无循环依赖的时候,结果如下:
Bean1()
Bean1 init()
[TRACE] 20:58:25.379 [main] o.s.a.a.a.AnnotationAwareAspectJAutoProxyCreator - Creating implicit proxy for bean 'bean1' with 0 common interceptors and 2 specific interceptors
Bean2()
Bean2 setBean1(bean1) class is: class org.springframework.aop.framework.autoproxy.A17_1$Bean1$$EnhancerBySpringCGLIB$$393b91a3
Bean2 init()
bean1是在初始化完成后才进行创建代理的,而bean2直接使用bean1的代理类
但是当存在循环依赖的时候
static class Bean1 {public void foo() {}public Bean1() {System.out.println("Bean1()");}@Autowired public void setBean2(Bean2 bean2) {System.out.println("Bean1 setBean2(bean2) class is: " + bean2.getClass());}@PostConstruct public void init() {System.out.println("Bean1 init()");}}static class Bean2 {public Bean2() {System.out.println("Bean2()");}@Autowired public void setBean1(Bean1 bean1) {System.out.println("Bean2 setBean1(bean1) class is: " + bean1.getClass());}@PostConstruct public void init() {System.out.println("Bean2 init()");}}
对应的结果如下:
Bean1()Bean2()[TRACE] 21:00:32.516 [main] o.s.a.a.a.AnnotationAwareAspectJAutoProxyCreator - Creating implicit proxy for bean 'bean1' with 0 common interceptors and 2 specific interceptors Bean2 setBean1(bean1) class is: class org.springframework.aop.framework.autoproxy.A17_1$Bean1$$EnhancerBySpringCGLIB$$b7a50cf3Bean2 init()Bean1 setBean2(bean2) class is: class org.springframework.aop.framework.autoproxy.A17_1$Bean2Bean1 init()
首先也是调用了bean1的构造,接下来按照bean1的流程来讲需要setbean2,但是此时没有,按照bean1的流程来讲,需要暂停进入bean2的流程。在bean2这个流程中,但是问题来了,bean2的依赖注入需要bean1,当注入了一个代理的bean1时,因此bean1的代理就会在bean2的依赖注入之前就被创建出来。因此它的代理对象提前被创建出来了。
总结
代理的创建时间有两个
一个是无循环依赖,那么就是 创建 -> 依赖注入 -> 初始化 这三个流程中的初始化之后创建代理。
如果有循环依赖,那么就是 创建 -> 依赖注入 -> 初始化 这三个流程中 依赖注入之前创建代理,并且将其存入到二级缓存当中。
循环依赖
我们都知道,单例Bean初始化完成,要经历三步:
注入就发生在第二步,属性赋值,结合这个过程,Spring 通过三级缓存解决了循环依赖:
- 一级缓存 : Map singletonObjects ,单例池,用于保存实例化、属性赋值(注入)、初始化完成的 bean 实例
- 二级缓存 : Map earlySingletonObjects ,早期曝光对象,用于保存实例化完成的 bean 实例
- 三级缓存 : Map> singletonFactories ,早期曝光对象工厂,用于保存 bean 创建工厂,以便于后面扩展有机会创建代理对象。
我们来看一下三级缓存解决循环依赖的过程:
当 A、B 两个类发生循环依赖时:
A实例的初始化过程:
创建A实例,实例化的时候把A对象⼯⼚放⼊三级缓存,表示A开始实例化了,虽然我这个对象还不完整,但是先曝光出来让大家知道
A注⼊属性时,发现依赖B,此时B还没有被创建出来,所以去实例化B
同样,B注⼊属性时发现依赖A,它就会从缓存里找A对象。依次从⼀级到三级缓存查询A,从三级缓存通过对象⼯⼚拿到A,发现A虽然不太完善,但是存在,把A放⼊⼆级缓存,同时删除三级缓存中的A,此时,B已经实例化并且初始化完成,把B放入⼀级缓存。
接着A继续属性赋值,顺利从⼀级缓存拿到实例化且初始化完成的B对象,A对象创建也完成,删除⼆级缓存中的A,同时把A放⼊⼀级缓存
最后,⼀级缓存中保存着实例化、初始化都完成的A、B对象
所以,我们就知道为什么Spring能解决setter注入的循环依赖了,因为实例化和属性赋值是分开的,所以里面有操作的空间。如果都是构造器注入的话,那么都得在实例化这一步完成注入,所以自然是无法支持了。
为什么要三级缓存?⼆级不⾏吗?
不行,主要是为了⽣成代理对象。如果是没有代理的情况下,使用二级缓存解决循环依赖也是OK的。但是如果存在代理,三级没有问题,二级就不行了。
因为三级缓存中放的是⽣成具体对象的匿名内部类,获取Object的时候,它可以⽣成代理对象,也可以返回普通对象。使⽤三级缓存主要是为了保证不管什么时候使⽤的都是⼀个对象。
假设只有⼆级缓存的情况,往⼆级缓存中放的显示⼀个普通的Bean对象,Bean初始化过程中,通过 BeanPostProcessor 去⽣成代理对象之后,覆盖掉⼆级缓存中的普通Bean对象,那么可能就导致取到的Bean对象不一致了。
切面加载顺序
还是拿前面的例子举例
static class Target1 {public void foo() {System.out.println("target1 foo");}}static class Target2 {public void bar() {System.out.println("target2 bar");}}@Aspect // 高级切面类@Order(1)static class Aspect1 {@Before("execution(* foo())")public void before1() {System.out.println("aspect1 before1...");}@After("execution(* foo())")public void before2() {System.out.println("aspect1 before2...");}}@Configurationstatic class Config {@Bean // 低级切面public Advisor advisor3(MethodInterceptor advice3) {AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();pointcut.setExpression("execution(* foo())");DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advice3);return advisor;}@Beanpublic MethodInterceptor advice3() {return invocation -> {System.out.println("advice3 before...");Object result = invocation.proceed();System.out.println("advice3 after...");return result;};}}Object o1 = creator.wrapIfNecessary(new Target1(), "target1", "target1");System.out.println(o1.getClass());Object o2 = creator.wrapIfNecessary(new Target2(), "target2", "target2");System.out.println(o2.getClass());((Target1) o1).foo();
结果:
advice3 before...
aspect1 before1...
target1 foo
aspect1 before2...
advice3 after...
可以看到低级切面类先被执行了。
可以通过设置setOrder 或者Order()注解来修改执行顺序
高级切面转换过程
static class Aspect {@Before("execution(* foo())")public void before1() {System.out.println("before1");}@Before("execution(* foo())")public void before2() {System.out.println("before2");}public void after() {System.out.println("after");}public void afterReturning() {System.out.println("afterReturning");}public void afterThrowing() {System.out.println("afterThrowing");}public Object around(ProceedingJoinPoint pjp) throws Throwable {try {System.out.println("around...before");return pjp.proceed();} finally {System.out.println("around...after");}}}static class Target {public void foo() {System.out.println("target foo");}}
转换过程
@SuppressWarnings("all")public static void main(String[] args) throws Throwable {AspectInstanceFactory factory = new SingletonAspectInstanceFactory(new Aspect());// 高级切面转低级切面类List<Advisor> list = new ArrayList<>();for (Method method : Aspect.class.getDeclaredMethods()) {if (method.isAnnotationPresent(Before.class)) {// 解析切点String expression = method.getAnnotation(Before.class).value();AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();pointcut.setExpression(expression);// 通知类// 根据注解的不同,选择不同的通知实现AspectJMethodBeforeAdvice advice = new AspectJMethodBeforeAdvice(method, pointcut, factory);// 切面Advisor advisor = new DefaultPointcutAdvisor(pointcut, advice);list.add(advisor);}}for (Advisor advisor : list) {System.out.println(advisor);}/*@Before 前置通知会被转换为下面原始的 AspectJMethodBeforeAdvice 形式, 该对象包含了如下信息a. 通知代码从哪儿来b. 切点是什么(这里为啥要切点, 后面解释)c. 通知对象如何创建, 本例共用同一个 Aspect 对象类似的通知还有1. AspectJAroundAdvice (环绕通知)2. AspectJAfterReturningAdvice3. AspectJAfterThrowingAdvice4. AspectJAfterAdvice (环绕通知)*/}
最终解析出来的结果
org.springframework.aop.support.DefaultPointcutAdvisor: pointcut [AspectJExpressionPointcut: () execution(* foo())]; advice [org.springframework.aop.aspectj.AspectJMethodBeforeAdvice: advice method [public void org.springframework.aop.framework.autoproxy.A17_2$Aspect.before2()]; aspect name '']org.springframework.aop.support.DefaultPointcutAdvisor: pointcut [AspectJExpressionPointcut: () execution(* foo())]; advice [org.springframework.aop.aspectj.AspectJMethodBeforeAdvice: advice method [public void org.springframework.aop.framework.autoproxy.A17_2$Aspect.before1()]; aspect name '']