Spring AOP 注解配置 源码学习

作者: jwfy | 来源:发表于2018-02-07 20:17 被阅读18次

    上篇Spring AOP学习中已经基本介绍了AOP是如何使用的,本文章来说说AOP注解方法的源码细节
    先提几个问题,在接下来的源码学习中发现答案

    • <aop:aspectj-autoproxy />的工作原理是什么,能带来什么作用
    • @Aspect是在什么时候被操作的
    • 各个切入点是按照什么样的顺序执行
    • 切入点是如何和对应的bean绑定在一起的

    具体的demo可以看Spring AOP学习#注解

    XML配置解析

    AOP的配置最简单的方法就是在xml中加入<aop:aspectj-autoproxy />,应该很清楚spring是选择什么命名空间去解析了,直接定位到AopNamespaceHandler 类,而且定位的是AspectJAutoProxyBeanDefinitionParser解析器,如果这点有什么疑问的话,最好是再看看spring xml的bean提取 源码学习,看完就懂了为什么会直接定位到该解析器

    public class AopNamespaceHandler extends NamespaceHandlerSupport {
    
        @Override
        public void init() {
            // In 2.0 XSD as well as in 2.1 XSD.
            registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
            registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
            // AspectJAutoProxyBeanDefinitionParser 是我们这次关注的类
            registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());
            registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
        }
    }
    

    AspectJAutoProxyBeanDefinitionParser 类

    class AspectJAutoProxyBeanDefinitionParser implements BeanDefinitionParser {
        
        @Override
        public BeanDefinition parse(Element element, ParserContext parserContext) {
    AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
          // 可以生成AnnotationAwareAspectJAutoProxyCreator类的beandefinition
          // 并注册到IOC容器中
            extendBeanDefinition(element, parserContext);
            // 扩展该bean的属性信息
            return null;
        }
    
        private void extendBeanDefinition(Element element, ParserContext parserContext) {
            BeanDefinition beanDef =
                    parserContext.getRegistry().getBeanDefinition(AopConfigUtils.AUTO_PROXY_CREATOR_BEAN_NAME);
          // 该bean其实就是上面所说的AnnotationAwareAspectJAutoProxyCreator的beandefinition
            if (element.hasChildNodes()) {
                addIncludePatterns(element, parserContext, beanDef);
                // 如果包含了子节点可以添加额外的includePattern
            }
        }
    
        private void addIncludePatterns(Element element, ParserContext parserContext, BeanDefinition beanDef) {
            ManagedList<TypedStringValue> includePatterns = new ManagedList<TypedStringValue>();
            NodeList childNodes = element.getChildNodes();
            for (int i = 0; i < childNodes.getLength(); i++) {
                Node node = childNodes.item(i);
                if (node instanceof Element) {
                    Element includeElement = (Element) node;
                    TypedStringValue valueHolder = new TypedStringValue(includeElement.getAttribute("name"));
                    // 获取子属性key为name的值
                    valueHolder.setSource(parserContext.extractSource(includeElement));
                    includePatterns.add(valueHolder);
                }
            }
            if (!includePatterns.isEmpty()) {
                includePatterns.setSource(parserContext.extractSource(element));
                beanDef.getPropertyValues().add("includePatterns", includePatterns);
                // 给该beandefinition赋属性值,includePatterns
            }
        }
    }
    

    如上述代码中的AnnotationAwareAspectJAutoProxyCreator类创建中,如果跳进去能够看到还可以为该类设置proxy-target-classexpose-proxy两个boolean属性值。
    也可以设置子属性值name,例如下面的设置

    <aop:aspectj-autoproxy proxy-target-class="false" expose-proxy="true">
       <aop:include name="******" />
    </aop:aspectj-autoproxy>
    

    这样就清楚了这个xml配置的原理是如何,其实就是新添加了一个beandefinition而已。

    Aspect 扫描和实例化

    spring正常的注册的bean,如果没有加@Aspect注解也是没有用的,那么就来学习下是在什么时候被扫描到,以及后续的操作是什么。

    如果大概看了AnnotationAwareAspectJAutoProxyCreator类的相关内容,可以知道AOP所有的aspect以及pointCut都存储在该对象中,所以扫描所有的注解@Aspect类,肯定是在AnnotationAwareAspectJAutoProxyCreator类的实例化之后

    在refresh()代码中,在实例化具体的bean之前,已经完成了spring层面的bean实例


    image.png

    在接下来对org.springframework.context.event.internalEventListenerProcessor对象实例化的过程中会完成对@aspect类的扫描操作,并把相关信息注入到上图圈住的AnnotationAwareAspectJAutoProxyCreator类中。

    随着代码调试到AbstractAutoProxyCreator文件

    AbstractAutoProxyCreator 类

    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {  
       Object cacheKey = getCacheKey(beanClass, beanName);
       // 从当前的cache容器获取到bean的name
        if (beanName == null || !this.targetSourcedBeans.contains(beanName)) {
            if (this.advisedBeans.containsKey(cacheKey)) {
                return null;
                // 该advisedBeans已经包含了该key,则直接退出不考虑
            }
            if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
               // 该bean不符合所有获取到的切面,设置为false
               // shouldSkip方法看下面的代码
                this.advisedBeans.put(cacheKey, Boolean.FALSE);
                return null;
            }
        }
    
       // 所有的注解信息已经获取到了
        // 具体通过代理类生成一个对象信息
        if (beanName != null) {
            TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
            if (targetSource != null) {
                this.targetSourcedBeans.add(beanName);
                Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
                Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
                // 利用ProxyFactory 生成一个新的对象
                this.proxyTypes.put(cacheKey, proxy.getClass());
                return proxy;
            }
        }
    
        return null;
    }
    

    AspectJAwareAdvisorAutoProxyCreator 类

    protected boolean shouldSkip(Class<?> beanClass, String beanName) {
        List<Advisor> candidateAdvisors = findCandidateAdvisors();
        // 获取所有的advisor,也就是PointCut,具体细节可看下面的函数执行过程代码
        for (Advisor advisor : candidateAdvisors) {
            if (advisor instanceof AspectJPointcutAdvisor) {
                if (((AbstractAspectJAdvice) advisor.getAdvice()).getAspectName().equals(beanName)) {
                    return true;
                }
            }
        }
        return super.shouldSkip(beanClass, beanName);
    }
    

    BeanFactoryAdvisorRetrievalHelper 类

    这个类是AnnotationAwareAspectJAutoProxyCreator的一个对象

        public List<Advisor> findAdvisorBeans() {
            String[] advisorNames = null;
            synchronized (this) {
                advisorNames = this.cachedAdvisorBeanNames;
                if (advisorNames == null) {
                  // 还没准备好,首次进入该函数时,为null
                    advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
                            this.beanFactory, Advisor.class, true, false);
                   // 获取到IOC容器中,所有类的类型是Advisor.class的
                   // 当然就我们当前的demo而言肯定是没有的,只有加上了Advisor.class注解的类
                   // 会继续进入到**buildAspectJAdvisors函数**中去获取到注解为Advisor.class的类
                    this.cachedAdvisorBeanNames = advisorNames;
                }
            }
            if (advisorNames.length == 0) {
                return new LinkedList<Advisor>();
            }
    
            List<Advisor> advisors = new LinkedList<Advisor>();
            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;
                                if (this.beanFactory.isCurrentlyInCreation(bce.getBeanName())) {
                                    if (logger.isDebugEnabled()) {
                                        logger.debug("Skipping advisor '" + name +
                                                "' with dependency on currently created bean: " + ex.getMessage());
                                    }
                                    // Ignore: indicates a reference back to the bean we're trying to advise.
                                    // We want to find advisors other than the currently created bean itself.
                                    continue;
                                }
                            }
                            throw ex;
                        }
                    }
                }
            }
            return advisors;
        }
    
    public List<Advisor> buildAspectJAdvisors() {
        List<String> aspectNames = this.aspectBeanNames;
    
        if (aspectNames == null) {
           // 第一次进入,确实为null
            synchronized (this) {
                aspectNames = this.aspectBeanNames;
                // spring中为了确保线程安全,存在大量的类似double-check的代码
                if (aspectNames == null) {
                    List<Advisor> advisors = new LinkedList<Advisor>();
                    aspectNames = new LinkedList<String>();
                    String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
                            this.beanFactory, Object.class, true, false);
                    // 获取IOC所有类型为Object的类名称(肯定包含了注解@Aspect的相关类)
                    for (String beanName : beanNames) {
                        if (!isEligibleBean(beanName)) {
                           // 类名和includePatterns正则匹配不通过,则跳过,起到一个过滤的作用
                           // 匹配的是类似于com.demo.*这样的类名称
                           // 和上面说的<aop:include name="******" />相关联
                            continue;
                        }
                        
                        Class<?> beanType = this.beanFactory.getType(beanName);
                        if (beanType == null) {
                           // 获取其类型,如果为null,无效,跳过
                            continue;
                        }
                        if (this.advisorFactory.isAspect(beanType)) {
                           // 注解包含了@Aspect 并且没有被AspectJ编译(字段以ajc$开头)的类
                            aspectNames.add(beanName);
                            // 本demo的beanName就是animalAop
                            AspectMetadata amd = new AspectMetadata(beanType, beanName);
                            if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
                                MetadataAwareAspectInstanceFactory factory =
                                        new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
                                List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
                                // 获取该类所有的方法,例如@Before、@After等
                                // 取到该类所有的方法,按照Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class 的顺序排序
                                // 依次遍历,如果发现了上述的注解,则保存pointCut到一个容器中
                                
                                // 获取该类所有的共有方法,如果符合要求,也保存到容器中,最后返回到List中
                                
                                if (this.beanFactory.isSingleton(beanName)) {
                                    this.advisorsCache.put(beanName, classAdvisors);
                                    // 如果是单例,则保存到cache共,否则保存到工厂cache中
                                }
                                else {
                                    this.aspectFactoryCache.put(beanName, factory);
                                }
                                advisors.addAll(classAdvisors);
                            }
                            else {
                                // Per target or per this.
                                if (this.beanFactory.isSingleton(beanName)) {
                                    throw new IllegalArgumentException("Bean with name '" + beanName +
                                            "' is a singleton, but aspect instantiation model is not singleton");
                                }
                                MetadataAwareAspectInstanceFactory factory =
                                        new PrototypeAspectInstanceFactory(this.beanFactory, beanName);
                                this.aspectFactoryCache.put(beanName, factory);
                                advisors.addAll(this.advisorFactory.getAdvisors(factory));
                            }
                        }
                    }
                    // 这里给aspectNames 赋值
                    // 需要注意到一旦赋值了,就不会为空,则不会再去扫描所有的bean,得到全部的AOP信息
                    this.aspectBeanNames = aspectNames;
                    return advisors;
                }
            }
        }
        
        // 能运行到这里的肯定是第二次执行,获取到需要的AOP的pointCut信息
        if (aspectNames.isEmpty()) {
            return Collections.emptyList();
        }
        List<Advisor> advisors = new LinkedList<Advisor>();
        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;
    }
    

    最后实例化代理对象AnimalImpl对象,经过调试来到了


    image.png

    AbstractAutoProxyCreator 文件

    protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
        if (beanName != null && 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;
        }
    
        // Create proxy if we have advice.
        Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
        // 得到相关的advice
        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;
        }
    
        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return bean;
    }
    

    如下图,调试中生成了最终的代理类


    image.png

    至此,整个的代理类的实现过程就全部完成了。

    总结下整个AOP注解操作的过程

    1、先利用xml配置,添加AnnotationAwareAspectJAutoProxyCreator类到IOC容器中
    2、在实例化各种beanpostprocessor的时候,扫描IOC所有的bean,如果配置了includePattern属性,还需要对IOC容器的所有bean进行正则匹配,通过的才会进行接下来的操作
    3、所有合适的bean遍历找到注解为@Aspect的类,轮询bean的方法,如果包含了Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class的注解方法,也需要收集起来,最后形成kv对,添加到AnnotationAwareAspectJAutoProxyCreator的cache容器中
    4、实例化被代理的类之前需要实例化本身
    5、从AnnotationAwareAspectJAutoProxyCreator存储的所有advisor匹配出符合规则的advisor

    具体可看AopUtils抽象类的findAdvisorsThatCanApply 方法

    6、最后生成一个ProxyFactory,添加上述得到的各种数据,生成代理类

    再来回答文章前提到的几个问题

    • <aop:aspectj-autoproxy />的工作原理是什么,能带来什么作用

    无需再强调,如果还是无法理解,可以仔细阅读spring xml的bean提取 源码学习,带来的作用就是新增了一个管理Aspect的类,所有的advisor都是存放在这个类中

    • @Aspect是在什么时候被操作的

    beanpostprocessor实例化的时候,特指org.springframework.context.event.internalEventListenerProcessor类的实例化时,会去扫描所有的bean,检查是否存在@Aspect注解,如果有该注解,则添加到AnnotationAwareAspectJAutoProxyCreator中

    • 各个切入点是按照什么样的顺序执行

    切入点是按照Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class自定义的排序规则sort的操作的

    • 切入点是如何和对应的bean绑定在一起的

    相互独立的,只有在bean具体生成的时候从AnnotationAwareAspectJAutoProxyCreator中获取到合适的切面以及切点信息,最后生成代理类。

    相关文章

      网友评论

        本文标题:Spring AOP 注解配置 源码学习

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