美文网首页
一文搞懂AOP

一文搞懂AOP

作者: 爱撒谎的男孩 | 来源:发表于2019-07-05 22:15 被阅读0次

    简介

    • 面向切面编程,是面向对象编程的重要组成部分,在不改变业务逻辑功能的基础上,对横切逻辑进行扩展

    添加依赖

        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-aspects</artifactId>
          <version>5.1.5.RELEASE</version>
        </dependency>
    

    通知

    • @Before(前置通知):在业务方法执行之前调用
    • @After(后置通知):在方法之后执行
    • @AfterReturning(正常返回通知):在方法之后执行,只有在业务方法没有出现异常的时候才会执行
    • @AfterThrowing(异常通知) : 在方法之后执行,只有在业务方法出现异常的时候才会执行
    • @Around (环绕通知):在业务方法执行之前和之后执行

    连接点

    • 业务层的所有方法,叫做连接点
    • 业务类中可以被增强的方法都叫做连接点

    切点

    • 能切入切面逻辑的方法,叫做切点
    • 实际被增强的方法叫做切入点 ,其他的那些没有被增强的方法(连接点)不是切点

    切面

    • 定义了增强方法的类就叫做切面

    实现

    • 在配置类上开启切面,使用@EnableAspectJAutoProxy
    @Configuration
    @ComponentScan(value = {"cn.tedu.demo"})
    @EnableAsync
    @EnableAspectJAutoProxy
    public class FirstConfig {}
    
    • 使用五个不同的通知完成切入:
    /**
     * 切面,使用@Aspect标注
     */
    @Component
    @Aspect
    public class CustomAspect {
    
        /**
         * 使用@PointCut定义切入点
         */
        @Pointcut(value = "execution(* cn.tedu.demo.aspect.AspectInvok.invok(..))")
        public void pointCut(){}
    
        /**
         * 前置通知,在指定方法之前执行
         * @param point JoinPoint对象,可以获取切点的各种属性,比如入参的参数等
         */
        @Before(value = "pointCut()")
        public void before(JoinPoint point){
            //获取入参的参数的值
            Object[] args = point.getArgs();
            System.out.println(Arrays.asList(args));
            //获取MethodSignature,其中可以获取切点的各种属性,比如方法返回类型,参数等等
            MethodSignature signature = (MethodSignature) point.getSignature();
            String[] parameterNames = signature.getParameterNames();
            System.out.println(Arrays.asList(parameterNames));
            System.out.println("在方法之前执行");
        }
    
        /**
         * 在切点之后执行
         * @param point JoinPoint对象
         */
        @After(value = "pointCut()",argNames = "point")
        public void after(JoinPoint point){
            System.out.println("在方法之后执行");
        }
    
        /**
         * 在方法前后都会执行
         * @param point
         */
        @Around(value = "pointCut()")
        public void around(ProceedingJoinPoint point) throws Throwable {
            System.out.println("前置执行");
            //执行方法,可以获取返回值,否则方法将不会执行
            Object result = point.proceed(point.getArgs());
            System.out.println("后置执行,执行的结果=="+result);
        }
    
        /**
         * 正常返回通知,
         * @param point Joinpoint对象
         * @param result 方法执行返回的结果,需要和@AfterReturning注解中returning中的属性值相同,否则不能自动装配
         */
        @AfterReturning(value = "pointCut()",returning = "result")
        public void afterReturning(JoinPoint point,Object result){
            System.out.println("正常返回执行,执行的结果为:"+result);
        }
    
        /**
         * 异常返回执行,程序出现异常了才会执行
         * @param point
         * @param ex 切入点执行抛出的异常,需要和@AfterThrowing注解的throwing值相同,否则不能完成自动装配
         */
        @AfterThrowing(value = "pointCut()",throwing = "ex")
        public void afterThrowing(JoinPoint point,Exception ex){
            System.out.println("异常返回执行,执行的异常为:"+ex);
        }
    }
    

    注解的实现

    @Target({ElementType.METHOD, ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface MyAnnotation {
        String name() default "陈加兵";
        int age() default 22;
    }
    
    • 定义一个切面,使用注解如下:
    @Component
    @Aspect
    public class CustomAspect {
    
        /**
         * 使用@PointCut定义切入点
         */
        @Pointcut(value = "@annotation(cn.tedu.demo.aspect.MyAnnotation)")
        public void pointCut(){}
    
        /**
         * 前置通知,在指定方法之前执行
         * @param point JoinPoint对象,可以获取切点的各种属性,比如入参的参数等
         */
        @Before(value = "pointCut()")
        public void before(JoinPoint point){
            //获取MethodSignature,其中可以获取切点的各种属性,比如方法返回类型,参数等等
            MethodSignature signature = (MethodSignature) point.getSignature();
            Method method = signature.getMethod();
            //获取方法上指定的注解
            MyAnnotation myAnnotation = method.getAnnotation(MyAnnotation.class);
            System.out.println("name="+myAnnotation.name());
            System.out.println("在方法之前执行");
        }
    }
    

    源码解析

    @EnableAspectJAutoProxy

    • @EnableAspectJAutoProxy该注解中可以看出使用了@Import(AspectJAutoProxyRegistrar.class),因此实际作用的类就是AspectJAutoProxyRegistrar,因此我们必须跟进源码去看。
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Import(AspectJAutoProxyRegistrar.class)
    public @interface EnableAspectJAutoProxy {
    

    AspectJAutoProxyRegistrar

    • 该类实现ImportBeanDefinitionRegistrar【向容器中注入Bean】。
    • 该类的主要作用就是向ioc容器中注入AnnotationAutoProxyCreator
    class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
    
        /**
        * 向容器中注入AnnotationAutoProxyCreator
        */
        @Override
        public void registerBeanDefinitions(
                AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
            //调用方法注册AnnotationAutoProxyCreator
            AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
    
            //获取@EnableAspectJAutoProxy注解中两个属性的值
            AnnotationAttributes enableAspectJAutoProxy =
                    AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
            //判断注解属性的值
            if (enableAspectJAutoProxy != null) {
                if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
                    AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
                }
                if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
                    AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
                }
            }
        }
    
    }
    

    AnnotationAutoProxyCreator【创建代理】

    概念

    • advisor(增强器):实现Advisor接口,简单的说一个advisor存储的就是被五个通知的标注的方法的信息。
    • advisedBeans:Map<Object, Boolean>类型,key是Bean,value存储的是这个Bean是否需要被增强器(通知方法)增强

    分析

    • @EnableAspectJAutoProxy注解的主要作用就是向容器中注入AnnotationAutoProxyCreator,ID为org.springframework.aop.config.internalAutoProxyCreator,因此切面的关键类就是这个,我们看看继承关系图,如下:

    [图片上传失败...(image-e8798a-1562336107568)]

    • 从继承关系中可以看到该类及其父类实现了两个重要的接口,如下:
      • InstantiationAwareBeanPostProcessor:spring的后置处理器,在实例化前后,初始化前后执行
      • BeanFactoryAware:自动注入BeanFactory
    • 通过上面的分析可以知道,AOP的主要功能必定是在AnnotationAutoProxyCreator这个Bean的初始化和实例化前后执行逻辑,无非就是上面两个接口定义的方法,我们可以在对应的方法中打断点跟踪代码。

    代码跟踪

    • 刷新容器种执行registerBeanPostProcessors(beanFactory)方法将全部的后置处理器注入到容器中,在其中执行执行后续的setBeanFactory的方法

      • 根据Bean的生命周期我们可以判断最新执行的一定是实现Aware方法的setBeanFactory方法【在invokeAwareMethods()方法中执行】,实际执行也是符合我们的猜想,第一个执行的方法就是org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator#setBeanFactory
        • 创建AspectJAdvisorFactory:顾名思义,当然是从标注@Aspect的类中创建增强器【切面增强的方法】
        • 创建BeanFactoryAspectJAdvisorsBuilder:简单的说就是创建增强器的辅助类,用来构造增强器
      @Override
        public void setBeanFactory(BeanFactory beanFactory) {
              //调用父类的方法
            super.setBeanFactory(beanFactory);
            if (!(beanFactory instanceof ConfigurableListableBeanFactory)) {
                throw new IllegalArgumentException(
                        "AdvisorAutoProxyCreator requires a ConfigurableListableBeanFactory: " + beanFactory);
            }
              //实际执行的是initBeanFactory
            initBeanFactory((ConfigurableListableBeanFactory) beanFactory);
        }
      
      /*******************************initBeanFactory**************************/
        @Override
        protected void initBeanFactory(ConfigurableListableBeanFactory beanFactory) {
            super.initBeanFactory(beanFactory);
              //创建切面增强器工厂
            if (this.aspectJAdvisorFactory == null) {
                this.aspectJAdvisorFactory = new ReflectiveAspectJAdvisorFactory(beanFactory);
            }
              //创建切面增强生成器
            this.aspectJAdvisorsBuilder =
                    new BeanFactoryAspectJAdvisorsBuilderAdapter(beanFactory, this.aspectJAdvisorFactory);
        }
      
    • 刷新容器方法中执行finishBeanFactoryInitialization(beanFactory)【实例化剩余的单例Bean】

      • getBean()--->doGetBean--->createBean,在createBean方法中执行Object bean = resolveBeforeInstantiation(beanName, mbdToUse);【在实例化之前给一个机会返回一个代理】

        • 内部执行bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);

          • 内部循环遍历后置处理器,判断类型为InstantiationAwareBeanPostProcessor,执行postProcessBeforeInstantiation()方法【在实例化之前调用,接口定义】。

            • 此时执行到AbstractAutoProxyCreator#postProcessBeforeInstantiation,源码如下:
              • 这个方法的主要作用就是查找切面类和已经有了TargetSource的类,这些类都是不需要增强的,设置adviseBean的value为false,后续就不需要创建代理对象了
            /**
            * 在实例化之前执行逻辑,如果返回不为null,后续的属性赋值等操作将不会执行
            *  主要的作用
            */
            public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
                    //获取cacheKey,主要是和FactoryBean对象区别
                    Object cacheKey = getCacheKey(beanClass, beanName);
                    //beanName为空,或者targetSourcedBeans不包含当前Bean
                    if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
                            //如果需要增强的Bean已经包含了当前Bean,后续流程不需要执行,返回null即可
                        if (this.advisedBeans.containsKey(cacheKey)) {
                            return null;
                        }
                        /**isInfrastructureClass(beanClass):【如果当前Bean是基础的类【实现了Advice、Pointcut、Advisor、AopInfrastructureBean】并且满足this.aspectJAdvisorFactory不为null&&当前类被@Aspect注解标注了】
                        *shouldSkip(beanClass, beanName):
                        *       ①获取所有的候选的增强器【List<Advisor> candidateAdvisors = findCandidateAdvisors();】
                        *       ②循环候选增强器,判断类型是否是AspectJPointcutAdvisor
                        */
                        if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
                            //如果满足条件,那么表示该类是一个切面,不是需要增强的Bean,设置value为false
                            this.advisedBeans.put(cacheKey, Boolean.FALSE);
                            return null;
                        }
                    }
            
                    //获取自定义的TargetSource
                    TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
                        //如果存在,那么可以直接创建代理对象
                    if (targetSource != null) {
                        if (StringUtils.hasLength(beanName)) {
                            this.targetSourcedBeans.add(beanName);
                        }
                        Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
                        Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
                        this.proxyTypes.put(cacheKey, proxy.getClass());
                        return proxy;
                    }
            
                    return null;
                }
            
            • 代码继续执行,发现postProcessPropertiespostProcessBeforeInitializationpostProcessBeforeInitialization都是默认的实现,这里跳过。
            • 真正创建代理对象的是在postProcessAfterInitialization中,逻辑如下:
              • ①判断bean是否为null和earlyProxyReferences中是否存在当前的Bean
              • ②如果满足条件,调用wrapIfNecessary(bean, beanName, cacheKey)方法,封装Bean,其实就是创建代理对象
              • ③进入wrapIfNecessary方法
                • ①做一些判断,包括是否是targetSourcedBeans是否包含,是否是基础类,是否应该跳过,是否adviseBean中的value为false【不需要增强的Bean】,满足以上条件的都直接返回bean,不需要创建代理
                • ②调用方法获取能够作用于当前Bean的增强器(通知方法集合)封装在specificInterceptors,代码跟进,最重要的实现是在org.springframework.aop.support.AopUtils.canApply(org.springframework.aop.Advisor, java.lang.Class<?>, boolean)这个方法中,大致的思想就是
                • ③判断specificInterceptors是否为null,如果为空,表示当前Bean不需要增强,设置adviseBean的值为false,反之需要增强,此时就需要创建代理了,此时主要调用的是Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean))
              • ④ 进入createProxy方法
                • ① 创建ProxyFactory,主要作用就是创建代理的工厂,其中封装了Advisor,targetSource,getProxy()【创建代理】方法等
                • ② 判断是否应该直接代理目标类【if (!proxyFactory.isProxyTargetClass())】
                • ③ 调用evaluateProxyInterfaces(beanClass, proxyFactory);方法,主要的作用就是检查当前Bean实现的接口是否符合要求能够创建代理对象【不能是InitializingBeanDisposableBean,Closeable,AutoCloseable等接口】,如果满足直接设置到ProxyFactory中,否则设置proxyTargetClass=true【直接代理目标类targetSource】
                • ④ 调用Advisor[] advisors = buildAdvisors(beanName, specificInterceptors)方法,将公共的拦截器和适配的拦截器specificInterceptors一起返回
                  • ① 根据指定的this.interceptorNames拦截器名称从容器中获取Bean,将其解析成commonInterceptors
                  • ② 遍历commonInterceptors和specificInterceptors将其封装成Advisor数组返回
                • ⑤ 将advisor和targetSource添加到ProxyFactory中
                • ⑥ 调用proxyFactory.getProxy(getProxyClassLoader())方法,获取代理对象
                  • ① 最终调用的是org.springframework.aop.framework.DefaultAopProxyFactory.createAopProxy的方法创建代理对象,是AopProxyFactory默认实现类中的方法,具体逻辑如下:
                    • 如果targetClass是接口或者继承Proxy使用JDK的动态代理,否则使用Cglib代理
              • ⑤ 将创建的代理对象proxy存放在proxyTypes中,返回proxy
              • ⑥ 至此代理对象创建完成

    代理执行

    • 上面解释了代理对象的创建流程,下面就开始真正的执行和增强的流程了,需要了解到五种通知类型在什么时候执行,执行的顺序是什么?
    • 未完待续。。。。。

    相关文章

      网友评论

          本文标题:一文搞懂AOP

          本文链接:https://www.haomeiwen.com/subject/dobyhctx.html