美文网首页一些收藏面试
AOP原理分析(一)准备阶段和代理阶段

AOP原理分析(一)准备阶段和代理阶段

作者: IT拾荒者x | 来源:发表于2022-07-15 08:58 被阅读0次

    Aop的执行原理,我们应该基本都了解:通过编写切面类,我们可以在指定的切入点处插入我们额外的代码块,就好比代理模式中,我们可以在执行目标方法的前后干一些自己想干的事情。那么这是怎么样实现的呢?
    我们自己写的类中的代码是硬编码写死的,要想改变一个已经写好的类,我们常见的操作就是动态代理了,没错,AOP的底层就是将切入点所在的类创建成了代理对象。
    我们知道Spring中的一个主要功能就是管理所有的bean对象,在创建对象的时候,为我们提供了很多的扩展点,可以方便我们来干预对象的创建,那么Aop究竟是在哪一个扩展点的地方帮我们创建了代理对象呢?本文就来讲解下从Aop的前置准备到创建代理对象的整个流程。

    一、@EnableAspectJAutoProxy

    熟悉Spring开发的模式的话,我们都知道,开启一个新的功能的话,我们基本需要在配置类上加上一个@EnableXxx的注解,而这类@EnableXxx的注解多半是向Spring容器中注入了影响bean创建生命周期的bean信息,开启基于注解的Aop的@EnableAspectJAutoProxy注解同样于此,他为我们导入了AnnotationAwareAspectJAutoProxyCreator类的定义信息。我们来看下AnnotationAwareAspectJAutoProxyCreator的继承实现结构图:

    AnnotationAwareAspectJAutoProxyCreator结构图.png

    在整个结构图中,能干预bean的生命周期的是左上角的BeanPostProcessor接口,那么整个AOP功能的具体实现就是在对于InstantiationAwareBeanPostProcessor的方法的具体实现中,就是AbstractAutoProxyCreator类中postProcessBeforeInstantiation方法和postProcessAfterInitialization方法,我们下面就关注它的具体实现:

    二、Aop的前置准备

    1、AOP基础准备工作

    首先,我们来看AbstractAutoProxyCreator类中postProcessBeforeInstantiation方法的具体实现,他实现的接口是InstantiationAwareBeanPostProcessor,而InstantiationAwareBeanPostProcessor的postProcessBeforeInstantiation的触发时机是在Spring容器开始准备创建bean之前,Spring给我们提供了一个机会可以自己在这创建对象,而不走Spring的创建对象流程,但是AOP会在此就创建对象吗?我们继续往下看。
    我们先看下前半部分代码:

        Object cacheKey = getCacheKey(beanClass, beanName);
    
            if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
                if (this.advisedBeans.containsKey(cacheKey)) {//advisedBeans已经分析过的组件,不是增强过的组件
                    return null;
                }//所有增强了的组件会被缓存在advisedBeans中,如果是我们需要增强的bean,就放在缓存中
                //isInfrastructureClass(beanClass):判断当前类是否有@Aspect注解,即当前类是否是切面;shouldSkip中解析了所有的切面类并封装了切面类中的切面方法
                if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {// shouldSkip:如果是切面类,则跳过处
                    this.advisedBeans.put(cacheKey, Boolean.FALSE);
                    return null;
                }
            }
    

    这段代码中主要在管理advisedBeans这个集合,这个集合中key为bean的名称,value为boolean值,个人理解为这个集合的意义是,key表示处理过的beanName,而value值表示这个bean应不应该被增强【不代理】。那什么样的类是不需要增强的呢?这就需要关注if中的两个条件了,一个是isInfrastructureClass(beanClass),另一个是shouldSkip(beanClass, beanName),满足这两个条件之一的都会加入advisedBeans集合,并且标记为不增强;一起来看看这两个方法:

    1. isInfrastructureClass(beanClass):判断是不是AOP的基础设施类,如果是的话,就加入到advised集合中,并标记为不应该增强,那么究竟什么类是AOP的基础设施类呢?深入到代码里我们发现:分为两大块:一个是实现了Advice,Pointcut,Advisor,AopInfrastructureBean这四个接口的类属于AOP的基础设施类;另一个是这个类上面标注了@Aspect,即切面类
    2. shouldSkip(beanClass, beanName):这个方法是AOP准备过程中的一个重要点,因为这个方法中干了很多事情,为后面的AOP代理做好了铺垫;代码如下:
            protected boolean shouldSkip(Class<?> beanClass, String beanName) {
            // TODO: Consider optimization by caching the list of the aspect names
            List<Advisor> candidateAdvisors = findCandidateAdvisors();
            for (Advisor advisor : candidateAdvisors) {
                if (advisor instanceof AspectJPointcutAdvisor &&
                        ((AspectJPointcutAdvisor) advisor).getAspectName().equals(beanName)) {
                    return true;
                }
            }
            return super.shouldSkip(beanClass, beanName);
        }
    

    整个应不应该跳过的逻辑是:获取所有的Advisor集合,然后遍历Advisor集合,如果当前bean的名称和其中的一个AspectJPointcutAdvisor的aspectName相同,其实就是说明这个类是切面类,那么就会跳过。到这里,我相信有的小伙伴就会发出一个疑问了:前面不是处理过了吗?为什么此处还是需要处理呢?其实这里我也有点疑问,但是它干了更多的事情,那就是创建并缓存了所有的Advisor对象。
    在findCandidateAdvisors方法的逻辑中首先是super执行了AbstractAdvisorAutoProxyCreator中的逻辑:找到所有的Advisor的实现类的bean名称,并进行getBean创建对象;其次时寻找@Aspect注解的类,然后为通知方法构建Advisor,整个的构建流程可以归纳如下:

    1. 拿到容器中所有的bean名称
    2. 循环遍历beanName,拿到beanType,判断是不是切面类(@Aspect)
    3. 是切面类的话,就拿到类中除去标有@Pointcut的方法。然后遍历方法:
    4. 把每个切面方法构建为Advisor【InstantiationModelAwarePointcutAdvisorImpl】
    5. 处理@DeclareParents属性注解,最终会构建DeclareParentsAdvisor【属于IntroductionAdvisor,类级别的切入】,其中对应的Advice为DelegatePerTargetObjectIntroductionInterceptor【属于IntroductionInterceptor,同样也是MethodInterceptor】
    6. 缓存进advisorsCache 【beanName--->List<Advisor>】

    2、Advice的构建

    其中,在构建InstantiationModelAwarePointcutAdvisorImpl中,会构建当前增强方法的Advice,也就是构造方法中的instantiateAdvice方法,最终会调用ReflectiveAspectJAdvisorFactory的getAdvice方法来构建相应的Advice,关键代码如下:

        switch (aspectJAnnotation.getAnnotationType()) {
                case AtPointcut:
                    if (logger.isDebugEnabled()) {
                        logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'");
                    }
                    return null;
                case AtAround:
                    springAdvice = new AspectJAroundAdvice(
                            candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
                    break;
                case AtBefore:
                    springAdvice = new AspectJMethodBeforeAdvice(
                            candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
                    break;
                case AtAfter:
                    springAdvice = new AspectJAfterAdvice(
                            candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
                    break;
                case AtAfterReturning:
                    springAdvice = new AspectJAfterReturningAdvice(
                            candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
                    AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation();
                    if (StringUtils.hasText(afterReturningAnnotation.returning())) {
                        springAdvice.setReturningName(afterReturningAnnotation.returning());
                    }
                    break;
                case AtAfterThrowing:
                    springAdvice = new AspectJAfterThrowingAdvice(
                            candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
                    AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation();
                    if (StringUtils.hasText(afterThrowingAnnotation.throwing())) {
                        springAdvice.setThrowingName(afterThrowingAnnotation.throwing());
                    }
                    break;
                default:
                    throw new UnsupportedOperationException(
                            "Unsupported advice type on method: " + candidateAdviceMethod);
            }
    
            // Now to configure the advice...
            springAdvice.setAspectName(aspectName);
            springAdvice.setDeclarationOrder(declarationOrder);
            String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod);
            if (argNames != null) {
                springAdvice.setArgumentNamesFromStringArray(argNames);
            }
            springAdvice.calculateArgumentBindings();
    

    最终会把切面类beanName和其中的增强方法Advisor存放到advisorsCache缓存中,方便后面使用。

    可以说准备阶段,Spring主要就干了两件事吧:第一件事是标记那些不需要代理的AOP基础设施类;第二件事就是寻找并创建容器中所有的Advisor,这一步也分为两小步:其一,去寻找并创建所有直接实现了Advisor接口的;其二,处理标注了@Aspect注解的类,并将其以aspectName及List<Advisor>的形式缓存到advisorsCache中。

    3、Advisor & Advice

    有必要介绍下AOP中的Advisor和Advice,这两个东西到底有什么区别和关系呢?Advice就是我们要干预正常代码而额外加的一部分代码逻辑,其实可以理解为拦截器,而Advisor可以说成是对Advice的一种封装吧,因为每个Advisor包含一个Advice,然后Advisor还应该包括Advice增强的增强表达式,即它应该什么时候进行增强,即增强条件吧;在Spring中,提供了两个Advisor的子接口,分别是IntroductionAdvisor和PointcutAdvisor,IntroductionAdvisor提供的getClassFilter()方法和PointcutAdvisor中提供的getPointcut()方法就是各自的增强条件(如下图),从增强条件我们也可以看出:IntroductionAdvisor是基于类级别的增强,而PointcutAdvisor是基于方法级别或者类级别的增强,显然后者方法级别的增强是更加细粒度的,也是我们常用的@Aspect注解的Advisor。如果这么说还是过于抽象的话,那我们拿我们常用的切面类@Aspect来进行来进行类比:


    Advisor.png
        @Component  //切面也是容器中的组件
    @Aspect //说明这是切面
    public class LogAspect {
    
        public LogAspect() {
            System.out.println("LogAspect....");
        }
    
    
        @DeclareParents(value = "com.spring.aop.HelloService", defaultImpl = DeclareParentsTestImpl.class)
        private DeclareParentsTest declareParentsTest;
    
        //前置通知
        @Before("execution(* com.spring.aop.HelloService.sayHello(..))")
        public void logStart(JoinPoint joinPoint) {
            String name = joinPoint.getSignature().getName();
            System.out.println("logStart()===>" + name + "...【args:" + Arrays.asList(joinPoint.getArgs()) + "】");
        }
    
        //返回通知
        @AfterReturning(value = "execution(* com.spring.aop.HelloService.sayHello(..))",returning = "result")
        public void logReturn(JoinPoint joinPoint,Object result) {
            String name = joinPoint.getSignature().getName();
            System.out.println("logReturn()==>" + name + "...【args:"+ Arrays.asList(joinPoint.getArgs())+"】【result:"+result+"】");
        }
    
        //后置通知
        @After("execution(* com.spring.aop.HelloService.sayHello(..))")
        public void logEnd(JoinPoint joinPoint) {
            String name = joinPoint.getSignature().getName();
            System.out.println("logEnd()===>" + name + "...【args:" + Arrays.asList(joinPoint.getArgs()) + "】");
        }
    
        //异常通知
        @AfterThrowing(value = "execution(* com.spring.aop.HelloService.sayHello(..))",throwing = "e")
        public void logError(JoinPoint joinPoint,Exception e) {
            String name = joinPoint.getSignature().getName();
            System.out.println("logError()==>" + name + "...【args:"+ Arrays.asList(joinPoint.getArgs())+"】【result:"+e+"】");
        }
    }
    

    像上面logStart,logReturn,logEnd,logError等就可以理解为一个Advice,而@Before,@AfterReturning,@After,@AfterThrowing中的表达式就会说一个Pointcut,然后方法加上注解中的表达式就构成了一个Advisor(PointcutAdvisor),相信这么说,大家应该都能明白了吧。

    三、Aop生成代理

    Spring提供了两个地方来生成Aop代理对象,下面我们来看看在哪两个地方可以生成Aop的代理对象:

    1、Aop生成代理对象的第一个地方

    第一个地方是在AbstractAutoProxyCreator的postProcessBeforeInstantiation方法中,Aop的准备工作做完后,就会查看有没有自定义的TargetSource,如果有符合的TargetSource的话就会在此处直接创建bean的代理对象,不会继续走Spring的创建bean的流程,但是,这个地方有个麻烦的点,就是我们需要干预到AnnotationAwareAspectJAutoProxyCreator的创建,需要修改其Bean的定义信息,将我们自定义的TargetSourceCreator赋值给AnnotationAwareAspectJAutoProxyCreator的customTargetSourceCreators属性,我们可以如下操作:
    自定义的TargetSource:

        public class MyTargetSource implements TargetSource {
        private final Object target;
    
        public MyTargetSource(Object target) {
            this.target = target;
        }
    
        @Override
        public Class<?> getTargetClass() {
            return HelloService.class;
        }
    
        @Override
        public boolean isStatic() {
            return true;
        }
    
        @Override
        public Object getTarget() throws Exception {
            return target;
        }
    
        @Override
        public void releaseTarget(Object target) throws Exception {
    
        }
    }
    

    封装成TargetSourceCreator:

        public class MyTargetSourceCreator implements TargetSourceCreator {
        @Override
        public TargetSource getTargetSource(Class<?> beanClass, String beanName) {
            if (beanName.equals("helloService")) {
                try {
                    return new MyTargetSource(ReflectionUtils.accessibleConstructor(HelloService.class).newInstance());
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            return null;
        }
    }
    

    实现BeanFactoryPostProcessor修改AnnotationAwareAspectJAutoProxyCreator的bean定义信息:

        @Component
    public class TargetSourceCreatorBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
        @Override
        public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
            BeanDefinition bd = beanFactory.getBeanDefinition(AopConfigUtils.AUTO_PROXY_CREATOR_BEAN_NAME); // AnnotationAwareAspectJAutoProxyCreator的beanName
            bd.getPropertyValues().add("customTargetSourceCreators",new TargetSourceCreator[]{new MyTargetSourceCreator()});
        }
    }
    

    如上述配置后,HelloService的对象创建就会在AOP准备工作做好后,通过如下代码,获取到自定义TargetSource后直接创建AOP代理对象,不走这个bean后面的生命周期了:

    //创建个代理,如果为这个类指定了targetSource会在此就生成代理直接返回了,不走这个bean后面的生命周期了
        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;
            }
    

    2、Aop生成代理对象的第二个地方

    另一种情况是,当没有为对象指定自定义的TargetSource时,Spring会在bean的对象创建完成后的AbstractAutoProxyCreator的postProcessAfterInitialization方法中的wrapIfNecessary方法中创建Aop代理对象,代码如下:

        Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
            if (specificInterceptors != DO_NOT_PROXY) {
                this.advisedBeans.put(cacheKey, Boolean.TRUE);
                Object proxy = createProxy(//创建代理对象,specificInterceptors所有的增强器
                        bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
    

    两处创建Aop代理对象的逻辑都是一样的,仅仅是时机不一样罢了,Spring默认使用的是CGLIB来创建代理对象的,具体的创建过程在此就不说了,后续有时间再补上,我们需要知道的是:当我们执行被AOP增强的类时,需要回调DynamicAdvisedInterceptor这个类中的intercept方法,这也是后面我们讲述AOP执行流程的入口。

    相关文章

      网友评论

        本文标题:AOP原理分析(一)准备阶段和代理阶段

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