好文转载 :spring aop源码解读(Advice、Pointcut、Advisor)

90 阅读10分钟

源文档

这篇关于spring aop 原理的文章写得简单明了,是不可多得的好文档。但是我发现看得人 挺少的,所以再次转发一下。让这批文章继续 发光发热。 感谢作者。

spring aop介绍 面向切面编程是一种编程思想,解决的场景是将项目中一些零散的,通用的逻辑从原业务逻辑的硬编码调用中脱离出来,并在运行时再动态的编织进去。这就是面向切面技术,如日志,注解等功能就经常采用切面技术。 spring aop是spring的一项核心功能,采用的是动态代理的思想,主要有jdk自身的动态代理和cglib动态代理两种。

说到代理,我们看下代理模式的类图:

进入正题 我们知道了spring aop的实现是基于动态代理的,代理可以在原有目标对象的前后增加一些逻辑以实现需求,那具体是怎样增加的呢?

讲之前我们引入几个概念:

Advice:通知,标识逻辑织入的位置,常见的有BeforeAdvice,AfterAdvice等。 PointCut:切入点,标识对什么方法进入代理,常见的有JdkRegexpMethodPointcut根据正则表达式选取切入点,NameMatchMethodPointcut根据方法名选择切入点。 Advisor:通知器,是通知与切入点的集合。 目前spring aop的切点还只是局限在方法级的粒度,所以通知也就是规定织入的逻辑在方法执行的什么时候执行: 如BeforeAdvice有MethodBeforeAdvice实现:

public interface MethodBeforeAdvice extends BeforeAdvice {

	/**
	 * Callback before a given method is invoked.
	 */
	void before(Method method, Object[] args, @Nullable Object target) throws Throwable;
}

同时Pointcut切点也表示的是在哪个方法上织入逻辑,依赖的接口是MethodMatcher:

public interface MethodMatcher {
	boolean matches(Method method, Class<?> targetClass);
	boolean matches(Method method, Class<?> targetClass, Object... args);
}

主要的就是matches方法,判断该方法是否需要织入逻辑,可以根据正则表达式JdkRegexpMethodPointcut、方法名NameMatchMethodPointcut、注解AnnotationMethodMatcher等方式去实现

示例 下面看一个通过ProxyFactory实现的aop示例:

定义目标切点接口:

/**
 * <pre>类名: ITargetImpl</pre>
 * <pre>描述: aop目标接口</pre>
 * <pre>版权: web_chen@163.com</pre>
 * <pre>日期: 2020/11/18 17:28</pre>
 * <pre>作者: chenwb</pre>
 */
public interface ITarget {
	void say();
}  

实现类:

/**
 * <pre>类名: TargetImpl</pre>
 * <pre>描述: aop目标实现类</pre>
 * <pre>版权: web_chen@163.com</pre>
 * <pre>日期: 2020/11/18 17:28</pre>
 * <pre>作者: chenwb</pre>
 */
public class TargetImpl implements ITarget {

	@Override
	public void say() {
		System.out.println("say");
	}
}

定义通知Advice,表示在什么时候做什么事:

import java.lang.reflect.Method;

/**
 * <pre>类名: MyBeforeAdvice</pre>
 * <pre>描述: 定义一个MyBeforeAdvice通知</pre>
 * <pre>版权: web_chen@163.com</pre>
 * <pre>日期: 2020/11/18 17:29</pre>
 * <pre>作者: chenwb</pre>
 */
public class MyBeforeAdvice implements MethodBeforeAdvice {

	@Override
	public void before(Method method, Object[] args, Object target) throws Throwable {
		System.out.println("before say");
	}
}

/**
 * <pre>类名: MyAfterReturningAdvice</pre>
 * <pre>描述: 定义一个MyAfterReturningAdvice通知</pre>
 * <pre>版权: web_chen@163.com</pre>
 * <pre>日期: 2020/11/18 17:29</pre>
 * <pre>作者: chenwb</pre>
 */
public class MyAfterReturningAdvice implements org.springframework.aop.AfterReturningAdvice {
	@Override
	public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
		System.out.println("afterReturning say");
	}
}

两个通知的定义表明:需要在方法执行之前,和之后打印语句。但现在只是一个通知,并不知道需要在哪个方法的执行前后,也就是pointcut

import org.aopalliance.aop.Advice;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.RegexpMethodPointcutAdvisor;

/**
 * <pre>类名: AopTest</pre>
 * <pre>描述: aop测试</pre>
 * <pre>版权: web_chen@163.com</pre>
 * <pre>日期: 2020/11/18 17:29</pre>
 * <pre>作者: chenwb</pre>
 */
public class AopTest {

	public static void main(String[] args) {
		// 创建ProxyFactory
		ITarget target = new TargetImpl();
		ProxyFactory pf = new ProxyFactory(target);

		// 新建两个通知,表明在什么时机切入
		Advice afterReturningAdvice = new MyAfterReturningAdvice();
		Advice beforeAdvice = new MyBeforeAdvice();

		// Advisor包含Pointcut和Advice,叫通知器,这里就设置了切点pointcut
		RegexpMethodPointcutAdvisor regexpAdvisor1 = new RegexpMethodPointcutAdvisor();
		regexpAdvisor1.setPattern("aop.ITargetImpl.say()");
		regexpAdvisor1.setAdvice(afterReturningAdvice);

		RegexpMethodPointcutAdvisor regexpAdvisor2 = new RegexpMethodPointcutAdvisor();
		regexpAdvisor2.setPattern("aop.ITargetImpl.say()");
		regexpAdvisor2.setAdvice(beforeAdvice);

		// 将通知器Advisor注册到ProxyFactory
		pf.addAdvisor(regexpAdvisor1);
		pf.addAdvisor(regexpAdvisor2);
		// 生成代理,执行方法
		ITarget proxy = (ITarget) pf.getProxy();
		proxy.say();
	}
}

上例中使用了RegexpMethodPointcutAdvisor,它是利用正则表达式匹配需要织入逻辑的方法(也就是切点pointcut),同时将Advice也设置进入。最后将Advisor注册到代理工厂,生成代理。

执行结果:

before say
say
afterReturning say

源码 我们示例代码中使用的ProxyFactory生成代理对象,它是一个生成代理的工厂类,而在spring上下文初始化过程中,创建代理对象使用的其实是ProxyFactoryBean。ProxyFactory可以看作是ProxyFactoryBean的一个子集。接下来我们将代理创建还是按照真实场景,通过ProxyFactoryBean实现: 这个类所在路径是aop包下的org.springframework.aop.framework.ProxyFactoryBean

ProxyFactoryBean是一个工厂bean,它是用来创建bean的,FactoryBean是一个接口,其中主要的就是getObject方法:

public Object getObject() throws BeansException {
         // 初始化通知器链,看有哪些通知器
		initializeAdvisorChain();
         // 如果是单例对象,则创建单例的代理对象
		if (isSingleton()) {
			return getSingletonInstance();
		}
		else {
			if (this.targetName == null) {
				logger.info("Using non-singleton proxies with singleton targets is often undesirable. " +
						"Enable prototype proxies by setting the 'targetName' property.");
			}
            // 否则是原型对象
			return newPrototypeInstance();
		}
	}


	private synchronized void initializeAdvisorChain() throws AopConfigException, BeansException {
        // ...省略部分代码
		if (!ObjectUtils.isEmpty(this.interceptorNames)) {
			for (String name : this.interceptorNames) {
				if (name.endsWith(GLOBAL_SUFFIX)) {
					// 如果是全局的通知器配置,获取到整个ioc容器中的通知器
					addGlobalAdvisor((ListableBeanFactory) this.beanFactory,
							name.substring(0, name.length() - GLOBAL_SUFFIX.length()));
				}
				else {
					// 如果是特定名字的通知器配置,则从ioc中获取到该bean,加入到通知器链中
					Object advice;
					if (this.singleton || this.beanFactory.isSingleton(name)) {
						advice = this.beanFactory.getBean(name);
					}
					else {
						advice = new PrototypePlaceholderAdvisor(name);
					}
					addAdvisorOnChainCreation(advice, name);
				}
			}
		}
}

上面初始化了项目中的通知器链之后,就要开始创建代理对象了。我们以单例对象为例:

        private synchronized Object getSingletonInstance() {
		if (this.singletonInstance == null) {
			// 从ioc获取被代理对象
			this.targetSource = freshTargetSource();
			// ...省略部分代码
			// 生成代理对象并返回
			this.singletonInstance = getProxy(createAopProxy());
		}
		return this.singletonInstance;
	}

那么代理对象时如何生成的呢? 首先是createAopProxy()方法,它是创建一个AopProxy的地方,也就是选择是JDK还是Cglib方式的地方。

protected final synchronized AopProxy createAopProxy() {
		//...
		return getAopProxyFactory().createAopProxy(this);
	}

其中ProxyFactoryBean继承了ProxyCreatorSupport,AopProxyFactory的默认实现是DefaultAopProxyFactory,看一下DefaultAopProxyFactory#createAopProxy:

public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
		if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
			Class<?> targetClass = config.getTargetClass();
			// 如果目标对象是实现了接口的,或者已经是代理对象,则使用jdk动态代理
			if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
				return new JdkDynamicAopProxy(config);
			}
			// 否则是cglib生成代理
			return new ObjenesisCglibAopProxy(config);
		}
		else {
			return new JdkDynamicAopProxy(config);
		}
	}

看完了createAopProxy,我们了解了AopProxy的生成过程,以及如何在jdk方式和cglib方式的选择,在选择完AopProxy之后,我们看下是如何生成代理的,以jdk动态代理为例:

protected Object getProxy(AopProxy aopProxy) {
		return aopProxy.getProxy(this.proxyClassLoader);
	}

	public Object getProxy(@Nullable ClassLoader classLoader) {
		// 对代理接口进行分类
		Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
		// 接口是否实现了equals或hashCode方法
		findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
		// 生成代理
		return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
	}

我们知道,Jdk动态代理的核心就是InvocationHandler接口:

public interface InvocationHandler {
    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
}

实现InvocationHandler 接口的实现类和接口传入到 Proxy.newProxyInstance方法中生成代理对象。执行被代理的接口方法就会执行代理对象的invoke方法,执行各类advisor拦截器,所以我们看一下JdkDynamicAopProxy类的invoke方法:

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		Object oldProxy = null;
		boolean setProxyContext = false;

		TargetSource targetSource = this.advised.targetSource;
		Object target = null;

		try {
			// 如果执行的方法是equals或hashCode等特殊方法,则执行对应的代理方法
			if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
				return equals(args[0]);
			}
			else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
				return hashCode();
			}
			else if (method.getDeclaringClass() == DecoratingProxy.class) {
				return AopProxyUtils.ultimateTargetClass(this.advised);
			}
			else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
					method.getDeclaringClass().isAssignableFrom(Advised.class)) {
				return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
			}

			Object retVal;
			// 获取目标代理对象和类型
			target = targetSource.getTarget();
			Class<?> targetClass = (target != null ? target.getClass() : null);
			// 获取针对这个method的所有通知器(关键)
			List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
			// 如果通知器为空,则通过AopUtils.invokeJoinpointUsingReflection直接执行原来的方法
			if (chain.isEmpty()) {
				Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
				retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
			}
			else {
				// 如果通知器链不为空,则执行调用链(关键)
				MethodInvocation invocation =
						new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
				retVal = invocation.proceed();
			}

			// 收集方法的返回值,方法被代理了,返回对象也可能被修改
			Class<?> returnType = method.getReturnType();
			if (retVal != null && retVal == target &&
					returnType != Object.class && returnType.isInstance(proxy) &&
					!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
				retVal = proxy;
			}
			else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
				throw new AopInvocationException(
						"Null return value from advice does not match primitive return type for: " + method);
			}
			return retVal;
		}
		finally {
			// ...
		}
	}

以上invoke方法的大致逻辑如下图:

这里主要需要看的是获取拦截器链和递归调用拦截器链:

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

// 这里就是从我们一开始初始化的通知器中寻找当前method的拦截器

public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
			Advised config, Method method, @Nullable Class<?> targetClass) {
		AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
		Advisor[] advisors = config.getAdvisors();
		List<Object> interceptorList = new ArrayList<>(advisors.length);
		Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
		Boolean hasIntroductions = null;
		
		// advisors就是所有通知器的集合
		for (Advisor advisor : advisors) {
			if (advisor instanceof PointcutAdvisor) {
				PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
				// 通知器是否满足ClassFilter过滤
				if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
					MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
					boolean match;
					if (mm instanceof IntroductionAwareMethodMatcher) {
						if (hasIntroductions == null) {
							hasIntroductions = hasMatchingIntroductions(advisors, actualClass);
						}
						match = ((IntroductionAwareMethodMatcher) mm).matches(method, actualClass, hasIntroductions);
					}
					else {
						match = mm.matches(method, actualClass);
					}
					// 是否满足MethodMatcher的matches过滤,如果满足,则将通知器中的拦截器加入了拦截器数组
					if (match) {
						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;
	}

获取到拦截器链之后就开始递归调用了: 我们看一下ReflectiveMethodInvocation的proceed方法:

public Object proceed() throws Throwable {
		// 拦截器执行完了,则执行原方法
		if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
			return invokeJoinpoint();
		}
		// 获取下一个拦截器
		Object interceptorOrInterceptionAdvice =
				this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
		if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
			InterceptorAndDynamicMethodMatcher dm =
					(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
			Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
			// 匹配拦截器,则执行拦截器逻辑
			if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
				return dm.interceptor.invoke(this);
			}
			// 否则就透传,递归执行下一个拦截器
			else {
				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);
		}
	}

我们讲了获取拦截器链和执行拦截器链,然而还没串起来,关键问题在于我们还有一点没讲,就是获取拦截器链:

MethodInterceptor[] interceptors = registry.getInterceptors(advisor);

这个Registry就是GlobalAdvisorAdapterRegistry,默认实现是DefaultAdvisorAdapterRegistry,它是通知器的适配器注册中心,有哪些通知器都定义这里面:

public DefaultAdvisorAdapterRegistry() {
		registerAdvisorAdapter(new MethodBeforeAdviceAdapter());
		registerAdvisorAdapter(new AfterReturningAdviceAdapter());
		registerAdvisorAdapter(new ThrowsAdviceAdapter());
	}

这是他的构造器,可以看到MethodBeforeAdviceAdapter、AfterReturningAdviceAdapter、ThrowsAdviceAdapter都被注册了进去。 我们选择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);
	}
}

AdvisorAdapter定义了两个方法,一个是是否支持这个通知,第二个就是返回前置通知的拦截器, 一不做,二不休,看看前置通知拦截器做了啥:

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中before方法
		this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
		// 执行下一个调用
		return mi.proceed();
	}
}

而getInterceptors方法:

public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {
		List<MethodInterceptor> interceptors = new ArrayList<>(3);
		Advice advice = advisor.getAdvice();
		if (advice instanceof MethodInterceptor) {
			interceptors.add((MethodInterceptor) advice);
		}
		// 如果适配器支持这个通知,则将类似前面说的前置拦截器加入进来,
		for (AdvisorAdapter adapter : this.adapters) {
			if (adapter.supportsAdvice(advice)) {
				interceptors.add(adapter.getInterceptor(advisor));
			}
		}
		if (interceptors.isEmpty()) {
			throw new UnknownAdviceTypeException(advisor.getAdvice());
		}
		return interceptors.toArray(new MethodInterceptor[0]);
	}

最后多个拦截器链就构成了一个ReflectiveMethodInvocation对象,并执行proceed方法,由此就实现了拦截器链的递归调用。 从实现来看,DefaultAdvisorAdapterRegistry中注册的AdvisorAdapter的顺序是重要的,它决定了拦截器的执行顺序。

小结 1、理解aop的实现思路可以帮助我们理解一些开发问题,如:

aop是通过factorybean实现的,在spring ioc初始化的时候,如果发现一个对象是代理对象,则会为该对象生成代理。 为什么在同类中调用类似@Transactional接口,事务会失效? @Transactional注解新增事务是通过aop实现的,需要调用代理对象的invoke(jdk动态代理)方法,才能享受拦截器新增事务的待遇。而在同类中调用注解方法,是方法硬调用,并没有调用到代理对象,所以事务就不生效了。 2、留给大家和自己一个问题,如果一个类有@Transactional注解,那么ioc中会同时存在原对象和代理对象吗? 如果都存在,那在调用对象方法的时候,ioc是如何选择代理对象,还是原对象的?

欢迎大家留言。分享你的答案。 ———————————————— 版权声明:本文为CSDN博主「c&0xff00」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:blog.csdn.net/weixin_3796…