深入源码分析SpringAOP实现原理

本文深入探讨SpringAOP的实现原理,从AOP概念、自定义标签开启AOP、注册AnnotationAwareAspectJAutoProxyCreator,到AOP代理的创建和执行流程。通过源码分析,阐述如何寻找和应用增强器,以及如何根据切点信息创建代理,最终执行AOP通知。理解这些将有助于更好地掌握Spring的事务管理和AOP代理机制。
摘要由CSDN通过智能技术生成

深入源码分析SpringAOP实现原理

阅读这篇文章前,最好有代理模式的基础,以及了解关于Spring扩展点例如BeanPostProcessor和如何使用自定义标签集成Spring,这些文章在我的博客里都能找到。当然,也最好有使用AOP的经验,这篇文章不会讲解如何使用AOP。

1. AOP简介

说到AOP,其实这是一个面向方面的编程思想 ,它解决了OOP的一些弊端,例如我们需要为多个不具有继承关系的类引入一个公共行为, 比如说日志、权限验证、事务管理等等,我们需要将这些代码重复的添加到一系列的类中,将产生大量的重复代码,如果需要修改,将在每个类中去进行修改,不便于维护,代码的侵入性极高。所以就有了AOP这样面向方面编程的编程思想,其功能可以为每个需要的类加入共同的行为 ,如果需要修改,只需要修改切面中的代码,改一处等于改多处,并且便于编程,写一个切面类即可达到在每个类中加入重复代码的目的。

阅读此篇文章,你将了解Spring是如何实现AOP(前置通知、后置通知、环绕通知),由于Spring中的事务管理是基于AOP的功能来做的,所以你将更好的能理解Spring是如何将事务统一管理起来的。

2. 自定义标签开启AOP

只要用过AOP都知道,如果需要使用AOP,需要在配置文件中写这样一段配置:

<aop:aspectj-autoproxy />

只有写了这段配置才可以开启AOP功能,那么这个自定义标签又做了什么呢?在上一篇讲解自定义标签的文章中详细讲到了,此时我们需要关注其标签头aop去寻找对应的命名空间:

xmlns:aop="http://www.springframework.org/schema/aop"

全局搜索命名空间http\://www.springframework.org/schema/aop,注意http后加一个 “\” ,可以找到spring.handlers文件中对应的handler类:

http\://www.springframework.org/schema/aop=org.springframework.aop.config.AopNamespaceHandler

这样就找到了命名空间对应的handler:

public class AopNamespaceHandler extends NamespaceHandlerSupport {
   

	/**
	 * Register the {@link BeanDefinitionParser BeanDefinitionParsers} for the
	 * '{@code config}', '{@code spring-configured}', '{@code aspectj-autoproxy}'
	 * and '{@code scoped-proxy}' tags.
	 */
	@Override
	public void init() {
   
		// In 2.0 XSD as well as in 2.1 XSD.
		registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
		registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
		registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());

		// Only in 2.0 XSD: moved to context namespace as of 2.1
		registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
	}
}

回到开头配置自定义标签,我们使用了aspectj-autoproxy这个Parser。在init方法中,我们找到aspectj-autoproxy对应的Parser是AspectJAutoProxyBeanDefinitionParser这个类:

@Override
@Nullable
//我们只关注解析的主方法,parse方法
public BeanDefinition parse(Element element, ParserContext parserContext) {
   
    //注册一个类到IOC容器中
    AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
    extendBeanDefinition(element, parserContext);
    return null;
}

3. 注册AnnotationAwareAspectJAutoProxyCreator

AnnotationAwareAspectJAutoProxyCreator 是实现AOP功能的主要类,我们先来看看这个类的结构:
在这里插入图片描述
此类实现了BeanPostProcessor ,稍后将关注其后置处理Bean的方法postProcessAfterInitialization ,并且实现了BeanFactorAware接口,此类将取得并存有一个BeanFactory实例对象。

回到主线,关注注册此类的方法:

public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary(
    ParserContext parserContext, Element sourceElement) {
   
//将一个类作为Bean注册到IOC容器中
    BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(
        parserContext.getRegistry(), parserContext.extractSource(sourceElement));
    //处理proxy-target-class与expose-proxy属性
    useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
    //注册组件并通知
    registerComponentIfNecessary(beanDefinition, parserContext);
}

其中,在注册这个类的过程中主要完成了3件事:

  1. 注册AnnotationAwareAspectJAutoProxyCreator

    @Nullable
    public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry,
                                                                                      @Nullable Object source) {
         
    //将AnnotationAwareAspectJAutoProxyCreator这个类注册到IOC容器中
        return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
    }
    
    @Nullable
    private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry,
                                                                  @Nullable Object source) {
         
    
        Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
    
        //如果IOC容器中已经存在了此类型的Bean,则需要判断优先级
        if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
         
            //获取此类的BeanDefinition信息
            BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
            //如果此Bean的ClassName与AnnotationAwareAspectJAutoProxyCreator类的
            //ClassName不同的话,判断优先级
            if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
         
                int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
                int requiredPriority = findPriorityForClass(cls);
                //如果已存在Bean优先级小于Creator的优先级
                if (currentPriority < requiredPriority) {
         
                    //将ClassName替换成Creator
                    apcDefinition.setBeanClassName(cls.getName());
                }
            }
            //不进行注册,因为已经注册了
            return null;
        }
    
        //如果到这里,说明IOC容器中没有配置对应Creator
        //使用Crearir的Class构造一个BeanDefinition
        RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
        beanDefinition.setSource(source);
        //配置依赖属性order,将其设置为最高优先级
        beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
        beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
        //将设置好属性的BeanDefinition注册进IOC容器中
        registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
        return beanDefinition;
    }
    
  2. 处理proxy-target-classexpose-proxy属性

    private static void useClassProxyingIfNecessary(BeanDefinitionRegistry registry, @Nullable Element sourceElement) {
         
        if (sourceElement != null) {
         
            boolean proxyTargetClass = Boolean.valueOf(sourceElement.getAttribute(PROXY_TARGET_CLASS_ATTRIBUTE));
            //处理proxy-target-class属性
            if (proxyTargetClass) {
         
                AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
            }
            boolean exposeProxy = Boolean.valueOf(sourceElement.getAttribute(EXPOSE_PROXY_ATTRIBUTE));
            //处理expose-proxy属性
            if (exposeProxy) {
         
                AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
            }
        }
    }
    

    其中设置属性的过程:

    public static void forceAutoProxyCreatorToUseClassProxying(BeanDefinitionRegistry registry) {
         
        if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
         
            //根据之前注册的BeanName取出Creator
            BeanDefinition definition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
            //将Creator的BeanDefinition的属性proxyTargetClass设置为true
            definition.getPropertyValues().add("proxyTargetClass", Boolean.TRUE);
        }
    }
    
    public static void forceAutoProxyCreatorToExposeProxy(BeanDefinitionRegistry registry) {
         
        if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
         
            //根据之前注册的BeanName取出Creator
            BeanDefinition definition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
            //将Creator的BeanDefinition的属性exposeProxy设置为true
            definition.getPropertyValues().add("exposeProxy", Boolean.TRUE);
        }
    }
    
    • proxy-target-class:在Spring的AOP中,默认的如果目标类实现了至少一个接口,将使用JDK动态代理实现AOP,否则使用CGLib动态代理实现AOP,如果希望AOP都使用CGLib实现,你就可以设置proxy-target-class属性为true,但要注意几个问题:

      • 无法对final的方法进行动态代理 ,原因很简单,CGLib使用继承实现,final方法无法重写,所以final的方法不能应用AOP。
      • 需要配置CGLib的JAR包
    • expose-proxy:在讲解事务的那篇文章中有提到,如果一个类中的事务A方法调用了同一个类中的事务B方法,B方法将没有事务,这个道理在AOP中也是这样的,相同类下的不同方法互相调用,内部方法将无法被应用通知(无法进行AOP),此时你需要将expose-proxy属性设置为true,暴露一个代理类(此属性的原理在下面会有详细讲解),然后在A方法中需要调用B方法的话需要这样写:

      public class Service{
             
          public void A(){
             
              ((Service)AopContext.currentProxy()).B();
          }
      
          public void B(){
             
      		//do something...
          }
      }
      

      这样,B方法就算再A方法内也可以被AOP。其中AopContext 是存放线程变量的类,形象的称之为AOP的上下文

4. 实现AOP代理

4.1 创建AOP代理

上面,自定义标签的配置完成了对Creator类的自动注册,我们可以知道,此类实现了BeanPostProcessor接口,将会在IOC容器初始化每个Bean时都调用此类的postProcessAfterInitialization 方法,此方法即为AOP代理的入口,此方法在抽象父类AbstractAutoProxyCreator实现:

@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) throws BeansException {
   
    if (bean != null) {
   
        //先从缓存中获取Key,由要代理的Bean的Class与benaName组成
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        //判断是否是过早暴露的Bean,此概念在讲IOC解决循环依赖中有提到
        //如果是过早暴露的Bean,则此时连依赖注入都没有完成,则不对其进行代理
        //待其真正初始化之后再尝试代理
        if (!this.earlyProxyReferences.contains(cacheKey)) {
   
            //如果符合条件进行AOP代理
            return wrapIfNecessary(bean, beanName, cacheKey);
        }
    }
    return bean;
}
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
   
    //如果先前已经处理过的,不进行处理
    if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
   
        return bean;
    }
    //如果此Bean已经被标记为无法代理,不进行处理
    if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
   
        return bean;
    }
    //如果Bean为AOP类的类型,或是需要跳过的类型,不进行处理
    if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
   
        //标记为不代理
        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return bean;
    }

    // Create proxy if we have advice.
    //寻找符合此Bean的增强方法(通知方法)
    Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
    //如果寻找到的增强方法列表不为空,也就是不为DO_NOT_PROXY
    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());
        //将代理对象作为Bean返回给IOC容器
        return proxy;
    }
	//如果走到这里,说明代理失败,标记为代理失败
    this.advisedBeans.put(cacheKey, Boolean.FALSE);
    return bean;
}

文章到了这里,就已经基本完成AOP的实现了,剩下我们需要关注的就是两件事:

  1. 如何寻找符合Bean的增强器
  2. 如何对Bean创建动态代理

4.2 寻找所有的增强器

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

这里有一个Advisor的概念,其中Advisor封装了切点信息与advise通知方法等等信息

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
   
    //寻找所有适用的Advisor
    List<Advisor> candidateAdvisors = findCandidateAdvisors();
    //从所有Advisor中选出适合被当前Bean使用的Advisor
    List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
    extendAdvisors(eligibleAdvisors);
    if (!eligibleAdvisors.isEmpty()) {
   
        eligibleAdvisors = sortAdvisors(eligibleAdvisors);
    }
    return eligibleAdvisors;
}
4.2.1 寻找已存在的Advisor

首先,执行下面的方法寻找合适的Advisor(此方法在子类Creator中得到实现):

@Override
protected List<Advisor> findCandidateAdvisors() {
   
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值