Spring AOP的实现,全网最细致的讲解

如果你正打算深入学习Spring,但是不知从何学起,那么我强烈推荐你可以按照这个系列做一遍。本系列将Spring框架的各个部分从它庞杂的代码体系中抽取出来,然后对每一个部分进行讲解,并最终搭建成简易版Spring。我以人格保证:如果你可以坚持做下来,那么你对Spring这块的知识就基本都掌握清楚了! 附上该系列地址:https://blog.csdn.net/zhang_qing_yun/article/details/120084497
在开始阅读本文之前,我提几个问题,如果你能全部答上来,那么请绕行!否则,请带着问题在本文中寻找答案:
1.众所周知,Spring AOP是基于Spring IOC来实现的,但是是基于IOC的哪个扩展点完成的?
2.Spring处理注解版AOP的入口是哪里?
3.AOP信息如何、何时加载到容器?
4.JDK动态代理和CGLIB动态代理方式的选择?
5.如何判断是否为一个Bean生成代理对象?
6.你是否了解通过职责链模式来完成切面的织入?

AOP

本文需要用到动态代理的知识,具体可以移步:https://blog.csdn.net/zhang_qing_yun/article/details/119672574

作用

在不修改原来的代码的前提下增加新的功能,它是对OOP的补充,它能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等系统层面的、OOP无法处理的需求)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的拓展和维护。

相关概念

连接点:类里的哪些方法可以被增强,这些方法称为连接点。
切入点:实际被真正增强的方法称为切入点。
通知:实际增强的逻辑部分称为通知或增强。
包含:前置通知、后置通知、环绕通知、异常通知、最终通知
切面:是一个动作,即把通知应用到切入点的过程。

常用注解

注解

Spring AOP实现

要想启用Spring AOP,需要在配置类上加注解@EnableAspectJAutoProxy,这个注解通过@Import注解向容器引入了AspectJAutoProxyRegistrar类,然后通过这个类的registerBeanDefinitions方法往Spring容器中注入了一个AnnotationAwareAspectJAutoProxyCreator,它继承了AbstractAutoProxyCreator,实现了BeanPostProcessor接口。值得注意的是@EnableAspectJAutoProxy有一个属性proxyTargetClass,如果将属性值设置为true的话,则在生成代理对象的时候会强制使用CGLIB的方式。

然后在对象的创建过程中,执行完初始化方法后会去遍历所有的BeanPostProcessor,执行它们的后置处理方法。所以会在这时去调用AbstractAutoProxyCreator的postProcessAfterInitialization方法,然后又会去调用wrapIfNecessary方法,作用是通过AOP的配置信息判断是否需要为该Bean生成代理对象。

在wrapIfNecessary方法中会去调用shouldSkip方法(这里不是第一次调用,首次调用是在postProcessBeforeInstantiation方法中),这个方法很重要,如果是第一次调用该方法,则会去将我们配置在切面@Aspect中的信息解析出来。具体的做法是,循环遍历注册在容器中的Bean,判断它是不是一个切面(通过反射判断是否加了@Aspect注解),如果是则会去遍历这个加了@Aspect注解的Bean的所有方法,然后为加了@Before、@After等注解的方法生成一个Advisor。然后会将这次解析的结果缓存起来,具体来说就是将所有加了@Aspect的Bean的beanName缓存起来,然后将它们中每一个Bean所解析出来的所有Advisor缓存起来(放在一个key为加了@Aspect的beanName,value为List的Map中)。这样的话,下次想要获取所有的Advisor时只需要遍历所有加了的@Aspect的beanName,然后从Map中获取所有的Advisor即可。

那么在有了所有的Advisor信息的前提下,wrapIfNecessary方法是如何判断是否要为该bean生成代理对象呢?具体的做法就是遍历所有的Advisor,然后收集切点是该Bean中方法的所有Advisor,然后会对Advisor进行排序操作,前置通知、后置通知等通知的顺序就是在这里确定好的。如果收集到的结果不为空,则为该Bean生成代理对象,并且在创建代理对象的时候会将收集并且已经排好序到的该Bean所对应的Advisor信息传进去

创建代理对象的过程使用了工厂模式,调用工厂方法getProxy就可以获取到代理对象,通过工厂模式就屏蔽了多种创建代理对象的方式,将选择哪种方式的决定权交给了工厂对象,使得不再需要去了解各种方式的细节(其实,在Spring中只有两种方式:JDK动态代理和CGLIB动态代理)。在工厂内部会去选择代理的方式,如果该Bean没有实现接口或者设置了proxyTargetClass属性为true的话就会使用CGLIB动态代理的方式来生成代理类,否则就会使用JDK动态代理的方式。

通过代理类调用方法的过程应用了职责链模式。无论是JDK动态代理还是CGLIB动态代理,调用方法时都会被拦截下来,然后先去生成该方法的拦截链,具体来说就是就是去遍历该Bean对应的所有的Advisor,然后判断该Advisor的切点是不是该调用方法,如果是则将其添加到一个List中,遍历结束后该List就是该方法所对应的拦截链,然后将生成的该方法所对应的拦截链缓存起来。如果拦截链为空,则说明没有Advisor与该方法匹配,即切点不是该方法,所以会直接通过反射的方式去调用目标对象的目标方法。如果拦截链不为空,则会去调用ReflectiveMethodInvocation的proceed方法,在这个方法中我们会去遍历拦截链,然后去调用它们的invoke方法,在invoke方法中我们又会去在合适的位置调用proceed方法,所以最终我们会通过递归调用proceed方法的方式来将拦截链执行完。

举个拦截器链执行过程的例子,例如拦截链中有两个拦截器,一个是前置通知@Before所生成的拦截器,一个是后置通知@After所生成的拦截器(值得注意的是在给拦截器排序时,拦截器的顺序正好和执行顺序相反,@AfterReturning > @After > @Before)。所以会先去调用@After的invoke方法:

public Object invoke(MethodInvocation mi) throws Throwable {
   try {
      return mi.proceed();
   }
   finally {
      invokeAdviceMethod(getJoinPointMatch(), null, null);
   }
}

可以发现,会先去递归地调用proceed()方法,然后才会去调用增强方法。这次递归调用proceed()方法时会去调用@Before的invoke方法:

public Object invoke(MethodInvocation mi) throws Throwable {
   this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
   return mi.proceed();
}

可以发现,这时会先去执行前置通知的增强方法,然后再去递归调用proceed()方法。这次调用proceed()时发现拦截器链已经执行完毕,所以就会去通过反射执行目标方法。这时@Before的invoke方法的逻辑执行完毕,就会去执行@After的增强方法。所以最终的执行顺序就是:前置通知的增强方法 —> 目标方法 —> 后置通知的增强方法。

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值