切面(advisor):切点和增强(引介)组成,它既包括了横切逻辑的定义,也包括了连接点的定义,Spring AOP就是负责实施切面的框架,它将切面所定义的横切逻辑织入到切面所指定的连接点中。 advisor: pointCut advice
简单的理解--》一类功能的增强:比如 事务切面,缓存切面,日志切面
切点(pointCut ):每个程序类都拥有多个连接点,如一个拥有两个方法的类,这两个方法都是连接点,即连接点是程序类中客观存在的事物。AOP通过“切点”定位特定的连接点。连接点相当于数据库中的记录,而切点相当于查询条件。切点和连接点不是一对一的关系,一个切点可以匹配多个连接点。在Spring中,切点通过org.springframework.aop.Pointcut接口进行描述,它使用类和方法作为连接点的查询条件,Spring AOP的规则解析引擎负责切点所设定的查询条件,找到对应的连接点。其实确切地说,不能称之为查询连接点,因为连接点是方法执行前、执行后等包括方位信息的具体程序执行点,而切点只定位到某个方法上,所以如果希望定位到具体连接点上,还需要提供方位信息。
简单的理解--》 一类Joinpoint 的集合
增强(advice ):增强是织入到目标类连接点上的一段程序代码,在Spring中,增强除用于描述一段程序代码外,还拥有另一个和连接点相关的信息,这便是执行点的方位。结合执行点方位信息和切点信息,我们就可以找到特定的连接点。
简单的理解--》执行的切面的方法
连接点(Joinpoint): 程序执行的某个特定位置:如类开始初始化前、类初始化后、类某个方法调用前、调用后、方法抛出异常后。一个类或一段程序代码拥有一些具有边界性质的特定点,这些点中的特定点就称为“连接点”。Spring仅支持方法的连接点,即仅能在方法调用前、方法调用后、方法抛出异常时以及方法调用前后这些程序执行点织入增强。连接点由两个信息确定:第一是用方法表示的程序执行点;第二是用相对点表示的方位。
简单的理解--》拦截到的具体某个方法
代理(Proxy) :一个类被AOP织入增强后,就产出了一个结果类,它是融合了原类和增强逻辑的代理类。根据不同的代理方式,代理类既可能是和原类具有相同接口的类,也可能就是原类的子类,所以我们可以采用调用原类相同的方式调用代理类。
spring 大部分功能的应用都是会封装成对象的
IOC 的bean实例封装成BeanDifinition类型
类的相关信息封装成metaData类型
AOP通知(增强)功能信息封装成 MethodInterceptor 类型
spring AOP 是在bean实例化和注入之后做的,如果有切面则会生成代理对象,代理对象会取代之前实例化的bean。
的实现方式有两种:
1.完全基于注解
2.基于xml 配置,就是我们spring 5以前常用的配置文件声明的方式,在前面spring IOC xml bean的收集的过程中封装的
下面我们重点解读基于注解的AOP入口。
基于注解的AOP入口
例子信息如下图:
1.配置AOP的入口基本信息
2. 进入@EnableAspectJAutoProxy 注解类里面
3. 进入AspectJAutoProxyRegistrar类里面
4. 进入AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry)类的方法里面
注册入口类:AnnotationAwareAspectJAutoProxyCreator,把入口类封装成BeanDefition对象,并注册
注册完BeanDefinition,为了是在Bean实例化和注册之后是使用,在这里就开始埋点了。
前面已经注册了AOP注解的处理类,那什么时候开始使用呢?
AOP 的封装工作,是在Bean实例化和注入完成之后进行的,在类 AbstractAutowireCapableBeanFactory 的doCreateBean()方法中
5. 进入initializeBean()方法中,AOP的入口就在这个方法,如果存在切面,则会返回一个代理实例
6. BeanPostProcessor 处理器调用 postProcessAfterInitialization()方法,并关注AbstractAutoProxyCreator类
7.切面的封装方法,完成封装之后生成代理对象方法,这一步很重要
1)切面封装的方法调用
2)如果有切面,则生成切面的代理对象,并返回
8. 进入切面的封装过程 getAdvicesAndAdvisorsForBean() 方法
1)查找合格的切面信息
9. 进入findEligibleAdvisors() 查找合格切面的方法中
1)找到候选的切面,其实就是寻找@Aspect注解的过程,把工程中所有的这个注解封装成Advisor返回
2)判断候选切面是否用在当前BeanClass,就是一个切面和BeanClass匹配的过程
3)对BeanClass 上的切面进行排序,毕竟切面的增强功能也是有先后的
10. 进入当前bean的@Aspect 的收集,和封装方法
11.进入 AnnotationAwareAspectJAutoProxyCreator的 findCandidateAdvisors()方法中
1)创建候选切面 buildAspectJAdvisors
12.进入buildAspectJAdvisors()方法
1)先获取所有的BeanDefinition
2)循环判断是否有@Aspect注解信息
3)创建获取有@Aspect注解类的实例工厂,负责获取有@Aspect注解类的实例
4)创建切面advisor对象
13.进入getAdvisors()创建切面的过程方法
1) 从工厂中获取有@Aspect注解的类Class
2) 从工厂中获取有@Aspect注解的类的名称
3) 这里循环没有@Pointcut注解的方法
4) 开始封装切面信息Advisor
14. 进入getAdvisor()方法, 这里有重要的两步:
1)获取pointCut对象,重要的是从注解中获取表达式
2)创捷切面类,这里才是真正的创建
15. 获取pointCut对象,最重要的是从注解中获取表达式,封装表达式,进入getPoinCut()方法
1)获取Pointcut.class, Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class 注解信息
2)封装注解信息成 AspectJAnnotation对象
3)创建一个PointCut类,并且把前面从注解里面解析的表达式设置进去
findAspectJAnnotationOnMethod() 方法
16. 返回14 步继续进行创建切面Advisor 的类中 InstantiationModelAwarePointcutAdvisorImpl,并创建advice对象
1)设置切面的相关信息
2)创建切面对象 this.instantiatedAdvice = instantiateAdvice(this.declaredPointcut);
17.进入instantiateAdvice()方法中
1)根据不同的注解类型创建不同的advice
2)进入getAdvice,并把他封装成AspectJAnnotation对象类型
例如@Before 创建的是AspectJMethodBeforeAdvice实现了MethodBeforeAdvice接口
@After 创建的是 AspectJAfterAdvice 实现了MethodInterceptor接口
3)完成beanClass切面的封装
18. 返回到第9步进行切面的排序,然后第9步继续往上返回到第7步,切面封装好之后,就是代理实例的生成过程了
1)进入创建代理的方法 createProxy
2)创建代理工厂
3)判断是接口形式的代理还是类的根据 proxyTargetClass 属性的true ,false,设置为true时,使用CGLib代理
4)把advice类型的增强包装成advisor切面
5)获取代理实例
19. 根据目标对象(被代理对象)是否有接口来判断采用什么代理方式
代理实例创建完之后就会返回到入口initializeBean()
用代理实例替换掉之前实例化的bean
1)如果是接口则采用jdk代理
2)否则采用CGLIB 代理
以上就是AOP注解切面的封装过程,调用过程下一章节解读。
来源:oschina
链接:https://my.oschina.net/huangguangsheng/blog/4275200