`

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

阅读更多
SpringAOP要注意的地方有很多,下面就举一个,之后想到了再列出来:
(1)SpringAOP对于最外层的函数只拦截public方法,不拦截protected和private方法,另外不会对最外层的public方法内部调用的其他方法也进行拦截,即只停留于代理对象所调用的方法。如下案例:
B类有两个public方法,foo1()和foo2(),foo1内部调用了foo2,简单如下:

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拦截函数,如下:

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开始,如下:

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():
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()就是代理对象,然后转换成目标对象或者目标接口,执行相应的方法:

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.

若想转载请注明出处: http://lgbolgger.iteye.com/blog/2123895
作者:iteye的乒乓狂魔
分享到:
评论
3 楼 shuhucy 2017-11-27  
必须赞啊,源码理解的很深,解决一个困扰两天的问题
2 楼 乒乓狂魔 2014-10-01  
Aspectj功能比较齐全,需要额外的编译器,直接修改字节码吧,有空的时候也需要好好学下。
1 楼 y60024651 2014-10-01  
一直用的AspectJ

相关推荐

    AOP流程源码分析-SpringAOP中定义的类图

    AOP流程源码分析-SpringAOP中定义的类图AOP流程源码分析-SpringAOP中定义的类图AOP流程源码分析-SpringAOP中定义的类图AOP流程源码分析-SpringAOP中定义的类图AOP流程源码分析-SpringAOP中定义的类图AOP流程源码分析...

    五、Spring源码分析——Spring Aop

    《Spring AOP 源码分析》 在深入探讨Spring AOP之前,我们先要理解AOP(面向切面编程)的基本概念。AOP是一种编程范式,它将关注点分离,使得我们可以将横切关注点(如日志、事务管理、安全检查等)与业务逻辑解耦...

    Java spring AOP源码

    ### Java Spring AOP源码分析 #### 概述 在探讨Spring AOP源码之前,我们首先需要了解Spring AOP的基本概念以及它的工作原理。面向切面编程(Aspect-Oriented Programming, AOP)是一种编程范式,它通过将横切关注...

    Spring AOP源码分析.mmap

    有关于Spring,我们最常用的两个功能就是IOC和AOP,前几篇文章从源码级别介绍了Spring容器如何为我们生成bean及bean之间的依赖关系 下面我们接着来看AOP的源码实现。 有关于AOP,我们在面试中也被无数次问到...

    spring源码--AOP流程--笔记.docx

    Spring AOP 源码分析笔记 Spring AOP(Aspect-Oriented Programming)是一种编程范式,它允许开发者 modularize cross-cutting concerns,即将横切关注点模块化。AOP 使得开发者可以将一些公共的功能模块化,以便在...

    Java流行框架源码分析:Spring源码、SpringBoot源码、SpringAOP源码、SpringSecurity源码、

    Java流行框架源码分析:Spring源码、SpringBoot源码、SpringAOP源码、SpringSecurity源码、SpringSecurity OAuth2源码、JDK源码、Netty源码

    spring-aop源码

    《深入剖析Spring AOP源码》 Spring AOP(Aspect Oriented Programming,面向切面编程)是Spring框架的重要组成部分,它提供了一种在不修改源代码的情况下,对现有代码进行功能增强的技术。本文将深入探讨Spring ...

    spring-aop.pdf 源码电子书

    标题和描述中提到的是关于Spring AOP源码的电子书。Spring AOP(Aspect-Oriented Programming)是Spring框架的一个重要组成部分,它支持面向切面编程的实践,是为了解决面向对象编程中的横切关注点问题而设计的。在...

    Spring AOP实现 项目源码 Myeclipse 直接导入可用

    **三、项目源码分析** 项目的源码包含了一个简单的Spring AOP示例,可能包括以下几个部分: 1. `Aspect`类:包含了切面逻辑的类,通常有多个通知方法。 2. `Service`类:业务逻辑类,切面将会影响到的方法。 3. `...

    Spring Aop 引用新功能 源码

    通过源码分析,我们可以深入了解Spring AOP的工作原理,以及如何通过`IntroductionInterceptor`或`@AspectJ`来实现引入。理解并熟练运用这一特性,将有助于我们在实际开发中更好地进行代码解耦和模块化,提升软件的...

    Spring AOP源码笔记

    【Spring AOP源码笔记】 Spring AOP是Spring框架的核心组件之一,它实现了面向切面编程(Aspect-Oriented Programming,简称AOP),允许开发者定义“切面”,这些切面可以封装横切关注点,如日志记录、事务管理等。...

    死磕Spring之AOP篇 - Spring AOP两种代理对象的拦截处理(csdn)————程序.pdf

    在深入理解 Spring AOP 的源码时,需要熟悉 Spring IoC 的工作原理,以及 AOP 相关的概念,如切点表达式、通知类型等。了解这些基础知识可以帮助我们更好地掌握 Spring AOP 的实现细节。在分析源码时,可以参考作者...

    Java进阶之SpringAOP源码深度剖析共17页.pd

    通过对Spring AOP源码的深入分析,我们可以了解到其内部是如何通过代理模式和反射技术实现对方法调用的拦截,以及如何解析和执行切点表达式,理解通知的执行流程等。这些深入理解对于优化性能、排查问题以及自定义...

    Spring AOP框架实现的结构分析

    在本文中,我们将从实现的角度来认识 SpringAOP 框架,从外部接口、内部实现、组成部分、执行过程四个方面来介绍 Spring AOP 框架的结构分析。 最后,本文的目标是从实现的角度来认识 SpringAOP 框架,观察的角度是...

    老王读spring-aop源码分析&amp;测试代码.zip

    老王读 Spring AOP 系列文章的源码分析&测试代码 帮助大家学习 Spring 源码,更加了解 Spring 的底层 博客专栏地址:https://blog.csdn.net/wang489687009/category_11269905.html

    spring AOP的运用

    下面,我们通过源码分析Spring AOP的工作流程: 1. 当Spring容器启动时,会扫描所有带有`@Aspect`注解的类,并将其转换为对应的`AspectJExpressionPointcut`对象,这是Spring AOP的核心类,用于匹配切入点表达式。 2...

    spring aop 学习笔记

    Spring AOP(面向切面编程)是Spring框架的重要组成部分,它提供了一种模块化和抽象化的方法来处理系统中的交叉关注点,如日志、...在实际项目中,结合源码阅读和相关工具,能进一步提升对Spring AOP的掌握和运用水平。

    Spring aop 性能监控器

    本篇文章将深入探讨如何使用Spring AOP实现性能监控器,并通过源码分析来理解其工作原理。 首先,我们要了解AOP的核心概念——切面(Aspect)、通知(Advice)、连接点(Join Point)、切入点(Pointcut)和织入...

    springAOP核心组件分析.pdf

    Spring AOP(面向切面编程)是Spring框架的一个重要组成部分,它允许开发者将横切关注点与业务逻辑分离,实现业务逻辑的模块化。AOP核心组件包括几个关键概念,如切面(Aspect)、通知(Advice)、连接点(Joinpoint...

    spring-aop-ProxyFactoryBean 源码分析

    5. **源码分析**: 在`ProxyFactoryBean`的`getObject()`方法中,会调用`createAopProxy()`生成`AopProxy`,然后`AopProxy`的`getProxy()`方法返回实际的代理对象。`createAopProxy()`会根据配置判断使用JDK还是...

Global site tag (gtag.js) - Google Analytics