Spring5源码(33)-SpringAop获取增强(一)

简介: Spring5源码(33)-SpringAop获取增强(一)


上一节分析了aspectj-autoproxy标签的解析过程,并注册了AnnotationAwareAspectJAutoProxyCreator。但是该类的作用是什么呢,看起来茫然无措,这时不妨查看一下类的继承关系结构。

AnnotationAwareAspectJAutoProxyCreator类图结构

从上图可以看到,AnnotationAwareAspectJAutoProxyCreator类实现了BeanPostProcessor接口,读过之前的章节或者对bean的生命周期有所了解的同学一定知道,在bean实例化完成之前和完成之后分别会自动BeanPostProcessor接口的postProcessBeforeInitialization和postProcessAfterInitialization方法。

依次打开AnnotationAwareAspectJAutoProxyCreator的父类,在AbstractAutoProxyCreator类里,找到postProcessBeforeInitialization方法和postProcessAfterInitialization方法,开始分析。

1. postProcessBeforeInstantiation方法

public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
    Object cacheKey = getCacheKey(beanClass, beanName);
    // 1、预处理判断
    if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
        // 判断该类是否应被处理过
        if (this.advisedBeans.containsKey(cacheKey)) {
            return null;
        }
        // 判断beanClass是否需要被代理
        // isInfrastructureClass-->判断beanClass是否为AOP基础类例如Advice(增强),Advisors(切面),Pointcut(切点)
        // shouldSkip-->判断beanClass是否指定了不需要代理
        if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
            // 缓存找到的切面类信息
            this.advisedBeans.put(cacheKey, Boolean.FALSE); 
            return null;
        }
    }
    // Create proxy here if we have a custom TargetSource.
    // Suppresses unnecessary default instantiation of the target bean:
    // The TargetSource will handle target instances in a custom fashion.
    // 2、如果有自定义TargetSource的话,则在此创建代理
    /**
     *  自定义TargetSource示例:
     *  <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
     *      <property name="customTargetSourceCreators">
     *          <list>
     *              <bean class="org.springframework.aop.framework.autoproxy.target.LazyInitTargetSourceCreator"/>        
     *          </list>
     *      </property>
     *  </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;
    }
    return null;
}

postProcessBeforeInstantiation一共做了两件事情

  1. 缓存切面类的信息
    先判断对应的beanClass是否为Aop的基础类,如果是的话,直接缓存。
    然后通过shouldSkip方法判断beanClass是否需要被自动代理,如果不需要被自动代理的话,返回true,否则返回false。
  2. 判断有无自定义TargetSource,如果有的话,则在此方法里创建代理
    关于自定义TargetSource已经给出了一个简单的实例,感兴趣的同学可以自己debug代码。

其中第一步的shouldSkip(beanClass, beanName)方法,看似简单,其实这里面包含了非常复杂的业务逻辑处理。·即增强的提取

2. shouldSkip方法简析

/**
 * 如果给定的bean不应该被认为是后处理器自动代理的,子类应该重写这个方法以返回true。
 * @param beanClass the class of the bean
 * @param beanName the name of the bean
 * @return
 */
@Override
protected boolean shouldSkip(Class<?> beanClass, String beanName) {
    // TODO: Consider optimization by caching the list of the aspect names
    // 1、查找所有候选增强
    List<Advisor> candidateAdvisors = findCandidateAdvisors();
    // 2、循环判断所有的增强,如果增强是AspectJPointcutAdvisor的实例
    //    并且其名称与当前bean的名称相同,则返回true,即该bean无需代理
    for (Advisor advisor : candidateAdvisors) {
        if (advisor instanceof AspectJPointcutAdvisor
                && ((AspectJPointcutAdvisor) advisor).getAspectName().equals(beanName)) {
            return true;
        }
    }
    return super.shouldSkip(beanClass, beanName);
}

该方法主逻辑很简单,查找所有的候选增强并循环判断当前beanName是否需要被代理,关键是findCandidateAdvisors()方法中获取增强的过程。

/**
 * AnnotationAwareAspectJAutoProxyCreator
 * 查找所有候选增强方法
 * @return
 */
@Override
protected List<Advisor> findCandidateAdvisors() {
    // Add all the Spring advisors found according to superclass rules.
    // 1、从父类中获取所有的增强
    List<Advisor> advisors = super.findCandidateAdvisors();
    // Build Advisors for all AspectJ aspects in the bean factory.
    // 2、从当前BeanFactory中查找所有标记了@AspectJ的注解的bean,并返回增强注解集合
    if (this.aspectJAdvisorsBuilder != null) {
        advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
    }
    return advisors;
}

该方法也非常简单:

  • 从父类中获取所有的增强
  • 从当前BeanFactory中查找所有标记了@AspectJ的注解的bean,并返回增强注解集合
    逐个对其进行分析:
3. findCandidateAdvisors方法分析

protected List<Advisor> findCandidateAdvisors() {
    return this.advisorRetrievalHelper.findAdvisorBeans();
}

/**
 * 从当前BeanFactory中寻找所有的增强bean,忽略FactoryBean和正在创建的bean
 * Find all eligible Advisor beans in the current bean factory,
 * ignoring FactoryBeans and excluding beans that are currently in creation.
 * @return the list of {@link org.springframework.aop.Advisor} beans
 * @see #isEligibleBean
 */
public List<Advisor> findAdvisorBeans() {
    // Determine list of advisor bean names, if not cached already.
    // 获取缓存的增强
    String[] advisorNames = this.cachedAdvisorBeanNames;
    // 缓存增强为空,则重新查找增强并缓存
    if (advisorNames == null) {
        // Do not initialize FactoryBeans here: We need to leave all regular beans
        // uninitialized to let the auto-proxy creator apply to them!
        // 从当前BeanFactory中获取所有类型为Advisor的bean
        advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
                this.beanFactory,
                Advisor.class,
                true,
                false);
        this.cachedAdvisorBeanNames = advisorNames;
    }
    // 当前BeanFactory中没有类型为Advisor的bean则返回一个空的集合
    if (advisorNames.length == 0) {
        return new ArrayList<>();
    }
    // 循环所有获取到的bean
    List<Advisor> advisors = new ArrayList<>();
    for (String name : advisorNames) {
        if (isEligibleBean(name)) {
            // 跳过正在创建的增强
            if (this.beanFactory.isCurrentlyInCreation(name)) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Skipping currently created advisor '" + name + "'");
                }
            }
            else {
                try {
                    // 通过getBean方法获取bean实例
                    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());
                            }
                            // 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;
}
4. buildAspectJAdvisors方法分析

/**
 * 在当前BeanFactory中查找AspectJ-annotated的注解信息,并返回增强集合
 * Look for AspectJ-annotated aspect beans in the current bean factory,
 * and return to a list of Spring AOP Advisors representing them.
 * <p>Creates a Spring Advisor for each AspectJ advice method.
 * @return the list of {@link org.springframework.aop.Advisor} beans
 * @see #isEligibleBean
 */
public List<Advisor> buildAspectJAdvisors() {
    List<String> aspectNames = this.aspectBeanNames;
    // 1、提取增强
    if (aspectNames == null) {
        synchronized (this) {
            aspectNames = this.aspectBeanNames;
            if (aspectNames == null) {
                List<Advisor> advisors = new ArrayList<>();
                aspectNames = new ArrayList<>();
                // 1.1、获取容器中的所有bean
                String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
                        this.beanFactory, Object.class, true, false);
                // 1.2、循环所有的bean,并从切面类上提取增强
                for (String beanName : beanNames) {
                    // 判断该beanName是否符合条件
                    // 如果配置文件指定了<aop:include/>属性,那么只有符合表达式条件的切面类的增强才会被提取
                    // 例如:配置<aop:include name="dogAspect"></aop:include>
                    // 那么只有dogAspect切面类的增强才会被提取
                    if (!isEligibleBean(beanName)) {
                        continue;
                    }
                    // We must be careful not to instantiate beans eagerly as in this case they
                    // would be cached by the Spring container but would not have been weaved.
                    // 获取beanType类型
                    Class<?> beanType = this.beanFactory.getType(beanName);
                    if (beanType == null) {
                        continue;
                    }
                    // 如果beanType是一个切面类
                    if (this.advisorFactory.isAspect(beanType)) {
                        aspectNames.add(beanName);
                        AspectMetadata amd = new AspectMetadata(beanType, beanName);
                        /**
                         * 切面实例化模型简介
                         *
                         * singleton: 即切面只会有一个实例;
                         * perthis  : 每个切入点表达式匹配的连接点对应的AOP对象都会创建一个新切面实例;
                         *            使用@Aspect("perthis(切入点表达式)")指定切入点表达式;
                         *            例如: @Aspect("perthis(this(com.lyc.cn.v2.day04.aspectj.Dog))")
                         * pertarget: 每个切入点表达式匹配的连接点对应的目标对象都会创建一个新的切面实例;
                         *            使用@Aspect("pertarget(切入点表达式)")指定切入点表达式;
                         *            例如:
                         *
                         * 默认是singleton实例化模型,Schema风格只支持singleton实例化模型,而@AspectJ风格支持这三种实例化模型。
                         */
                        // singleton实例化模型处理
                        if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
                            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);
                        }
                        // perthis或pertarget实例化模型处理
                        else {
                            /**
                             * 注意:当使用perthis或pertarget属性时,切面类不能是单例bean,否则会抛出下面的异常
                             * 例如:<bean name="dogAspect" class="com.lyc.cn.v2.day04.aspectj.DogAspect" scope="singleton"/>
                             * 则会报错,应该为
                             *      <bean name="dogAspect" class="com.lyc.cn.v2.day04.aspectj.DogAspect" scope="prototype"/>
                             */
                            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));
                        }
                    }
                }
                this.aspectBeanNames = aspectNames;
                return advisors;
            }
        }
    }
    // 2、返回从缓存中获取提取到的增强方法
    if (aspectNames.isEmpty()) {
        return Collections.emptyList();
    }
    List<Advisor> advisors = new ArrayList<>();
    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;
}

其中的步骤在代码里都标注的很清晰了,这里会涉及到一个切面实例化模型的概念,如果初看AOP的源码,完全可以忽略该概念,把精力放在主线的分析上。其实该方法里大部分都是预处理、缓存、以及各种业务判断,增强的提取工作交给了List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);。这也是我们要分析的重点。




目录
相关文章
|
1月前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
76 2
|
1月前
|
数据采集 监控 前端开发
二级公立医院绩效考核系统源码,B/S架构,前后端分别基于Spring Boot和Avue框架
医院绩效管理系统通过与HIS系统的无缝对接,实现数据网络化采集、评价结果透明化管理及奖金分配自动化生成。系统涵盖科室和个人绩效考核、医疗质量考核、数据采集、绩效工资核算、收支核算、工作量统计、单项奖惩等功能,提升绩效评估的全面性、准确性和公正性。技术栈采用B/S架构,前后端分别基于Spring Boot和Avue框架。
|
22天前
|
存储 缓存 Java
Spring面试必问:手写Spring IoC 循环依赖底层源码剖析
在Spring框架中,IoC(Inversion of Control,控制反转)是一个核心概念,它允许容器管理对象的生命周期和依赖关系。然而,在实际应用中,我们可能会遇到对象间的循环依赖问题。本文将深入探讨Spring如何解决IoC中的循环依赖问题,并通过手写源码的方式,让你对其底层原理有一个全新的认识。
41 2
|
1月前
|
前端开发 Java 开发者
Spring生态学习路径与源码深度探讨
【11月更文挑战第13天】Spring框架作为Java企业级开发中的核心框架,其丰富的生态系统和强大的功能吸引了无数开发者的关注。学习Spring生态不仅仅是掌握Spring Framework本身,更需要深入理解其周边组件和工具,以及源码的底层实现逻辑。本文将从Spring生态的学习路径入手,详细探讨如何系统地学习Spring,并深入解析各个重点的底层实现逻辑。
67 9
|
2月前
|
Java Spring
Spring底层架构源码解析(三)
Spring底层架构源码解析(三)
168 5
|
2月前
|
XML Java 数据格式
Spring底层架构源码解析(二)
Spring底层架构源码解析(二)
|
2月前
|
设计模式 JavaScript Java
Spring 事件监听机制源码
Spring 提供了事件发布订阅机制,广泛应用于项目中。本文介绍了如何通过自定义事件类、订阅类和发布类实现这一机制,并展示了如何监听 SpringBoot 启动过程中的多个事件(如 `ApplicationStartingEvent`、`ApplicationEnvironmentPreparedEvent` 等)。通过掌握这些事件,可以更好地理解 SpringBoot 的启动流程。示例代码展示了从事件发布到接收的完整过程。
|
2月前
|
缓存 Java Spring
源码解读:Spring如何解决构造器注入的循环依赖?
本文详细探讨了Spring框架中的循环依赖问题,包括构造器注入和字段注入两种情况,并重点分析了构造器注入循环依赖的解决方案。文章通过具体示例展示了循环依赖的错误信息及常见场景,提出了三种解决方法:重构代码、使用字段依赖注入以及使用`@Lazy`注解。其中,`@Lazy`注解通过延迟初始化和动态代理机制有效解决了循环依赖问题。作者建议优先使用`@Lazy`注解,并提供了详细的源码解析和调试截图,帮助读者深入理解其实现机制。
72 1
|
2月前
|
XML Java 数据格式
手动开发-简单的Spring基于注解配置的程序--源码解析
手动开发-简单的Spring基于注解配置的程序--源码解析
53 0
|
2月前
|
XML Java 数据格式
手动开发-简单的Spring基于XML配置的程序--源码解析
手动开发-简单的Spring基于XML配置的程序--源码解析
88 0