XML 中 AOP 标签的使用
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<aop:aspectj-autoproxy />
<bean id="test" class="org.springframework.mytest.aop.TestBean"/>
<bean class="org.springframework.mytest.aop.AspectJTest"/>
</beans>
其中,<aop:aspectj-autoproxy />
就是 AOP 的标签,下面我们将就这个标签的解析展开分析。
注册标签解析器
在 AopNamespaceHandler
中,可以看到对于 aspectj-autoproxy
加入的对应的解析器。
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
进行解析,下面我们看看解析器的具体实现。
注册 AspectJAnnotationAutoProxyCreator
同其他解析器一样,parse
函数是所有解析器的入口。
public BeanDefinition parse(Element element, ParserContext parserContext) {
// 注册 AspectJAnnotationAutoProxyCreator
AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
// 对于注解中子类的处理
extendBeanDefinition(element, parserContext);
return null;
}
其中 registerAspectJAnnotationAutoProxyCreatorIfNecessary
函数是核心逻辑。
public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary(
ParserContext parserContext, Element sourceElement) {
// 注册或升级 AutoProxyCreator 的 BeanDefinition
BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(
parserContext.getRegistry(), parserContext.extractSource(sourceElement));
// 对于 proxy-target-class 以及 expose-proxy 属性的处理
useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
// 注册组件并通知,便于监听器做进一步处理
// 其中 beanDefinition 的 className 为 AspectJAnnotationAutoProxyCreator
registerComponentIfNecessary(beanDefinition, parserContext);
}
注册或升级 AutoProxyCreator 的 BeanDefinition
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(
BeanDefinitionRegistry registry, @Nullable Object source) {
return registerOrEscalateApcAsRequired(
AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}
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)) {
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 所对应的 class 属性就相当于改变了 bean
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;
}
上述代码实现的 AutoProxyCreator
相关类的注册功能,同时还通过判断优先级保证优先级最高的被注册。
处理 proxy-target-class 以及 expose-proxy 属性
private static void useClassProxyingIfNecessary(BeanDefinitionRegistry registry, @Nullable Element sourceElement) {
if (sourceElement != null) {
// 对于 proxy-target-class 属性的处理
boolean proxyTargetClass = Boolean.parseBoolean(sourceElement.getAttribute(PROXY_TARGET_CLASS_ATTRIBUTE));
if (proxyTargetClass) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
// 对于 expose-proxy 属性的处理
boolean exposeProxy = Boolean.parseBoolean(sourceElement.getAttribute(EXPOSE_PROXY_ATTRIBUTE));
if (exposeProxy) {
AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
}
}
}
下面将对 proxy-target-class 以及 expose-proxy 这两个属性进行一定的讲解。
proxy-target-class
Spring AOP 对实现了至少一个接口的类才用 JDK 动态代理,而其他的使用 CGLIB 来为目标对象创建代理。两种的主要差别如下:
- JDK 动态代理:代理对象必须是某个接口的实现,它是通过运行期间创建一个借口的实现类来完成对目标对象的代理。
- CGLIB 代理:实现原理是在运行期间生成的代理对象是针对目标类扩展的子类。CGLIB 是高校的代码生成包,底层是依靠 ASM (开源的 Java 字节码编辑类库)操作字节码实现的,性能比 JDK 强。
当需要使用 CGLIB 代理和 @AspectJ 自动代理支持,可以按如下方法设置:
<aop:aspectj-autoproxy proxy-target-class="true"/>
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() 不会执行 b 事务切面,为了解决该问题可以按如下方式配置:
<aop:aspectj-autoproxy expose-proxy="true" />
然后将代码中 “this.b()” 改为 “((AService) AopContext.currentProxy()).b()” 即可。