本文将对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;

对上述的源码分析作下简单的总结

  1. 首先对Advice/Advisor/AopInfrastructureBean接口的实现类不进行代理操作;对beanName为aspectName的bean对象也不采取代理操作

  2. 再而需要查找beanClass有几个advisor与之相关联,这当然与每个Advisor接口内的PointCut对象对应的表达式expression有关。只有关联的Advisor个数不为0,才可采取代理(此处查找Advisor关联个数的源码读者可自行分析AspectJAwareAdvisorAutoProxyCreator#shouldSkip()方法)

  3. 真正的代理操作是由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());
}

对以上的源码也作下简单的总结

  1. 采用ProxyFactory对象来创建代理对象

  2. 如果AOP没有指定proxyTargetClass属性,则针对beanClass含有非InitializingBean/DisposableBean/Aware接口的bean采用静态代理,反之采用动态代理

  3. 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的更多相关文章

  1. SpringAop源码情操陶冶-JdkDynamicAopProxy

    承接前文SpringAop源码情操陶冶-AspectJAwareAdvisorAutoProxyCreator,本文在前文的基础上稍微简单的分析默认情况下的AOP代理,即JDK静态代理 JdkDyna ...

  2. Spring源码情操陶冶-AOP之ConfigBeanDefinitionParser解析器

    aop-Aspect Oriented Programming,面向切面编程.根据百度百科的解释,其通过预编译方式和运行期动态代理实现程序功能的一种技术.主要目的是为了程序间的解耦,常用于日志记录.事 ...

  3. SpringMVC源码情操陶冶-FreeMarker之web配置

    前言:本文不讲解FreeMarkerView视图的相关配置,其配置基本由FreeMarkerViewResolver实现,具体可参考>>>SpringMVC源码情操陶冶-ViewRe ...

  4. Spring源码情操陶冶-AbstractApplicationContext#finishBeanFactoryInitialization

    承接前文Spring源码情操陶冶-AbstractApplicationContext#registerListeners 约定web.xml配置的contextClass为默认值XmlWebAppl ...

  5. Spring源码情操陶冶-AbstractApplicationContext#registerListeners

    承接前文Spring源码情操陶冶-AbstractApplicationContext#onRefresh 约定web.xml配置的contextClass为默认值XmlWebApplicationC ...

  6. Spring源码情操陶冶-AbstractApplicationContext#onRefresh

    承接前文Spring源码情操陶冶-AbstractApplicationContext#initApplicationEventMulticaster 约定web.xml配置的contextClass ...

  7. Spring源码情操陶冶-AbstractApplicationContext#initApplicationEventMulticaster

    承接前文Spring源码情操陶冶-AbstractApplicationContext#initMessageSource 约定web.xml配置的contextClass为默认值XmlWebAppl ...

  8. Spring源码情操陶冶-AbstractApplicationContext#initMessageSource

    承接前文Spring源码情操陶冶-AbstractApplicationContext#registerBeanPostProcessors 约定web.xml配置的contextClass为默认值X ...

  9. Spring源码情操陶冶-AbstractApplicationContext#registerBeanPostProcessors

    承接前文Spring源码情操陶冶-AbstractApplicationContext#invokeBeanFactoryPostProcessors 瞧瞧官方注释 /** * Instantiate ...

随机推荐

  1. webstorm配置scss环境

    1.下载 Ruby  (安装过程中记得勾选添加到环境变量,安装结束最后可能会弹出一个cmd弹框,可以忽略) 2. cmd安装sass gem install sass 3. cmd检查是否安装 sas ...

  2. 关于DbContext能不能单次请求内唯一?DbContex需不需要主动释放?欢迎各路大侠来“参战”!

    基于前篇文章<HiBlogs>重写笔记[1]--从DbContext到依赖注入再到自动注入园友@Flaming丶淡蓝@ 吴瑞祥 提出了讨论和质疑,吓得我连夜查询资料(玩笑~). 本来文章的 ...

  3. Markdown 编写规范

    说明及目的 作为一个在博客园混迹了俩三年的人,一直在这里看别人的博客,现在准备开始写自己的博客,目的呢,就是一下几点吧: 项目过程中的历史经验教训积累记载,吃一堑长一智,不想在同一个坑掉进去好几次 学 ...

  4. Spring 具名参数NamedParameterJdbcTemplate

    具名参数: 具名参数:SQL 按名称(以冒号开头)而不是按位置进行指定. 具名参数更易于维护, 也提升了可读性. 具名参数由框架类在运行时用占位符取代 我们之前一直是用JDBCTemplate  进行 ...

  5. 洗礼灵魂,修炼python(7)--元组,集合,不可变集合

    前面已经把列表的基本用法讲解完 接着讲python的几大核心之--元组(tuple) 1.什么是元组? 类似列表,但为不可变对象,之前提到列表是可变对象,所谓可变对象就是支持原处修改,并且在修改前后对 ...

  6. 机器学习之numpy库中常用的函数介绍(一)

    1. mat() mat()与array的区别: mat是矩阵,数据必须是2维的,是array的子集,包含array的所有特性,所做的运算都是针对矩阵来进行的. array是数组,数据可以是多维的,所 ...

  7. Vue使用总结

    好久没更新博客,确实是自己已经懒癌晚期,最近毕业刚工作3个月,公司开发一直在用Vue,自己个人也比较喜欢这个框架,今天就对自己学习到和用到的知识点作一些总结,希望能帮到大家. Vue 知道Vue也一定 ...

  8. asp.net 的发布与执行

    asp.net的工程文件在测试完成后就是发布(部署)和执行. 发布 本地发布的例子 1,菜单项  生成--发布 2,如果是第一次发布,点击自定义,设置配置文件的名称 3,发布方法--文件系统,设置本地 ...

  9. ZOJ 2002 Copying Books 二分 贪心

    传送门:Zoj2002 题目大意:从左到右把一排数字k分,得到最小化最大份,如果有多组解,左边的尽量小. 思路:贪心+二分(参考青蛙过河). 方向:从右向左. 注意:有可能最小化时不够k分.如     ...

  10. Kvm虚拟化的一种打包及部署方案(采用tar包,lvm方式)

    Kvm虚拟化的一种打包部署方案(采用tar包,lvm方式) –-–-–-2016年终总结 一 毕业之后跟师兄学到的第一块主要内容,理解花了不少时间.期间经历了shell的入门.linux基础知识入门. ...