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

《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门,即可获取!

//所以下面也要进行区分开来

//个人总感觉这种设置不太好。。。。。。

//判断是不是引介增强

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 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);

}

}

最后

面试是跳槽涨薪最直接有效的方式,马上金九银十来了,各位做好面试造飞机,工作拧螺丝的准备了吗?

掌握了这些知识点,面试时在候选人中又可以夺目不少,暴击9999点。机会都是留给有准备的人,只有充足的准备,才可能让自己可以在候选人中脱颖而出。

《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门,即可获取!
e() || Proxy.isProxyClass(targetClass)) {

//创建JdkDynamicAopProxy

return new JdkDynamicAopProxy(config);

}

//如果被代理的类型既不是接口,而且也不是一个Proxy.class

//创建CglibAopProxy

return new ObjenesisCglibAopProxy(config);

}

else {

//创建JdkDynamicAopProxy

return new JdkDynamicAopProxy(config);

}

}

最后

面试是跳槽涨薪最直接有效的方式,马上金九银十来了,各位做好面试造飞机,工作拧螺丝的准备了吗?

掌握了这些知识点,面试时在候选人中又可以夺目不少,暴击9999点。机会都是留给有准备的人,只有充足的准备,才可能让自己可以在候选人中脱颖而出。

[外链图片转存中…(img-gzepZWAH-1714767583163)]

[外链图片转存中…(img-W33Q2uz7-1714767583163)]

《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门,即可获取!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值