Spring源码分析:AOP分析之Advice

原文发布于: http://blog.ztgreat.cn/article/61

前言

上篇文章中我们先从动态代理技术谈起,简单的分析了动态代理技术,接着对Spring 中的AOP 以ProxyFactoryBean为例,通过简单的配置,对Spring AOP的基本实现和工作流程进行了简单的梳理,通过ProxyFactoryBean 得到AopProxy对象。

对于JDK的AopProxy代理对象,使用是InvocationHandler的invoke回调入口;而对于CGLIB的AopProxy代理对象,使用的是设置好的callback回调,在这些callback 回调中,对于AOP实现,是通过DynamicAdvisedInterceptor 来完成的,而DynamicAdvisedInterceptor的回调入口是intercept方法。

今天我们的主题就是看看Spring 中Advice的实现,下面已JdkDynamicAopProxy为例,展开来讨论。

本文是在Spring源码分析:AOP分析(一)的基础上的深入

Advice通知的实现

在上文中我们简要的分析了JdkDynamicAopProxy 中的invoke 方法,其中一行代码则是获取拦截器链:

// Get the interception chain for this method.
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

这里便是我们的入口,可以看看Spring 是如何创建这个拦截器链的。

AdvisedSupport

advised是一个AdvisedSupport 对象,这个类的继承关系如下:

20181107203530

这个AdvisedSupport 类同时也是ProxyFactoryBean的基类。从AdvisedSupport的代码中可以看到getInterceptorsAndDynamicInterceptionAdvice的实现,在这个方法中取得了拦截器链,在取得拦截器链的时候,为提高取得拦截器链的效率,还为这个拦截器链设置了缓存。

public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, @Nullable Class<?> targetClass) {
	MethodCacheKey cacheKey = new MethodCacheKey(method);
	List<Object> cached = this.methodCache.get(cacheKey);
	if (cached == null) {
		cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
					this, method, targetClass);
			this.methodCache.put(cacheKey, cached);
	}
	return cached;
}

取得拦截器链的工作是由配置好的advisorChainFactory来完成的,从名字上可以猜到,它是一个生成通知器链的工厂。在这里,advisorChainFactory被配置成一个DefaultAdivsorChainFactory对象,在DefaultAdivsorChainFactory 中实现了interceptor链的获取过程。

DefaultAdvisorChainFactory

public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
			Advised config, Method method, @Nullable Class<?> targetClass) {

	List<Object> interceptorList = new ArrayList<Object>(config.getAdvisors().length);
	Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
	boolean hasIntroductions = hasMatchingIntroductions(config, actualClass);
	//全局获取 AdvisorAdapterRegistry  单例
	AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();

	for (Advisor advisor : config.getAdvisors()) {
		if (advisor instanceof PointcutAdvisor) {
			// Add it conditionally.
			PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
			if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
					MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
				//匹配当前方法
				if (MethodMatchers.matches(mm, method, actualClass, hasIntroductions)) {
					//适配advisor成方法拦截器
					MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
					if (mm.isRuntime()) {
						for (MethodInterceptor interceptor : interceptors) {
							interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
						}
					}
					else {
						interceptorList.addAll(Arrays.asList(interceptors));
					}
				}
			}
		}
		else if (advisor instanceof IntroductionAdvisor) {
			IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
			if (config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) {
				Interceptor[] interceptors = registry.getInterceptors(advisor);
				interceptorList.addAll(Arrays.asList(interceptors));
			}
		}
		else {
			Interceptor[] interceptors = registry.getInterceptors(advisor);
			interceptorList.addAll(Arrays.asList(interceptors));
		}
	}
	return interceptorList;
}

在这个获取过程中,首先设置一个List,其长度由配置的通知器的个数来决定的,这个配置就是XML中对ProxyFactoryBean做的interceptNames属性的配置。然后,DefaultAdivsorChainFactory会通过一个AdvisorAdapterRegistry来实现拦截器的注册,AdvisorAdapterRegistry对advice通知的织入功能起了很大的作用。这个在稍后将会阐述,有了AdvisorAdapterRegistry注册器,利用它来从ProxyFactoryBean 配置中得到的通知进行适配,从而获取相应的拦截器,再把它加入前面设置好的List中去,完成所谓的拦截器注册过程。在拦截器适配和注册完成以后,List中的拦截器会被JDK生成AopProxy代理对象的invoke方法或者CGLIB代理对象的intercept拦截方法取得,并启动拦截器的invoke调用,最终触发通知的切面增强。

AdvisorAdapterRegistry

AdvisorAdapterRegistry 从 GlobalAdvisorAdapterRegistry 中获取,GlobalAdvisorAdapterRegistry的实现很简洁

public abstract class GlobalAdvisorAdapterRegistry {

	private static AdvisorAdapterRegistry instance = new DefaultAdvisorAdapterRegistry();


	public static AdvisorAdapterRegistry getInstance() {
		return instance;
	}

	static void reset() {
		instance = new DefaultAdvisorAdapterRegistry();
	}

}

从代码中可以看出,GlobalAdvisorAdapterRegistry是一个单例模式的实现,它配置了一个静态飞final变量instance,这个对象是在加载类的时候就生成的,而且GlobalAdvisorAdapterRegistry还是一个抽象类,不能被实例化,这样就保证了instance对象的唯一性。

下面看一下 DefaultAdvisorAdapterRegistry 中究竟发生了什么

public class DefaultAdvisorAdapterRegistry implements AdvisorAdapterRegistry, Serializable {

    //持有一个AdvisorAdapter的list,这个List中的Adapter
    //是与Sping AOP的advice增强功能相应的
	private final List<AdvisorAdapter> adapters = new ArrayList<>(3);

	/**
	 * 这里把已有的advice实现的Adapter加进来
	 */
	public DefaultAdvisorAdapterRegistry() {
		registerAdvisorAdapter(new MethodBeforeAdviceAdapter());
		registerAdvisorAdapter(new AfterReturningAdviceAdapter());
		registerAdvisorAdapter(new ThrowsAdviceAdapter());
	}
	//后面代码省略
}

DefaultAdvisorChainFactorygetInterceptorsAndDynamicInterceptionAdvice方法中,我们看到了AdvisorAdapterRegistry 调用了getInterceptors 方法来获取方法拦截器,这个就是核心代码了。

getInterceptors

public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {
	List<MethodInterceptor> interceptors = new ArrayList<>(3);
	//从Advisor通知配置中取得advice通知
	Advice advice = advisor.getAdvice();
	
	//如果是MethodInterceptor类型,不需要再适配,直接加入集合
	if (advice instanceof MethodInterceptor) {
		interceptors.add((MethodInterceptor) advice);
	}
	//对adice 进行适配
	for (AdvisorAdapter adapter : this.adapters) {
	    //适配器支持当前adive
		if (adapter.supportsAdvice(advice)) {
		    //适配并返回结果
			interceptors.add(adapter.getInterceptor(advisor));
		}
	}
	if (interceptors.isEmpty()) {
		throw new UnknownAdviceTypeException(advisor.getAdvice());
	}
	return interceptors.toArray(new MethodInterceptor[0]);
}

从代码中我们可以看到,最后通过adapter的getInterceptor 方法,来获取最后的拦截器,我们来看看

AdvisorAdapter

AdvisorAdapter的实现关系:

20181108105933

从这几个适配器的名字上可以看到,它们完全和advice一一对应,在这里,它们作为适配器加入到adapters 中。

MethodBeforeAdviceAdapter

已MethodBeforeAdviceAdapter为例,它的实现很简单:

class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {

	@Override
	public boolean supportsAdvice(Advice advice) {
		return (advice instanceof MethodBeforeAdvice);
	}

	@Override
	public MethodInterceptor getInterceptor(Advisor advisor) {
		MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();
		return new MethodBeforeAdviceInterceptor(advice);
	}

}

supportsAdvice这个方法是对advice的类型进行判断。

getInterceptor方法把advice从advisor 中取出,然后创建一个MethodBeforeAdviceInterceptor 对象,通过这个对象把advice包装起来,完成适配过程。

MethodBeforeAdviceInterceptor
public class MethodBeforeAdviceInterceptor implements MethodInterceptor, BeforeAdvice, Serializable {

	private final 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 进行增加
		this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
		//再继续执行 MethodInvocation的proceed 方法
		//后续拦截器调用,如果拦截器执行完毕,则会进行方法调用
		return mi.proceed();
	}

}

在MethodBeforeAdviceInterceptor的invoke 回调中,首先触发了advice的before回调,然后才是MethodInvocation的proceed 方法调用。

接下来我们需要回到在上篇AOP中简单分析的内容,在 JdkDynamicAopProxy-> invoken 方法中。

回顾 invoke

JdkDynamicAopProxy-> invoken:


List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

if (chain.isEmpty()) {
 proxying.
   Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
   retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
}
else {
  
   invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
   retVal = invocation.proceed();
}

当我们获取到拦截器链后,如果该拦截器链不为空,那么构造ReflectiveMethodInvocation 对象,然后调用proceed 方法。

ReflectiveMethodInvocation

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);
   }
}

这里开始遍历拦截器链,启动对拦截器链的invoke 方法调用,根据不同的拦截器类型,执行不同的调用(Spring 会根据不同的advice类型,封装不同的拦截器)。我们例子中使用的的是MethodBeforeAdvice,因此封装的是MethodBeforeAdviceInterceptor,触发MethodBeforeAdviceInterceptor的invoke 方法调用,而在MethodBeforeAdviceInterceptor的invoke 中,会先调用advice的before方法,再继续执行 MethodInvocation的proceed 方法,继续对后续拦截器的调用,当拦截器调用完成后,再调用真正方法,这就是MethodBeforeAdvice所需要对目标对象的增强效果(在方法调用之前完成通知增强)。

通过上面的流程分析,我们知道了Advice是如果完成目标对象的增强的,在上面分析中,我们看到一些Pointcut ,MethodMatcher这些关键词,但是并没有进入深入分析,因为最开始我们只需要把握骨架就可以了,下面我们从使用的角度出发,再来进一步了解Spring AOP中的一些细节,现在有了骨架认识,再来回过头来看这些小组件就会豁然开朗了。

PointCut

PointCut(切点)决定了Advice通知应该作用于哪个连接点,也就是说通过Pointcut来定义需要增强的方法的集合,这些集合的选取可以按照一定的规则来完成。

查看 PointCut 接口设计:

public interface Pointcut {
	ClassFilter getClassFilter();
	MethodMatcher getMethodMatcher();
	Pointcut TRUE = TruePointcut.INSTANCE;
}

ClassFilter getClassFilter() ,该方法返回一个类过滤器,由于一个类可能会被多个代理类代理,于是Spring引入了责任链模式,

另一个方法则是 MethodMatcher getMethodMatcher() ,返回一个方法匹配器,通过某种方式来匹配方法的名称来决定是否对该方法进行增强,这就是 MethodMatcher 的作用。

默认的 Pointcut 实例,该实例对于任何方法的匹配结果都是返回 true。

MethodMatcher

看一下 MethodMatcher 接口:

public interface MethodMatcher {
	boolean matches(Method method, @Nullable Class<?> targetClass);
	boolean isRuntime();
	boolean matches(Method method, @Nullable Class<?> targetClass, Object... args);
	MethodMatcher TRUE = TrueMethodMatcher.INSTANCE;
}

该接口定义了静态方法匹配器和动态方法匹配器。

静态方法匹配器:它仅对方法名签名(包括方法名和入参类型及顺序)进行匹配,静态匹配仅会判别一次;

动态方法匹配器:会在运行期检查方法入参的值,而动态匹配因为每次调用方法的入参都可能不一样,所以每次都必须判断。

方法匹配器的类型由isRuntime()返回值决定,返回false表示是静态方法匹配器,返回true表示是动态方法匹配器。

Advice 通知

Advice 定义在连接点做什么。为切面增强提供织入接口。

我们前面使用的MethodBeforeAdvice就继承至它,当然还有其它类似的方法。

Advisor

Advisor通知器,将 Advice 和 PointCut 结合起来,通过Advisor可以定义在哪个通知(advice)并在哪个关注点(pointcut)使用它.

public interface Advisor {

	Advice EMPTY_ADVICE = new Advice() {};

	Advice getAdvice();

	boolean isPerInstance();

}

一个重要的子接口 PointcutAdvisor

public interface PointcutAdvisor extends Advisor {

	Pointcut getPointcut();
	
}

针对前面的userService,这里我们自己写一个advisor 来把整个过程连接起来。

定义一个PointCut:

public class UserPointCut implements Pointcut {
    public ClassFilter getClassFilter() {
        return ClassFilter.TRUE;
    }

    public MethodMatcher getMethodMatcher() {
        return new MethodMatcher() {

            public boolean matches(Method method, Class<?> targetClass, Object[] args) {
                if (method.getName().equals("add")) {
                    return true;
                }
                return false;
            }

            public boolean matches(Method method, Class<?> targetClass) {
                if (method.getName().equals("add")) {
                    return true;
                }
                return false;
            }

            public boolean isRuntime() {
                return true;
            }
        };
    }
}

定义一个advice(增强逻辑),这里采用AfterReturningAdvice

public class UserAfterAdvice implements AfterReturningAdvice {

    public void afterReturning(Object returnValue, Method method,
                               Object[] args, Object target) throws Throwable {
        System.out.println(
                "after " + target.getClass().getSimpleName() + "." + method.getName() + "()");
    }

}

定义Advisor,把advice和pointcut连接起来。

public class UserAdvisor implements PointcutAdvisor {

    public Advice getAdvice() {
        return new UserAfterAdvice();
    }
    public boolean isPerInstance() {
        return false;
    }
    public Pointcut getPointcut() {
        return new UserPointCut();
    }
}

最后XML配置:

<bean id="userAdvisor" class="com.study.spring.aop.PointCut.UserAdvisor"></bean>
<bean id="userService" class="com.study.spring.aop.UserServiceImpl"/>
<bean id="userServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
   <property name="targetName">
      <value>userService</value>
   </property>
   <property name="interceptorNames">
      <list>
         <value>userAdvisor</value>
      </list>
   </property>
</bean>

最后运行结果就不展示了,通过这样一个例子,加上前面对Advice的分析,总算是把Spring的AOP骨架弄明白了,当然很多地方,我们是忽略了的,但是这并不影响我们理解整个AOP,当有了整体认识后,再回过头来,挖掘其中的一些细节,这样才会更好的理解,而且理解也一定会更加的深刻。

参考

Spring 技术内幕

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值