【小家Spring】探索Spring AOP中aopalliance的Joinpoint、MethodInvocation、Interceptor、MethodInterceptor...

2019-09-03 16:16:53 浏览数 (1)

前言

在这篇博文:【小家Spring】详解Spring AOP中底层代理模式之JdkDynamicAopProxy和CglibAopProxy(ObjenesisCglibAopProxy)的源码分析

我们已经能够知道了,代理对象创建好后,其实最终的拦截工作都是交给了MethodInvocation,JDK交给:ReflectiveMethodInvocation,CGLIB交给CglibMethodInvocation

备注:此处所说的MethodInvocation是AOP联盟包里的,也就是org.aopalliance.intercept.MethodInvocation。 AOP联盟包里和cglib包里都有的叫:MethodInterceptor,不要弄混了。

org.aopalliance.intercept.Joinpoint

首先需要注意的是,一般我们会接触到两个Joinpoint

  1. org.aspectj.lang.JoinPoint:该对象封装了SpringAop中切面方法的信息,在切面方法中添加JoinPoint参数,可以很方便的获得更多信息。(一般用于@Aspect标注的切面的方法入参里),它的API很多,常用的有下面几个:
  2. Signature getSignature(); :封装了署名信息的对象,在该对象中可以获取到目标方法名,所属类的Class等信息
  3. Object[] getArgs();:传入目标方法的参数们
  4. Object getTarget();:被代理的对象(目标对象)
  5. Object getThis();:该代理对象

备注:ProceedingJoinPoint对象是JoinPoint的子接口,该对象只用在@Around的切面方法中

  1. org.aopalliance.intercept.Joinpoint是本文的重点,下面主要看看它的解释和相关方法:
代码语言:javascript复制
// 此接口表示运行时的连接点(AOP术语)  (和aspectj里的连接点意思有点像)
public interface Joinpoint {

	// 执行此拦截点,并进入到下一个连接点
	Object proceed() throws Throwable;
	// 返回保存当前连接点静态部分【的对象】。  这里一般指的target
	Object getThis();
	// 返回此静态连接点  一般就为当前的Method(至少目前的唯一实现是MethodInvocation,所以连接点得静态部分肯定就是本方法喽)
	AccessibleObject getStaticPart();
}

org.aopalliance.intercept.Invocation

它的中文意思:祈祷; 乞求,它继承自Joinpoint

这个类没有同名的,只有的aopalliance里有。

代码语言:javascript复制
// 此接口表示程序中的调用~
// 该调用是一个可以被拦截器拦截的连接点
public interface Invocation extends Joinpoint {
	// 获得参数们。比如方法的入参们
	Object[] getArguments();
}

org.aopalliance.intercept.MethodInvocation

接口到了这一层,就比较具象了。它表示方法的执行器,显然就是和Method方法有关喽

代码语言:javascript复制
// 方法调用时,对这部分进行描述
public interface MethodInvocation extends Invocation {
	// 返回正在被调用得方法~~~  返回的是当前Method对象。
	// 此时,效果同父类的AccessibleObject getStaticPart() 这个方法
	Method getMethod();
}

MethodInvocation作为aopalliance里提供的最底层接口了。Spring提供了相关的实现,如下图:

Spring自己也定义了一个接口,来进行扩展和统一管理:ProxyMethodInvocation

org.springframework.aop.ProxyMethodInvocation

这个接口是Spring提供的对aopallianceMethodInvocation的继承扩展接口

代码语言:javascript复制
// 这是Spring提供的对MethodInvocation 的一个扩展。
// 它允许访问  方法被调用的代理对象以及其它相关信息
public interface ProxyMethodInvocation extends MethodInvocation {
	
	// 返回代理对象
	Object getProxy();
	
	// 克隆一个,使用的Object得clone方法
	MethodInvocation invocableClone();
	MethodInvocation invocableClone(Object... arguments);

	// 设置参数  增强器、通知们执行的时候可能会用到
	void setArguments(Object... arguments);
	// 添加一些属性kv。这些kv并不会用于AOP框架内,而是保存下来给特殊的一些拦截器实用
	void setUserAttribute(String key, @Nullable Object value);
	@Nullable
	Object getUserAttribute(String key);
}

下面我们就是主菜了,Spring给我们提供的唯一(其实算唯二吧)的实现类,它执行着拦截的核心逻辑。会让所有的通知器都执行~~

ReflectiveMethodInvocation

Reflective中文意思:可被反射的

显然它作为实现类,需要实现包括父接口在内的所有的方法们。

它也是JdkDynamicAopProxy最终执行时候new出来的执行对象,话不多说,下面看看具体的逻辑吧~~

代码语言:javascript复制
public class ReflectiveMethodInvocation implements ProxyMethodInvocation, Cloneable {

	protected final Object proxy; // 代理对象
	@Nullable
	protected final Object target; // 目标对象
	protected final Method method; // 被拦截的方法

	protected Object[] arguments = new Object[0];
	@Nullable
	private final Class<?> targetClass;

	@Nullable
	private Map<String, Object> userAttributes;
	protected final List<?> interceptorsAndDynamicMethodMatchers;
	
	// currentInterceptorIndex初始值为 -1 
	private int currentInterceptorIndex = -1;

	// 唯一的构造函数。注意是protected  相当于只能本包内、以及子类可以调用。外部是不能直接初始化的此对象的(显然就是Spring内部使用的类了嘛)
	//invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
	// proxy:代理对象
	// target:目标对象
	// method:被代理的方法
	// args:方法的参数们
	// targetClass:目标方法的Class (target != null ? target.getClass() : null)
	// interceptorsAndDynamicMethodMatchers:拦截链。  this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass)这个方法找出来的
	protected ReflectiveMethodInvocation(
			Object proxy, @Nullable Object target, Method method, @Nullable Object[] arguments,
			@Nullable Class<?> targetClass, List<Object> interceptorsAndDynamicMethodMatchers) {

		this.proxy = proxy;
		this.target = target;
		this.targetClass = targetClass;
		// 找到桥接方法,作为最后执行的方法。至于什么是桥接方法,自行百度关键字:bridge method
		// 桥接方法是 JDK 1.5 引入泛型后,为了使Java的泛型方法生成的字节码和 1.5 版本前的字节码相兼容,由编译器自动生成的方法(子类实现父类的泛型方法时会生成桥接方法)
		this.method = BridgeMethodResolver.findBridgedMethod(method);
		// 对参数进行适配
		this.arguments = AopProxyUtils.adaptArgumentsIfNecessary(method, arguments);
		this.interceptorsAndDynamicMethodMatchers = interceptorsAndDynamicMethodMatchers;
	}

	@Override
	public final Object getProxy() {
		return this.proxy;
	}
	@Override
	@Nullable
	public final Object getThis() {
		return this.target;
	}
	// 此处:getStaticPart返回的就是当前得method
	@Override
	public final AccessibleObject getStaticPart() {
		return this.method;
	}
	// 注意:这里返回的可能是桥接方法哦
	@Override
	public final Method getMethod() {
		return this.method;
	}
	@Override
	public final Object[] getArguments() {
		return this.arguments;
	}
	@Override
	public void setArguments(Object... arguments) {
		this.arguments = arguments;
	}


	// 这里就是核心了,要执行方法、执行通知、都是在此处搞定的
	// 这里面运用 递归调用 的方式,非常具有技巧性
	@Override
	@Nullable
	public Object proceed() throws Throwable {
		//	currentInterceptorIndex初始值为 -1  如果执行到链条的末尾 则直接调用连接点方法 即 直接调用目标方法
		if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
			// 这个方法相当于调用了目标方法~~~下面会分析
			return invokeJoinpoint();
		}

		// 获取集合中的 MethodInterceptor(并且currentInterceptorIndex   1了哦)
		Object interceptorOrInterceptionAdvice =
				this.interceptorsAndDynamicMethodMatchers.get(  this.currentInterceptorIndex);

		//InterceptorAndDynamicMethodMatcher它是Spring内部使用的一个类。很简单,就是把MethodInterceptor实例和MethodMatcher放在了一起。看看在advisor chain里面是否能够匹配上
		if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
			InterceptorAndDynamicMethodMatcher dm =
					(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
			
			// 去匹配这个拦截器是否适用于这个目标方法  试用就执行拦截器得invoke方法
			if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
				return dm.interceptor.invoke(this);
			}
			else {
				// 如果不匹配。就跳过此拦截器,而继续执行下一个拦截器
				// 注意:这里是递归调用  并不是循环调用
				return proceed();
			}
		}
		else {
			// 直接执行此拦截器。说明之前已经匹配好了,只有匹配上的方法才会被拦截进来的
			// 这里传入this就是传入了ReflectiveMethodInvocation,从而形成了一个链条了
			return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
		}
	}
	

	// 其实就是简单的一个:method.invoke(target, args);
	// 子类可以复写此方法,去执行。比如它的唯一子类CglibAopProxy内部类  CglibMethodInvocation就复写了这个方法  它对public的方法做了一个处理(public方法调用MethodProxy.invoke)
	@Nullable
	protected Object invokeJoinpoint() throws Throwable {
		// 此处传入的是target,而不能是proxy,否则进入死循环
		return AopUtils.invokeJoinpointUsingReflection(this.target, this.method, this.arguments);
	}

	@Override
	public MethodInvocation invocableClone() {
		Object[] cloneArguments = this.arguments;
		if (this.arguments.length > 0) {
			// Build an independent copy of the arguments array.
			cloneArguments = new Object[this.arguments.length];
			System.arraycopy(this.arguments, 0, cloneArguments, 0, this.arguments.length);
		}
		return invocableClone(cloneArguments);
	}
	@Override
	public MethodInvocation invocableClone(Object... arguments) {
		if (this.userAttributes == null) {
			this.userAttributes = new HashMap<>();
		}
		try {
			ReflectiveMethodInvocation clone = (ReflectiveMethodInvocation) clone();
			clone.arguments = arguments;
			return clone;
		} catch (CloneNotSupportedException ex) {
			throw new IllegalStateException(
					"Should be able to clone object of type ["   getClass()   "]: "   ex);
		}
	}
	@Override
	public void setUserAttribute(String key, @Nullable Object value) {
		if (value != null) {
			if (this.userAttributes == null) {
				this.userAttributes = new HashMap<>();
			}
			this.userAttributes.put(key, value);
		}
		else {
			if (this.userAttributes != null) {
				this.userAttributes.remove(key);
			}
		}
	}
	@Override
	@Nullable
	public Object getUserAttribute(String key) {
		return (this.userAttributes != null ? this.userAttributes.get(key) : null);
	}
	public Map<String, Object> getUserAttributes() {
		if (this.userAttributes == null) {
			this.userAttributes = new HashMap<>();
		}
		return this.userAttributes;
	}

	@Override
	public String toString() {
		// Don't do toString on target, it may be proxied.
		StringBuilder sb = new StringBuilder("ReflectiveMethodInvocation: ");
		sb.append(this.method).append("; ");
		if (this.target == null) {
			sb.append("target is null");
		}
		else {
			sb.append("target is of class [").append(this.target.getClass().getName()).append(']');
		}
		return sb.toString();
	}

}

从这里我们需要注意到的是:ProxyMethodInvocationReflectiveMethodInvocation)是代理执行的入口。然后内部会把所有的 增强器 都拿出来 递归执行(比如前置通知,就在目标方法之前执行) **这就实现了指定次序的链式调用**

CglibMethodInvocation

它是继承自ReflectiveMethodInvocation,是CglibAopProxy自己使用的执行器。

代码语言:javascript复制
	private static class CglibMethodInvocation extends ReflectiveMethodInvocation {

		private final MethodProxy methodProxy;

		private final boolean publicMethod;

		public CglibMethodInvocation(Object proxy, @Nullable Object target, Method method,
				Object[] arguments, @Nullable Class<?> targetClass,
				List<Object> interceptorsAndDynamicMethodMatchers, MethodProxy methodProxy) {
			
			// 调用父类的构造  完成基本参数得初始化
			super(proxy, target, method, arguments, targetClass, interceptorsAndDynamicMethodMatchers);
			
			// 自己的个性化参数:
			
			// 这个参数是子类多传的,表示:它是CGLIb拦截的时候的类MethodProxy
			//MethodProxy为生成的代理类对方法的代理引用。cglib生成用来代替Method对象的一个对象,使用MethodProxy比调用JDK自身的Method直接执行方法效率会有提升
			// 它有两个重要的方法:invoke和invokeSuper
			this.methodProxy = methodProxy;
			// 方法是否是public的  对应下面的invoke方法的处理 见下面
			this.publicMethod = Modifier.isPublic(method.getModifiers());
		}

		@Override
		protected Object invokeJoinpoint() throws Throwable {
			// 如果是public的方法,调用methodProxy去执行目标方法
			// 否则直接执行method即可
			if (this.publicMethod) {
				// 此处务必注意的是,传入的是target,而不能是proxy,否则进入死循环
				return this.methodProxy.invoke(this.target, this.arguments);
			} else {
				return super.invokeJoinpoint();
			}
		}
	}


org.aopalliance.intercept.MethodInterceptor

需要说明的cglib包里也存在一个MethodInterceptor,它的主要作用是CGLIB内部使用,一般是和Enhancer一起来使用而创建一个动态代理对象。

而本处我们讲到的 org.aopalliance.intercept.MethodInterceptor,那些@AspectJ定义的通知们(增强器们),或者是自己实现的MethodBeforeAdviceAfterReturningAdvice…(总是都是org.aopalliance.aop.Advice一个通知器),最终都会被包装成一个org.aopalliance.intercept.MethodInterceptor,最终交给MethodInvocation(其子类ReflectiveMethodInvocation)去执行,它会把你所有的增强器都给执行了,这就是我们面向切面编程的核心思路过程。

这里面其实有两个标记接口(没有任何方法):

顶层接口:Advice 中文意思:建议,忠告。 实现通知的方式可议是任何方式,比如Interceptors拦截器得方式

中间层接口:Interceptor 继承自Advice接口。它就是以拦截器方式去实现通知的效果

此处需要说明的是,Interceptor得子接口有两个:MethodInterceptorConstructorInterceptor,但是ConstructorInterceptor连Spring都没有提供实现类,因此本文不会讲述本接口。

代码语言:javascript复制
// 从名字里都能看出来,它是通过拦截方法的执行来实现通知得效果的~~~~
@FunctionalInterface
public interface MethodInterceptor extends Interceptor {
	// 可议在此方法里  在方法执行之前、之后做对应的处理。
	// 需要执行的时候,调用invocation.proceed()方法即可
	Object invoke(MethodInvocation invocation) throws Throwable;
}

Spring给我们提供的MethodInterceptor实现非常非常的多:

有很多我们非常熟悉的面孔。下面就抽出几个,简单的看看实现代码:

关于AspectJ切面相关的增强器

一共5个对应着AspectJ提供的那五个注解。每个注解都是一个最终被包装好的Advice(其实是个AspectJAfterAdvice) 此处知道就可,暂时此处不做详细介绍~~

MethodBeforeAdviceInterceptor

这个源代码很简单,就是一层代理。把MethodBeforeAdvice包装成了一个MethodInterceptor

代码语言:javascript复制
public class MethodBeforeAdviceInterceptor implements MethodInterceptor, Serializable {

	private MethodBeforeAdvice advice;
	
	public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) {
		Assert.notNull(advice, "Advice must not be null");
		this.advice = advice;
	}
	@Override
	public Object invoke(MethodInvocation mi) throws Throwable {
		// 在目标方法执行之前,先执行advice得before方法~~~
		this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
		
		// 注意此处继续调用了 mi.proceed()。相当于去执行下一个增强器。类似于递归执行了,这样就行程了一个链式得调用执行
		return mi.proceed();
	}
}
AfterReturningAdviceInterceptor

显然它是对AfterReturningAdvice的一个包装

代码语言:javascript复制
public class AfterReturningAdviceInterceptor implements MethodInterceptor, AfterAdvice, Serializable {

	private final AfterReturningAdvice advice;
	public AfterReturningAdviceInterceptor(AfterReturningAdvice advice) {
		Assert.notNull(advice, "Advice must not be null");
		this.advice = advice;
	}
	@Override
	public Object invoke(MethodInvocation mi) throws Throwable {
		Object retVal = mi.proceed();
		// 嗲用afterReturning,它是能够享受到返回值的
		this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
		return retVal;
	}

}
ThrowsAdviceInterceptor

包装的ThrowsAdvice,实际处理起来稍微复杂点~~~

代码语言:javascript复制
public class ThrowsAdviceInterceptor implements MethodInterceptor, AfterAdvice {
	...
}

上面三个拦截器,可议参考适配器:

代码语言:javascript复制
public class DefaultAdvisorAdapterRegistry implements AdvisorAdapterRegistry, Serializable {
	...
	public DefaultAdvisorAdapterRegistry() {
		registerAdvisorAdapter(new MethodBeforeAdviceAdapter());
		registerAdvisorAdapter(new AfterReturningAdviceAdapter());
		registerAdvisorAdapter(new ThrowsAdviceAdapter());
	}	 
	...
}
CacheInterceptor / AsyncExecutionInterceptor

这两个分别涉及到Spring的cache部分以及Async异步部分,这在讲述这块的时候会具体的进行分析。

总结

aopalliance属于AOP联盟,定义了一些标准。一共只几个接口,总结如下:

org.aopalliance.aop
  • Advice:通知的标记接口。实现可以是任意类型,比如下面的Interceptor
  • AspectException:所有的AOP框架产生异常的父类。它是个RuntimeException
org.aopalliance.intercept
  • Interceptor:它继承自Advice,它通过拦截器得方式实现通知的效果(也属于标记接口)
  • MethodInterceptor:具体的接口。拦截方法 (Spring提供了非常多的具体实现类)
  • ConstructorInterceptor:具体接口。拦截构造器 (Spring并没有提供实现类)
  • Joinpoint:AOP运行时的连接点(顶层接口)
  • Invocation:继承自Joinpoint。 表示执行,提供了Object[] getArguments()来获取执行所需的参数
  • MethodInvocation:(和MethodInterceptor对应,它的invoke方法入参就是它)表示一个和方法有关的执行器。提供方法Method getMethod() (Spring提供了唯一(唯二)实现类:ProxyMethodInvocation
  • ConstructorInvocation:和构造器有关。Constructor<?> getConstructor(); (Spring没有提供任何实现类)

这就是AOP联盟为我们提供的所有的类,它里面全部是接口(那个异常类除外),相当于它定义了一套AOP的标准类。Spring对核心的Method相关的拦截、执行器做了对应的实现。

注意,Spring的AOP实现并不依赖于AspectJ任何类,它自己实现了一套AOP的。比如它Spring自己提供的BeforeAdviceAfterAdvice都是对AOP联盟规范的标准实现。以及Spring自己抽象出来的对Advice的包装:org.springframework.aop.Advisor贯穿Spring AOP的始终 但是在当前注解驱动的流行下,基于POJO(xml方式)以及编程的方式去书写AOP代理,显得非常的繁琐。因此Spring提供了另外一种实现:基于AspectJ,到这才使用到了AspectJ的相关注解、以及类。 但是还需要说明一点:哪怕使用到了AspectJ的相关注解和类,但核心的AOP织入的逻辑,还都是Spring自己用动态代理去实现的,没用AspectJ它那种特殊的语法和特有的编译器

最后说一句,若在Spring AOP中想使用AspectJ的方式去实现(也是当下最流行的方式),必须导入Jar包:aspectjweaver-1.9.2.jar,而Spring的这个包org.springframework.aop.aspectj下面的所有类,都是专门为了使用@Aspect的方式去服务的,毕竟AOP功能是Spring自己实现的,而不是依赖于AspectJ这个组件的

0 人点赞