SpringAop源码情操陶冶-AspectJAwareAdvisorAutoProxyCreator
本文将对SpringAop中如何为AspectJ切面类创建自动代理的过程作下简单的分析,阅读本文前需要对AOP的Spring相关解析有所了解,具体可见Spring源码情操陶冶-AOP之ConfigBeanDefinitionParser解析器
官方注释
注释内容如下
/**
* {@link org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator}
* subclass that exposes AspectJ's invocation context and understands AspectJ's rules
* for advice precedence when multiple pieces of advice come from the same aspect.
*
* @author Adrian Colyer
* @author Juergen Hoeller
* @author Ramnivas Laddad
* @since 2.0
*/
从注释上可知此类是org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator抽象类的继承类,主要为AspectJ切面服务。具体的针对如何创建代理,肯定是在父类中实现了,我们在此研究下父类。
AbstractAutoProxyCreator-AOP代理实现的抽象父类
继承结构上为extends ProxyProcessorSupport implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware,而其中的SmartInstantiationAwareBeanPostProcessor是beanPostProcessor接口的子类,此处注意下此接口。下面分析的内容建立在spring在实例化bean对象的时候会调用beanPostProcessor的公有接口。
AbstractAutoProxyCreator#postProcessAfterInitialization()-创建代理的入口函数
先奉上源码内容如下
/**
* Create a proxy with the configured interceptors if the bean is
* identified as one to proxy by the subclass.
* @see #getAdvicesAndAdvisorsForBean
*/
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
// 如果缓存中不存在则调用wrapIfNecessary()方法创建代理
if (!this.earlyProxyReferences.contains(cacheKey)) {
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
以上调用的是beanPostProcessor接口的postProcessAfterInitialization(Object bean,String beanName)的方法,其一般是bean对象被实例化的最后一步操作的,此处避而不谈了,我们转而查看wrapIfNecessary()的内部是如何操作的
AbstractAutoProxyCreator#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) {
// 查看缓存是否存在此bean
if (beanName != null && this.targetSourcedBeans.contains(beanName)) {
return bean;
}
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
}
// Advice/Advisor/AopInfrastructureBean接口的beanClass不进行代理以及对beanName为aop内的切面名也不进行代理,此处可查看子类复写的sholdSkip()方法
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
// Create proxy if we have advice.
// 查找对代理类相关的advisor对象集合,此处就与ponit-cut表达式有关了
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
// 对相应的advisor不为空才采取代理
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
// 调用createProxy()方法创建真正的代理对象
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;
对上述的源码分析作下简单的总结
首先对
Advice/Advisor/AopInfrastructureBean接口的实现类不进行代理操作;对beanName为aspectName的bean对象也不采取代理操作再而需要查找beanClass有几个advisor与之相关联,这当然与每个
Advisor接口内的PointCut对象对应的表达式expression有关。只有关联的Advisor个数不为0,才可采取代理(此处查找Advisor关联个数的源码读者可自行分析AspectJAwareAdvisorAutoProxyCreator#shouldSkip()方法)真正的代理操作是由
AbstractAutoProxyCreator#createProxy()内部方法实现
AbstractAutoProxyCreator#createProxy()-创建真正的AOP代理
对符合条件的beanClass进行相应的代理操作,简单看下源码
/**
* 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);
}
// 创建AOP代理工厂,拷贝相同的属性
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.copyFrom(this);
// 是否采用动态代理
if (!proxyFactory.isProxyTargetClass()) {
if (shouldProxyTargetClass(beanClass, beanName)) {
proxyFactory.setProxyTargetClass(true);
}
else {
// 查看beanClass对应的类是否含有InitializingBean.class/DisposableBean.class/Aware.class接口,无则采用静态代理,有则采用动态代理
evaluateProxyInterfaces(beanClass, proxyFactory);
}
}
// 获得所有关联的Advisor集合
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
for (Advisor advisor : advisors) {
proxyFactory.addAdvisor(advisor);
}
// 此处的targetSource一般为SingletonTargetSource
proxyFactory.setTargetSource(targetSource);
customizeProxyFactory(proxyFactory);
proxyFactory.setFrozen(this.freezeProxy);
// 是否设置预过滤模式,此处针对本文为true
if (advisorsPreFiltered()) {
proxyFactory.setPreFiltered(true);
}
return proxyFactory.getProxy(getProxyClassLoader());
}
对以上的源码也作下简单的总结
采用ProxyFactory对象来创建代理对象
如果AOP没有指定proxyTargetClass属性,则针对beanClass含有非
InitializingBean/DisposableBean/Aware接口的bean采用静态代理,反之采用动态代理ProxyFactory创建代理对象前也需要设置与beanClass相关联的advisor集合
ProxyFactory#getProxy()-最终创建代理类
public Object getProxy(ClassLoader classLoader) {
return createAopProxy().getProxy(classLoader);
}
以上的源码我们只需要关注createAopProxy()方法即可,此处我们直接查看DefaultAopProxyFactory#createAopProxy()源码,参考如下
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (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.");
}
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}
上述源码很简单,默认是采用JDK静态代理,对beanClass为非接口实现类采取CGLIB动态代理
小结
循序渐进的分析的总结见以上的各部分小结,我们只需要知道AOP代理的beanClass必须有相应的
Advisor接口与之绑定,才会创建AOP代理创建的代理类无非是JDK代理抑或是CGLIB代理,但是具体关联的
Advisor集合的处理逻辑见下文讲解
SpringAop源码情操陶冶-AspectJAwareAdvisorAutoProxyCreator的更多相关文章
- SpringAop源码情操陶冶-JdkDynamicAopProxy
承接前文SpringAop源码情操陶冶-AspectJAwareAdvisorAutoProxyCreator,本文在前文的基础上稍微简单的分析默认情况下的AOP代理,即JDK静态代理 JdkDyna ...
- Spring源码情操陶冶-AOP之ConfigBeanDefinitionParser解析器
aop-Aspect Oriented Programming,面向切面编程.根据百度百科的解释,其通过预编译方式和运行期动态代理实现程序功能的一种技术.主要目的是为了程序间的解耦,常用于日志记录.事 ...
- SpringMVC源码情操陶冶-FreeMarker之web配置
前言:本文不讲解FreeMarkerView视图的相关配置,其配置基本由FreeMarkerViewResolver实现,具体可参考>>>SpringMVC源码情操陶冶-ViewRe ...
- Spring源码情操陶冶-AbstractApplicationContext#finishBeanFactoryInitialization
承接前文Spring源码情操陶冶-AbstractApplicationContext#registerListeners 约定web.xml配置的contextClass为默认值XmlWebAppl ...
- Spring源码情操陶冶-AbstractApplicationContext#registerListeners
承接前文Spring源码情操陶冶-AbstractApplicationContext#onRefresh 约定web.xml配置的contextClass为默认值XmlWebApplicationC ...
- Spring源码情操陶冶-AbstractApplicationContext#onRefresh
承接前文Spring源码情操陶冶-AbstractApplicationContext#initApplicationEventMulticaster 约定web.xml配置的contextClass ...
- Spring源码情操陶冶-AbstractApplicationContext#initApplicationEventMulticaster
承接前文Spring源码情操陶冶-AbstractApplicationContext#initMessageSource 约定web.xml配置的contextClass为默认值XmlWebAppl ...
- Spring源码情操陶冶-AbstractApplicationContext#initMessageSource
承接前文Spring源码情操陶冶-AbstractApplicationContext#registerBeanPostProcessors 约定web.xml配置的contextClass为默认值X ...
- Spring源码情操陶冶-AbstractApplicationContext#registerBeanPostProcessors
承接前文Spring源码情操陶冶-AbstractApplicationContext#invokeBeanFactoryPostProcessors 瞧瞧官方注释 /** * Instantiate ...
随机推荐
- 献身说法---修复bug时的一些小技巧
最近,修复了项目当中的一些bug,觉着有些思路可以分享出来供大家借鉴. 场景一 开发环境中系统正常运行,测试环境中,部分机器未能正常运行. 解决过程:远程连接了测试环境中的机器,观察了系统的运行情况, ...
- IOS7 点击空白处隐藏键盘的几种方法
IOS7 点击空白处隐藏键盘的几种方法 iOS开发中经常要用到输入框,默认情况下点击输入框就会弹出键盘,但是必须要实现输入框return的委托方法才能取消键盘的显示,对于用户体验来说很不友好,我们 ...
- linux学习笔记:1.基础知识和命令行基本操作
初次学习linux系统,想在这里记录自己的学习痕迹,如发现有不足之处,希望能指出,谢谢啦,之后的学习是在虚拟机VMware 10下的Red Hat Enterprise linux 6 的操作. 一. ...
- Codeforces 845 A. Chess Tourney 思路:简单逻辑题
题目: 题意:输入一个整数n,接着输入2*n个数字,代表2*n个选手的实力. 实力值大的选手可以赢实力值小的选手,实力值相同则都有可能赢. 叫你把这2*n个选手分成2个有n个选手的队伍. ...
- netty4.x 传输文件
一:简介 netty传输文件的例子并不多,当前的项目刚才需要使用netty,所以就记录一下使用方法,使用netty传输文件,首先需要启动一个服务端,等待服务端请求监听,然后传输文件的时候,启动一个客户 ...
- ASP.NET没有魔法——ASP.NET MVC 与数据库之EF实体类与数据库结构
大家都知道在关系型数据库中每张表的每个字段都会有自己的属性,如:数据类型.长度.是否为空.主外键.索引以及表与表之间的关系.但对于C#编写的类来说,它的属性只有一个数据类型和类与类之间的关系,但是在M ...
- 第三章 MySQL高级查询(一)
第三章 MySQL高级查询(一) 一.SQL语言的四个分类 1. DML(Data Manipulation Language)(数据操作语言):用来插入,修改和删除表中的数据,如INSE ...
- iOS开发必不可少的76个工具
如果你去到一位熟练的木匠的工作室,你总是能发现他/她有一堆工具来完成不同的任务. 软件开发同样如此.你可以从软件开发者如何使用工具中看出他水准如何.有经验的开发者精于使用工具.对你目前所使用的工具不断 ...
- 【原创】修复ios输入框获取焦点时不支持fixed的bug
前些日子,做了一个手机站的项目,有一个页面是这样的, 有一个固定(position:fixed)的头部和底部导航,中间是一些表单内容,没啥特别的.但是到了ios中,正常滚动页面没有问题,一旦触发了文本 ...
- Mvc 流程调用分析
链接地址 https://www.processon.com/view/link/59e71fbbe4b09000f03ce78e 总结: 1. 在Global.ascx 中我们使用RouteColl ...