• springboot笔记-4-.aop编程的简要概述


    前言

      前面几个章节说明了下spring是如何发现并注册类到容器,以及最终的实例化。本文则讲下spring中较为核心的内容,aop概念,又叫切面式编程。切面编程可以做到无侵入性的改变代码的执行逻辑,比如我们经常涉及到的日志,事物等概念便可以用切面的思维来解决。spring aop会涉及到两个比较重要的概念,1是动态代理  2是spring bean的生命周期   。两者在作者以前的博文里都有讲到,不了解的话可以翻翻,这儿就不着重讲了。

    案例

      在讲解之前我们先做一个小功能,模拟一个切面式编程。通过这个小功能相信你会对aop有一个大体的认知。我们知道springboot对于事物有简化处理,也就是声明式事物,我们只需要一个@Transcational注解就可以让方法包裹在事物中。那我们就先模拟一个简单的事物注解功能,了解下spring aop的大致工作原理。

    1.先声明一个类似的事物注解@MyTrans ,只要方法上有该注解说明方法受事物控制

    @Target({ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface MyTrans {
    }

    2. 声明一个BeanPostProcessor,实现其初始化后置方法

      之前讲解spring bean的实例化步骤时,讲解到在其声明周期中实例化的最后一步会执行所有的BeanPostProcessor的postProcessAfterInitialization方法来让我们做一些自定义的处理。我们就从这儿入手。有不了解的可以查看本系列的前一篇博客。

    @Configuration
    public class CustomizeCut implements BeanPostProcessor {
        @Override
        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
            List<Method> methods;
            if ((methods = shoudSkip(beanName,bean)).isEmpty()){
                return bean;
            }
            return CglibProxy.createProxy(bean,methods);
    
        }
    
        private static List<Method> shoudSkip(String beanName, Object bean){
            List<Method> transMethods = new ArrayList<>();
            Class<?> currentClass;
            if (!StringUtils.hasLength(beanName)
                    || Objects.isNull(bean)
                    || Modifier.isFinal((currentClass = bean.getClass()).getModifiers())
                    || Modifier.isPrivate(currentClass.getModifiers() )
                    || Modifier.isInterface(currentClass.getModifiers())){
                return transMethods;
            }
            Method[] methods = currentClass.getDeclaredMethods();
            for (Method method : methods) {
                if (method.getDeclaredAnnotation(MyTrans.class) != null){
                    transMethods.add(method);
                }
            }
    
            return transMethods;
    
    
        }
    }

      该类其实作用很简单,判断传入的bean是否需要被代理,如果不需要就直接返回原始bean,如果需要代理就进行代理。如何判断是否需要呢,逻辑也很简单,先判断该类是否满足被代理的基本条件,这儿我设置的比较严格,当然只是为了模拟,该类不能为接口,不能为prive或者final等。然后如果有方法上声明了@MyTrans注解则进行代理

    3.代理方法

      上述代码可以看到最终使用CglibProxy.createProxy(bean,methods);进行代理,我们可以看一下。这儿主要使用了cglib代理,不了解的建议先了解cglib代理,之前有博客讲到可以翻翻

    public class CglibProxy implements MethodInterceptor {
    
        private Object source;
    
        private List<Method> methods;
    
        private CglibProxy(Object source, List<Method> methods) {
            this.source = source;
            this.methods = methods;
        }
    
        public static Object createProxy(Object bean, List<Method> methods){
            return new CglibProxy(bean,methods).createNewInstance();
        }
    
        private Object createNewInstance(){
            Enhancer enhancer = new Enhancer();
            enhancer.setCallbacks(new Callback[]{this});
            enhancer.setSuperclass(source.getClass());
            return enhancer.create();
    
        }
        @Override
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            if (methods.contains(method)){
                System.out.println("开始事物控制");
                Object invoke ;
                try {
                     invoke = methodProxy.invoke(source, objects);
                }catch (Exception e){
                    System.out.println(String.format("发现异常 : %s 回滚事物控制",e.getMessage()));
                    throw e;
                }
                System.out.println("结束事物控制");
                return invoke;
            }
            return methodProxy.invoke(source, objects);
    
    
        }
    }

      可以看到在intercept方法中我们对其做了判断,如果被代理方法是在声明了@MyTrans注解的方法列表中则就将其加上事物控制。

      这样,一个简单的事物控制我们就实现了。可以看下效果

    4.测试

      使用springmvc测试一下

      主启动类

    @SpringBootApplication
    public class Application   {
    
        public static void main(String[] args) {
            SpringApplication.run(Application.class,args);
        }
    }

      测试类

    @RestController
    public class UserController {
    
    
    
        @GetMapping("/get1")
        public String getUser1(){
            System.out.println("执行方法体1");
            return "hello word" ;
        }
    
        @GetMapping("/get2")
        @MyTrans
        public String getUser2(){
            System.out.println("执行方法体2");
            return "hello word" ;
        }
    
    
    }

      可以看到我们get1方法没有加事物控制,而get2方法加上了事物控制。我们分别访问一下

      访问get1结果如下

       

      访问get2结果如下

      

      可以看到事物已经成功,我们可以试下在get2中加上一句会报错的代码,get2方法修改如下

        @GetMapping("/get2")
        @MyTrans
        public String getUser2(){
            System.out.println("执行方法体2");
            int i = 1/0;
            return "hello word" ;
        }

      然后再访问get2方法

       可以看到发现异常后事物已经成功回滚。

      小结

      这个案例到此就讲完了,我们是不是也很轻松的实现了一个类似的无侵入性的切面控制事物的方法,只要项目加入我们的@MyTrans注解,事物有关的东西就会被统一控制,而业务逻辑无需再关注。其实核心就两个步骤,通过自定义的BeanPostProcessor来拦截springBean初始化的最后一步,通过确定该bean是否要被代理。然后通过代理来植入我们想要的处理逻辑。最后spring的容器中得到的bean其实就是我们代理后的bean。

      其实spring aop的核心步骤也主要就是这两步,1.通过一个BeanPostProcessor判断该类是否需要被代理 ,即我们编写的切面是否有符合该类的   2.通过cglib代理植入所有找到的符合该类的切面逻辑。  当然了其判断过程肯定比我们来说较为繁琐,代理过程也较为繁琐并且其也支持jdk动态代理。不过大体上的流程是和我们上面一致的,有了总体的认知肯定分析起来要容易一些。

    正文

      现在我们开始正式讲解spring-aop代理,讲解之前依旧先看一下aop 代理的大致模样吧,记得先引入相关的包

        compile("org.springframework.boot:spring-boot-starter-aop:2.0.3.RELEASE")

      然后一般的我们所写的切面代码的大概会是如下的样子

    @Aspect
    @Component
    @Order(-1)
    public class AspectCut {
    
        @Pointcut("execution(public * com.test.controller.UserController.*(..))")
        public void excudeService2() {
    
        }
        @Around("excudeService2()")
        public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
            System.out.println("之前的行为");
            Object result = pjp.proceed();
            System.out.println("之后的行为");
            return result;
        }
    }

      使用@Aspect 注解声明该类是一个定义切面的类。 @Pointcut声明切点 。  @Around定义环绕行为。通过我们前面的案例来看,spring要做的操作肯定是将所有的这种切面找到。然后声明一个BeanPostProcessor ,着啊用那个bean实例化的后置处理器中进行验证,如果有某个切面符合bean,那么就为其创建代理,并将@Around方法的逻辑植入这个代理中。所以我们第一步就是要先找到这个BeanPostProcessor。关于这个入口其实可以从spring的自动化配置入手。

      在本系列第一章就有讲过spring自动化配置的关键,其会查找依赖下的META-INF/spring.factories文件。尤其是spring-boot-autoconfigure包下会引入大量的依赖。而我们打开这个包里面的META-INF/spring..factories文件。

       会发现就有个关于aop的自动配置类,所以我们打开这个类看一下。

    @Configuration
    @ConditionalOnClass({ EnableAspectJAutoProxy.class, Aspect.class, Advice.class,
            AnnotatedElement.class })
    @ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
    public class AopAutoConfiguration {
    
        @Configuration
        @EnableAspectJAutoProxy(proxyTargetClass = false)
        @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false", matchIfMissing = false)
        public static class JdkDynamicAutoProxyConfiguration {
    
        }
    
        @Configuration
        @EnableAspectJAutoProxy(proxyTargetClass = true)
        @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true", matchIfMissing = true)
        public static class CglibAutoProxyConfiguration {
    
        }
    
    }

      会发现其中果然就有根据依赖情况进行自动配置的地方。如果我们引入了aop相关的依赖包,那么这个配置就会起作用。里面有两个方法我们注意看,会发现是声明不同的代理,上面的是jdk,下面的是cglib,我们仔细看条件,也就是@CondinalOnProperty  会发现如果我们没有设置

    spring.aop.proxy-target-class的值 根据这个matchIfMsiiing的值来看来看,spring默认将会使用cglib动态代理了,但是如果被代理的类为接口,或者已经是jdk代理类,或者其实现了cglib代理需要的接口  (因为cglib是通过继承实现,jdk代理类为final   ,接口和final类型的类都不被继承)则使用jdk代理。具体分析会在后面给出,之前的版本默认还是有接口的话使用jdk动态代理。我们看下@EnableAspectJAutoProxy这个注解里面的代码究竟是怎么样的。

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Import(AspectJAutoProxyRegistrar.class)
    public @interface EnableAspectJAutoProxy {
    
        /**
         * Indicate whether subclass-based (CGLIB) proxies are to be created as opposed
         * to standard Java interface-based proxies. The default is {@code false}.
         */
        boolean proxyTargetClass() default false;
    
        /**
         * Indicate that the proxy should be exposed by the AOP framework as a {@code ThreadLocal}
         * for retrieval via the {@link org.springframework.aop.framework.AopContext} class.
         * Off by default, i.e. no guarantees that {@code AopContext} access will work.
         * @since 4.3.1
         */
        boolean exposeProxy() default false;
    
    }

      会发现有@Import注解引入了AspectJAutoProxyRegistrar这个类,记得前面我们在讲解spring的bean的发现配置时说道,spring在解析配置时@Import里面的类会分为两种来解析,一种是ImportBeanDefinitionRegistrar类型,该类主要用来注入自定义的BeanDifinition,一类是ImportSelector,主要用来加载自定义的配置类信息。这儿我们看AspectJAutoProxyRegistrar的结构会发现其属于ImportBeanDefinitionRegistrar。那也就是说主要用来注册自定义的BeanDifinition。这儿其实我们就可以猜想到了,这一步便是用来注册其处理所需要的BeanPostProcessor.

    1.注册自定义的切面处理PostProcessor

    class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
    
        @Override
        public void registerBeanDefinitions(
                AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
            //注册AnnotationAwareAspectJAutoProxyCreator
            AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
    
            //根据注解中proxyTargetClass的属性为AnnotationAwareAspectJAutoProxyCreator的BeanDefinition添加proxyTargetClass属性
            AnnotationAttributes enableAspectJAutoProxy =
                    AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
            if (enableAspectJAutoProxy != null) {
                if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
                    AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
                }
                if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
                    AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
                }
            }
        }
    
    }

      可以看到该方法第一步就是注册BeanPostProcessor,然后根据我们传入的proxyTargetClass属性为@BeanPostProcessor也设置proxyTargetClass属性,默认为true。

      我们主要看下注册逻辑。

    @Nullable
    private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry,
            @Nullable Object source) {
    
        Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
        //如果已经被注册  且和当前名字不一样,那就覆盖一下名字
        if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
            BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
            if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
                int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
                int requiredPriority = findPriorityForClass(cls);
                if (currentPriority < requiredPriority) {
                    apcDefinition.setBeanClassName(cls.getName());
                }
            }
            return null;
        }
        //声明一个RootBeanDefinition   记住类型设置为了AnnotationAwareAspectJAutoProxyCreator
        RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
        //设置源对象  这儿为null
        beanDefinition.setSource(source);
        //添加order  即在bean容器中的顺序  这儿使用最高优先级
        beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
        //设置role
        beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
        //beanDefinition将注册进spring容器中  名字为org.springframework.aop.config.internalAutoProxyCreator
        registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
        return beanDefinition;
    }

      可以看到这就是一个简单的spring容器注册代码,将自定义的BeanDefinition注册到spring中,而后面spring实例化该类的时候就是根据这个BeanDefinition,记住其使用了最高优先级,也就是说最早初始化。这儿的BeanDefinition所指向的类型便是AnnotationAwareAspectJAutoProxyCreator

      我们可以了解下该类的结构图,方便后续使用。

       这儿注意两个圈中的地方,其实现了aware与beanPostProcessor,说明该类在spring的bean实例及初始化中还是扮演着较为重要的角色,当然了,本文还是主要看其实现了BeanPostProcssor的postProcessAfterInitialization方法,该方法是spring bean实例化中的最后一环。着重看这个方法

    2.执行BeanPostProcessor的后置方法

      我们从AnnotationAwareAspectJAutoProxyCreator实现了BeanPostProcssor的postProcessAfterInitialization方法开始,注意具体的实现在其抽象父类AbstractAutoProxyCreator中,不要找错了。

        @Override
        public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) throws BeansException {
            if (bean != null) {
                //如果bean不为null  且没有在缓存中
                Object cacheKey = getCacheKey(bean.getClass(), beanName);
                if (!this.earlyProxyReferences.contains(cacheKey)) {
                    //该方法就如果有必要包装代理就包装代理
                    return wrapIfNecessary(bean, beanName, cacheKey);
                }
            }
            return bean;
        }

      该方法很简单,先查询缓存,如果没有则执行wrapIfNecessary,顾名思义就是根据需要选择是否包装返回,我们接着看

    protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
        //依旧先检查类是否可用,是否已经被代理过了
        if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
            return bean;
        }
        //如果该类不需要代理则直接返回
        if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
            return bean;
        }
        // 判断是否是基础类,或者简要判断下是否应该跳过
        if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
            this.advisedBeans.put(cacheKey, Boolean.FALSE);
            return bean;
        }
    
        // 查询满足该类的所有代理逻辑
        Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
        //如果不为空则返回代理
        if (specificInterceptors != DO_NOT_PROXY) {
            //为该类打上标签需要代理
            this.advisedBeans.put(cacheKey, Boolean.TRUE);
            //根据bean  以及所有的切面逻辑,创建代理返回
            Object proxy = createProxy(
                    bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));        
            this.proxyTypes.put(cacheKey, proxy.getClass());
            return proxy;
        }
        //为该类打上标签不用代理
        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return bean;
    }

      该类中也是先做了个简要判断,然后再根据寻找结果决定是否生成代理。这儿我们先看下简要判断,也就是if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName))   。

      这儿有两,第一个是isInfrastructureClass,也就是判断是否为基础类,可以看下spring认为哪些是基础类

        protected boolean isInfrastructureClass(Class<?> beanClass) {
            boolean retVal = Advice.class.isAssignableFrom(beanClass) ||
                    Pointcut.class.isAssignableFrom(beanClass) ||
                    Advisor.class.isAssignableFrom(beanClass) ||
                    AopInfrastructureBean.class.isAssignableFrom(beanClass);
            if (retVal && logger.isTraceEnabled()) {
                logger.trace("Did not attempt to auto-proxy infrastructure class [" + beanClass.getName() + "]");
            }
            return retVal;
        }

      advice,Pointcut,Advisor,AopInfrastructureBean都被认为是基础类,这种类不会被代理。

      第二个判断逻辑是shouldSkip,注意先看AspectJAwareAdvisorAutoProxyCreator的shouldSkip方法。

        protected boolean shouldSkip(Class<?> beanClass, String beanName) {
            // 找到所有的切面Advisor  然后循环判断是否为AspectJPointcutAdvisor且名字为我们传入的类
            // 也就是我们的@Aspect 声明的类是不会被代理的
            List<Advisor> candidateAdvisors = findCandidateAdvisors();
            for (Advisor advisor : candidateAdvisors) {
                if (advisor instanceof AspectJPointcutAdvisor &&
                        ((AspectJPointcutAdvisor) advisor).getAspectName().equals(beanName)) {
                    return true;
                }
            }
            //写死了返回为false
            return super.shouldSkip(beanClass, beanName);
        }

      方法中findCandidateAdvisors为找到系统所有的定义了的切面工具,这个我们后面会讲到,这儿暂时不讲

    3.查询符合的条件的切面逻辑

      也就是第2节中第二步代码中的如下代码,该方法便会找到所有的切面,然后每个都和当前bean进行匹配,如果成功便加入数组返回

    Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);

      我们看下具体的逻辑,注意具体的逻辑在AbstractAdvisorAutoProxyCreator的getAdvicesAndAdvisorsForBean中,不要找错了

        protected Object[] getAdvicesAndAdvisorsForBean(
                Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {
    
            List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
            if (advisors.isEmpty()) {
                //DO_NOT_PROXY为null
                return DO_NOT_PROXY;
            }
            return advisors.toArray();
        }

      简单的判断代码,这儿注意返回的数组是Advisor类型的。接着看findEligibleAdvisors代码

    protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
        //找到所有的Advisor
        List<Advisor> candidateAdvisors = findCandidateAdvisors();
        //实现出和当前匹配的Advisor
        List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
        //为eligibleAdvisors添加一个新的默认Advisor
        extendAdvisors(eligibleAdvisors);
        if (!eligibleAdvisors.isEmpty()) {
            //将切面处理排序
            eligibleAdvisors = sortAdvisors(eligibleAdvisors);
        }
        return eligibleAdvisors;
    }

      这儿的核心逻辑主要有两步,找到系统所有的切面处理工具,找到匹配当前类的切面。这两步我们核心的说一下。

    3.1 找到所有切面

      我们讲到spring-aop中使用切面需要为其加上一个@Aspect 注解,那想必查找逻辑我们也能推断出,遍历整个spring容器中的类,然后将符合条件的有@Aspect注解的类找出来,然后解析封装为advisor返回。具体的逻辑是不是这样我们看下查找的逻辑就知道了。

      记住我们此时在AnnotationAwareAspectJAutoProxyCreator.findCandidateAdvisors中。

        @Override
        protected List<Advisor> findCandidateAdvisors() {
            // Add all the Spring advisors found according to superclass rules.
            List<Advisor> advisors = super.findCandidateAdvisors();
            // Build Advisors for all AspectJ aspects in the bean factory.
            if (this.aspectJAdvisorsBuilder != null) {
                advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
            }
            return advisors;
        }

      可以看到分了两步加载,第一步是使用父类也就是AbstractAdvisorAutoProxyCreator的findCandidateAdvisors加载,然后在使用当前类的aspectJAdvisorsBuilder.buildAspectJAdvisors中查找。我们两个分开看下。

      先看父类的查找

        protected List<Advisor> findCandidateAdvisors() {
            Assert.state(this.advisorRetrievalHelper != null, "No BeanFactoryAdvisorRetrievalHelper available");
            return this.advisorRetrievalHelper.findAdvisorBeans();
        }

      注意advisorRetrievalHelper为BeanFactoryAdvisorRetrievalHelper,我们看BeanFactoryAdvisorRetrievalHelper.findAdvisorBeans()方法、

        public List<Advisor> findAdvisorBeans() {
            
            String[] advisorNames = null;
            synchronized (this) {
                advisorNames = this.cachedAdvisorBeanNames;
                if (advisorNames == null) {
                    advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
                            this.beanFactory, Advisor.class, true, false);
                    this.cachedAdvisorBeanNames = advisorNames;
                }
            }
            if (advisorNames.length == 0) {
                return new LinkedList<>();
            }
    
            List<Advisor> advisors = new LinkedList<>();
            for (String name : advisorNames) {
                if (isEligibleBean(name)) {
                    if (this.beanFactory.isCurrentlyInCreation(name)) {
                        if (logger.isDebugEnabled()) {
                            logger.debug("Skipping currently created advisor '" + name + "'");
                        }
                    }
                    else {
                        try {
                            advisors.add(this.beanFactory.getBean(name, Advisor.class));
                        }
                        catch (BeanCreationException ex) {
                            Throwable rootCause = ex.getMostSpecificCause();
                            if (rootCause instanceof BeanCurrentlyInCreationException) {
                                BeanCreationException bce = (BeanCreationException) rootCause;
                                String bceBeanName = bce.getBeanName();
                                if (bceBeanName != null && this.beanFactory.isCurrentlyInCreation(bceBeanName)) {
                                    if (logger.isDebugEnabled()) {
                                        logger.debug("Skipping advisor '" + name +
                                                "' with dependency on currently created bean: " + ex.getMessage());
                                    }
     
                                    continue;
                                }
                            }
                            throw ex;
                        }
                    }
                }
            }
            return advisors;
    }

        该方法就就是一个从spring的容器中加载所有的实现了Advisor接口的类,我就不细讲了。代码虽然多但相信大家很容易看懂。我们主要讲下this.aspectJAdvisorsBuilder.buildAspectJAdvisors()这个方法,是如何解析返回的。注意aspectJAdvisorsBuilder类型为BeanFactoryAspectJAdvisorsBuilderAdapter,但其buildAspectJAdvisors则的逻辑则在其父类BeanFactoryAspectJAdvisorsBuilder中。我们直接看其buildAspectJAdvisors()方法。

      

        public List<Advisor> buildAspectJAdvisors() {
            //aspectBeanNames主要缓存已经发现了的切面定义类
            List<String> aspectNames = this.aspectBeanNames;
            //如果为null则执行如下逻辑
            if (aspectNames == null) {
                synchronized (this) {
                    aspectNames = this.aspectBeanNames;
                    if (aspectNames == null) {
                        List<Advisor> advisors = new LinkedList<>();
                        //初始化aspectNames
                        aspectNames = new LinkedList<>();
                        //将所有的类查出类
                        String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
                                this.beanFactory, Object.class, true, false);
                        for (String beanName : beanNames) {
                            //查看该类是否属于要过滤的
                            if (!isEligibleBean(beanName)) {
                                continue;
                            }
                            //获取bean的class信息
                            Class<?> beanType = this.beanFactory.getType(beanName);
                            if (beanType == null) {
                                continue;
                            }
                            //查看是否为切面类  
                            //1.  要有@Aspect 注解
                            //2. 没有属性值以ajc$开头
                            if (this.advisorFactory.isAspect(beanType)) {
                                //将该bean加入aspectNames缓存中
                                aspectNames.add(beanName);
                                //获取该切面的元数据
                                AspectMetadata amd = new AspectMetadata(beanType, beanName);
                                //如果切面类型显示为sington类型
                                if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
                                    //根据构建的MetadataAwareAspectInstanceFactory创建Advisor
                                    MetadataAwareAspectInstanceFactory factory =
                                            new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
                                    List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
                                    //加入缓存
                                    if (this.beanFactory.isSingleton(beanName)) {
                                        this.advisorsCache.put(beanName, classAdvisors);
                                    }
                                    else {
                                        this.aspectFactoryCache.put(beanName, factory);
                                    }
                                    advisors.addAll(classAdvisors);
                                }
                                else {
                                    // 如果切面类型不是sington但  spring容器中显示其为sington则抛异常
                                    if (this.beanFactory.isSingleton(beanName)) {
                                        throw new IllegalArgumentException("Bean with name '" + beanName +
                                                "' is a singleton, but aspect instantiation model is not singleton");
                                    }
                                    //根据构建的MetadataAwareAspectInstanceFactory创建Advisor
                                    MetadataAwareAspectInstanceFactory factory =
                                            new PrototypeAspectInstanceFactory(this.beanFactory, beanName);
                                    //加入缓存
                                    this.aspectFactoryCache.put(beanName, factory);
                                    advisors.addAll(this.advisorFactory.getAdvisors(factory));
                                }
                            }
                        }
                        this.aspectBeanNames = aspectNames;
                        return advisors;
                    }
                }
            }
            //不为null但为空,说明已经被实例化,之前就没找到,所以直接返回空集合
            if (aspectNames.isEmpty()) {
                return Collections.emptyList();
            }
            //迭代之前的aspectNames然后获取到advisorsCache缓存中的Advisor并返回
            List<Advisor> advisors = new LinkedList<>();
            for (String aspectName : aspectNames) {
                List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName);
                if (cachedAdvisors != null) {
                    advisors.addAll(cachedAdvisors);
                }
                else {
                    MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName);
                    advisors.addAll(this.advisorFactory.getAdvisors(factory));
                }
            }
            return advisors;
        }

      其实这儿就和我们预想的几乎一致,就是遍历spring容器中的所有的类,然后挨个验证是否为切面类,如果是就解析该类构建Advisor,并加入到缓存中。具体的解析流程我们可以简单看下,也就是如下代码

    List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);

      注意advisorFactory为ReflectiveAspectJAdvisorFactory。看下其getAdvisors();

        @Override
        public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
            Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
            String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName();
            validate(aspectClass);
    
            //创建切面类的factory
            MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory =
                    new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory);
    
            List<Advisor> advisors = new LinkedList<>();
            //遍历每个method  //有@PointCut注解的除外
            for (Method method : getAdvisorMethods(aspectClass)) {
                //根据元数据信息构建Advisor
                Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName);
                if (advisor != null) {
                    advisors.add(advisor);
                }
            }
    
            ../省略代码
            return advisors;
        }

      我们会发现其会将切面类的所有方法(有@Pointcut注解的除外)进行迭代,然后创建Advisor并返回,如果结果不为null则添加到advisor容器中。我们看下getAdvisor方法的逻辑

        public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory,
                int declarationOrderInAspect, String aspectName) {
    
            validate(aspectInstanceFactory.getAspectMetadata().getAspectClass());
            //根据方法获取对应的切点,如果没有就返回null
            AspectJExpressionPointcut expressionPointcut = getPointcut(
                    candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());
            if (expressionPointcut == null) {
                return null;
            }
            //如果有就返回一个InstantiationModelAwarePointcutAdvisorImpl  (实现了advisor接口)
            return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,
                    this, aspectInstanceFactory, declarationOrderInAspect, aspectName);
        }

      可以看到会根据每个方法类获取切点,如果能获取到就构建InstantiationModelAwarePointcutAdvisorImpl返回,不能获取到就返回null代表该方法不是一个advisor。

      

      到此,我们就成功构建了advisor了,其实总的来说很简单,就是遍历spring中的类,判断是否为切面类,如果是就解析该类的所有没有@Pointcut注解的方法并进行迭代,如果某个方法能找到对应的切点,那么就将该方法和其切点信息包装为一个InstantiationModelAwarePointcutAdvisorImpl并会即可。

      到此我们已经获取到了所有的advisor了,可以知道一个advisor对应着的就是一个切面方法,也就是我们案例中使用@Around 或者@Before 类似注解的方法。

    3.2 advisor与bean进行匹配,找到合适的advisor。

      3.1中我们找到了所有的advisor,但并不是所有的都满足,所以我们需要找到满足当前bean的advisor。我们回到第2节中的方法中的如下代码就是进行过滤

    List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);

      跟着代码走我们最终会找到如下代码,记住我们的advisor类型为PointcutAdvisor

        public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) {
            if (advisor instanceof IntroductionAdvisor) {
                return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);
            }
            else if (advisor instanceof PointcutAdvisor) {
                PointcutAdvisor pca = (PointcutAdvisor) advisor;
                return canApply(pca.getPointcut(), targetClass, hasIntroductions);
            }
            else {
                // It doesn't have a pointcut so we assume it applies.
                return true;
            }
        }

      最终会执行canApply来判定当前类是否合适,由于篇幅有限,且spring解析时会调用AspectJ的切面校验的方法异常繁琐会涉及到表达式,或者注解等多种匹配算法。所以这儿就不细讲,有兴趣的同学可以继续往下看。之所以说spring-aop依赖aspectJ也主要就是使用aspectj来进行匹配校验。

    3.3 每个advisor添加一个DefaultPointcutAdvisor,然后进行排序

      

        //添加默认的DefaultPointcutAdvisor   记住advisor过滤后不为空才会添加
        extendAdvisors(eligibleAdvisors);
            if (!eligibleAdvisors.isEmpty()) {
                //不为空则排序
                eligibleAdvisors = sortAdvisors(eligibleAdvisors);
            }

      这个排序最终会根据我们切面上面的@Order的进行排序,经常被问到spring多个切面执行顺序的同学要注意了。至于切面不同的切入类型  例如@Before ,@Around的执行类型的顺序下面有一张图可以参考,图出处https://www.cnblogs.com/zxf330301/p/10813647.html

    4.根据匹配的advisor创建代理,织入我们的逻辑。

      回到第2步,我们已经找到了符合条件的advisor,我们接着看是如何创建代理并返回的。

        Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
            if (specificInterceptors != DO_NOT_PROXY) {
                this.advisedBeans.put(cacheKey, Boolean.TRUE);
                Object proxy = createProxy(
                        bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
                this.proxyTypes.put(cacheKey, proxy.getClass());
                return proxy;
            }

      可以看到如果合适的advisor不为null的话,就会创建代理。我们还是来看下代理的逻辑。

    protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
                @Nullable Object[] specificInterceptors, TargetSource targetSource) {
    
            if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
                AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
            }
            //创建代理工厂
            ProxyFactory proxyFactory = new ProxyFactory();
            proxyFactory.copyFrom(this);
            //即我们一开始传入的注解上的ProxyTargetClass属性  默认为true
            if (!proxyFactory.isProxyTargetClass()) {
                if (shouldProxyTargetClass(beanClass, beanName)) {
                    proxyFactory.setProxyTargetClass(true);
                }
                else {
                    evaluateProxyInterfaces(beanClass, proxyFactory);
                }
            }
            //确定一下需要的Advisor
            Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
            //设置匹配的advisors
            proxyFactory.addAdvisors(advisors);
            //设置我们包装的原始bean信息
            proxyFactory.setTargetSource(targetSource);
            //自定义处理下代理工厂  扩展方法
            customizeProxyFactory(proxyFactory);
            proxyFactory.setFrozen(this.freezeProxy);
            if (advisorsPreFiltered()) {
                proxyFactory.setPreFiltered(true);
            }
            //获取代理
            return proxyFactory.getProxy(getProxyClassLoader());
        }

      该类也是做了一些特殊处理,然后包装了一个代理工厂ProxyFactory,由他来创建代理。

      我们看下proxyFactory.getProxy(getProxyClassLoader());

        public Object getProxy(@Nullable ClassLoader classLoader) {
            return createAopProxy().getProxy(classLoader);
        }

      可以看到其先创建了代理工厂,然后创建代理类。spring是如何判断应该使用哪个代理工厂呢?  其实我们看下源代码就知道了。最终创建代码如下

        @Override
        public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
            //Optimize默认为false    ProxyTargetClass则是aop注解中传入的 默认为true   没有实现任何代理有关的接口
            if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
                Class<?> targetClass = config.getTargetClass();
                //不能为null
                if (targetClass == null) {
                    throw new AopConfigException("TargetSource cannot determine target class: " +
                            "Either an interface or a target is required for proxy creation.");
                }
                //如果其为接口或者已经是代理类了,就必须使用jdk动态代理
                if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
                    return new JdkDynamicAopProxy(config);
                }
                //使用cglib代理
                return new ObjenesisCglibAopProxy(config);
            }
            else {
                //否则使用jdk代理
                return new JdkDynamicAopProxy(config);
            }
        }

      我们这儿其实就可以看到spring常被问到的默认代理该如何回答了。其默认使用cglib代理,但是如果被代理的类为接口,或者已经是代理类,或者其实现了cglib代理需要的接口  (因为cglib时通过继承实现,代理类为final   ,接口和final类型的类都不被继承)则使用jdk代理

    public Object getProxy(@Nullable ClassLoader classLoader) {
            if (logger.isDebugEnabled()) {
                logger.debug("Creating CGLIB proxy: target source is " + this.advised.getTargetSource());
            }
    
            try {
                //得到要被代理的class类
                Class<?> rootClass = this.advised.getTargetClass();
                Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");
    
                Class<?> proxySuperClass = rootClass;
                //判断下其是否已经是cglib代理类
                if (ClassUtils.isCglibProxyClass(rootClass)) {
                    proxySuperClass = rootClass.getSuperclass();
                    Class<?>[] additionalInterfaces = rootClass.getInterfaces();
                    for (Class<?> additionalInterface : additionalInterfaces) {
                        this.advised.addInterface(additionalInterface);
                    }
                }
    
                // 验证一下
                validateClassIfNecessary(proxySuperClass, classLoader);
    
                // 配置Enhancer  也就是cglib代理工具
                Enhancer enhancer = createEnhancer();
                //设置classloader
                if (classLoader != null) {
                    enhancer.setClassLoader(classLoader);
                    if (classLoader instanceof SmartClassLoader &&
                            ((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
                        enhancer.setUseCache(false);
                    }
                }
                //设置其要继承的类
                enhancer.setSuperclass(proxySuperClass);
                //设置其接口
                enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
                //命名
                enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
                enhancer.setStrategy(new ClassLoaderAwareUndeclaredThrowableStrategy(classLoader));
    
                //生成需要的callback  也就是将我们的advisor按照一定的规则写入到实现了MethodInterceptor的类中
                Callback[] callbacks = getCallbacks(rootClass);
                Class<?>[] types = new Class<?>[callbacks.length];
                for (int x = 0; x < types.length; x++) {
                    types[x] = callbacks[x].getClass();
                }
                // fixedInterceptorMap only populated at this point, after getCallbacks call above
                enhancer.setCallbackFilter(new ProxyCallbackFilter(
                        this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
                enhancer.setCallbackTypes(types);
    
                // 创建并返回代理类
                return createProxyClassAndInstance(enhancer, callbacks);
            }
            catch (CodeGenerationException | IllegalArgumentException ex) {
                throw new AopConfigException("Could not generate CGLIB subclass of " + this.advised.getTargetClass() +
                        ": Common causes of this problem include using a final class or a non-visible class",
                        ex);
            }
            catch (Throwable ex) {
                // TargetSource.getTarget() failed
                throw new AopConfigException("Unexpected AOP exception", ex);
            }
        }

      这个方法的部分代码看着挺眼熟的,其实就是创建Enhancer然后再生成代理类,大概和我们的案例一致,当然了,其中核心的属性肯定是callback,这是代理的执行逻辑。

    Callback[] callbacks = getCallbacks(rootClass);

    我们也可以看下getCallbacks。

    private Callback[] getCallbacks(Class<?> rootClass) throws Exception {
            // Parameters used for optimization choices...
            boolean exposeProxy = this.advised.isExposeProxy();
            boolean isFrozen = this.advised.isFrozen();
            boolean isStatic = this.advised.getTargetSource().isStatic();
    
            // Choose an "aop" interceptor (used for AOP calls).
            Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised);
    
            ../代码略
            return callbacks;
        }

      无关的代码略,主要就看这个aopInterceptor ,这个里面包含的就是我们的advisor。

    private static class DynamicAdvisedInterceptor implements MethodInterceptor, Serializable {
            //该类中包含了我们的所有该类匹配的advisors
            private final AdvisedSupport advised;
    
            public DynamicAdvisedInterceptor(AdvisedSupport advised) {
                this.advised = advised;
            }
    
            @Override
            @Nullable
            public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                Object oldProxy = null;
                boolean setProxyContext = false;
                Object target = null;
                TargetSource targetSource = this.advised.getTargetSource();
                try {
                    if (this.advised.exposeProxy) {
                        // Make invocation available if necessary.
                        oldProxy = AopContext.setCurrentProxy(proxy);
                        setProxyContext = true;
                    }
                    target = targetSource.getTarget();
                    Class<?> targetClass = (target != null ? target.getClass() : null);
                    List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
                    Object retVal;
                    //如果该方法没有合适的切面
                    if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
                        //就直接执行
                        Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
                        retVal = methodProxy.invoke(target, argsToUse);
                    }
                    else {
                        // 执行切面逻辑
                        retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
                    }
                    //对返回结果进行 处理 也就是@AfterReturn之类的之类的
                    retVal = processReturnType(proxy, target, method, retVal);
                    return retVal;
                }
                finally {
                    if (target != null && !targetSource.isStatic()) {
                        targetSource.releaseTarget(target);
                    }
                    if (setProxyContext) {
                        // Restore old proxy.
                        AopContext.setCurrentProxy(oldProxy);
                    }
                }
            }

      可以发现其会根据当前方法是否有合适匹配的adviser,如果有的话就会执行切面逻辑,如果没有就直接执行方法并返回。至于具体的执行逻辑,也就是retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();这行代码由于篇幅问题就不在讲了,本篇主要讲下spring-aop简要原理介绍。不过相信也肯定是拿到AdvisedSupport 中的所有符合该方法的advisor(调用的也是aspectJ的匹配校验方法),然后根据类型在不同的时候执行即可。

      到此spring-aop编程的简要概述就讲完了。我们可以简要总结下其步骤   主要就是动态代理,静态织入

    1. 通过spring的自动化配置执行了AopAutoConfiguration配置,并向spring中注册了添加了AspectJAutoProxyRegistrar
    2. AspectJAutoProxyRegistrar会向spring中注册一个BeanPostProcessor =》AnnotationAwareAspectJAutoProxyCreator
    3. AnnotationAwareAspectJAutoProxyCreator在后置方法postProcessAfterInitialization中会查询当前类是否需要被代理
    4. 查找系统系统中所有的切面(有@Aspect注解的类),并根据其定义的切面信息为每个有效的切面方法生成一个advisor =》InstantiationModelAwarePointcutAdvisorImpl
    5. 通过各种匹配方式(包名  注解等等..)筛选出和当前bean匹配的advisor
    6. 如果advisor不为空,则选择一个代理工厂为其创建代理(会根据条件选择jdk还是cglib动态代理,默认一般情况使用cglib)
    7. cglib动态代理中所有的advisor会被放置到AdvisedSupport类中,并将该类设置到DynamicAdvisedInterceptor(实现了MethodInterceptor,也就是一个callback)类中
    8. DynamicAdvisedInterceptor在其interceptor方法中会根据是否有合适的advisor来选择是否执行切面逻辑,如果有则根据advisor的类型在不同的时机执行。
    9. 最终代理类被创建成功并返回,我们获取到的就为代理类,且切面即advisor已经生效。
  • 相关阅读:
    窗体设计器出不来
    maven ...../.m2/settings.xml
    myeclipse.ini
    人民币大小写
    驱动程序无法通过使用安全套接字层(SSL)加密与 SQL Server 建立 安全连接。错误:
    写了一个浏览器插件
    用Excel计算加权平均分和GPA
    cfree使用cygwin编译程序出现计算机丢失cygwin1.dll解决办法
    apt-cyg update --2017-02-17 07:57:24-- http://mirrors.163.com/cygwin//x86_64/setup.bz2 正在解析主机 mirrors.163.com... 123.58.173.185, 123.58.173.186 正在连接 mirrors.163.com|123.58.173.185|:80... 已连接。 已发出 HTT
    生产者消费者问题
  • 原文地址:https://www.cnblogs.com/hetutu-5238/p/12408410.html
Copyright © 2020-2023  润新知