Spring(十八):AOP——寻找匹配的增强器

回顾

上一篇,我们已经看了如何AOP如何对标签进行解析,并且生成了一系列的增强器,比如建言增强器、同步实例化增强器、DeclaredParent增强器,但找到这些增强器并不一定都适用,还要进行匹配的

返回AbstractAdvisorAutoProxyCretor

在这里插入图片描述
上前两篇已经针对findCandidateAdvisors这个方法看了其底层的细节,解析得到了所有的增强器,但是对于增强器来说,并不一定都是适用的,还要去挑选出适合的增强器,也就是满足我们通配符的增强器,具体体现在findAdvisorsThatCanApply方法里面

拓展:这个通配符指的就是上一篇讲到过的方法规则式拦截,既然AOP给建言方法定义了这个语法,那就肯定要有对应的校验机制去判断这个语法,所以就需要findCandidateAdvisors来校验匹配了

在这里插入图片描述

findAdvisorsThatCanApply

源码如下

	protected List<Advisor> findAdvisorsThatCanApply(
			List<Advisor> candidateAdvisors, Class<?> beanClass, String beanName) {

        //这个ProxyCreationContext记录现在正在处理的AOP BeanDefinition
		ProxyCreationContext.setCurrentProxiedBeanName(beanName);
		try {
            //适用AopUtil来寻找适合的增强器
			return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
		}
		finally {
            //处理完毕后,清空记录
			ProxyCreationContext.setCurrentProxiedBeanName(null);
		}
	}

该方法的关键是调用AopUtils去寻找适合的增强器

所以可以找到,去匹配适合的增强器是交由AopUtils去实现的

AopUtils

调用了findAdvisorsThatCanApply方法去寻找合适的增强器

源码如下

	public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
		//判断进来的候选增强器是否为空,
        //如果为空,直接返回原来给他
        //不需要进行匹配了
        if (candidateAdvisors.isEmpty()) {
			return candidateAdvisors;
		}
        //新建的ArrayList集合来存放匹配的增强器
		List<Advisor> eligibleAdvisors = new ArrayList<>();
        //遍历所有的候选增强器
		for (Advisor candidate : candidateAdvisors) {
            //如果增强器是IntroductionAdvisor类型的
            //也就是引介增强
            //并且canApply的
			if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
				//添加结果集中
                eligibleAdvisors.add(candidate);
			}
		}
        //判断此时的结果集是否空,即是否有推荐的
		boolean hasIntroductions = !eligibleAdvisors.isEmpty();
        //再次遍历候选增强器
		for (Advisor candidate : candidateAdvisors) {
            //不再处理那些IntroducetionAdvisor类型的
            //也就是引介增强
            //因为已经处理过了
			if (candidate instanceof IntroductionAdvisor) {
				// already processed
				continue;
			}
            //判断普通增强是否适用
			if (canApply(candidate, clazz, hasIntroductions)) {
				//适用的话也添加进结果集中
                eligibleAdvisors.add(candidate);
			}
		}
        //返回结果集
		return eligibleAdvisors;
	}

从这里看到,原来增强器引介增强器、普通增强器之分

拓展:什么是引介增强器,对应我们常用的一些增强器的注解@Before、@After、@Around、@AfterThrowing、@AfterReturing,这些增强的对象都是针对方法级别的,当然我们可以将类的所有方法都被增强到,这也能达到类级别(这也是我们一般的方法,在方法规则式拦截里使用通配符来拦截所有的方法,但这也是一种不太完整的类级别),而引介增强,对应的则是对类级别的增强,我们可以通过引介增强来给目标类去添加新的属性、新的方法

具体使用的话可以参考这篇博客spring学习笔记(14)引介增强详解:定时器实例:无侵入式动态增强类功能_总结沉淀-CSDN博客_引介增强

里面有一个重要的点,就是引介增强的实现类要去实现引介增强接口IntroductionInterceptor,这也是为什么在上面会有IntroductionAdvisors之分了。。。。。。

而且这里还有一个比较重要的点,在判断普通增强是否适用的时候,已经对引介增强进行筛选了,而且此时的结果集是否为空,会影响普通增强的筛选,说白了就是hasIntroductions参数

下面就来看看canApply方法的细节

canApply方法

首先来看一下对于介质增强的canApply方法

在这里插入图片描述
并且可以看到,其调用了重载的方法,并且这个方法也是对于普通增强适用的canApply方法

该重载方法的具体源码如下

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;
            //再次适用重载的canApply去进行匹配
			return canApply(pca.getPointcut(), targetClass, hasIntroductions);
		}
		else {
			// It doesn't have a pointcut so we assume it applies.
			return true;
		}
	}

Emmmmm,后面的匹配过程太复杂了,没看懂。。。。。。

现在我们已经获取到了所有增强器了,下面就要开始创建切面了

返回到上面的AbstractAutoProxyCreator的wrapIfNecessary方法里面

在这里插入图片描述
现在我们已经完成了getAdvicesAndAdvisorsForBean方法,获取到了所有匹配的增强器,下面就是要给该Bean去创建代理对象了,注意这里不要搞混了,之前我们找代理对象是通过遍历容器里面的所有Bean去找到的,如今当前的Bean是准备被代理的Bean,此时ProxyFactory已经有了所有增强器和此时被代理的Bean的信息

AOP——创建切面

createProxy方法

该方法的源码如下

	protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
			@Nullable Object[] specificInterceptors, TargetSource targetSource) {
		//要注意,此时的bean是从IOC容器里面加载出来的Bean,已经是一个Object了
        //这个bean代表的就是切面类的在IOC容器存储的Bean
        //这里传进来的是这个bean的Class对象
		if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
			AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
		}

        //新创建一个ProxyFactory对象
		ProxyFactory proxyFactory = new ProxyFactory();
        //从当前的类中去获取拷贝一些相关属性
		proxyFactory.copyFrom(this);
        //判断给定的bean是否应该使用targetClass
        //如果targetClass是Proxy.class
		if (proxyFactory.isProxyTargetClass()) {
            //如果使用的是targetClass
            //证明使用的不是它的接口来进行代理
			// Explicit handling of JDK proxy targets (for introduction advice scenarios)
            //判断当前的传进来的beanClass是不是Proxy.class
            //如果是proxy.class那就要将里面的接口添加进来
            //Proxy.class是JDK自带的代理类,所以支持JDK代理
			if (Proxy.isProxyClass(beanClass)) {
				// Must allow for introductions; can't just set interfaces to the proxy's interfaces only.
				///遍历beanClass里面的接口,并且添加进切面工厂里面
                //这里是将代理接口放进去工厂里面
                for (Class<?> ifc : beanClass.getInterfaces()) {
					proxyFactory.addInterface(ifc);
				}
			}
		}
		else {
			// No proxyTargetClass flag enforced, let's apply our default checks...
			if (shouldProxyTargetClass(beanClass, beanName)) {
				proxyFactory.setProxyTargetClass(true);
			}
			else {
                //这里同样是将代理接口代理工厂里面
				evaluateProxyInterfaces(beanClass, proxyFactory);
			}
		}
		//将增强器集合对象转化成一个数组
		Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
        //注入进proxyFactory里面
		proxyFactory.addAdvisors(advisors);
        //将targetSource注入进ProxyFactory
        //即设置要代理的类
        //这里要代理的类是一个SingletonTargetSource
        //并且是使用bean来创建的
		proxyFactory.setTargetSource(targetSource);
        //定制工厂
		customizeProxyFactory(proxyFactory);
		//注入Frozen,冻结的意思
        //用来控制代理工厂被配置之后是否还允许修改
        //默认为false
		proxyFactory.setFrozen(this.freezeProxy);
		if (advisorsPreFiltered()) {
			proxyFactory.setPreFiltered(true);
		}

		// Use original ClassLoader if bean class not locally loaded in overriding class loader
		// 获取切面的ClassLoader
        ClassLoader classLoader = getProxyClassLoader();
		if (classLoader instanceof SmartClassLoader && classLoader != beanClass.getClassLoader()) {
			classLoader = ((SmartClassLoader) classLoader).getOriginalClassLoader();
		}
        //切面工厂使用ClassLoader来获取切面
		return proxyFactory.getProxy(classLoader);
	}

获取属性的方法如下

在这里插入图片描述
可以看到,创建代理分为下面几个部分

  • 获取当前类中的属性去创建ProxyFactory
  • 添加代理接口
  • 封装Advisor并加入到ProxyFactroy中
  • ProxyFactory注入要代理的类,一个SingletonTargetSource,并且前面这个是使用Bean来创建出来的
  • 定制工厂
  • 设置Frozen属性,控制工厂是否被冻结
  • 获取ClassLoader,并且ProxyFactory使用ClassLoader来实例化切面,进行获取代理操作

比较重要的两点是封装Advisor并且加入到ProxyFactory、和获取ClassLoader,使用ProxyFactory来创建代理的操作

封装Advisors

在这里插入图片描述
对应的操作就是buildAdvisors方法

该方法源码如下

protected Advisor[] buildAdvisors(@Nullable String beanName, @Nullable Object[] specificInterceptors) {
		// Handle prototypes correctly...
    	//获取其他常用的拦截器
		Advisor[] commonInterceptors = resolveInterceptorNames();
		//创建一个ArrayList来存放所有的增强器
		List<Object> allInterceptors = new ArrayList<>();
    	//判断传进来的增强器是否为空
		if (specificInterceptors != null) {
            //如果不为空
			if (specificInterceptors.length > 0) {
				// specificInterceptors may equal PROXY_WITHOUT_ADDITIONAL_INTERCEPTORS
                //将传进来的增强器存进allInterceptors里面
				allInterceptors.addAll(Arrays.asList(specificInterceptors));
			}
            //判断常用的拦截器是否为空
			if (commonInterceptors.length > 0) {
                //如果设置了优先应用常用的拦截器
				if (this.applyCommonInterceptorsFirst) {
                    //往allInterceptors首位进行添加
					allInterceptors.addAll(0, Arrays.asList(commonInterceptors));
				}
				else {
                    //如果没有设置优先应用常用的拦截器
                    //继续往后进行添加常用的拦截器即可
					allInterceptors.addAll(Arrays.asList(commonInterceptors));
				}
			}
		}
		if (logger.isTraceEnabled()) {
            //一些日志输出,不太重要
			int nrOfCommonInterceptors = commonInterceptors.length;
			int nrOfSpecificInterceptors = (specificInterceptors != null ? specificInterceptors.length : 0);
			logger.trace("Creating implicit proxy for bean '" + beanName + "' with " + nrOfCommonInterceptors +
					" common interceptors and " + nrOfSpecificInterceptors + " specific interceptors");
		}
		//创建结果数组,长度为之前得来的全部拦截器
		Advisor[] advisors = new Advisor[allInterceptors.size()];
    	//遍历前面将常用拦截器与解析得来的增强器组合起来的总集
		for (int i = 0; i < allInterceptors.size(); i++) {
            //调用wrap方式封装,并且添加进advisors数组里面
			advisors[i] = this.advisorAdapterRegistry.wrap(allInterceptors.get(i));
		}
		return advisors;
	}

整个过程就是对常用的拦截器和解析出来的增强器进行组合,得到一个总的拦截器集合之后,对这个总的拦截器进行遍历,给各个拦截器调用wrap的方式来进行封装,然后转移到一个数组里面,而前面会根据设置的是否优先引用常用拦截器而改变顺序,如果设置了优先应用常用拦截器,那么就会将常用拦截器添加进总拦截器集合里面的首位,如果没有设置,那就继续往后进行添加,跟在了增强器的后面

下面看看wrap方法做了什么事情

在这里插入图片描述
可以看到,Wrap是由AdvisorAdapterRegistry接口提供的,并且只有一个DefaultAdvisorAdapterRegistry实现,也就是说wrap的动作做是交由DefaultAdvisorAdapterRegistry去做的

源码如下

	@Override
	public Advisor wrap(Object adviceObject) throws UnknownAdviceTypeException {
        //判断是不是Advisor类型
        //@DeclaredParent就是Advisor类型的
		if (adviceObject instanceof Advisor) {
            //如果是就直接返回
			return (Advisor) adviceObject;
		}
        //如果不是Advisor类型,判断是不是Advice类型
		if (!(adviceObject instanceof Advice)) {
            //如果不是Advice类型
            //证明既不是Advisor也不是Advice,抛错
			throw new UnknownAdviceTypeException(adviceObject);
		}
        //下面就是处理Advice类型的逻辑
		Advice advice = (Advice) adviceObject;
        //判断是不是MethodInterceptor类型
        //建言一般都是Advice类型,并且有MethodInterceptor性质的
		if (advice instanceof MethodInterceptor) {
			// So well-known it doesn't even need an adapter.
            //如果是MethodInterceptor类型的,就使用DefaultPointcutAdvisor来进行封装
			return new DefaultPointcutAdvisor(advice);
		}
        //遍历所有的适配器
        //如果有对应的适配器就使用DefaultPointcutAdvisor来进行封装
		for (AdvisorAdapter adapter : this.adapters) {
			// Check that it is supported.
			if (adapter.supportsAdvice(advice)) {
				return new DefaultPointcutAdvisor(advice);
			}
		}
		throw new UnknownAdviceTypeException(advice);
	}

在这里插入图片描述
可以看到,由于Spring中涉及了过多的拦截器、增强器、增强方法等,所以有必要统一封装成Advisors来进行代理的创建,然而对于Advisors和Advice有不同的处理逻辑,对于Advisors直接返回即可,但对于Advice(建言)就要封装成DefaultPointcutAdvice进行

创建代理

回到我们的createProxy方法,最后一步就是创建代理了

在这里插入图片描述
步骤大概如下

  • 获取ClassLoader
  • 判断ClassLoader是不是SmartClassLoader类型的,并且与beanClass不是相同类型的ClassLoadder
    • 如果满足条件的话,就使用ClassLoader的originalClassLoader
  • 调用proxyFactory的getProxy方法去生成切面

那么下面我们就来看看这ClassLoader怎么获取的

在这里插入图片描述
可以知道这个ClassLoader本质上来自于ProxyProcessorSupport的
在这里插入图片描述
并且是默认的是从ClassUtils中获取的

在这里插入图片描述
可以看到,会优先从Thread ClassLoader中取,如果Thread ClassLoader没有定义,就会取ClassUtils的ClassLoader,如果也没有哦,最终会取SystemClassLoader,也就是说,这个ClassLoader很大可能会是一个启动型ClassLoader,关于这几种ClassLoader,可以去看一下JVM系列的类加载器模型

现在看一下这个切面是如何生成的

在这里插入图片描述
可以看到,首先调用了createAopProxy方法,所以我们也先看createAopProxy方法做了什么

creatAopProxt

源码如下

	protected final synchronized AopProxy createAopProxy() {
        //可以看到这是一个加锁的方法
        //判断是否激活
		if (!this.active) {
            //如果没有激活就进行activate
			activate();
		}
        //使用AopProxyFactory来根据当前的配置(当前的proxyFactory)来创建AopProxy
		return getAopProxyFactory().createAopProxy(this);
	}

getAopProxyFactory仅仅只是拿到了AopProxyFactory对象,并且还是DefaultAopProxtFactory

在这里插入图片描述
所以说白了仅仅只是调用DefaultAopProxyFactory来创建AopProxy

在这里插入图片描述
源码如下

@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
    //下面就是判断创建哪种代理了
    //JDK代理还是Cglib代理
   	//可以看到这里使用哪种代理,有下面的条件受限制的
    //1.isOptimize
    //2.isProxyTargetClass
    //3.hasUserSuppliedProxtInterfaces
   if (!NativeDetector.inNativeImage() &&
         (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config))) {
       //获取被代理的类型
      Class<?> targetClass = config.getTargetClass();
       //判断是否为空 
      if (targetClass == null) {
         throw new AopConfigException("TargetSource cannot determine target class: " +
               "Either an interface or a target is required for proxy creation.");
      }
       //如果被代理的类型是一个接口、或者是一个Proxy.class
      if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
          //创建JdkDynamicAopProxy
         return new JdkDynamicAopProxy(config);
      }
       //如果被代理的类型既不是接口,而且也不是一个Proxy.class
       //创建CglibAopProxy
      return new ObjenesisCglibAopProxy(config);
   }
   else {
       //创建JdkDynamicAopProxy
      return new JdkDynamicAopProxy(config);
   }
}

可以看到,创建Proxy的具体是交由DefaultAopProxyFactory的,并且创建的Proxy有两种类型

  1. JdkDynamicAopProxy
  2. ObjebnesisCglibAopProxy

并且有三个条件去影响着创建哪种切面

  1. isOptimize:这个是用来控制通过Cglib创建的代理是否使用激进的优化策略,这个策略是比较难理解的,通常我们都不设置的,如果对完全知道AOP代理如何代理优化的,可以去设置一下这个,目前这个属性仅仅用于Cglib代理,对于JDK动态代理无效
  2. proxyTargetClass:这是Aop标签里面的一个属性,对应位proxt-target-class=“true”,当这个属性为true时,会让目标类本身被代理而不是目标类的接口,而且Cglib代理将会被创建
  3. hasNoUserSuppliedProxyInterfaces:是否存在代理接口

首先我们来区分一下两个Proxy究竟有什么区别,先知道区别,然后反过来去认识哪种情况下使用JDk,哪种情况使用Cglib的

用过动态代理的都知道,JDK动态代理是代理接口的,而Cglib代理的是具体的类的。

那么对于如果存在接口的对象来说,既可以使用JDK动态代理,也可以去使用Cglib代理,Spring会默认采用JDK代理

那么对于没有接口的对象来说,只可以使用Cglib代理

因此,只有设置了cglib的优化测量、或者使用了proxt-traget-class(直接表明了要代理类)、或者被代理的对象没有实现接口才会去考虑Cglib代理,并且如果满足上面三个条件之一,但被代理的对象的类型是个Proxy.class或者是一个接口,也会转用JDK动态代理

现在已经搞清楚了什么时候创建JDK动态代理,什么时候创建Cglib动态代理了,并且对于JDK动态代理的Bean,本质上是一个JdkDynamicAopProxy;而对于Cglib动态代理的Bean本质上是一个ObjenesisCglibAopProxy

并且此时我们的createPrxy方法到这里就返回了,返回到AbstractAutoProxyCreator的wrapIfNecessary的方法,直接把代理对象(JdkDynamicAopProxy或者ObjenesisCglibAopProxy)返回给上层的Bean进行加载的时候调用的postProcessAfterInitialization方法了,此时就完成了当前加载的Bean的代理了

直到现在我们知道了整个给Bean代理的过程,总体上也差不多了,但既然都来到这里了,不如直接把那两个代理对象也给研究一下吧,23333,由于篇幅过长,还是摆在下一篇吧

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值