死磕Spring AOP系列3:剖析Bean处理器之DefaultAdvisorAutoProxyCreator

简介:

这是<死磕Spring AOP系列>的第三篇。经过前面的讲解,已经掌握了以下知识点

  1. Spring AOP的底层支持,是基于ProxyFactory+ProxyConfig+Advisor生成的

  2. Spring容器的代理对象生成:在Bean生命周期过长中调用BeanPostProcessor,将对象进行包装,生成代理对象。

  3. Advisor的指定:不管编程式指定,还是自动匹配指定。虽然形式不同,但本质是相同的。

如果,还对上面的问题存疑,请回看以上内容。

本篇主要对剖析另外一个BeanPostProcessor,他就是DefaultAdvisorAutoProxyCreator,他是BeanNameAutoProxyCreator的升级版。形象点说:如果ProxyFactory是弓箭的话,代表原始,BeanNameAutoProxyCreator就是步枪,需要自己装配,DefaultAdvisorAutoProxyCreator就是自动步枪了,Spring可以完成自动匹配的部分工作了。

本章主要内容

  1. 使用DefaultAdvisorAutoProxyCreator,完成一个“AOP"例子,讲解下如何配置

  2. 对比BeanNameAutoProxyCreator,对DefaultAdvisorAutoProxyCreator进行剖析

  3. 分析DefaultAdvisorAutoProxyCreator的Advisor的匹配过程

  4. 体会自动化


1.使用DefaultAdvisorAutoProxyCreator,完成一个“AOP"例子

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
/**
  *模拟业务接口
  */
public  interface  UserService {
     public  void  updateUser();
}
  
/**
  *模拟具体业务
  */
public  class  UserServiceImpl  implements  UserService{
  
     @Override
     public  void  updateUser() {
         System.out.println( "$$$$$$执行业务逻辑$$$$$" );
     }
}
  
/**
  * 模拟切面1
  */
public  class  SecurityInterceptor  implements  MethodInterceptor {
     @Override
     public  Object invoke(MethodInvocation methodInvocation)  throws  Throwable {
         System.out.println( "==========执行安全校验====================" );
         return  methodInvocation.proceed();
     }
}
  
/**
  * 模拟切面2
  */
public  class  LoggerBeforeAdvice  implements  MethodBeforeAdvice {
     @Override
     public  void  before(Method method, Object[] args, Object target)  throws  Throwable {
         System.out.println( "=======保存更新日志=========" );
     }
}

xml(proxy_test.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
<? xml  version = "1.0"  encoding = "UTF-8" ?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd">
 
< beans >
    < bean  class = "org.springaop.chap01.UserServiceImpl"  id = "userService" ></ bean >
    < bean  class = "org.springaop.chap01.LoggerBeforeAdvice"  id = "loggerBeforeAdvice" ></ bean >
    < bean  class = "org.springaop.chap01.SecurityInterceptor"  id = "securityInterceptor" ></ bean >
 
 
    <!-- 自动代理所有的advisor -->
    < bean  class = "org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" >
       < property  name = "usePrefix"  value = "true" ></ property >
       < property  name = "advisorBeanNamePrefix"  value = "advisor" ></ property >
    </ bean >
 
    < bean  id = "advisor1"  class = "org.springframework.aop.support.RegexpMethodPointcutAdvisor" >
       < property  name = "pattern" >
          < value >.*update.*</ value >   <!-- 业务实现方法名匹配 -->
       </ property >
       < property  name = "advice" >
          < ref  bean = "loggerBeforeAdvice" />
       </ property >
    </ bean >
 
    < bean  id = "advisor2"  class = "org.springframework.aop.support.RegexpMethodPointcutAdvisor" >
       < property  name = "pattern" >
          < value >.*update.*</ value >   <!-- 业务实现方法名匹配 -->
       </ property >
       < property  name = "advice" >
          < ref  bean = "securityInterceptor" />
       </ property >
    </ bean >
</ beans >

Main

1
2
3
4
5
6
7
8
9
public  class  ContextMain {
     public  static  void  main(String[] args) {
         ApplicationContext ctx =  new  ClassPathXmlApplicationContext( "org/springaop/chap03/proxy_test.xml" );
         UserService userService =(UserService) ctx.getBean( "userService" );
//        UserService userService2 =(UserService) ctx.getBean("userService2");
         userService.updateUser();
//        userService2.updateUser();
     }
}

执行结果

=======保存更新日志=========
==========执行安全校验====================
$$$$$$执行业务逻辑$$$$$


说明:usePrefix和advisorBeanNamePrefix配合使用,如果usePrefix设置为false,advisorBeanNamePrefix设置没有作用。通过上面的设置配置,advisor2和advisor都有效果。

2.剖析DefaultAdvisorAutoProxyCreator类

2.1 结构层次图

wKiom1dSRECQVFl4AACratmjUuY978.png


可知,DefaultAdvisorAutoProxyCreator,BeanNameAutoProxyCreator是AbstractAutoProxyCreator的子类。通过前面讲解,知道AbstractAutoProxyCreator有个抽象方法。

方法声明如下

1
2
3
4
//是否被代理?返回interceptors
//Return whether the given bean is to be proxied, what additional advices (e.g. AOP Alliance interceptors) and advisors to apply.
protected  abstract  Object[] getAdvicesAndAdvisorsForBean(
       Class<?> beanClass, String beanName, TargetSource customTargetSource)  throws  BeansException;

DefaultAdvisorAutoProxyCreator 和BeanNameAutoProxyCreator分别实现各自逻辑。BeanNameAutoProxyCreator的实现不再赘述,在死磕Spring AOP系列2已经讲过,代码很简单。

2.2 剖析getAdvicesAndAdvisorsForBean方法

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
public  abstract  class  AbstractAdvisorAutoProxyCreator  extends  AbstractAutoProxyCreator {
...
protected  Object[] getAdvicesAndAdvisorsForBean(Class beanClass, String beanName, TargetSource targetSource) {
    List advisors = findEligibleAdvisors(beanClass, beanName);
    if  (advisors.isEmpty()) {
       return  DO_NOT_PROXY;
    }
    return  advisors.toArray();
}
 
protected  List<Advisor> findEligibleAdvisors(Class beanClass, String beanName) {
    List<Advisor> candidateAdvisors = findCandidateAdvisors(); //获取所有的Advisors
    //获取可以应用于beanName的Advisor(逻辑尽在此)
    List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
    extendAdvisors(eligibleAdvisors);
    if  (!eligibleAdvisors.isEmpty()) {
       eligibleAdvisors = sortAdvisors(eligibleAdvisors);
    }
    return  eligibleAdvisors;
}
//Find all candidate Advisors to use in auto-proxying.
protected  List<Advisor> findCandidateAdvisors() {
    //委托给BeanFactoryAdvisorRetrievalHelper处理
    return  this .advisorRetrievalHelper.findAdvisorBeans();
}
 
}
public  class  BeanFactoryAdvisorRetrievalHelper {
     public  List<Advisor> findAdvisorBeans() {
 
...
           if  (advisorNames ==  null ) {
               //获取容器中声明的Advisor
              // Do not initialize FactoryBeans here: We need to leave all regular beans
              // uninitialized to let the auto-proxy creator apply to them!
              advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
                    this .beanFactory, Advisor. class true false );
...
        }
        if  (advisorNames.length ==  0 ) {
           return  new  LinkedList<Advisor>();
        }
     
        List<Advisor> advisors =  new  LinkedList<Advisor>();
        for  (String name : advisorNames) {
            //判断返回
           if  (isEligibleBean(name) && ! this .beanFactory.isCurrentlyInCreation(name)) {
              try  {
                 advisors.add( this .beanFactory.getBean(name, Advisor. class ));
              }
              catch  (BeanCreationException ex) {
                 ...
                 throw  ex;
              }
           }
        }
        return  advisors;
     }
}

通过层层抽丝剥茧,定位到了AopUtils.findAdvisorsThatCanApply,负责具体的Advisor匹配工作。

3.剖析AopUtils.findAdvisorsThatCanApply方法,(匹配逻辑)

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
75
76
77
78
protected  List<Advisor> findAdvisorsThatCanApply(
       List<Advisor> candidateAdvisors, Class beanClass, String beanName) {
 
    ProxyCreationContext.setCurrentProxiedBeanName(beanName);
    try  {
       return  AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
    }
    finally  {
       ProxyCreationContext.setCurrentProxiedBeanName( null );
    }
}
 
public  static  List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
    if  (candidateAdvisors.isEmpty()) {
       return  candidateAdvisors;
    }
    List<Advisor> eligibleAdvisors =  new  LinkedList<Advisor>();
    for  (Advisor candidate : candidateAdvisors) { //遍历
       if  (candidate  instanceof  IntroductionAdvisor && canApply(candidate, clazz)) {
       //是否IntroductionAdvisor
          eligibleAdvisors.add(candidate);
       }
    }
    boolean  hasIntroductions = !eligibleAdvisors.isEmpty();
    for  (Advisor candidate : candidateAdvisors) {
       if  (candidate  instanceof  IntroductionAdvisor) {
          // already processed
          continue ;
       }
       if  (canApply(candidate, clazz, hasIntroductions)) { //执行
          eligibleAdvisors.add(candidate);
       }
    }
    return  eligibleAdvisors;
}
 
public  static  boolean  canApply(Advisor advisor, Class<?> targetClass,  boolean  hasIntroductions) {
    if  (advisor  instanceof  IntroductionAdvisor) {
       return  ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);
    }
    else  if  (advisor  instanceof  PointcutAdvisor) {
       PointcutAdvisor pca = (PointcutAdvisor) advisor;
       return  canApply(pca.getPointcut(), targetClass, hasIntroductions);
    }
    else  {
       // It doesn't have a pointcut so we assume it applies.
       return  true ;
    }
}
 
 
public  static  boolean  canApply(Pointcut pc, Class<?> targetClass,  boolean  hasIntroductions) {
    Assert.notNull(pc,  "Pointcut must not be null" );
    if  (!pc.getClassFilter().matches(targetClass)) {
       return  false ;
    }
 
    MethodMatcher methodMatcher = pc.getMethodMatcher();
    IntroductionAwareMethodMatcher introductionAwareMethodMatcher =  null ;
    if  (methodMatcher  instanceof  IntroductionAwareMethodMatcher) {
       introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
    }
 
    Set<Class> classes =  new  HashSet<Class>(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
    classes.add(targetClass);
    for  (Class<?> clazz : classes) {
       Method[] methods = clazz.getMethods();
       for  (Method method : methods) {
          if  ((introductionAwareMethodMatcher !=  null  &&
                introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions)) ||
                methodMatcher.matches(method, targetClass)) { //methodMatcher具体完成匹配
             return  true ;
          }
       }
    }
 
    return  false ;
}


wKioL1dSUh-BaDsIAABPQ2m8W1U171.png

通过图得知Advisor,持有Advice的聚合信息和PointCut对象,而PointCut接口持有MethodMatcher。

最后MethodMatcher完成方法匹配工作。以当前例子为例,AbstractRegexpMethodPointcut同时也是MethodMatcher.

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
public  abstract  class  AbstractRegexpMethodPointcut  extends  StaticMethodMatcherPointcut
       implements  Serializable {
public  boolean  matches(Method method, Class targetClass) {
    return  ((targetClass !=  null  && matchesPattern(targetClass.getName() +  "."  + method.getName())) ||
          matchesPattern(method.getDeclaringClass().getName() +  "."  + method.getName()));
}
}
 
protected  boolean  matchesPattern(String signatureString) {
    for  ( int  i =  0 ; i <  this .patterns.length; i++) {
       boolean  matched = matches(signatureString, i);
       if  (matched) {
          for  ( int  j =  0 ; j <  this .excludedPatterns.length; j++) {
             boolean  excluded = matchesExclusion(signatureString, j);
             if  (excluded) {
                return  false ;
             }
          }
          return  true ;
       }
    }
    return  false ;
}
 
public  class  JdkRegexpMethodPointcut  extends  AbstractRegexpMethodPointcut {
/**
  * Returns {@code true} if the {@link Pattern} at index {@code patternIndex}
  * matches the supplied candidate {@code String}.
  */
@Override
protected  boolean  matches(String pattern,  int  patternIndex) {
    Matcher matcher =  this .compiledPatterns[patternIndex].matcher(pattern);
    return  matcher.matches();
}
 
/**
  * Returns {@code true} if the exclusion {@link Pattern} at index {@code patternIndex}
  * matches the supplied candidate {@code String}.
  */
@Override
protected  boolean  matchesExclusion(String candidate,  int  patternIndex) {
    Matcher matcher =  this .compiledExclusionPatterns[patternIndex].matcher(candidate);
    return  matcher.matches();
}
 
 
/**
  * Compiles the supplied {@code String[]} into an array of
  * {@link Pattern} objects and returns that array.
  */
private  Pattern[] compilePatterns(String[] source)  throws  PatternSyntaxException {
    Pattern[] destination =  new  Pattern[source.length];
    for  ( int  i =  0 ; i < source.length; i++) {
       destination[i] = Pattern.compile(source[i]);
    }
    return  destination;
}
}

4.自动化

比起BeanNameAutoProxyCreator,DefaultAdvisorAutoProxyCreator有一个非常优越的地方。那就是advisor已有了自动匹配方法的能力。具体实现逻辑,通过上面的分析就是正则表达式的使用。其实很简单,确定一个方法是否被应用于advice,其实就是讲方法的签名字符串与定义PointCut的pattern进行匹配。今天提到的JdkRegexpMethodPointcut,只是实现之一。另外一个更强大的PointCut是什么?下次再说。



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

相关文章
|
1月前
|
XML 安全 Java
|
11天前
|
存储 Java Spring
【Spring】获取Bean对象需要哪些注解
@Conntroller,@Service,@Repository,@Component,@Configuration,关于Bean对象的五个常用注解
|
11天前
|
存储 Java 应用服务中间件
【Spring】IoC和DI,控制反转,Bean对象的获取方式
IoC,DI,控制反转容器,Bean的基本常识,类注解@Controller,获取Bean对象的常用三种方式
|
17天前
|
XML Java 数据格式
Spring容器Bean之XML配置方式
通过对以上内容的掌握,开发人员可以灵活地使用Spring的XML配置方式来管理应用程序的Bean,提高代码的模块化和可维护性。
53 6
|
18天前
|
XML Java 数据格式
🌱 深入Spring的心脏:Bean配置的艺术与实践 🌟
本文深入探讨了Spring框架中Bean配置的奥秘,从基本概念到XML配置文件的使用,再到静态工厂方式实例化Bean的详细步骤,通过实际代码示例帮助读者更好地理解和应用Spring的Bean配置。希望对你的Spring开发之旅有所助益。
80 3
|
1月前
|
安全 Java 开发者
Spring容器中的bean是线程安全的吗?
Spring容器中的bean默认为单例模式,多线程环境下若操作共享成员变量,易引发线程安全问题。Spring未对单例bean做线程安全处理,需开发者自行解决。通常,Spring bean(如Controller、Service、Dao)无状态变化,故多为线程安全。若涉及线程安全问题,可通过编码或设置bean作用域为prototype解决。
34 1
|
2月前
|
监控 安全 Java
什么是AOP?如何与Spring Boot一起使用?
什么是AOP?如何与Spring Boot一起使用?
72 5
|
2月前
|
XML 监控 安全
深入调查研究Spring AOP
【11月更文挑战第15天】
49 5
|
4月前
Micronaut AOP与代理机制:实现应用功能增强,无需侵入式编程的秘诀
AOP(面向切面编程)能够帮助我们在不修改现有代码的前提下,为应用程序添加新的功能或行为。Micronaut框架中的AOP模块通过动态代理机制实现了这一目标。AOP将横切关注点(如日志记录、事务管理等)从业务逻辑中分离出来,提高模块化程度。在Micronaut中,带有特定注解的类会在启动时生成代理对象,在运行时拦截方法调用并执行额外逻辑。例如,可以通过创建切面类并在目标类上添加注解来记录方法调用信息,从而在不侵入原有代码的情况下增强应用功能,提高代码的可维护性和可扩展性。
84 1
|
2月前
|
安全 Java 编译器
什么是AOP面向切面编程?怎么简单理解?
本文介绍了面向切面编程(AOP)的基本概念和原理,解释了如何通过分离横切关注点(如日志、事务管理等)来增强代码的模块化和可维护性。AOP的核心概念包括切面、连接点、切入点、通知和织入。文章还提供了一个使用Spring AOP的简单示例,展示了如何定义和应用切面。
195 1
什么是AOP面向切面编程?怎么简单理解?