• Java核心知识体系:AOP原理和切面应用


    1 概述

    我们所说的Aop(即面向切面编程),即面向接口,也面向方法,在基于IOC的基础上实现。

    Aop最大的特点是对指定的方法进行拦截并增强,这种增强的方式不需要业务代码进行调整,无需侵入到业务代码中,使业务与非业务处理逻辑分离。

    以Spring举例,通过事务的注解配置,Spring会自动在业务方法中开启、提交业务,并且在业务处理失败时,执行相应的回滚策略。

    aop的实现主要包括了两个部分:

    • 匹配符合条件的方法(Pointcut)
    • 对匹配的方法增强(JDK代理、cglib代理)
      spring针对xml配置和配置自动代理的Advisor有很大的处理差别,在IOC中主要是基于XML配置分析的,在AOP的源码解读中,则主要从自动代理的方式解析,分析完注解的方式,再分析基于xml的方式。

    2 案例分析

    下面是spring aop的用法 也是用于源码分析的案例

    切面类:TracesRecordAdvisor

    1. @Aspect
    2. @Component
    3. public class TracesRecordAdvisor {
    4. @Pointcut("execution(* spring.action.expend.aop.services.*.*(..))")
    5. public void expression() {
    6. }
    7. @Before("expression()")
    8. public void beforePrint()
    9. {
    10. System.out.println("进入服务,在服务执行之前,记录日志....");
    11. }
    12. @AfterReturning("expression()")
    13. public void afterPrint()
    14. {
    15. System.out.println("退出服务,在服务执行结束之后,记录日志.....");
    16. }
    17. }

    xml配置: aop的注解启用只需要在xml中配置这段代码即可,这个是作为入口

    <aop:aspectj-autoproxy/>

    服务类:PayServiceImpl 使用jdk代理 所以要有一个接口

    1. @Service
    2. public class PayServiceImpl implements PayService {
    3. public void payMoneyMenthod() {
    4. System.out.println("正在执行付款...");
    5. }
    6. }

    测试方法:

    1. @Test
    2. public void springAopTestService() {
    3. ClassPathXmlApplicationContext applicationContext=new ClassPathXmlApplicationContext("spring-aop.xml");
    4. PayService payService= (PayService) applicationContext.getBean("payServiceImpl");
    5. payService.payMoneyMenthod();

    执行结果:

    1. 进入服务,在服务执行之前,记录日志....
    2. 正在执行付款...
    3. 退出服务,在服务执行结束之后,记录日志.....

     从上面的执行结果看,payMoneyMenthod 方法的确是被增强了。

    3 BeanFactoryPostProcessor

    spring源码的时候,可以首先看下BeanFactoryPostProcessor和BeanPostProcess,这两个接口都是在spring通过配置文件或者xml获取bean声明,生成BeanDefinition后,允许我们再对生成的BeanDefinition,进行入口包装和增强。

    我们看看BeanFactoryPostProcessor的定义

    1. public interface BeanFactoryPostProcessor {
    2. void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
    3. }

     

    方法postProcessBeanFactory的参数为ConfigurableListableBeanFactory,我们之前讨论过beanFactory用来获取bean的,而ConfigurableListableBeanFactory继承接口SingletonBeanRegistry和BeanFactroy,所以可以访问到已经生成过的BeanDefinitions集合,如果某个类实现该接口,spring会注册这个类,然后执行这个类的postProcessBeanFactory方法,以便我们对BeanDefinition进行扩展。

    接下来的代码表示Spring是如何注册BeanFactoryPostProcessor并执行postProcessBeanFactory的。

    1. @Override
    2. public void refresh() throws BeansException, IllegalStateException {
    3. synchronized (this.startupShutdownMonitor) {
    4. prepareRefresh();
    5. //核心方法1
    6. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
    7. prepareBeanFactory(beanFactory);
    8. try {
    9. postProcessBeanFactory(beanFactory);
    10. //核心方法2 执行BeanFactoryPostProcessor
    11. invokeBeanFactoryPostProcessors(beanFactory);
    12. //核心方法 3 注册BeanPostProcessor
    13. registerBeanPostProcessors(beanFactory);
    14. // Initialize message source for this context.
    15. initMessageSource();
    16. // Initialize event multicaster for this context.
    17. initApplicationEventMulticaster();
    18. // Initialize other special beans in specific context subclasses.
    19. onRefresh();
    20. // Check for listener beans and register them.
    21. registerListeners();
    22. // Instantiate all remaining (non-lazy-init) singletons.
    23. finishBeanFactoryInitialization(beanFactory);
    24. // Last step: publish corresponding event.
    25. finishRefresh();
    26. }
    27. catch (BeansException ex) {
    28. ............
    29. throw ex;
    30. }
    31. finally {
    32. ............
    33. resetCommonCaches();
    34. }
    35. }
    36. }

    核心方法1obtainFreshBeanFactory就是前两篇所说的生成BeanDefinition的入口,invokeBeanFactoryPostProcessors核心方法2就是执行BeanFactoryPostProcessor接口的方法。

    1. protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
    2. PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
    3. }

    通过方法getBeanFactoryPostProcessors获取注册BeanFactoryPostProcessor,然后来看看如何添加一个处理器

    1. @Override
    2. public void addBeanFactoryPostProcessor(BeanFactoryPostProcessor beanFactoryPostProcessor) {
    3. this.beanFactoryPostProcessors.add(beanFactoryPostProcessor);
    4. }

     对于方法invokeBeanFactoryPostProcessors不再往下看了,里面的方法大致先对BeanFactoryPostProcessor进行排序,排序的标准是是否实现了PriorityOrdered,然后根据设置的order大小指定执行顺序,生成一个排序集合和一个普通的集合,最后执行invokeBeanFactoryPostProcessors

    1. private static void invokeBeanFactoryPostProcessors(
    2. Collectionextends BeanFactoryPostProcessor> postProcessors, ConfigurableListableBeanFactory beanFactory) {
    3. for (BeanFactoryPostProcessor postProcessor : postProcessors) {
    4. //执行到自定义的BeanFactoryPostProcessor
    5. postProcessor.postProcessBeanFactory(beanFactory);
    6. }
    7. }

    这个方法就会循环先前注册的BeanFactoryPostProcessor集合,然后执行postProcessBeanFactory。

    4 BeanPostProcess 解读

    与BeanFactoryPostProcessor相比,BeanPostProcess就重要得多了,因为Spring的注解、AOP等都是通过这个接口的方法拦截执行的,它贯穿了Bean创建过程的整个生命周期,在IOC阶段,Spring只注册BeanPostProcess,执行则放到了Bean的实例化创建阶段。

    首先看下BeanPostProcessor的接口定义

    1. public interface BeanPostProcessor {
    2. //在bean创建 属性赋值之后 Aware接口执行之后执行
    3. Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
    4. //在init-method afterPropertiesSet 执行之后执行
    5. Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
    6. }

    在bean的声明周期中,下面的序列是bean创建后要执行的接口和方法顺序:

    • 实例化(autowireConstructor或者instantiateBean)
    • 属性初始化(populateBean)
    • Aware接口(如果你的 bean 有进行实现)
    • BeanPostProcess.postProcessBeforeInitialization
    • PostConstructInitializingBean.afterPropertiesSet
    • BeanPostProcess.postProcessAfterInitialization

    其中通过注解引入依赖的方式就是在AutowiredAnnotationBeanPostProcessor这个类中实现的,而接下来要分析的Spring Aop也是从这里开始的,这个类叫AnnotationAwareAspectJAutoProxyCreator,

    5 NameSpaceHanlder 解读

    在Spring中,任何的技术都是在IOC的基础上进行的,Aop也不例外,程序会首先读取xml配置文件,然后对读取到的标签先查找命名空间,然后找对应的NameSpaceHandler,最终调用parse方法解析标签。

    aop标签的解析,使用纯注解的方式aop:aspectj-autoproxy和使用aop:config的配置解析不太一样,具体表现在生成PointCut和生成Before、After、Around等切面类时,使用aop:config的方式会为这些注解生成一个BeanDefinition,而这个BeanDefinition的构造函数是由3个BeanDefinition组成,表明这个类是合成类,即synthetic这个属性为true。然后跟解析普通的bean一样,生成这些实例对象,后面的过程就跟是用纯注解的方式相同了,接下来的分析是基于纯注解分析的,也就是解析从解析aop:aspectj-autoproxy这个标签开始。

    前面的xml文件的标签解析是通过parseDefaultElement方法解析默认的 标签的,而我们在配置文件里面配置了启动自动代理的方式  ,当Spring读取到这个标签,则会走parseCustomElement(root)这个方法了,这个方法的源码不再解析,主要完成的功能如下:

    • 获取element的nameSpaceUri
  • 相关阅读:
    Linux网路服务之DNS域名解析
    RandomAccessFile实现断点续传
    使用Julia语言和R语言实现K-均值
    罗马数字转整数[简单]
    string的接口测试与使用
    如何利用k-means算法对图片颜色进行聚类并实现图像压缩?(附Python代码+数据集)
    RabbitMQ(15672) 消息中间件 NOTE
    LuatOS-SOC接口文档(air780E)-- httpsrv - http服务端
    Vue3统一导出局部组件和全局组件
    鸿蒙原生应用再添两员新丁!​B站、58入局鸿蒙
  • 原文地址:https://blog.csdn.net/java_lujj/article/details/126767663