死磕Spring AOP系列4:剖析AOP schema方式原理

通过前3篇,大家应该可以清楚的知道:AOP代理原理有3元素

  1. BeanPostProcessor,作为代理对象初始入口

  2. Advisor&Pointcut&MethodMatcher完成匹配

  3. Advice的声明及链式结构维护

三个问题在前面的讲解中已经讲解了。其中:

Advice的链式结构,是通过ProxyFactory统一维护的管理的,在《编程式实现AOP》中已说明;

匹配在前面系列2及系列3中也做了说明;

代理对象初始入口也在系列第3讲中进行了讲解。

本文,主要从这3各方面入手,对常用的aop schema做一个全方位的剖析。主要内容

  1. 使用aop schema方式做一个简单的演示demo

  2. 确认并剖析相关的BeanPostProcessor

  3. 确认并剖析相关的Advisor,PointCut


1.使用aop schema方式做一个简单的演示demo

例子代码来源于http://www.tutorialspoint.com/spring/schema_based_aop_appoach.htm

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
//1 切面类
package com.tutorialspoint;
 
public class Logging {
 
   /** 
    * This is the method which I would like to execute
    * before a selected method execution.
    */
   public void beforeAdvice(){
      System.out.println("Going to setup student profile.");
   }
 
   /** 
    * This is the method which I would like to execute
    * after a selected method execution.
    */
   public void afterAdvice(){
      System.out.println("Student profile has been setup.");
   }
 
   /** 
    * This is the method which I would like to execute
    * when any method returns.
    */
   public void afterReturningAdvice(Object retVal){
      System.out.println("Returning:" + retVal.toString() );
   }
 
   /**
    * This is the method which I would like to execute
    * if there is an exception raised.
    */
   public void AfterThrowingAdvice(IllegalArgumentException ex){
      System.out.println("There has been an exception: " + ex.toString());   
   }
    
}
//2 业务模拟类
package com.tutorialspoint;
 
public class Student {
   private Integer age;
   private String name;
 
   public void setAge(Integer age) {
      this.age = age;
   }
   public Integer getAge() {
     System.out.println("Age : " + age );
      return age;
   }
 
   public void setName(String name) {
      this.name = name;
   }
   public String getName() {
      System.out.println("Name : " + name );
      return name;
   }
    
   public void printThrowException(){
      System.out.println("Exception raised");
       throw new IllegalArgumentException();
   }
}

XML(Beans.xml)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<?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-3.0.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop-3.0.xsd ">
 
    <aop:config>
        <aop:aspect id="log" ref="logging">
            <aop:pointcut id="selectAll"
                          expression="execution(* com.tutorialspoint.*.*(..))"/>
            <aop:before pointcut-ref="selectAll" method="beforeAdvice"/>
            <aop:after pointcut-ref="selectAll" method="afterAdvice"/>
            <aop:after-returning pointcut-ref="selectAll"
                                 returning="retVal"
                                 method="afterReturningAdvice"/>
            <aop:after-throwing pointcut-ref="selectAll"
                                throwing="ex"
                                method="AfterThrowingAdvice"/>
        </aop:aspect>
    </aop:config>
 
    <!-- Definition for student bean -->
    <bean id="student" class="com.tutorialspoint.Student">
        <property name="name"  value="Zara" />
        <property name="age"  value="11"/>
    </bean>
 
    <!-- Definition for logging aspect -->
    <bean id="logging" class="com.tutorialspoint.Logging"/>
 
</beans>

Main

1
2
3
4
5
6
7
8
9
10
11
12
13
public class MainApp {
   public static void main(String[] args) {
      ApplicationContext context = 
             new ClassPathXmlApplicationContext("com/tutorialspoint/Beans.xml");
 
            Student student = (Student) context.getBean("student");
 
      student.getName();
      student.getAge();
       
//      student.printThrowException();
   }
}

执行结果

Going to setup student profile.
Name : Zara
Student profile has been setup.
Returning:Zara
Going to setup student profile.
Age : 11
Student profile has been setup.
Returning:11


通过查看日志,可以捕获到以下信息,为咱们剖析源码找到了分析点。

...

DEBUG: org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator - Creating implicit proxy for bean 'student' with 0 common interceptors and 5 specific interceptors
DEBUG: org.springframework.aop.framework.CglibAopProxy - Creating CGLIB proxy: target source is SingletonTargetSource for target object [[email protected]]
DEBUG: org.springframework.aop.framework.CglibAopProxy - Unable to apply any optimisations to advised method: public java.lang.String com.tutorialspoint.Student.getName()

2.找寻aop schema对应的BeanPostProcessor

aop的schema 对应的spring beanPostProcessor,是Spring自动指派的,对我们是透明的,这一点和以前讲到的BeanNameAutoProxyCreator和DefaultAdvisorAutoProxyCreator不同,咱们没有声明。

首先,aop是客制化的标签(不是bean标签,都是客制化标签),要找到这个位置。熟悉spring自定义标签的朋友应该都知道,需要找到aop标签*.handler文件.位置在$base/spring-aop\src\main\resources\META-INF\spring.handlers.

内容如下

http\://www.springframework.org/schema/aop=org.springframework.aop.config.AopNamespaceHandler
2.1 分析AopNamespaceHandler

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
public class AopNamespaceHandler extends NamespaceHandlerSupport {
 
   /**
     完成一些解析器
    * Register the {@link BeanDefinitionParser BeanDefinitionParsers} for the
    
    */
   public void init() {
      // In 2.0 XSD as well as in 2.1 XSD.
      //负责解析<aop:config>
      registerBeanDefinitionParser("config"new ConfigBeanDefinitionParser());
      //负责即系<aspectj-autoproxy>
      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());
   }
}
//接下来关注的重点是ConfigBeanDefinitionParser
 
class ConfigBeanDefinitionParser implements BeanDefinitionParser {
public BeanDefinition parse(Element element, ParserContext parserContext) {
   CompositeComponentDefinition compositeDef =
         new CompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element));
   parserContext.pushContainingComponent(compositeDef);
    //配置the auto proxy creator
   configureAutoProxyCreator(parserContext, element);
 
   //接下来解析xml节点元素
   List<Element> childElts = DomUtils.getChildElements(element);
   for (Element elt: childElts) {
      String localName = parserContext.getDelegate().getLocalName(elt);
      if (POINTCUT.equals(localName)) {
         parsePointcut(elt, parserContext);
      }
      else if (ADVISOR.equals(localName)) {
         parseAdvisor(elt, parserContext);
      }
      else if (ASPECT.equals(localName)) {
         parseAspect(elt, parserContext);
      }
   }
 
   parserContext.popAndRegisterContainingComponent();
   return null;
}
 
private void configureAutoProxyCreator(ParserContext parserContext, Element element) {
   AopNamespaceUtils.registerAspectJAutoProxyCreatorIfNecessary(parserContext, element);
}
 
}
 
--------------------
public abstract class AopNamespaceUtils {
    public static void registerAspectJAutoProxyCreatorIfNecessary(
          ParserContext parserContext, Element sourceElement) {
     
       BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAutoProxyCreatorIfNecessary(
             parserContext.getRegistry(), parserContext.extractSource(sourceElement));
       useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
       registerComponentIfNecessary(beanDefinition, parserContext);
    }
     
}
--------------------
public abstract class AopConfigUtils {
public static final String AUTO_PROXY_CREATOR_BEAN_NAME =
      "org.springframework.aop.config.internalAutoProxyCreator";
//AspectJAwareAdvisorAutoProxyCreator注册到spring容器
public static BeanDefinition registerAspectJAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, Object source) {
   return registerOrEscalateApcAsRequired(AspectJAwareAdvisorAutoProxyCreator.class, registry, source);
}
}

到这儿,任务也算完成了。AspectJAwareAdvisorAutoProxyCreator就是我们苦苦寻找的BeanPostProcessor.

死磕Spring AOP系列4:剖析AOP schema方式原理

真巧它和DefaultAdvisorAutoProxyCreator是兄弟。接下来,就是剖析AspectJAwareAdvisorAutoProxyCreator。

死磕Spring AOP系列4:剖析AOP schema方式原理

死磕Spring AOP系列4:剖析AOP schema方式原理

结合《死磕Spring AOP系列3》,可以将getAdvicesAndAdvisorsForBean作为分析的起点,以前说过该方法是AbstractAutoProxyCreator的抽象方法,由子类实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public abstract class AbstractAdvisorAutoProxyCreator extends AbstractAutoProxyCreator {
 
...
@Override
protected Object[] getAdvicesAndAdvisorsForBean(Class beanClass, String beanName, TargetSource targetSource) {
   List advisors = findEligibleAdvisors(beanClass, beanName);
   if (advisors.isEmpty()) {
      return DO_NOT_PROXY;
   }
   return advisors.toArray();
}
//查找适合的Advisors
protected List<Advisor> findEligibleAdvisors(Class beanClass, String beanName) {
   List<Advisor> candidateAdvisors = findCandidateAdvisors();
   List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
   extendAdvisors(eligibleAdvisors);//交给子类实现扩展
   if (!eligibleAdvisors.isEmpty()) {
      eligibleAdvisors = sortAdvisors(eligibleAdvisors);
   }
   return eligibleAdvisors;
}
 
}

2.3剖析AspectJAwareAdvisorAutoProxyCreator.extendAdvisors方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
public class AspectJAwareAdvisorAutoProxyCreator extends AbstractAdvisorAutoProxyCreator {
    //添加 ExposeInvocationInterceptor to the beginning of the advice chain
    protected void extendAdvisors(List<Advisor> candidateAdvisors) {
       AspectJProxyUtils.makeAdvisorChainAspectJCapableIfNecessary(candidateAdvisors);
    }
}
 
public abstract class AspectJProxyUtils {
 
   public static boolean makeAdvisorChainAspectJCapableIfNecessary(List<Advisor> advisors) {
      // Don't add advisors to an empty list; may indicate that proxying is just not required
      if (!advisors.isEmpty()) {
         boolean foundAspectJAdvice = false;
         for (Advisor advisor : advisors) {
            // Be careful not to get the Advice without a guard, as
            // this might eagerly instantiate a non-singleton AspectJ aspect
            if (isAspectJAdvice(advisor)) {
               foundAspectJAdvice = true;
            }
         }
         if (foundAspectJAdvice && !advisors.contains(ExposeInvocationInterceptor.ADVISOR)) {
            advisors.add(0, ExposeInvocationInterceptor.ADVISOR);//添加到链
            return true;
         }
      }
      return false;
   }
 
   /**
    *判断是不是AspectJAdvice
    * Determine whether the given Advisor contains an AspectJ advice.
    * @param advisor the Advisor to check
    */
   private static boolean isAspectJAdvice(Advisor advisor) {
      return (advisor instanceof InstantiationModelAwarePointcutAdvisor ||
            advisor.getAdvice() instanceof AbstractAspectJAdvice ||
            (advisor instanceof PointcutAdvisor &&
                   ((PointcutAdvisor) advisor).getPointcut() instanceof AspectJExpressionPointcut));
   }
 
}
 
//就做一件事,对外曝光MethodInvocation,放到ThreadLocal中
public class ExposeInvocationInterceptor implements MethodInterceptor, Ordered, Serializable {
 
   /** Singleton instance of this class */
   public static final ExposeInvocationInterceptor INSTANCE = new ExposeInvocationInterceptor();
 
   private static final ThreadLocal<MethodInvocation> invocation =
         new NamedThreadLocal<MethodInvocation>("Current AOP method invocation");private ExposeInvocationInterceptor() {
}   public Object invoke(MethodInvocation mi) throws Throwable {
      MethodInvocation oldInvocation = invocation.get();
      invocation.set(mi);
      try {
         return mi.proceed();
      }
      finally {
         invocation.set(oldInvocation);
      }
   }
 
   public int getOrder() {
      return Ordered.HIGHEST_PRECEDENCE + 1;
   }
}

3.剖析Spring pointcut匹配逻辑

如图

死磕Spring AOP系列4:剖析AOP schema方式原理

重点关注AspectJExpressionPointcut.该类同时实现了MethodMatcher和ClassFilter两个接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
public class AspectJExpressionPointcut extends AbstractExpressionPointcut
      implements ClassFilter, IntroductionAwareMethodMatcher, BeanFactoryAware {
       
      //ClassFilter实现
   public boolean matches(Class targetClass) {
           checkReadyToMatch();
       try {
          return this.pointcutExpression.couldMatchJoinPointsInType(targetClass);
       catch (ReflectionWorldException e) {
          logger.debug("PointcutExpression matching rejected target class", e);  
       }
 }
//MethodMatcher实现
public boolean matches(Method method, Class targetClass, boolean beanHasIntroductions) {
   checkReadyToMatch();
   Method targetMethod = AopUtils.getMostSpecificMethod(method, targetClass);
   ShadowMatch shadowMatch = getShadowMatch(targetMethod, method);
   // Special handling for this, target, @this, @target, @annotation
   // in Spring - we can optimize since we know we have exactly this class,
   // and there will never be matching subclass at runtime.
   if (shadowMatch.alwaysMatches()) {
      return true;
   }
   else if (shadowMatch.neverMatches()) {
      return false;
   }
   else {
      // the maybe case
      return (beanHasIntroductions || matchesIgnoringSubtypes(shadowMatch) || matchesTarget(shadowMatch, targetClass));
   }
}
 
}
 
public class PointcutExpressionImpl implements PointcutExpression {
//AspectJExpressionPointcut 实现ClassFilter接口时候调用。
public boolean couldMatchJoinPointsInType(Class aClass) {
   ResolvedType matchType = world.resolve(aClass.getName());
   ReflectionFastMatchInfo info = new ReflectionFastMatchInfo(matchType, nullthis.matchContext, world);
   boolean couldMatch = pointcut.fastMatch(info).maybeTrue();
   if (MATCH_INFO) {
      System.out.println("MATCHINFO: fast match for '" this.expression + "' against '" + aClass.getName() + "': "
            + couldMatch);
   }
   return couldMatch;
}}

4序列图

死磕Spring AOP系列4:剖析AOP schema方式原理


5.总结

截止到现在已经讲解了BeanNameAutoProxyCreator,DefaultAdvisorAutoProxyCreator及今天的AspectJAwareAdvisorAutoProxyCreator。虽然复杂度有所区别,但底层设计本质是一样的。Spring 在AbstractAutoProxyCreator进行了抽象处理,实现了扩展性。



本文转自 randy_shandong 51CTO博客,原文链接:http://blog.51cto.com/dba10g/1786117,如需转载请自行联系原作者