《一线大厂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的信息
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面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》,点击传送门,即可获取!