《Spring(5.x)注解驱动开发》aop(一)

13.AOP(动态代理)

  1. 指在程序运行期间动态的将某段代码切入到指定方法指定位置进行运行的编程方法。
  2. 切面编程:
    • 将业务逻辑类和切面类加入到容器中
    • 在切面类上的方法标注通知注解,即告诉spring何时运行切面方法
    • 开启aop注解模式:@EnableAspectJAutoProxy
  3. aop注解:
    • @EnableAspectJAutoProxy:在配置类中加入该注解,告诉spring开启aop注解模式,aop注解生效
    • @Aspect:标注在切面类上,告诉spring该类为切面类
    • @Pointcut("execution()"):标注在切面类中的公共方法上,抽取公共的切入点表达式
      1. 在本类引用:直接写方法名:pointCut()
      2. 其他切面引用:写全路径名:com.tarot.aop.LogAspects.pointCut()
    • @Before:前置切面,在被切入方法运行前运行
    • @After:后置切面,在被切入方法运行结束后运行,无论方法正常结束还是异常结束都会执行
    • @AfterReturning:返回切面,在被切入方法正常返回后执行
    • @AfterThrowing:异常切面,在被切入方法出现异常后执行
    • @Around:环绕切面

14.AOP案例

  • 代码实现:在调用业务类方法的时候,加入切面执行其他相关辅助业务操作。

    1. 定义一个业务逻辑类(ServiceHandle.class):在业务逻辑运行的时候将日志进行打印(方法前,方法运行结束后,方法出现异常,)
        public class ServiceHandle {
            public int div(int i,int j) {
                return i / j;
            }
        }
    
    1. 定义一个日志切面类(LogAspects):切面类里面的方法需要动态感知ServiceHandle.div运行到哪一步
        @Aspect
        public class LogAspects {
    
            //抽取公共的切入点表达式
            //1.在本类引用:直接写方法名:pointCut()
            //2.其他切面引用:写全路径名:com.tarot.aop.LogAspects.pointCut()
            //切入表示式(指定从哪个方法切入)切入ServiceHandle中的有任意参数的任意方法
            //当有多个参数的时候,joinPoint要放在首位,不然spring无法识别
            @Pointcut("execution(public int com.tarot.aop.ServiceHandle.*(..))")
            public void pointCut() {
            }
    
            @Before("pointCut()")
            public void logStart(JoinPoint joinPoint) {
                //获取方法名
                String methodName = joinPoint.getSignature().getName();
                //获取方法参数列表
                Object[] args = joinPoint.getArgs();
                System.out.println(methodName + "-->@Before start....." + Arrays.asList(args));
            }
    
            @After("pointCut()")
            public void logEnd(JoinPoint joinPoint) {
                System.out.println(joinPoint.getSignature().getName() + "-->@After end.....");
            }
    
            @AfterReturning(value = "pointCut()", returning = "result")
            public void logReturn(JoinPoint joinPoint, Object result) {
                System.out.println(joinPoint.getSignature().getName() + "-->@AfterReturning return....." + result);
            }
    
            @AfterThrowing(value = "pointCut()", throwing = "e")
            public void logException(JoinPoint joinPoint, Exception e) {
                System.out.println(joinPoint.getSignature().getName() + "-->@AfterThrowing exception....." + e);
            }
        }
    
    1. 将切面类和业务逻辑类都加入到容器中
        @EnableAspectJAutoProxy
        @Configuration
        public class AopConfig {
    
            //加入业务逻辑类
            @Bean
            public ServiceHandle serviceHandle(){
                return new ServiceHandle();
            }
    
            //切面类加入到容器
            @Bean
            public LogAspects logAspects(){
                return new LogAspects();
            }
        }
    
    1. 测试(IOC容器加载该配置,对注入的bean进行测试)
        public class AopTest {
            @Test
            public void test01() {
                AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AopConfig.class);
    
                //使用容器中的
                ServiceHandle serviceHandle = applicationContext.getBean(ServiceHandle.class);
                serviceHandle.div(1, 1);
            }
        }
    

15.@EnableAspectJAutoProxy注解分析-开启AOP注解模式

  1. 点开该注解实现可以看到:

        @Target(ElementType.TYPE)
        @Retention(RetentionPolicy.RUNTIME)
        @Documented
        @Import(AspectJAutoProxyRegistrar.class)
        public @interface EnableAspectJAutoProxy {
             ···
             //代码
        }
    

    该注解实现了@Import(AspectJAutoProxyRegistrar.class):给容器注入AspectJAutoProxyRegistrar.class,即注入该Bean实现了AOP注解的开启

  2. 分析AspectJAutoProxyRegistrar.class方法实现

      1.在AspectJAutoProxyRegistrar.class类中registerBeanDefinitions()方法调用AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
      2.在registerAspectJAnnotationAutoProxyCreatorIfNecessary()方法调用本类方法,最后调用registerOrEscalateApcAsRequired()方法注入bean
      3.注入的bean是AnnotationAwareAspectJAutoProxyCreator.class
    
  3. Bean:AnnotationAwareAspectJAutoProxyCreator注入

     注入bean:AnnotationAwareAspectJAutoProxyCreator.class分析
                    -->extends:AspectJAwareAdvisorAutoProxyCreator
                       -->extends:AbstractAdvisorAutoProxyCreator
                           -->extends:AbstractAutoProxyCreator
                               -->extends:ProxyProcessorSupport;implements: SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware
                                  实现了后置处理器(bean初始化前后需要做的事)和注入BeanFactory工厂
     从基父类开始:
       AbstractAutoProxyCreator:
           注入工厂:setBeanFactory()
           后置处理器逻辑方法:postProcessXXX()
    
       AbstractAdvisorAutoProxyCreator:
           重写:setBeanFactory()
           调用本类:initBeanFactory()
    
       AspectJAwareAdvisorAutoProxyCreator :
    
       AnnotationAwareAspectJAutoProxyCreator:
           重写:initBeanFactory
    

16.IOC注册后置处理器的过程(AnnotationAwareAspectJAutoProxyCreator为例)

  • debug断点,在AopConfig中注册业务逻辑和切面类的时候打上断点,在源码方法上注解
  1. AbstractAutoProxyCreator.setBeanFactory()[源码]
  2. AnnotationAwareAspectJAutoProxyCreator.initBeanFactory()[源码]
  3. AopConfig.serviceHandle()
  4. AopConfig.logAspects()
  • debug上面程序,到第一个断点(setBeanFactory方法),所执行的逻辑分析


    debug执行步骤
  1. 创建IOC容器:
    在测试类AopTest中手动创建了IOC容器并加入配置文件,spring调用这里开始初始化容器:


    创建ioc容器
  2. 进入AnnotationConfigApplicationContext执行IOC容器创建

    1. 先使用无参构造器创建IOC容器
    2. 注册配置类
    3. 调用refresh()方法刷新容器,初始化所有bean


      容器实际创建步骤
  3. 由debug断点进入AbstractApplicationContext.refresh()方法进入

    • 531行:注册bean的后置处理器来拦截bean的创建,调用本类方法registerBeanPostProcessors();
    • registerBeanPostProcessors(beanFactory);调用PostProcessorRegistrationDelegate.registerBeanPostProcessors()方法


      refresh()方法
  4. 进入PostProcessorRegistrationDelegate类registerBeanPostProcessors()方法

    1. 先获取ioc容器中已经定义了的需要创建对象的所有BeanPostProcessor
    2. 给容器加入一些其他需要的BeanPostProcessor
    3. 优先注册实现了PriorityOrdered接口的BeanPostProcessor
    4. 再注册实现了Ordered接口的BeanPostProcessor
    5. 最后注册没有实现优先级接口的普通的BeanPostProcessor
    6. 遍历注册的BeanPostProcessor的集合,通过AbstractBeanFactory.getBean()方法创建BeanPostProcessor对象,注入到容器
        public static void registerBeanPostProcessors(
                ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) {
    
            //1. 先获取ioc容器中已经定义了的需要创建对象的所有BeanPostProcessor
            String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);
    
            //2.  给容器加入一些其他需要的BeanPostProcessor
            int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length;
            beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount));
    
                                ······
    
            // 3.优先注册实现了PriorityOrdered接口的BeanPostProcessor
            sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
            registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors);
    
            //  4.再注册实现了Ordered接口的BeanPostProcessor
            List<BeanPostProcessor> orderedPostProcessors = new ArrayList<>();
            for (String ppName : orderedPostProcessorNames) {
                BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
                orderedPostProcessors.add(pp);
                if (pp instanceof MergedBeanDefinitionPostProcessor) {
                    internalPostProcessors.add(pp);
                }
            }
            sortPostProcessors(orderedPostProcessors, beanFactory);
            // 注册该BeanPostprocessor
            registerBeanPostProcessors(beanFactory, orderedPostProcessors);
    
            //  5.  最后注册没有实现优先级接口的普通的BeanPostProcessor
            List<BeanPostProcessor> nonOrderedPostProcessors = new ArrayList<>();
            for (String ppName : nonOrderedPostProcessorNames) {
                BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
                nonOrderedPostProcessors.add(pp);
                if (pp instanceof MergedBeanDefinitionPostProcessor) {
                    internalPostProcessors.add(pp);
                }
            }
            registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors);
    
            // Finally, re-register all internal BeanPostProcessors.
            sortPostProcessors(internalPostProcessors, beanFactory);
            registerBeanPostProcessors(beanFactory, internalPostProcessors);
    
            // Re-register post-processor for detecting inner beans as ApplicationListeners,
            // moving it to the end of the processor chain (for picking up proxies etc).
            beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext));
        }
    
  5. AbstractBeanFactory.getBean()方法调用本类doGetBean()方法具体实现BeanPostProcessor对象的创建

    • 对于该注解的注入,即创建internalAutoProxyCreator()的BeanPostProcessor
    • 由于第一次创建 internalAutoProxyCreator(),所以进入getSingleton()方法


      AbstractBeanFactory.doGetBean()
  6. 进入DefaultSingletonBeanRegistry.getSingleton()方法开始创建internalAutoProxyCreator的单实例bean


    DefaultSingletonBeanRegistry.getSingleton()
  7. 回到AbstractBeanFactory.getBean中调用AbstractAutowireCapableBeanFactory中createBean()方法实现创建。


    createBean()
  8. AbstractAutowireCapableBeanFactory调用本类的doCreateBean()方法,完成创建,赋值和初始化工作

    Bean创建

    Bean赋值&初始化

    8.1 其中本类中initializeBean初始化Bean流程。
    ①.invokeAwareMethods():对Aware接口的赋值
    ②.applyBeanPostProcessorsBeforeInitialization():应用后置处理器的BeforeInitialization方法。
    ③.invokeInitMethods():执行自定义的初始化方法
    ④.applyBeanPostProcessorsAfterInitialization():执行后置处理器的AfterInitialization方法
    微信图片_20190108165807.png

  9. 进入本类中invokeAwareMethods()方法,进入第一个断点 AbstractAutoProxyCreator.setBeanFactory()方法


    注入BeanFactory工厂
  10. 在AbstractAutoProxyCreator.setBeanFactory()调用第二个断点AnnotationAwareAspectJAutoProxyCreator.initBeanFactory()方法,重新初始化一下BeanFactory(再封装)


    AnnotationAwareAspectJAutoProxyCreator.initBeanFactory()
  11. 在AnnotationAwareAspectJAutoProxyCreator.initBeanFactory()方法,对BeanFactory重新包装


    AnnotationAwareAspectJAutoProxyCreator.initBeanFactory()
  12. 完成AnnotationAwareAspectJAutoProxyCreator的注册(创建),完成上述操作回调到,见第四步中代码。

17.AnnotationAwareAspectJAutoProxyCreator后置处理器注入容器后,在ioc创建其他Bean的时候。该后置处理器的处理流程。

  1. AnnotationAwareAspectJAutoProxyCreator实现的后置处理器类型

①.由16.3中可知AnnotationAwareAspectJAutoProxyCreator底层父类实现了一个SmartInstantiationAwareBeanPostProcessor接口类型的后置处理器。
②.而SmartInstantiationAwareBeanPostProcessor接口继承了InstantiationAwareBeanPostProcessor接口,
③.而InstantiationAwareBeanPostProcessor接口实现最底层BeanPostProcessor接口。
④.所以AnnotationAwareAspectJAutoProxyCreator是InstantiationAwareBeanPostProcessor这种类型的后置处理器

  1. 探究在AnnotationAwareAspectJAutoProxyCreator这个后置处理器完成后,这个后置处理器所作的事情和作用。
  • Debug断点:AbstractAutoProxyCreator.postProcessBeforeInstantiation()方法
  1. 由16可知完成了一些BeanPostProcessor的注册(创建),从创建完成后置处理器后,进行初始化后续的单实例Bean。即在AbstractApplicationContext.refresh()完成了16.3的步骤,然后执行本类本方法中以下步骤:


    AbstractApplicationContext.refresh()方法
  2. 进入AbstractApplicationContext.finishBeanFactoryInitialization()方法,调用DefaultListableBeanFactory.preInstantiateSingletons()对所有非懒加载的单实例Bean进行初始化。


    AbstractApplicationContext.finishBeanFactoryInitialization()方法
  3. 进入DefaultListableBeanFactory.preInstantiateSingletons()方法中,遍历获取容器中所有的Bean,依次使用AbstractBeanFactory.getBean(beanName)方法创建。


    DefaultListableBeanFactory.preInstantiateSingletons()方法
  4. 接下来Bean的创建过程中与上述16.5 - 16.8基本一样,由于满足的条件不尽相同所以创建流程方法中根据条件调用的方法会有差别,但整体创建Bean的流程不变。
    6.1.创建Bean的流程:
    getBean() -> doGetBean() ->getSingleton() -> getObject() -> createBean() -> doCreateBean()
    6.2.思考一个问题?
     ①.由17.5步中,会遍历所有容器中的Bean,并依次创建实例。而之前16中创建的BeanPostProcessor实质上也是Bean实例,那么两次创建的时候肯定会遇见Bean单实例问题,因为容器中只有一个Bean实例,所以容器在Bean创建步骤的doGetBean()步骤时,会检查ioc容器的单实例缓存中是否存在该Bean实例,若存在直接返回,而不进行后续步骤。

    如何保证ioc容器的单实例Bean为单实例

     ②.创建Bean时,会先从缓存中获取该Bean,r若可以获取,说明是被创建过的,直接使用,否则再创建,所有创建好的单实例Bean都会被缓存起来

  5. 进入在创建Bean的createBean()步骤中

    1. 在创建该Bean之前,先调用方法尝试从后置处理器中获取该Bean的代理对象,若能够获取,直接返回该代理对象。
    2. 若不能返回,则使用本类中的doCreateBean()方法来完成Bean的创建,此后步骤与16中创建Bean步骤相同。


      AbstractAutowireCapableBeanFactory.createBean()方法
  6. 由7.1进入本类中的resolveBeforeInstantiation()方法,使用后置处理器先尝试返回对象


    AbstractAutowireCapableBeanFactory.resolveBeforeInstantiation()方法

    8.1 第一个方法:

      bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
    

    1.遍历所有的后置处理器,如果是InstantiationAwareBeanPostProcessor类型的,就执行AbstractAutoProxyCreator.postProcessBeforeInstantiation()方法
    2.由17.1可知AnnotationAwareAspectJAutoProxyCreator是这种类型的后置处理器,故此他会在Bean创建之前尝试获取对象
    3.总结可知后置处理器的用处不尽相同
    ①.BeanPostProcessor:作用是在Bean对象创建完成初始化前后调用的
    ②.InstantiationAwareBeanPostProcessor:作用是在创建Bean实例之前尝试获取用后置处理器返回对象实例的。

    AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInstantiation()方法

    8.2 第二个方法

      bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
    

    1.上述两个方法为后置处理器方法,每一个后置处理器都会拥有,在此处由于这种类型的后置处理器是在Bean实例前调用的,因此在获取代理对象判断时,若Before方法成功,则再调用After()方法,若失败,直接跳过。
    2.在前面16.8的8.1直接创建Bean的时候,initializeBean()方法也会调用后置处理器的这两个方法来进行增强Bean操作,不过此时的后置处理器为BeanPostProcessor类型。
    3.所以在创建Bean和Bean之前创建代理对象都会使用后置处理器,因为后置处理器类型不同,只决定在什么时候调用。

  7. 结论:由上述8.1可知,进入AbstractAutoProxyCreator.postProcessBeforeInstantiation()方法,该方法所打断点位置,即:AnnotationAwareAspectJAutoProxyCreator继承的父类方法。由此结论

AnnotationAwareAspectJAutoProxyCreator在所有Bean创建之前会有一个拦截,因为该后置处理器也是实现了 InstantiationAwareBeanPostProcessor接口(其父类AbstractAutoProxyCreator实现的)。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,980评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,178评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,868评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,498评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,492评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,521评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,910评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,569评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,793评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,559评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,639评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,342评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,931评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,904评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,144评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,833评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,350评论 2 342

推荐阅读更多精彩内容