Spring AOP 源码分析

面向方面编程(AOP)补充了面向对象编程(OOP),提供了另一种思考程序结构的方式。OOP中模块化的关键单元是类,而AOP中模块化的单元是方面。 方面可以实现关注点的模块化,比如跨越多个类型和对象的事务管理。(在AOP文献中,这种关注点通常被称为横切关注点。)

简述Spring AOP实现

在之前BeanFactory及ApplicationContext的源码分析中都有提到过,Spring AOP的实现是通过实现BeanPostProcessor接口,在前置或者后置方法中,通过返回代理对象,完成AOP的实现。这里实现一个简单的BeanPostProcessor:

public class LogInterceptor implements BeanPostProcessor {
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof UserService) {
            UserService us = (UserService) bean;
            Object proxyInstance = Proxy.newProxyInstance(LogInterceptor.class.getClassLoader(), new Class[]{UserService.class},
                    (proxy, method, args) -> {
                        System.out.println("LogInterceptor UserService: " + method.getName() + " invoked");
                        return method.invoke(us, args);
                    });

            return proxyInstance;
        }
        return bean;
    }
}

LogInterceptor是一个简单的日志拦截器,在bean构建过程中,init之后,就会遍历所有的BeanPostProcessor的postProcessAfterInitialization方法,当检测到当前bean是UserService实例时,就会返回一个代理对象,代理对象在原对象每个方法调用时都会打印一行日志,这样一个简单的日志拦截器就完成了。

运行程序

UserService userService = context.getBean("userService", UserService.class);
System.out.println(userService.getName());
System.out.println(userService.getMoney());
System.out.println(userService.getIntegral());

得到输出

LogInterceptor UserService: getName invoked
Hello World
LogInterceptor UserService: getMoney invoked
999
LogInterceptor UserService: getIntegral invoked
998

可以看到,只要实现BeanPostProcessor,在实现接口方法类完成对想要增强bean拦截,然后完成需要的增强处理,就可以了。当然上例只是简单的接口代理,对于有些没有接口的bean,就需要CGlib完成代理了。

Spring AOP实现

当然如果每次增强操作都要这样手动处理就太麻烦了,还要考虑没有接口的类,所以Spring内部实现了AOP以简化开发。

Spring实现AOP的方式有两种,一种为ProxyFactoryBean,对单个bean做代理;另一种为BeanPostProcessor,根据规则,对一类bean做代理。

ProxyFactoryBean

ProxyFactoryBean是用于创建代理类的通用FactoryBean,通过配置,可以在构建bean的时候返回代理对象,而代理对象通过对 target 对象进行增强,这样在执行bean方法的时候就可以达到想要的增强后的效果。示例

spring-aop.xml 文件

<!--拦截器-->
<bean id="debugInterceptor" class="com.zero.demo.advice.DebugInterceptor"/>
<!--target-->
<bean id="mockTask" class="com.zero.demo.aop.MockTask"/>
<bean id="task" class="org.springframework.aop.framework.ProxyFactoryBean">
    <property name="proxyInterfaces" value="com.zero.demo.aop.ITask"/>
    <property name="target" ref="mockTask"/>
    <property name="interceptorNames">
        <list>
            <value>debugInterceptor</value>
        </list>
    </property>
</bean>

main方法

public static void main(String[] args) {
    DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
    XmlBeanDefinitionReader configReader = new XmlBeanDefinitionReader(beanFactory);
    configReader.loadBeanDefinitions("classpath:spring-aop.xml");
    ITask task = beanFactory.getBean("task", ITask.class);
    task.execute();
}

可以通过调试进入beanFactory#getBean方法,查看bean的构建,在AbstractBeanFactory#doGetBean方法内

if (mbd.isSingleton()) {
  // 构建bean,实际构建的是ProxyFactoryBean bean
   sharedInstance = getSingleton(beanName, () -> {
      try {
         return createBean(beanName, mbd, args);
      }
      catch (BeansException ex) {
         destroySingleton(beanName);
         throw ex;
      }
   });
  // 根据是否是FactoryBean,构建proxy对象
   bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}

对于FactoryBean,最后容器中存放的是FactoryBean#getObject方法获得的对象,在getObjectForBeanInstance方法内会根据 sharedInstance 是否为FactoryBean,然后决定是否调用getObject方法获取代理的对象。

protected Object getObjectForBeanInstance(
      Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {

   if (BeanFactoryUtils.isFactoryDereference(name)) {
      if (beanInstance instanceof NullBean) {
         return beanInstance;
      }
      if (!(beanInstance instanceof FactoryBean)) {
         throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass());
      }
   }
   if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
      return beanInstance;
   }

  // 到这里说明一定是一个FactoryBean
   Object object = null;
   if (mbd == null) {
      object = getCachedObjectForFactoryBean(beanName);
   }
   if (object == null) {
      // 从工厂返回bean实例。
      FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
      // Caches object obtained from FactoryBean if it is a singleton.
      if (mbd == null && containsBeanDefinition(beanName)) {
         mbd = getMergedLocalBeanDefinition(beanName);
      }
      boolean synthetic = (mbd != null && mbd.isSynthetic());
      object = getObjectFromFactoryBean(factory, beanName, !synthetic);
   }
   return object;
}

getObjectFromFactoryBean方法主要是创建bean以及创建之后的处理,创建bean的方法为doGetObjectFromFactoryBean,这是从给定的FactoryBean获取要公开的对象的方法,核心逻辑就是factory.getObject(),也就是ProxyFactoryBeangetObject方法

public Object getObject() throws BeansException {
   // 创建advisor(interceptor)链。根据interceptorNames,从BeanFactory取出相应的advisor(interceptor)
   initializeAdvisorChain();
   if (isSingleton()) {
      return getSingletonInstance();
   }
   else {
      if (this.targetName == null) {
         logger.warn(...);
      }
      return newPrototypeInstance();
   }
}

先创建advisor(interceptor)链。根据interceptorNames,从BeanFactory取出相应的advisor(interceptor),然后getSingletonInstance方法主要是调用createAopProxy方法创建AopProxy,然后调用返回的aopProxy的getProxy方法,获取最后的proxy对象。createAopProxy方法的调用DefaultAopProxyFactory#createAopProxy方法,返回AopProxy对象

//DefaultAopProxyFactory#createAopProxy
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
  // 判断使用ObjenesisCglibAopProxy还是JdkDynamicAopProxy
   if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
      Class<?> targetClass = config.getTargetClass();
      if (targetClass == null) {
         throw new AopConfigException(...);
      }
      if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
         return new JdkDynamicAopProxy(config);
      }
      return new ObjenesisCglibAopProxy(config);
   }
   else {
      return new JdkDynamicAopProxy(config);
   }
}

根据条件判断是返回ObjenesisCglibAopProxy还是JdkDynamicAopProxy

// JdkDynamicAopProxy
public Object getProxy(@Nullable ClassLoader classLoader) {
   ... log ..
   Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
   findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
   return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}

newProxyInstance方法InvocationHandler参数传的是this,也就是说,方法调用会调用自身的invoke方法

// JdkDynamicAopProxy#invoke
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
   MethodInvocation invocation;
   Object oldProxy = null;
   boolean setProxyContext = false;

   TargetSource targetSource = this.advised.targetSource;
   Object target = null;

   try {
      ... equal、hashCode 等等

      Object retVal;

      if (this.advised.exposeProxy) {
         // Make invocation available if necessary.
         oldProxy = AopContext.setCurrentProxy(proxy);
         setProxyContext = true;
      }
     
      target = targetSource.getTarget();
      Class<?> targetClass = (target != null ? target.getClass() : null);

      // 获取此方法的拦截链。之前初始化过
      List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

      if (chain.isEmpty()) {
         Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
         retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
      }
      else {
         // 创建一个方法调用
         invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
         retVal = invocation.proceed();
      }

      // Massage return value if necessary.
      Class<?> returnType = method.getReturnType();
      if (retVal != null && retVal == target &&
            returnType != Object.class && returnType.isInstance(proxy) &&
            !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
         retVal = proxy;
      }
      else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
         throw new AopInvocationException(...);
      }
      return retVal;
   }
   finally {
      if (target != null && !targetSource.isStatic()) {
         // Must have come from TargetSource.
         targetSource.releaseTarget(target);
      }
      if (setProxyContext) {
         // Restore old proxy.
         AopContext.setCurrentProxy(oldProxy);
      }
   }
}

当拦截链不为空,则创建一个ReflectiveMethodInvocation,调用其proceed方法,proceed方法的调用会递归调用,直到所有的MethodInterceptor调用完

public Object proceed() throws Throwable {
   // We start with an index of -1 and increment early.
   if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
      return invokeJoinpoint();
   }

  // currentInterceptorIndex索引递增,每次调用将获取下一个MethodInterceptor
   Object interceptorOrInterceptionAdvice =
         this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
  // 动态方法匹配
   if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
            InterceptorAndDynamicMethodMatcher dm =
            (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
      if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
         return dm.interceptor.invoke(this);
      }
      else {
        // 递归调用
         return proceed();
      }
   }
   else {
     // methodInterceptor传参为this,只要methodInterceptor内调用proceed方法,将递归调用
      return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
   }
}

这样Spring AOP拦截链就调用完毕。

BeanPostProcessor

Spring更为常见的AOP使用是BeanPostProcessor,使用BeanPostProcessor可以通过名称或者正则表达式,更加便捷的对一类bean进行AOP操作。下面是简单xml配置

<bean id="debugInterceptor" class="com.zero.demo.advice.DebugInterceptor"/>
<bean id="logInterceptor" class="com.zero.demo.advice.LogInterceptor"/>
<bean id="task" class="com.zero.demo.aop.MockTask"/>

<bean id="processor" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
    <property name="beanNames" value="task,*Service,*Dao"/>
    <property name="interceptorNames">
        <list>
            <value>debugInterceptor</value>
            <value>logInterceptor</value>
        </list>
    </property>
</bean>

示例是使用BeanNameAutoProxyCreator,根据名称自动匹配,名称可以用通配符。main函数如下

public static void main(String[] args) {
    DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
    XmlBeanDefinitionReader configReader = new XmlBeanDefinitionReader(beanFactory);
    configReader.loadBeanDefinitions("classpath:spring-aop.xml");
    // 因为使用的是beanFactory,所以需要手动注册BeanPostProcessor,ApplicationContex会自动注册
    BeanPostProcessor processor = beanFactory.getBean("processor", BeanPostProcessor.class);
    beanFactory.addBeanPostProcessor(processor);

    ITask task = beanFactory.getBean("mockTask", ITask.class);
    task.execute();
}

这里使用的BeanPostProcessor是BeanNameAutoProxyCreatorBeanNameAutoProxyCreator实现了SmartInstantiationAwareBeanPostProcessor接口

SmartInstantiationAwareBeanPostProcessor的继承关系及方法

image

SmartInstantiationAwareBeanPostProcessor及继承的接口都会影响bean的构建,所以实际上只要看看BeanNameAutoProxyCreatorSmartInstantiationAwareBeanPostProcessor接口的实现即可。

其中,直接可以返回代理bean的方法有

  1. postProcessBeforeInstantiation,在bean构建开始之前拦截
  2. postProcessBeforeInitialization,在bean init之前拦截
  3. postProcessAfterInitialization,在bean init之后拦截

其他的方法主要是bean的类型,构造函数以及属性注入等。

postProcessBeforeInstantiation方法

public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
   Object cacheKey = getCacheKey(beanClass, beanName);

   if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
      if (this.advisedBeans.containsKey(cacheKey)) {
         return null;
      }
      if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
         this.advisedBeans.put(cacheKey, Boolean.FALSE);
         return null;
      }
   }

  // 如果我们有一个定制的TargetSource,在这里创建代理。
   TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
   if (targetSource != null) {
      if (StringUtils.hasLength(beanName)) {
         this.targetSourcedBeans.add(beanName);
      }
      Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
     // 创建proxy
      Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
      this.proxyTypes.put(cacheKey, proxy.getClass());
      return proxy;
   }

   return null;
}

也就是xml文件中配置了customTargetSourceCreators属性,则会在bean的构建之前拦截。createProxy是核心创建代理的方法,后面详细分析。

postProcessBeforeInitialization方法直接返回传入的bean,无其他逻辑

postProcessAfterInitialization方法

public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) throws BeansException {
   if (bean != null) {
      Object cacheKey = getCacheKey(bean.getClass(), beanName);
      if (!this.earlyProxyReferences.contains(cacheKey)) {
         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;
  }
  if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
    return bean;
  }
  if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
    this.advisedBeans.put(cacheKey, Boolean.FALSE);
    return bean;
  }

  // 如果有advice,创建代理。
  Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
  if (specificInterceptors != DO_NOT_PROXY) {
    this.advisedBeans.put(cacheKey, Boolean.TRUE);
    Object proxy = createProxy(
      bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
    this.proxyTypes.put(cacheKey, proxy.getClass());
    return proxy;
  }

  this.advisedBeans.put(cacheKey, Boolean.FALSE);
  return bean;
}

postProcessAfterInitialization方法如果有advice,则创建代理,创建代理的逻辑和postProcessBeforeInstantiation方法类似,下面重点看一下createProxy方法

protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
      @Nullable Object[] specificInterceptors, TargetSource targetSource) {

   if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
      AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
   }

   ProxyFactory proxyFactory = new ProxyFactory();
   proxyFactory.copyFrom(this);

   if (!proxyFactory.isProxyTargetClass()) {
      if (shouldProxyTargetClass(beanClass, beanName)) {
         proxyFactory.setProxyTargetClass(true);
      }
      else {
         evaluateProxyInterfaces(beanClass, proxyFactory);
      }
   }

   Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
   proxyFactory.addAdvisors(advisors);
   proxyFactory.setTargetSource(targetSource);
   customizeProxyFactory(proxyFactory);

   proxyFactory.setFrozen(this.freezeProxy);
   if (advisorsPreFiltered()) {
      proxyFactory.setPreFiltered(true);
   }

   return proxyFactory.getProxy(getProxyClassLoader());
}

可以看到核心逻辑有3处

  1. 创建ProxyFactory:ProxyFactory proxyFactory = new ProxyFactory();
  2. 获得advisors:Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
  3. 构建代理:return proxyFactory.getProxy(getProxyClassLoader());

ProxyFactorygetProxy方法其实和前面介绍的ProxyFactoryBean类似,ProxyFactoryProxyFactoryBean共同继承ProxyCreatorSupport,实现代理的逻辑都来自于超类ProxyCreatorSupport

Spring 事务

稍带提一句,Spring 事物的拦截处理主要由TransactionInterceptor实现。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容