在 Spring5.2 源码学习 - 读取 XML 的 Bean 配置信息 阶段知道:
AOP 相关配置属于自定义标签解析,对应 AopNamespaceHandler 如下:
public class AopNamespaceHandler extends NamespaceHandlerSupport {
@Override
public void init() {
// In 2.0 XSD as well as in 2.5+ XSDs
registerBeanDefinitionParser("config"
, new ConfigBeanDefinitionParser());
registerBeanDefinitionParser("aspectj-autoproxy"
, new AspectJAutoProxyBeanDefinitionParser());
registerBeanDefinitionDecorator("scoped-proxy"
, new ScopedProxyBeanDefinitionDecorator());
// Only in 2.0 XSD: moved to context namespace in 2.5+
registerBeanDefinitionParser("spring-configured"
, new SpringConfiguredBeanDefinitionParser());
}
}
所以遇到 aspectj-autoproxy 配置就会进入 AspectJAutoProxyBeanDefinitionParser 解析器,其继承 BeanDefinitionParser 那么 parse 方法:
@Override
@Nullable
public BeanDefinition parse(Element element, ParserContext parserContext) {
// 注册 AnnotationAwareAspectJAutoProxyCreator
AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext
, element);
// 对注解的子类的处理
extendBeanDefinition(element, parserContext);
return null;
}
进入方法查看注册
public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary(ParserContext parserContext
, Element sourceElement) {
// 注册 AnnotationAwareAspectJAutoProxyCreator
BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(
parserContext.getRegistry(), parserContext.extractSource(sourceElement));
// 对 proxy-target-class 以及 expose-proxy 属性的处理
useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
// 注册并通知
registerComponentIfNecessary(beanDefinition, parserContext);
}
注册 AnnotationAwareAspectJAutoProxyCreator
@Nullable
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(
BeanDefinitionRegistry registry, @Nullable Object source) {
return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}
@Nullable
private static BeanDefinition registerOrEscalateApcAsRequired(
Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
// 如果 AUTO_PROXY_CREATOR_BEAN_NAME 存在
BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
// 并且和传入的不一样,那么比较下优先级(集合索引)
// 已经存在的
int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
// 传入的
int requiredPriority = findPriorityForClass(cls);
if (currentPriority < requiredPriority) {
// 谁在集合中索引位置大,谁赢
// 改变bean就改变其className
apcDefinition.setBeanClassName(cls.getName());
}
}
return null;
}
RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
beanDefinition.setSource(source);
beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
return beanDefinition;
}
- 如果判断没有bean AUTO_PROXY_CREATOR_BEAN_NAME 那么就直接创建;
- 如果已经存在了,就判断下是否和传入的一致,不一致的话,那么就比较下优先级。优先级大的成为新的。
proxy-target-class 以及 expose-proxy 属性的处理
很明显,根据配置信息,向 BeanDefinition 中添加属性。代码如下所示:private static void useClassProxyingIfNecessary(BeanDefinitionRegistry registry
, @Nullable Element sourceElement) {
if (sourceElement != null) {
// 如果配置了 proxy-target-class="true",那么使用添加属性 proxyTargetClass=true
boolean proxyTargetClass
= Boolean.parseBoolean(sourceElement.getAttribute(PROXY_TARGET_CLASS_ATTRIBUTE));
if (proxyTargetClass) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
// expose-proxy ture,那么添加属性exposeProxy=true
boolean exposeProxy = Boolean.parseBoolean(sourceElement.getAttribute(EXPOSE_PROXY_ATTRIBUTE));
if (exposeProxy) {
AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
}
}
}
这里涉及到两个配置:proxy-target-class 、expose-proxy
Spring Aop 使用 JDK 动态代理或者 CGLIB 代理,如果被代理目标至少有一个接口,那么就会使用 JDK 动态代理,代理其所有实现接口的方法。如果被代理对象没有任何接口,那么就会创建 CBLIB 代理,代理其所有的方法。如果想强制使用 CGLIB 代理,则需要配置<aop:config proxy-target-class="true">
来强制使用。如果强制使用 CGLIB 需要考虑如下问题:
- 无法通知(advise) Final 方法,因为它们不能被重写;
- CGLIB 是独立的包,需要引入项目中;
有些时候对象内部的自我调用无法切中增强,这个时候就可以用到 expose-proxy。
public interface Aservice{
public void a();
public void b();
}
@Service
public class AServiceImpl implements AService{
@Transactional(propagation = Propagation.REQUIRED)
public void a(){
this.b();
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void b(){
}
}
此时调用 this.b() ,并不能执行增强,解决这个问题可以增加如下配置:
<aop:aspectj-autoproxy expose-proxy="true"/>
方法调用调整为:修改为 “((AService) AopContext.currentProxy()).b();” 即可.
registerComponentIfNecessary
注册通知。