Sping AOP 源码解析(一、动态 AOP 自定义标签 aop:aspectj-autoproxy)

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()” 即可。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值