- 前言
- 第一部分 核心实现
- 第 1 章 Spring 整体架构和环境搭建
- 第 2 章 容器的基本实现
- 第 3 章 默认标签的解析
- 第 4 章 自定义标签的解析
- 第 5 章 bean 的加载
- 第 6 章 容器的功能扩展
- 第 7 章 AOP
- 第二部分 企业应用
- 第 8 章 数据库连接 JDBC
- 第 9 章 整合 MyBatis
- 第 10 章 事务
- 第 11 章 SpringMVC
- 第 12 章 远程服务
- 第 13 章 Spring 消息
7.2.1 注册 AnnotationAwareAspectJAutoProxyCreator
所有解析器,因为是对 BeanDefinitionParser 接口的统一实现,入口都是从 parse 函数开始的,AspectJAutoProxyBeanDefinitionParser 的 parse 函数如下:
public BeanDefinition parse(Element element, ParserContext parserContext) {
//注册 AnnotationAwareAspectJAutoProxyCreator
AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary
(parserContext, element);
//对于注解中子类的处理
extendBeanDefinition(element, parserContext);
return null;
}
其中 registerAspectJAnnotationAutoProxyCreatorIfNecessary 函数是我们比较关心的,也是关键逻辑的实现。
/**
* 注册 AnnotationAwareAspectJAutoProxyCreator
* @param parserContext
* @param sourceElement
*/
public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary(
ParserContext parserContext, Element sourceElement) {
//注册或升级 AutoProxyCreator 定义 beanName 为 org.Springframework.aop.config.
internalAutoProxyCreator 的 BeanDefinition
BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAuto
ProxyCreatorIfNecessary(
parserContext.getRegistry(), parserContext.extractSource(sourceElement));
//对于 proxy-target-class 以及 expose-proxy 属性的处理
useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
//注册组件并通知,便于监听器做进一步处理
//其中 beanDefinition 的 className 为 AnnotationAwareAspectJAutoProxyCreator
registerComponentIfNecessary(beanDefinition, parserContext);
}
在 registerAspectJAnnotationAutoProxyCreatorIfNecessary 方法中主要完成了 3 件事情,基本上每行代码就是一个完整的逻辑。
1.注册或者升级 AnnotationAwareAspectJAutoProxyCreator
对于 AOP 的实现,基本上都是靠 AnnotationAwareAspectJAutoProxyCreator 去完成,它可以根据 @Point 注解定义的切点来自动代理相匹配的 bean。但是为了配置简便,Spring 使用了自定义配置来帮助我们自动注册 AnnotationAwareAspectJAutoProxyCreator,其注册过程就是在这里实现的。
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary
(BeanDefinitionRegistry registry, Object source) {
return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.
class, registry, source);
}
private static BeanDefinition registerOrEscalateApcAsRequired(Class cls, BeanDefinition
Registry registry, Object source) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
//如果已经存在了自动代理创建器且存在的自动代理创建器与现在的不一致那么需要根据优先级来判断到底需要使用哪
if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
//AUTO_PROXY_CREATOR_BEAN_NAME =
//"org.Springframework.aop.config.internalAutoProxyCreator";
BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_
CREATOR_BEAN_NAME);
if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
int currentPriority = findPriorityForClass(apcDefinition.getBean ClassName());
int requiredPriority = findPriorityForClass(cls);
if (currentPriority < requiredPriority) {
//改变 bean 最重要的就是改变 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);
//AUTO_PROXY_CREATOR_BEAN_NAME =
//"org.Springframework.aop.config.internalAutoProxyCreator";
registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
return beanDefinition;
}
以上代码中实现了自动注册 AnnotationAwareAspectJAutoProxyCreator 类的功能,同时这里还涉及了一个优先级的问题,如果已经存在了自动代理创建器,而且存在的自动代理创建器与现在的不一致,那么需要根据优先级来判断到底需要使用哪个。
2.处理 proxy-target-class 以及 expose-proxy 属性
useClassProxyingIfNecessary 实现了 proxy-target-class 属性以及 expose-proxy 属性的处理。
private static void useClassProxyingIfNecessary(BeanDefinitionRegistry registry, Element
sourceElement) {
if (sourceElement != null) {
//对于 proxy-target-class 属性的处理。
boolean proxyTargetClass = Boolean.valueOf(sourceElement.getAttribute
(PROXY_TARGET_CLASS_ATTRIBUTE));
if (proxyTargetClass) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
//对于 expose-proxy 属性的处理
boolean exposeProxy = Boolean.valueOf(sourceElement.getAttribute (EXPOSE_
PROXY_ATTRIBUTE));
if (exposeProxy) {
AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
}
}
}
//强制使用的过程其实也是一个属性设置的过程
public static void forceAutoProxyCreatorToUseClassProxying(BeanDefinitionRegistry
registry) {
if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
BeanDefinition definition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_
BEAN_NAME);
definition.getPropertyValues().add("proxyTargetClass", Boolean.TRUE);
}
}
static void forceAutoProxyCreatorToExposeProxy(BeanDefinitionRegistry registry) {
if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
BeanDefinition definition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_
BEAN_NAME);
definition.getPropertyValues().add("exposeProxy", Boolean.TRUE);
}
}
proxy-target-class:Spring AOP 部分使用 JDK 动态代理或者 CGLIB 来为目标对象创建代理。(建议尽量使用 JDK 的动态代理),如果被代理的目标对象实现了至少一个接口,则会使用 JDK 动态代理。所有该目标类型实现的接口都将被代理。若该目标对象没有实现任何接口,则创建一个 CGLIB 代理。如果你希望强制使用 CGLIB 代理,(例如希望代理目标对象的所有方法,而不只是实现自接口的方法)那也可以。但是需要考虑以下两个问题。
◆无法通知(advise)Final 方法,因为它们不能被覆写。
◆ 你需要将 CGLIB 二进制发行包放在 classpath 下面。
与之相较,JDK 本身就提供了动态代理,强制使用 CGLIB 代理需要将 <aop:config>的 proxy-target-class 属性设为 true:
<aop:config proxy-target-class="true"> ... </aop:config>
当需要使用 CGLIB 代理和 @AspectJ 自动代理支持,可以按照以下方式设置 <aop:aspectj-autoproxy>的 proxy-target-class 属性:
<aop:aspectj-autoproxy proxy-target-class="true"/>
而实际使用的过程中才会发现细节问题的差别,The devil is in the detail。
JDK 动态代理:其代理对象必须是某个接口的实现,它是通过在运行期间创建一个接口的实现类来完成对目标对象的代理。
CGLIB 代理:实现原理类似于 JDK 动态代理,只是它在运行期间生成的代理对象是针对目标类扩展的子类。CGLIB 是高效的代码生成包,底层是依靠 ASM(开源的 Java 字节码编辑类库)操作字节码实现的,性能比 JDK 强。
expose-proxy:有时候目标对象内部的自我调用将无法实施切面中的增强,如下示例:
public interface AService {
public void a();
public void b();
}
@Service()
public class AServiceImpl1 implements AService{
@Transactional(propagation = Propagation.REQUIRED)
public void a() {
this.b();
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void b() {
}
}
此处的 this 指向目标对象,因此调用 this.b() 将不会执行 b 事务切面,即不会执行事务增强,因此 b 方法的事务定义“@Transactional(propagation = Propagation.REQUIRES_NEW)”将不会实施,为了解决这个问题,我们可以这样做:
<aop:aspectj-autoproxy expose-proxy="true"/>
然后将以上代码中的“this.b();”修改为“((AService) AopContext.currentProxy()).b();”即可。通过以上的修改便可以完成对 a 和 b 方法的同时增强。
最后注册组件并通知,便于监听器做进一步处理,这里就不再一一赘述了。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论