spring——AOP原理及源码(四)
前情回顾:
上文我们一路分析了从容器创建开始直到我们的AOP注解导入的核心组件AnnotationAwareAspectJAutoProxyCreator执行postProcessBeforeInstantiation()方法的整个过程
分析得到:在所有bean创建之前,都会调用resolveBeforeInstantiation方法来试图返回一个代理对象
本篇预告
下图可以看到resolveBeforeInstantiation方法包含了
applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
applyBeanPostProcessorsAfterInitialization(bean, beanName);
两个方法
在本篇我们将完整走完 resolveBeforeInstantiation 全过程,并一直到返回代理对象为止
1、调试的起点
开始调试,还是一路跳到下一断点,直到AbstractAutoProxyCreator.postProcessBeforeInstantiation()(从resolveBeforeInstantiation方法进入到这里的过程上一篇已经分析了)
可以看到当前的bean为org.springframework.context.event.internalEventListenerProcessor,和我们要测试的AOP无关。
因为当前方法打上了断点,所以我们调到下一个断点直到来到class aop.MathCalculator
@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
Object cacheKey = getCacheKey(beanClass, beanName); if (beanName == null || !this.targetSourcedBeans.contains(beanName)) {
if (this.advisedBeans.containsKey(cacheKey)) {
return null;
}
if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return null;
}
} // Create proxy here if we have a custom TargetSource.
// Suppresses unnecessary default instantiation of the target bean:
// The TargetSource will handle target instances in a custom fashion.
if (beanName != null) {
TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
if (targetSource != null) {
this.targetSourcedBeans.add(beanName);
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
} return null;
}
postProcessBeforeInstantiation
从上往下一步步step voer,下面是对应行的讲解
3、获取bean在容器中的缓存
5、判断目标源Bean中是否存在有当前bean的缓存信息。
(可以在21行看到添加目标源bean的操作,在23行就创建了代理对象。所以这步也是相当于判断当前bean是否已经创建过代理对象。)
因为是第一次执行MathCalculator这个bean,这里我们是进入判断的
接下来 this.advisedBeans.containsKey(cacheKey) 判断advisedBeans中是否有缓存(这里我们将advisedBeans称为增强器)
我们这里判断是不满足的,接着往下走。
9、进行 isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName) 判断
下面分别进入第九行的两个方法进行查看。
1、isInfrastructureClass(beanClass) 是否是基础类型
进入后我们会发现有如下两个方法
super.isInfrastructureClass(beanClass) || this.aspectJAdvisorFactory.isAspect(beanClass)
先进入父类的 isInfrastructureClass 方法,经过一系列判断,最后返回false,表明当前bean不是基础类型。
接着来到 isAspect(beanClass) ,通过类上是否有@Aspect注解来判断这个类是否是切面(这里 MathCalculator显然不是一个切面)
返回出来可以看到 isInfrastructureClass(beanClass) 的判断为false
2、shouldSkip 判断是否要跳过
在方法中遍历所有的增强器,红框中表面获取的增强器便是日志方法。
并判断增强器是否是AspectJPointcutAdvisor类型,我们这里判断不满足
末尾来到父类的 shouldSkip 方法,进入可以看到直接返回了 false
最终我们来到外层的判断,可以看到返回了false
2、退出 applyBeanPostProcessorsBeforeInstantiation 方法
接着来到下面这块代码,看注释表明如果存在自定义目标Source,我们将在此创建代理对象
step voer,在259行判断targetSource为null,所以这里是没有自定义目标Source的
我们一直往下走,走完 applyBeanPostProcessorsBeforeInstantiation 方法,直到回到 resolveBeforeInstantiation 方法,返回的bean为null
所以接下来也不会进入 applyBeanPostProcessorsAfterInitialization 方法
到此为止,我们的 resolveBeforeInstantiation 方法执行完了,从以上可以得知,方法没有给我们返回代理对象
如下图所示,我们将接着执行 createBean 流程,接下来将调用 doCreateBean
3、postProcessAfterInitialization方法探究
我们执行 doCreateBean 方法,来到了配置类的bean方法
接着跳到下一个断点直到 postProcessAfterInitialization 方法,下面的方法栈我们是熟悉的
从finishBeanFactoryInitialization一路到initializeBean
不过我们现在进入的是postProcessAfterInitialization
从下图的 initializeBean 方法的流程也可以看明白
前面执行完了 applyBeanPostProcessorsBeforeInitialization 和 invokeInitMethods 两个方法
下面进入 postProcessAfterInitialization 方法:
如果先前的代理参考中不存在当前bean对象,就调用 wrapIfNecessary(bean, beanName, cacheKey) 并返回其结果
进入wrapIfNecessary(进行包装如果需要的话):
/**
* Wrap the given bean if necessary, i.e. if it is eligible for being proxied.
* @param bean the raw bean instance
* @param beanName the name of the bean
* @param cacheKey the cache key for metadata access
* @return a proxy wrapping the bean, or the raw bean instance as-is
*/
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
if (beanName != null && this.targetSourcedBeans.contains(beanName)) {
return bean;
}
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
}
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
} // Create proxy if we have advice.
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
} this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
wrapIfNecessary
9~18行:还是先进行判断,是否是基础类型等,在这里我们判断都不是
21:拿到增强器放入 Object[] specificInterceptors 中
24~25:传入拿到的增强器等信息,创建代理对象
下面我们着重来看 getAdvicesAndAdvisorsForBean 拿增强器的过程:
一进来先调用findEligibleAdvisors,找到合格的增强器(日志方法)
进入findEligibleAdvisors,方法先调用findCandidateAdvisors,获取所有候选增强器
然后又调用findAdvisoersThatCanApply从候选增强器中选出可以用于当前bean的
接着判断选出的增强器队列不为空,就给其排序,最后返回选出的增强器队列
findCandidateAdvisors 我们就不说了
重点看看 findAdvisoersThatCanApply
如下图,可以发现它是利用AopUtils,也就是AOP工具类进行筛选
进入工具类方法,看到方法又进行了一层判断,将最终符合条件的增强器(日志方法)放入选择队列中
获取完后我们一路返回,又回到了 findEligibleAdvisors
如下图可以看到,最终排序完后将返回有五个增强器的增强器队列
4、创建代理对象
最终我们获取到了需要的增强器(日志方法)放入一个叫特殊拦截器的数组(这里暂且称为拦截器数组)
判断不为空后,将当前bean的缓存放入adviseBeans 中
接着调用createProxy来创建代理对象
/**
* Create an AOP proxy for the given bean.
* @param beanClass the class of the bean
* @param beanName the name of the bean
* @param specificInterceptors the set of interceptors that is
* specific to this bean (may be empty, but not null)
* @param targetSource the TargetSource for the proxy,
* already pre-configured to access the bean
* @return the AOP proxy for the bean
* @see #buildAdvisors
*/
protected Object createProxy(
Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) { if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
} ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.copyFrom(this); if (!proxyFactory.isProxyTargetClass()) {
if (shouldProxyTargetClass(beanClass, beanName)) {
proxyFactory.setProxyTargetClass(true);
}
else {
evaluateProxyInterfaces(beanClass, proxyFactory);
}
} Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
proxyFactory.addAdvisors(advisors);
proxyFactory.setTargetSource(targetSource);
customizeProxyFactory(proxyFactory); proxyFactory.setFrozen(this.freezeProxy);
if (advisorsPreFiltered()) {
proxyFactory.setPreFiltered(true);
} return proxyFactory.getProxy(getProxyClassLoader());
}
createProxy
从上往下看
19行创建代理对象对象工厂proxyFactory ,31~34在代理对象工厂中加入增强器、目标Source等属性
41行调用proxyFactory.getProxy(getProxyClassLoader()) 获取当前bean的代理对象
先创建Aop代理对象
可以看到结果一系列调用后来到下图,有3种动态代理对象可能返回,我们这里返回的是Cglib动态代理对象
一步步将代理对象返回,执行完当前bean的 applyBeanPostProcessorsAfterInitialization方法,返回其代理对象
可以得知:返回的代理对象将代替bean对象存入容器中
到此为止,我们的代理对象创建步骤就完成了。
总结:
initializeBean方法在初始化bean时,将通过 applyBeanPostProcessorsAfterInitialization 创建并返回目标bean的代理对象,并存入容器中。
目前为止的过程,都是在初始化bean前完成的
下面两张图中的代码流程是关键
1、initializeBean流程
2、refresh流程(AOP中我们需要了解的)
- postProcessBeanFactory(beanFactory);
- invokeBeanFactoryPostProcessors(beanFactory);
- finishBeanFactoryInitialization(beanFactory);
refresh完整流程参考如下:
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh(); // Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory); try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory); // Initialize message source for this context.
initMessageSource(); // Initialize event multicaster for this context.
initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses.
onRefresh(); // Check for listener beans and register them.
registerListeners(); // Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event.
finishRefresh();
} catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
} // Destroy already created singletons to avoid dangling resources.
destroyBeans(); // Reset 'active' flag.
cancelRefresh(ex); // Propagate exception to caller.
throw ex;
} finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
refresh
在下一篇中,也是本系列的最后一篇,我们将探究增强器(日志方法)是如何通过代理对象,在代理对象方法执行的时候发挥作用的。
spring——AOP原理及源码(四)的更多相关文章
- spring——AOP原理及源码(一)
教程共分为五篇,从AOP实例的构建及其重要组件.基本运行流程.容器创建流程.关键方法调用.原理总结归纳等几个方面一步步走进AOP的世界. 本篇主要为读者演示构建AOP实例及AOP核心组件分析. 一.项 ...
- spring——AOP原理及源码(二)
回顾: 在上一篇中,我们提到@EnableAspectJAutoProxy注解给容器中加入了一个关键组件internalAutoProxyCreator的BeanDefinition,实际类型为 An ...
- spring——AOP原理及源码(三)
在上一篇中,我们创建并在BeanFactory中注册了AnnotationAwareAspectJAutoProxyCreator组件.本篇我们将要探究,这个组件是在哪里以及何时发挥作用的. 调试的起 ...
- spring——AOP原理及源码(五)
前情回顾: 在上一篇中,通过 wrapIfNecessary 方法,我们获取到了合适的增强器(日志方法)与业务类进行包装,最终返回了我们业务类的代理对象. 本篇我们将从业务方法的执行开始,看看增强器( ...
- 【Spring】Spring IOC原理及源码解析之scope=request、session
一.容器 1. 容器 抛出一个议点:BeanFactory是IOC容器,而ApplicationContex则是Spring容器. 什么是容器?Collection和Container这两个单词都有存 ...
- Spring AOP介绍及源码分析
转自:http://www.uml.org.cn/j2ee/201301102.asp 软件开发经历了从汇编语言到高级语言和从过程化编程到面向对象编程:前者是为了提高开发效率,而后者则使用了归纳法,把 ...
- Spring中AOP原理,源码学习笔记
一.AOP(面向切面编程):通过预编译和运行期动态代理的方式在不改变代码的情况下给程序动态的添加一些功能.利用AOP可以对应用程序的各个部分进行隔离,在Spring中AOP主要用来分离业务逻辑和系统级 ...
- spring MVC 原理及源码解析
首先要知道springmvc的核心控制器dispatcherServlet(继承自httpServlet) 一般httpServlet的请求过程: 1.初始化(创建servlet实例时)时会执行ser ...
- Spring MVC工作原理及源码解析(三) HandlerMapping和HandlerAdapter实现原理及源码解析
1.HandlerMapping实现原理及源码解析 在前面讲解Spring MVC工作流程的时候我们说过,前端控制器收到请求后会调⽤处理器映射器(HandlerMapping),处理器映射器根据请求U ...
随机推荐
- PAT甲级——1012 The Best Rank
PATA1012 The Best Rank To evaluate the performance of our first year CS majored students, we conside ...
- SGD/BGD/MBGD使用python简单实现
算法具体可以参照其他的博客: 随机梯度下降: # coding=utf-8 ''' 随机梯度下降 ''' import numpy as np # 构造训练数据 x = np.arange(0., 1 ...
- 牛客-Forsaken的数列(Treap)
题目传送门 sol:第一次看题还真信了是用线段树来做,但是没什么想法,看了题解发现是我不会的Treap,然后花了几天时间学习了一下并补掉题目 无旋Treap #include <bits/std ...
- mac下停止和启动mysql命令
启动MySQL服务 sudo /usr/local/MYSQL/support-files/mysql.server start 停止MySQL服务 sudo /usr/local/mysql/s ...
- [LC] 244. Shortest Word Distance II
Design a class which receives a list of words in the constructor, and implements a method that takes ...
- VM Storage Policies深度解析
- Visual Studio通过Cordova支持混合跨平台移动开发
Microsoft在Visual Studio 2013 Update 2中添加了对混合跨平台移动应用程序的本地支持. Microsoft早在2011年就已经开始了与PhoneGap的合作,那时候是为 ...
- python读取配置文件报keyerror-文件路径不正确导致的错误
- 在其他模块使用反射读取配置文件报错,但是在反射模块中读取GetData.check_list又是正确的 反射模块如下: # get_data.py from API_AUTO.p2p_projec ...
- 杂谈php之什么是cgi,fastcgi,fpm,cli,mod
杂谈PHP极少关注的问题 本话题来自于我使用PHP进行网页爬虫的一次经历.对于一个web开发者来说,PHP解释器本身却知之甚小,实在是惭愧呐! 首先这个话题要从几个提问开始. PHP是什么? 外文名: ...
- numpy的索引
import numpy as np A =np.arange(3,15).reshape(3,4) print(A) #第一行 print(A[2]) #返回元素 print(A[1][2]) pr ...