回顾
上一篇,我们已经看了如何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有两种类型
- JdkDynamicAopProxy
- ObjebnesisCglibAopProxy
并且有三个条件去影响着创建哪种切面
- isOptimize:这个是用来控制通过Cglib创建的代理是否使用激进的优化策略,这个策略是比较难理解的,通常我们都不设置的,如果对完全知道AOP代理如何代理优化的,可以去设置一下这个,目前这个属性仅仅用于Cglib代理,对于JDK动态代理无效
- proxyTargetClass:这是Aop标签里面的一个属性,对应位proxt-target-class=“true”,当这个属性为true时,会让目标类本身被代理而不是目标类的接口,而且Cglib代理将会被创建
- 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,由于篇幅过长,还是摆在下一篇吧