Spring AOP源码分析(八)SpringAOP要注意的地方

简介:
SpringAOP要注意的地方有很多,下面就举一个,之后想到了再列出来: 
(1)SpringAOP对于最外层的函数只拦截public方法,不拦截protected和private方法,另外不会对最外层的public方法内部调用的其他方法也进行拦截,即只停留于代理对象所调用的方法。如下案例: 
B类有两个public方法,foo1()和foo2(),foo1内部调用了foo2,简单如下:
 
?
1
2
3
4
5
6
7
8
public void foo2() { 
         System.out.println( "foo2" ); 
    
     
     public void foo1(){
         System.out.println( "foo1" );
         this .foo2();
     }

假如都对这两个方法进行拦截。当你调用,B对象.foo1()仅仅对foo1整个方法拦截,对于它内部调用的foo2()方法不会进行拦截。 
源码分析: 
判断上述this.foo2()方法是否被拦截的最本质的东西是看this到底是谁?有如下对象B类的对象b,和cglib生成的代理对象bProxy,代理对象bProxy内部拥有b。如果调用b对象的任何方法,肯定不会发生任何拦截,当调用bProxy的方法则都会进入拦截函数。 

当我们调用bProxy对象的foo1()方法时,先执行cglib之前设置的callback对象的intercept拦截函数,如下:
 
?
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
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
             Object oldProxy = null ;
             boolean setProxyContext = false ;
             Class<?> targetClass = null ;
             Object target = null ;
             try {
                 if ( this .advised.exposeProxy) {
                     // Make invocation available if necessary.
                     oldProxy = AopContext.setCurrentProxy(proxy);
                     setProxyContext = true ;
                 }
                 // May be null. Get as late as possible to minimize the time we
                 // "own" the target, in case it comes from a pool...
                 target = getTarget();
                 if (target != null ) {
                     targetClass = target.getClass();
                 }
                 List<Object> chain = this .advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
                 Object retVal;
                 // Check whether we only have one InvokerInterceptor: that is,
                 // no real advice, but just reflective invocation of the target.
                 if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
                     // We can skip creating a MethodInvocation: just invoke the target directly.
                     // Note that the final invoker must be an InvokerInterceptor, so we know
                     // it does nothing but a reflective operation on the target, and no hot
                     // swapping or fancy proxying.
                     retVal = methodProxy.invoke(target, args);
                 }
                 else {
                     // We need to create a method invocation...
                     retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
                 }
                 retVal = processReturnType(proxy, target, method, retVal);
                 return retVal;
             }
             finally {
                 if (target != null ) {
                     releaseTarget(target);
                 }
                 if (setProxyContext) {
                     // Restore old proxy.
                     AopContext.setCurrentProxy(oldProxy);
                 }
             }
         }

这个过程之前的文章已经分析过,这里就是首先取出拦截器链List<Object> chain,当foo1方法不符合我们所配置的pointcut时,拦截器链必然为空,然后就是直接执行目标对象的方法。 
当foo1方法符合所配置的pointcut时,拦截器链不为空,执行相应的通知advice,currentInterceptorIndex 从-1开始,如下:
 
?
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
public Object proceed() throws Throwable {
         //  We start with an index of -1 and increment early.
         if ( this .currentInterceptorIndex == this .interceptorsAndDynamicMethodMatchers.size() - 1 ) {
             return invokeJoinpoint();
         }
 
         Object interceptorOrInterceptionAdvice =
                 this .interceptorsAndDynamicMethodMatchers.get(++ this .currentInterceptorIndex);
         if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
             // Evaluate dynamic method matcher here: static part will already have
             // been evaluated and found to match.
             InterceptorAndDynamicMethodMatcher dm =
                     (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
             if (dm.methodMatcher.matches( this .method, this .targetClass, this .arguments)) {
                 return dm.interceptor.invoke( this );
             }
             else {
                 // Dynamic matching failed.
                 // Skip this interceptor and invoke the next in the chain.
                 return proceed();
             }
         }
         else {
             // It's an interceptor, so we just invoke it: The pointcut will have
             // been evaluated statically before this object was constructed.
             return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke( this );
         }
     }

随着通知不断的传递执行,最终this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1将会满足条件,将会来到执行目标对象的方法invokeJoinpoint():  
?
1
2
3
4
5
6
7
8
protected Object invokeJoinpoint() throws Throwable {
             if ( this .publicMethod) {
                 return this .methodProxy.invoke( this .target, this .arguments);
             }
             else {
                 return super .invokeJoinpoint();
             }
         }

在这里不管要拦截的目标方法是不是public方法,最终所传递的对象都是this.target,他是目标对象而不是代理对象,即执行上述foo1()函数的对象是目标对象而不是代理对象,所以它内部所调用的this.foo2()也是目标对象,因此不会发生拦截,如果是执行的是代理对象.foo2()则必然会进入intercept拦截过程。所以上述调用foo1函数,其内部调用的foo2函数是不会发生拦截的,因为this指的是目标对象,不是代理对象。 

如果你想实现foo1调用时内部的foo2也进行拦截,就必须把this换成代理对象。这时就要用到了,xml配置中的expose-proxy="true",即暴露出代理对象,它使用的是ThreadLocal设计模式,我们可以这样获取代理对象,AopContext.currentProxy()就是代理对象,然后转换成目标对象或者目标接口,执行相应的方法:
 
?
1
2
3
4
5
public void foo1(){
         System.out.println( "run foo1" );
         BServiceImpl proxy=(BServiceImpl) AopContext.currentProxy();
         proxy.foo2();
     }


最后再给出Spring的文档说明: 
Due to the proxy-based nature of Spring’s AOP framework, protected methods are by definition not intercepted, neither for JDK proxies (where this isn’t applicable) nor for CGLIB proxies (where this is technically possible but not recommendable for AOP purposes). As a consequence, any given pointcut will be matched against public methods only

If your interception needs include protected/private methods or even constructors, consider the use of Spring-driven native AspectJ weaving instead of Spring’s proxy-based AOP framework. This constitutes a different mode of AOP usage with different characteristics, so be sure to make yourself familiar with weaving first before making a decision. 

相关文章
|
7天前
|
监控 Java 应用服务中间件
Spring Boot整合Tomcat底层源码分析
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置和起步依赖等特性,大大简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是其与Tomcat的整合。
26 1
|
12天前
|
XML Java 数据安全/隐私保护
Spring Aop该如何使用
本文介绍了AOP(面向切面编程)的基本概念和术语,并通过具体业务场景演示了如何在Spring框架中使用Spring AOP。文章详细解释了切面、连接点、通知、切点等关键术语,并提供了完整的示例代码,帮助读者轻松理解和应用Spring AOP。
Spring Aop该如何使用
|
1月前
|
存储 缓存 Java
Spring高手之路23——AOP触发机制与代理逻辑的执行
本篇文章深入解析了Spring AOP代理的触发机制和执行流程,从源码角度详细讲解了Bean如何被AOP代理,包括代理对象的创建、配置与执行逻辑,帮助读者全面掌握Spring AOP的核心技术。
37 3
Spring高手之路23——AOP触发机制与代理逻辑的执行
|
17天前
|
Java Spring
[Spring]aop的配置与使用
本文介绍了AOP(面向切面编程)的基本概念和核心思想。AOP是Spring框架的核心功能之一,通过动态代理在不修改原代码的情况下注入新功能。文章详细解释了连接点、切入点、通知、切面等关键概念,并列举了前置通知、后置通知、最终通知、异常通知和环绕通知五种通知类型。
28 1
|
22天前
|
前端开发 Java Spring
Spring MVC源码分析之DispatcherServlet#getHandlerAdapter方法
`DispatcherServlet`的 `getHandlerAdapter`方法是Spring MVC处理请求的核心部分之一。它通过遍历预定义的 `HandlerAdapter`列表,找到适用于当前处理器的适配器,并调用适配器执行具体的处理逻辑。理解这个方法有助于深入了解Spring MVC的工作机制和扩展点。
31 1
|
23天前
|
前端开发 Java Spring
Spring MVC源码分析之DispatcherServlet#getHandlerAdapter方法
`DispatcherServlet`的 `getHandlerAdapter`方法是Spring MVC处理请求的核心部分之一。它通过遍历预定义的 `HandlerAdapter`列表,找到适用于当前处理器的适配器,并调用适配器执行具体的处理逻辑。理解这个方法有助于深入了解Spring MVC的工作机制和扩展点。
25 1
|
1月前
|
缓存 JavaScript Java
Spring之FactoryBean的处理底层源码分析
本文介绍了Spring框架中FactoryBean的重要作用及其使用方法。通过一个简单的示例展示了如何通过FactoryBean返回一个User对象,并解释了在调用`getBean()`方法时,传入名称前添加`&`符号会改变返回对象类型的原因。进一步深入源码分析,详细说明了`getBean()`方法内部对FactoryBean的处理逻辑,解释了为何添加`&`符号会导致不同的行为。最后,通过具体代码片段展示了这一过程的关键步骤。
Spring之FactoryBean的处理底层源码分析
|
13天前
|
安全 Java 测试技术
Java开发必读,谈谈对Spring IOC与AOP的理解
Spring的IOC和AOP机制通过依赖注入和横切关注点的分离,大大提高了代码的模块化和可维护性。IOC使得对象的创建和管理变得灵活可控,降低了对象之间的耦合度;AOP则通过动态代理机制实现了横切关注点的集中管理,减少了重复代码。理解和掌握这两个核心概念,是高效使用Spring框架的关键。希望本文对你深入理解Spring的IOC和AOP有所帮助。
28 0
|
2月前
|
设计模式 Java 测试技术
spring复习04,静态代理动态代理,AOP
这篇文章讲解了Java代理模式的相关知识,包括静态代理和动态代理(JDK动态代理和CGLIB),以及AOP(面向切面编程)的概念和在Spring框架中的应用。文章还提供了详细的示例代码,演示了如何使用Spring AOP进行方法增强和代理对象的创建。
spring复习04,静态代理动态代理,AOP
|
1月前
|
Java 编译器 Spring
Spring AOP 和 AspectJ 的区别
Spring AOP和AspectJ AOP都是面向切面编程(AOP)的实现,但它们在实现方式、灵活性、依赖性、性能和使用场景等方面存在显著区别。‌
77 2