1. Spring Aop 配置式Aop 概述
上篇是 Spring Aop 简单配置式编程的(通过 配置 ProxyFactoryBean 来针对指定 target 来创建代理对象), 但 ProxyFactoryBean 只能针对单个 target, 所以出现了AbstractAutoProxyCreator(自动获取 BeanFactory 中的所有 Advice/MethodInterceptor, 并针对所有符合 Pointcut 的对象创建代理对象), 这个类是其他自动获取 Advice/MethodInterecptor 的基类, 其主要有一下属性:
/**
* Convenience constant for subclasses: Return value for "do not proxy".
* @see #getAdvicesAndAdvisorsForBean
*/
// 代表不需要对这个 bean 动态代理的常量类
protected static final Object[] DO_NOT_PROXY = null;
/**
* Convenience constant for subclasses: Return value for
* "proxy without additional interceptors, just the common ones".
* @see #getAdvicesAndAdvisorsForBean
*/
// 代表 需要进行动态代理的常量
protected static final Object[] PROXY_WITHOUT_ADDITIONAL_INTERCEPTORS = new Object[0];
/** Logger available to subclasses */
protected final Log logger = LogFactory.getLog(getClass());
/** Default is global AdvisorAdapterRegistry */
// 这里的 advisorAdapterRegistry 中主要是将 advice, MethodInterceptor 包装成 Advisor, 与将 Advisor 转成 MethodInterceptor
private AdvisorAdapterRegistry advisorAdapterRegistry = GlobalAdvisorAdapterRegistry.getInstance();
/**
* Indicates whether or not the proxy should be frozen. Overridden from super
* to prevent the configuration from becoming frozen too early.
*/
// 标示是否 代理的配置属性不允许改变
private boolean freezeProxy = false;
/** Default is no common interceptors */
// 拦截器的类名 <- 这里适用通配符
private String[] interceptorNames = new String[0];
// 是否将 通过 "interceptorNames" 解析得出的 MethodInterceptor 放在前面
private boolean applyCommonInterceptorsFirst = true;
// 当配置了 customTargetSourceCreators, 则会在 postProcessBeforeInstantiation 方法中进行创建代理类
private TargetSourceCreator[] customTargetSourceCreators;
// Spring 的 IOC 工厂
private BeanFactory beanFactory;
/** 这里的 targetSourcedBeans 将与 earlyProxyReferences 一起分析
* targetSourcedBeans: 当在实例化前置方法 postProcessBeforeInstantiation 中创建了代理类, 则在 targetSourcedBeans 中将添加 beanName, 也就是 targetSourcedBeans 中含有 beanName 则说明这个类被动态代理了
* earlyProxyReferences: 当 Bean 被循环引用, 并且被暴露了, 则会通过 getEarlyBeanReference 来创建代理类; 在初始化后置方法 postProcessAfterInitialization 中也会通过判断 earlyProxyReferences 中是否存在 beanName 来决定是否需要对 target 进行动态代理
*/
private final Set<String> targetSourcedBeans =
Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>(16));
private final Set<Object> earlyProxyReferences =
Collections.newSetFromMap(new ConcurrentHashMap<Object, Boolean>(16));
// proxyTypes 中存储的是 beanClass <--> 最终被代理生成的类的类型
private final Map<Object, Class<?>> proxyTypes = new ConcurrentHashMap<Object, Class<?>>(16);
// advisedBeans 中存储的是 class <--> 是否应该被代理 (TRUE | FALSE)
private final Map<Object, Boolean> advisedBeans = new ConcurrentHashMap<Object, Boolean>(256);
这些属性都是为其创建代理对象而工作的, 而 AbstractAutoProxyCreator 本身也是一个 SmartInstantiationAwareBeanPostProcessor (具备在类实例化/初始化的前后设置钩子方法的类), 同时也是一个 ProxyProcessorSupport 类(这个类中配置类创建 Proxy 所需的属性信息), 再看一下对应的 UML 图:
AbstractAutoProxyCreator.png
图中清晰的呈现了 AbstractAutoProxyCreator 通过接口实现与继承 ProxyProcessorSupport 所具备的技能, 而我们常规的自动获取Advisor, 创建代理对象也是在 实例化/初始化 方法的前后钩子方法中.
2. Spring Aop 配置式Aop 创建动态代理方法
在 AbstractAutoProxyCreator 中创建动态代理主要通过:
1. postProcessBeforeInstantiation: 实例化前置方法 <- 当配置类自定义的 customTargetSourceCreators, 并且针对 Bean 能返回对应 TargetBean, 则就可以进行动态代理
2. postProcessAfterInitialization: 初始化后置方法, 这是一般类动态代理生成的地方
3. getEarlyBeanReference: 当 Spring 里面的类循环引用, 则通过 ObjectFactory 回调 getEarlyBeanReference 方法来提前对对象进行代理, 并暴露出去 (以上都是进行对类进行动态代理的方法入口, 但针对同一个类, 三个方法只有一个会被调用)
先来看一下 postProcessBeforeInstantiation, 这是一个类实例化的前置方法, 主逻辑如下:
1. 根据给定的 bean 的 class 和 name 构建出个 key, 格式: beanClassName_beanName
2. targetSourcedBeans.contains(beanName) = true -> 已经通过实例化前置动态代理过对象
2.1 advisedBeans.containsKey(cacheKey) = true -> 表示通过其他渠道对这个对象进行了动态代理
2.2 若是 Spring 容器的基础类 或 是被 @AspectJ 注解修饰的类, 则直接跳过
3. 通过 customTargetSourceCreators 来获取 targetSource (这里的 customTargetSourceCreators 是用户自定义的 TargetSource 创建器)
3.1 通过模版方法 getAdvicesAndAdvisorsForBean 来收集MethodInterceptor / Advice 对象 <- 这里主要分为两类 1. 直接获取 BeanFactory 中所有的 Advisor 2. 获取 Spring 中所有类 @AspectJ 注解修饰的类, 并转化成Advisor, 最后再刷选出符合 Pointcut 的Advisor
3.2 调用 createProxy 方法来创建代理对象
与之对应的代码如下:
@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException { // 在 Bean 实例化前面 进行处理
// 根据给定的 bean 的 class 和 name 构建出个 key, 格式: beanClassName_beanName
Object cacheKey = getCacheKey(beanClass, beanName);
/** targetSourcedBeans
* 参考资料: http://jinnianshilongnian.iteye.com/blog/1492424
*
*/
if (beanName == null || !this.targetSourcedBeans.contains(beanName)) { // targetSourcedBeans.contains(beanName) = true, 表示已经通过实例化前置动态代理过对象
if (this.advisedBeans.containsKey(cacheKey)) { // 此 cacheKey 已经被动态代理了, 直接返回 null
return null;
}
if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {// 基础类 与 被 Aspect 注释的类 应该直接跳过
this.advisedBeans.put(cacheKey, Boolean.FALSE); // 在 advisedBeans 中设置 cacheKey 应该跳过
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); // 若配置了 TargetSourceCreator, 并且根据 beanClass, beanName 能获取 TargetBean <-- 这个很少用了
if (targetSource != null) {
this.targetSourcedBeans.add(beanName); // 从这里可以看出 targetSourcedBeans 是已经被代理的 bean的 name
/** 获取 运用到 动态代理上的 Advice / MethodInterceptor, 主要分为下面两类:
* 1. 获取 BeanFactory 中所有 Advisor, 并且仅仅获取 符合 beanClass 的 Advisor (PS: 在计算是否匹配时, 只要满足 其中一个方法匹配就选取)
* 2. 若配置的是 AnnotationAwareAspectJAutoProxyCreator, 则将获取所有被 @AspectJ 标注的类, 并解析成 Advisor, 最后再帅选合适的 Advisor
*/
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
/** 动态代理对象的创建
* 1. 将 interceptorNames 解析成的 MethodInterceptor / Advice 包装成 Advisor (包括上面的 specificInterceptors)
* 2. 选择合适的 AopProxy
* 3. 创建代理对象
*/
Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
this.proxyTypes.put(cacheKey, proxy.getClass()); // key 是 对应的 beanName, value 是代理类的类型 <-- 这里放置 proxy.getClass 主要是用于预测类型
return proxy;
}
}
return null;
}
与之对应的是 初始化后置方法 postProcessAfterInitialization, 通常我们的代理对象都是在这个方法里面创建的; 这里有个小逻辑, 就是 getEarlyBeanReference 与 postProcessAfterInitialization 这两个方法只会有其中一个会被调用, 所以就会有 earlyProxyReferences.contains(cacheKey) 这个判断, 其他的创建逻辑与上面的实例化前置方法类似!
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { // 生成代理对象
if (bean != null) {
// 根据给定的 bean 的 class 和 name 构建出个 key, 格式 beanClassName_beanName
Object cacheKey = getCacheKey(bean.getClass(), beanName);
// 是否已经 由于避免循环依赖而创建 bean 代理
if (!this.earlyProxyReferences.contains(cacheKey)) {// 这里有个判断 !earlyProxyReferences.contains(cacheKey), 主要还是可能上面 getEarlyBeanReference 方法已经调用过了, 已经生成了代理对象
// 如果它适合被代理, 则需要封装指定 bean
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
if (beanName != null && this.targetSourcedBeans.contains(beanName)) { // 可能 Bean 已经在 postProcessBeforeInstantiation 中处理过了 (PS: 已经生成代理类了)
return bean;
}
// 无需增强 下面 advisedBeans 可能此时还没有含有 cacheKey, 所以 get 出 null
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) { // advisedBeans 的值是在 AbstractAutoProxyCreator.postProcessBeforeInstantiation 里面进行设置
return bean;
}
// 给定的 bean 类是否代表一个基础设施类 ,基础设施类不应代理, 或者配置了指定 bean 不需要地动代理 (PS: 比如被 @AspectJ 注解修饰的类)
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
/** 获取 运用到 动态代理上的 Advice / MethodInterceptor, 主要分为下面两类:
* 1. 获取 BeanFactory 中所有 Advisor, 并且仅仅获取 符合 beanClass 的 Advisor (PS: 在计算是否匹配时, 只要满足 其中一个方法匹配就选取)
* 2. 若配置的是 AnnotationAwareAspectJAutoProxyCreator, 则将获取所有被 @AspectJ 标注的类, 并解析成 Advisor, 最后再帅选合适的 Advisor
*/
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors != DO_NOT_PROXY) { // DO_NOT_PROXY 代表着此类不需要进行动态代理, 见子类 BeanNameAutoProxyCreator
this.advisedBeans.put(cacheKey, Boolean.TRUE); // 设置 cacheKey 所代表的类被动态代理了
Object proxy = createProxy( // 根据 MethodInterceptor/Advice, class, beanName 创建动态代理类
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass()); // key 是 对应的 beanName, value 是代理类的类型 <-- 这里放置 proxy.getClass 主要是用于预测类型
return proxy;
}
this.advisedBeans.put(cacheKey, Boolean.FALSE); // 设置 cacheKey 所代表的类 没被动态代理了
return bean;
}
3. Spring Aop 配置式Aop AbstractAutoProxyCreator 的子类
AbstractAutoProxyCreator 主要有以下子类:
1. BeanNameAutoProxyCreator: 这个类继承 AbstractAutoProxyCreator, 在这个类中主要是根据 配置的 beanNames 来判断是否需要对这个类进行动态代理
2. AbstractAdvisorAutoProxyCreator: 这是一个抽象类, 在其内部通过 advisorRetrievalHelper 获取 BeanFactory 中所有的Advisor, 并通过 AopUtils.findAdvisorsThatCanApply 来查询所有适配的 Advisor
3. DefaultAdvisorAutoProxyCreator: 这个类是AbstractAdvisorAutoProxyCreator的子类, 其增加了 通过类名的前缀来判别是否需要对类进行动态代理
4. AspectJAwareAdvisorAutoProxyCreator: 这个类也是 AbstractAdvisorAutoProxyCreator 的子类, 主要是运用于 通过 aop namespace 的方式配置 Advisor/Pointcut/Advice(这几个对象在解析 XML 时就会生成对应的 AbstractAspectJAdvice, AspectJExpressionPointcut); AspectJAwareAdvisorAutoProxyCreator 主要完成了 1: 完成 Advisor 的排序, 2: 在shouldSkip方法中通过 advisorRetrievalHelper 获取容器中的所有 Advisor
5. AnnotationAwareAspectJAutoProxyCreator: 这是AbstractAutoProxyCreator中使用最频繁的一个类, 这个类主要通过aspectJAdvisorsBuilder解析被 @AspectJ 标注的类上面的其他注解信息来生成 Advisor, 对应的生成过程及策略是交由 aspectJAdvisorFactory 来完成的
4. Spring Aop 配置式Aop 通过 Aop 命名空间配置动态代理
AspectJAwareAdvisorAutoProxyCreator 这个自动代理创建器就是完成这个功能, 而对应得解析器则是 ConfigBeanDefinitionParser, 解析得步骤很复杂, 我们从入口窥得:
@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
CompositeComponentDefinition compositeDef =
new CompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element));
parserContext.pushContainingComponent(compositeDef);
configureAutoProxyCreator(parserContext, element); // 1. 在 DefaultListableBeanFactory 中注入 AspectJAwareAdvisorAutoProxyCreator
List<Element> childElts = DomUtils.getChildElements(element);// 2. 得到 <aop: config> 标签下面的所有子标签
for (Element elt: childElts) {
String localName = parserContext.getDelegate().getLocalName(elt);
if (POINTCUT.equals(localName)) { // 3. 解析 aop:pointcut 标签, 生成 AspectJExpressionPointcut, 并注入到 BeanFactory
parsePointcut(elt, parserContext);
}
else if (ADVISOR.equals(localName)) { // 4. 解析 aop:advisor 标签, 生成 DefaultBeanFactoryPointcutAdvisor, 并注入到容器中
parseAdvisor(elt, parserContext);
}
else if (ASPECT.equals(localName)) { // 5. 解析 aop:aspect 标签, 并解析其下面得所有子标签, 生成 AspectJPointcutAdvisor/AspectJExpressionPointcut/AbstractAspectJAdvice 并注入 BeanFactory
parseAspect(elt, parserContext);
}
}
parserContext.popAndRegisterContainingComponent();
return null;
}
入口分为: 注册 AspectJAwareAdvisorAutoProxyCreator, 解析 <aop:pointcut>, <aop:advisor>, <aop:aspect>
整个解析过程比较冗长, 下面就不叙述, 但其间会出现几个新的角色:
1. AbstractAspectJAdvice: 基于 aop namesapce 解析得到得 Advice 的基类, 它主要完成, 设置Advice方法上对应的参数名(包括 JoinPoint对应的参数名 THIS_JOIN_POINT), 获取方法的所有参数类型并与之对应的参数的名称, 创建统一的方法激活方法
2. AspectJMethodBeforeAdvice: 从 aop namespace 或org.aspectj.lang.annotation.Before注解注释的方法上解析出来的 BeforeAdvice<- 注意这个方法只是Advice, 要进行正真调用需要将其适配成 MethodInterceptor, MethodBeforeAdviceAdapter 就是完成这个功能的
3. AspectJAroundAdvice: 从 aop namespace 或org.aspectj.lang.annotation.Around注解注释的方法上解析出来的 Advice, 这里对应的是 MethodInterceptor, 在激活的方法中通常将 MethodInvocationProceedingJoinPoint 传入方法内
4. AspectJAfterThrowingAdvice: 从 aop namespace 或org.aspectj.lang.annotation.AfterThrowing注解注释的方法上解析出来的 ThrowingAdvice, 其还是先调用 MethodInvocation.preceed(), 若出现异常, 则在 catch 部分激活对应的 Advice 方法
5. AspectJAfterReturningAdvice: 从 aop namespace 或org.aspectj.lang.annotation.AfterReturning注解注释的方法上解析出来的 AfterReturningAdvice <- 这个只是 Advice, 需要 AfterReturningAdviceAdapter 将其转为 MethodInterceptor 后才能正真用于 动态代理的创建上
6. AspectJAfterAdvice: 从 aop namespace 或org.aspectj.lang.annotation.AfterReturning注解注释的方法上解析出来的 AfterAdvice <- 这是个 MethodInterceptor, 激活方法的地方在 finally 中
7. ReflectiveAspectJAdvisorFactory: Advisor/Advice 创建工厂类, 其实就是根据注解 Before/After/AfterReturing/AfterThrowing/Around的注解生成对应的 Advice, 最后在根据 AspectJExpressionPointcut 最终创建 Advisor
8. BeanFactoryAspectJAdvisorsBuilder: 这个工具类获取 BeanFactory中所有被 @AspectJ 注解标注的 Bean, 并通过 advisorFactory 解析成 Advisor, 存储到缓存中
最终 aop 命名空间解析器会将 xml 中的属性解析成 各种个样的 Advice/Advisor, 而在创建动态代理对象时, 在AspectJAwareAdvisorAutoProxyCreator 中 通过 shouldSkip -> findCandidateAdvisors 收集容器中所有的 Advisor
5. Spring Aop 配置式Aop 通过 AspectJ 注解声明 Aop
与之对应的 AbstractAutoProxyCreator 是 AnnotationAwareAspectJAutoProxyCreator, AnnotationAwareAspectJAutoProxyCreator在创建动态代理对象时会多一一部通过 BeanFactoryAspectJAdvisorsBuilder 获取所有的被 @AspectJ 注解标注的类, 并通过 AspectJAdvisorFactory 解析成各种 Advisor, 其他部分与AspectJAwareAdvisorAutoProxyCreator 差不多; 差点完了, 对应的命名空间解析器是 AspectJAutoProxyBeanDefinitionParser, 其主要还是完成 AnnotationAwareAspectJAutoProxyCreator 的注入工作
6. 总结
1. 声明式 aop 主要还是通过 AsbtractAutoProxyCreator 的子类来创建的
2. 声明式 aop 的创建过程主要在实例化前置方法, 初始化后置方法, 在循环依赖时 通过getEarlyBeanReference 提前获取对象时创建的
3. 基于 aop 命名空间申明的 Advice 与 基于 AspectJ 声明的 Advice, 最终都会生成 AbstractAspectJAdvice/AspectJExpressionPointcut
4. 在 aop 命名空间解析器 ConfigBeanDefinitionParser 中进行了大量的 解析工作, 对应的 AbstractAspectJAdvice/AspectJExpressionPointcut 也是在这里生成的
5. 基于 @AspectJ 注解的Aop <- 其获取Advisor/Advice 是在第一次创建 aop 对象时才操作, 也就是传说中的 lazy 加载, 主要还是通过 aspectJAdvisorsBuilder 获取所有被 @AspectJ 注解标注的类, 再通过 advisorFactory 解析成 Advisor
7. 参考:
Spring Aop核心源码分析
Spring技术内幕
Spring 揭秘
Spring 源码深度分析
开涛 Spring 杂谈
伤神 Spring 源码分析
Spring源码情操陶冶
网友评论