深入源码分析SpringAOP实现原理
阅读这篇文章前,最好有代理模式的基础,以及了解关于Spring扩展点例如BeanPostProcessor和如何使用自定义标签集成Spring,这些文章在我的博客里都能找到。当然,也最好有使用AOP的经验,这篇文章不会讲解如何使用AOP。
文章目录
1. AOP简介
说到AOP,其实这是一个面向方面的编程思想 ,它解决了OOP的一些弊端,例如我们需要为多个不具有继承关系的类引入一个公共行为, 比如说日志、权限验证、事务管理等等,我们需要将这些代码重复的添加到一系列的类中,将产生大量的重复代码,如果需要修改,将在每个类中去进行修改,不便于维护,代码的侵入性极高。所以就有了AOP这样面向方面编程的编程思想,其功能可以为每个需要的类加入共同的行为 ,如果需要修改,只需要修改切面中的代码,改一处等于改多处,并且便于编程,写一个切面类即可达到在每个类中加入重复代码的目的。
阅读此篇文章,你将了解Spring是如何实现AOP(前置通知、后置通知、环绕通知),由于Spring中的事务管理是基于AOP的功能来做的,所以你将更好的能理解Spring是如何将事务统一管理起来的。
2. 自定义标签开启AOP
只要用过AOP都知道,如果需要使用AOP,需要在配置文件中写这样一段配置:
<aop:aspectj-autoproxy />
只有写了这段配置才可以开启AOP功能,那么这个自定义标签又做了什么呢?在上一篇讲解自定义标签的文章中详细讲到了,此时我们需要关注其标签头aop
去寻找对应的命名空间:
xmlns:aop="http://www.springframework.org/schema/aop"
全局搜索命名空间http\://www.springframework.org/schema/aop
,注意http后加一个 “\” ,可以找到spring.handlers文件中对应的handler类:
http\://www.springframework.org/schema/aop=org.springframework.aop.config.AopNamespaceHandler
这样就找到了命名空间对应的handler:
public class AopNamespaceHandler extends NamespaceHandlerSupport {
/**
* Register the {@link BeanDefinitionParser BeanDefinitionParsers} for the
* '{@code config}', '{@code spring-configured}', '{@code aspectj-autoproxy}'
* and '{@code scoped-proxy}' tags.
*/
@Override
public void init() {
// In 2.0 XSD as well as in 2.1 XSD.
registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());
// Only in 2.0 XSD: moved to context namespace as of 2.1
registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
}
}
回到开头配置自定义标签,我们使用了aspectj-autoproxy
这个Parser。在init方法中,我们找到aspectj-autoproxy
对应的Parser是AspectJAutoProxyBeanDefinitionParser
这个类:
@Override
@Nullable
//我们只关注解析的主方法,parse方法
public BeanDefinition parse(Element element, ParserContext parserContext) {
//注册一个类到IOC容器中
AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
extendBeanDefinition(element, parserContext);
return null;
}
3. 注册AnnotationAwareAspectJAutoProxyCreator
AnnotationAwareAspectJAutoProxyCreator
是实现AOP功能的主要类,我们先来看看这个类的结构:
此类实现了BeanPostProcessor
,稍后将关注其后置处理Bean的方法postProcessAfterInitialization ,并且实现了BeanFactorAware
接口,此类将取得并存有一个BeanFactory
实例对象。
回到主线,关注注册此类的方法:
public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary(
ParserContext parserContext, Element sourceElement) {
//将一个类作为Bean注册到IOC容器中
BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(
parserContext.getRegistry(), parserContext.extractSource(sourceElement));
//处理proxy-target-class与expose-proxy属性
useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
//注册组件并通知
registerComponentIfNecessary(beanDefinition, parserContext);
}
其中,在注册这个类的过程中主要完成了3件事:
-
注册
AnnotationAwareAspectJAutoProxyCreator
:@Nullable public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, @Nullable Object source) { //将AnnotationAwareAspectJAutoProxyCreator这个类注册到IOC容器中 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"); //如果IOC容器中已经存在了此类型的Bean,则需要判断优先级 if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) { //获取此类的BeanDefinition信息 BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME); //如果此Bean的ClassName与AnnotationAwareAspectJAutoProxyCreator类的 //ClassName不同的话,判断优先级 if (!cls.getName().equals(apcDefinition.getBeanClassName())) { int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName()); int requiredPriority = findPriorityForClass(cls); //如果已存在Bean优先级小于Creator的优先级 if (currentPriority < requiredPriority) { //将ClassName替换成Creator apcDefinition.setBeanClassName(cls.getName()); } } //不进行注册,因为已经注册了 return null; } //如果到这里,说明IOC容器中没有配置对应Creator //使用Crearir的Class构造一个BeanDefinition RootBeanDefinition beanDefinition = new RootBeanDefinition(cls); beanDefinition.setSource(source); //配置依赖属性order,将其设置为最高优先级 beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE); beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); //将设置好属性的BeanDefinition注册进IOC容器中 registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition); return beanDefinition; }
-
处理proxy-target-class与expose-proxy属性
private static void useClassProxyingIfNecessary(BeanDefinitionRegistry registry, @Nullable Element sourceElement) { if (sourceElement != null) { boolean proxyTargetClass = Boolean.valueOf(sourceElement.getAttribute(PROXY_TARGET_CLASS_ATTRIBUTE)); //处理proxy-target-class属性 if (proxyTargetClass) { AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry); } boolean exposeProxy = Boolean.valueOf(sourceElement.getAttribute(EXPOSE_PROXY_ATTRIBUTE)); //处理expose-proxy属性 if (exposeProxy) { AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry); } } }
其中设置属性的过程:
public static void forceAutoProxyCreatorToUseClassProxying(BeanDefinitionRegistry registry) { if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) { //根据之前注册的BeanName取出Creator BeanDefinition definition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME); //将Creator的BeanDefinition的属性proxyTargetClass设置为true definition.getPropertyValues().add("proxyTargetClass", Boolean.TRUE); } } public static void forceAutoProxyCreatorToExposeProxy(BeanDefinitionRegistry registry) { if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) { //根据之前注册的BeanName取出Creator BeanDefinition definition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME); //将Creator的BeanDefinition的属性exposeProxy设置为true definition.getPropertyValues().add("exposeProxy", Boolean.TRUE); } }
-
proxy-target-class:在Spring的AOP中,默认的如果目标类实现了至少一个接口,将使用JDK动态代理实现AOP,否则使用CGLib动态代理实现AOP,如果希望AOP都使用CGLib实现,你就可以设置proxy-target-class属性为true,但要注意几个问题:
- 无法对final的方法进行动态代理 ,原因很简单,CGLib使用继承实现,final方法无法重写,所以final的方法不能应用AOP。
- 需要配置CGLib的JAR包
-
expose-proxy:在讲解事务的那篇文章中有提到,如果一个类中的事务A方法调用了同一个类中的事务B方法,B方法将没有事务,这个道理在AOP中也是这样的,相同类下的不同方法互相调用,内部方法将无法被应用通知(无法进行AOP),此时你需要将expose-proxy属性设置为true,暴露一个代理类(此属性的原理在下面会有详细讲解),然后在A方法中需要调用B方法的话需要这样写:
public class Service{ public void A(){ ((Service)AopContext.currentProxy()).B(); } public void B(){ //do something... } }
这样,B方法就算再A方法内也可以被AOP。其中
AopContext
是存放线程变量的类,形象的称之为AOP的上下文。
-
4. 实现AOP代理
4.1 创建AOP代理
上面,自定义标签的配置完成了对Creator类的自动注册,我们可以知道,此类实现了BeanPostProcessor
接口,将会在IOC容器初始化每个Bean时都调用此类的postProcessAfterInitialization 方法,此方法即为AOP代理的入口,此方法在抽象父类AbstractAutoProxyCreator
实现:
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) throws BeansException {
if (bean != null) {
//先从缓存中获取Key,由要代理的Bean的Class与benaName组成
Object cacheKey = getCacheKey(bean.getClass(), beanName);
//判断是否是过早暴露的Bean,此概念在讲IOC解决循环依赖中有提到
//如果是过早暴露的Bean,则此时连依赖注入都没有完成,则不对其进行代理
//待其真正初始化之后再尝试代理
if (!this.earlyProxyReferences.contains(cacheKey)) {
//如果符合条件进行AOP代理
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
//如果先前已经处理过的,不进行处理
if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
return bean;
}
//如果此Bean已经被标记为无法代理,不进行处理
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
}
//如果Bean为AOP类的类型,或是需要跳过的类型,不进行处理
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
//标记为不代理
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
// Create proxy if we have advice.
//寻找符合此Bean的增强方法(通知方法)
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
//如果寻找到的增强方法列表不为空,也就是不为DO_NOT_PROXY
if (specificInterceptors != DO_NOT_PROXY) {
//标记为已代理
this.advisedBeans.put(cacheKey, Boolean.TRUE);
//根据找到的增强方法,对此Bean进行动态代理
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
//将代理对象作为Bean返回给IOC容器
return proxy;
}
//如果走到这里,说明代理失败,标记为代理失败
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
文章到了这里,就已经基本完成AOP的实现了,剩下我们需要关注的就是两件事:
- 如何寻找符合Bean的增强器
- 如何对Bean创建动态代理
4.2 寻找所有的增强器
@Override
@Nullable
protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {
//寻找适合的Advisor
List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
if (advisors.isEmpty()) {
return DO_NOT_PROXY;
}
return advisors.toArray();
}
这里有一个Advisor
的概念,其中Advisor封装了切点信息与advise通知方法等等信息。
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
//寻找所有适用的Advisor
List<Advisor> candidateAdvisors = findCandidateAdvisors();
//从所有Advisor中选出适合被当前Bean使用的Advisor
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
extendAdvisors(eligibleAdvisors);
if (!eligibleAdvisors.isEmpty()) {
eligibleAdvisors = sortAdvisors(eligibleAdvisors);
}
return eligibleAdvisors;
}
4.2.1 寻找已存在的Advisor
首先,执行下面的方法寻找合适的Advisor
(此方法在子类Creator
中得到实现):
@Override
protected List<Advisor> findCandidateAdvisors() {