文前说明
作为码农中的一员,需要不断的学习,我工作之余将一些分析总结和学习笔记写成博客与大家一起交流,也希望采用这种方式记录自己的学习之旅。
本文仅供学习交流使用,侵权必删。
不用于商业目的,转载请注明出处。
1. 概述
- Spring 为目标 bean 筛选到合适的通知器(可查看 【Spring 笔记】AOP 通知筛选相关整理 了解)后,再通过 创建代理对象 的方式将 Advisor(通知器)所持有的 Advice(通知)织入到 bean 的某些方法前后,之后会执行通知逻辑。
- 当目标方法被多个通知匹配到时,Spring 通过引入 拦截器链 保证每个通知的正常执行。
expose-proxy
<!-- spring.xml -->
<bean id="testServiceImpl" class="spring.test.aop.TestServiceImpl"/>
<aop:aspectj-autoproxy expose-proxy="true"/>
<!-- 普通 bean,包含 AOP 切面逻辑 -->
<bean id="aopTest" class="spring.test.aop.AopTest"/>
<!-- 由 @Aspect 注解修饰的切面类 -->
<bean id="annotationAopTest" class="spring.test.aop.AnnotationAopTest"/>
<aop:config expose-proxy="true">
<aop:aspect ref="aopTest">
<!-- pointcut -->
<aop:pointcut id="testPointcut" expression="execution(* spring.test.aop.*.find*(..))" />
<!-- advoce -->
<aop:before method="before" pointcut-ref="testPointcut"/>
<aop:after method="after" pointcut-ref="testPointcut"/>
</aop:aspect>
</aop:config>
- expose-proxy 可配置在 <aop:config/> 和 <aop:aspectj-autoproxy /> 标签上。
- expose-proxy 的使用场景如下。
// TestServiceImpl.java
public class TestServiceImpl implements TestService {
public void save(String name) {
}
public void update(String name) {
}
public void delete(String name) {
System.out.println("TestServiceImpl`s delete");
}
public String findOne(String name) {
System.out.println("TestServiceImpl`s " + name);
return "";
}
public List<String> findAll() {
System.out.println("TestServiceImpl`s findAll");
findOne("findOne");
return new ArrayList<String>();
}
}
public class Test {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
TestService testService = (TestService) context.getBean("testServiceImpl");
testService.findAll();
testService.delete("");
}
}
/* print
AopTest`s before
TestServiceImpl`s findAll
TestServiceImpl`s findOne
AopTest`s after
AnnotationAopTest`s before
TestServiceImpl`s delete
AnnotationAopTest`s after
*/
- 其中
findOne()
被findAll()
方法调用,切面逻辑无法执行。 - 可以使用
AopContext.currentProxy()
获取当前的代理对象,但前提需要 expose-proxy 被配置为 true,使得该代理对象被放入ThreadLocal
中。
public class TestServiceImpl implements TestService {
public void save(String name) {
}
public void update(String name) {
}
public void delete(String name) {
System.out.println("TestServiceImpl`s delete");
}
public String findOne(String name) {
System.out.println("TestServiceImpl`s " + name);
return "";
}
public List<String> findAll() {
System.out.println("TestServiceImpl`s findAll");
((TestService) AopContext.currentProxy()).findOne("findOne");
return new ArrayList<String>();
}
}
/* print
AopTest`s before
TestServiceImpl`s findAll
AopTest`s before
TestServiceImpl`s findOne
AopTest`s after
AopTest`s after
AnnotationAopTest`s before
TestServiceImpl`s delete
AnnotationAopTest`s after
*/
- 否则会提示以下错误。
Exception in thread "main" java.lang.IllegalStateException: Cannot find current proxy: Set 'exposeProxy' property on Advised to 'true' to make it available, and ensure that AopContext.currentProxy() is invoked in the same thread as the AOP invocation context.
2. 原理
2.1 JdkDynamicAopProxy
AopProxy 体系-
JdkDynamicAopProxy
实现了InvocationHandler
接口。 - JDK 动态代理的代理逻辑封装在
InvocationHandler
接口实现类的invoke()
方法中。
// JdkDynamicAopProxy.java
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
MethodInvocation invocation;
Object oldProxy = null;
boolean setProxyContext = false;
TargetSource targetSource = this.advised.targetSource;
Class<?> targetClass = null;
Object target = null;
try {
......
Object retVal;
// 1. 如果 expose-proxy 属性为 true,则暴露代理对象
if (this.advised.exposeProxy) {
// 向 AopContext 中设置代理对象
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
// 2. 获取适合当前方法的拦截器
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
// 3. 如果拦截器链为空,则直接执行目标方法
if (chain.isEmpty()) {
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
// 通过反射执行目标方法
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
}
else {
// 4. 创建一个方法调用器,并将拦截器链传入其中
invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// 5. 执行拦截器链
retVal = invocation.proceed();
}
// 6. 获取方法返回值类型
Class<?> returnType = method.getReturnType();
if (retVal != null && retVal == target &&
returnType != Object.class && returnType.isInstance(proxy) &&
!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
// 如果方法返回值为 this,即 return this; 则将代理对象 proxy 赋值给 retVal
retVal = proxy;
}
// 如果返回值类型为基础类型,比如 int,long 等,当返回值为 null,抛出异常
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 {
if (target != null && !targetSource.isStatic()) {
targetSource.releaseTarget(target);
}
if (setProxyContext) {
AopContext.setCurrentProxy(oldProxy);
}
}
}
- 执行步骤。
- 步骤 1,检测 expose-proxy 是否为 true,若为 true,则暴露代理对象。
- 步骤 2,获取适合当前方法的拦截器。
- 步骤 3,如果拦截器链为空,则直接通过反射执行目标方法。
-
步骤 4,若拦截器链不为空,则创建方法调用
ReflectiveMethodInvocation
对象。 -
步骤 5,调用
ReflectiveMethodInvocation
对象的proceed()
方法启动拦截器链。 - 步骤 6,处理返回值,并返回该值。
MethodBeforeAdviceInterceptor
- 拦截器是用于对目标方法的调用进行拦截的一种工具。
- 以前置通知拦截器为例。
// MethodBeforeAdviceInterceptor.java
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 {
// 执行前置通知逻辑
this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
// 通过 MethodInvocation 调用下一个拦截器,若所有拦截器均执行完,则调用目标方法
return mi.proceed();
}
}
- 前置通知的逻辑在目标方法执行前被执行。
2.1.1 获取拦截器链
getInterceptorsAndDynamicInterceptionAdvice
// AdvisedSupport.java
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, Class<?> targetClass) {
MethodCacheKey cacheKey = new MethodCacheKey(method);
// 2.1 从缓存中获取
List<Object> cached = this.methodCache.get(cacheKey);
// 缓存未命中,则进行下一步处理
if (cached == null) {
// 2.2 获取所有的拦截器
cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
this, method, targetClass);
// 2.3 存入缓存
this.methodCache.put(cacheKey, cached);
}
return cached;
}
// DefaultAdvisorChainFactory.java
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
Advised config, Method method, Class<?> targetClass) {
List<Object> interceptorList = new ArrayList<Object>(config.getAdvisors().length);
Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
boolean hasIntroductions = hasMatchingIntroductions(config, actualClass);
// registry 为 DefaultAdvisorAdapterRegistry 类型
AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
// 2.2.1 遍历通知器列表
for (Advisor advisor : config.getAdvisors()) {
if (advisor instanceof PointcutAdvisor) {
PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
/*
* 2.2.2 调用 ClassFilter 对 bean 类型进行匹配,无法匹配则说明当前通知器
* 不适合应用在当前 bean 上
*/
if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
// 将 advisor 中的 advice 转成相应的拦截器
MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
// 通过方法匹配器对目标方法进行匹配
if (MethodMatchers.matches(mm, method, actualClass, hasIntroductions)) {
// 若 isRuntime 返回 true,则表明 MethodMatcher 要在运行时做一些检测
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;
// IntroductionAdvisor 类型的通知器,仅需进行类级别的匹配即可
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;
}
// DefaultAdvisorAdapterRegistry.java
public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {
List<MethodInterceptor> interceptors = new ArrayList<MethodInterceptor>(3);
Advice advice = advisor.getAdvice();
/*
* 若 advice 是 MethodInterceptor 类型的,直接添加到 interceptors 中即可。
* 比如 AspectJAfterAdvice 就实现了 MethodInterceptor 接口
*/
if (advice instanceof MethodInterceptor) {
interceptors.add((MethodInterceptor) advice);
}
/*
* 2.2.3 对于 AspectJMethodBeforeAdvice 等类型的通知,由于没有实现 MethodInterceptor
* 接口,所以这里需要通过适配器进行转换
*/
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[interceptors.size()]);
}
- 执行步骤。
- 步骤 2.1,从缓存中获取当前方法的拦截器链。
-
步骤 2.2,若缓存未命中,则调用
getInterceptorsAndDynamicInterceptionAdvice()
获取拦截器链。- 步骤 2.2.1,遍历通知器列表。
-
步骤 2.2.2,对于
PointcutAdvisor
类型的通知器,要调用通知器所持有的切点(Pointcut
)对类和方法进行匹配,匹配成功说明应向当前方法织入通知逻辑。 -
步骤 2.2.3,调用
getInterceptors()
方法对非MethodInterceptor
类型的通知进行转换。
- 步骤 2.3,返回拦截器数组,并在随后存入缓存中。
- 部分通知器没有实现
MethodInterceptor
接口,则需要通过适配器进行转换,例如AspectJMethodBeforeAdvice
。 - 以前置通知适配器
MethodBeforeAdviceAdapter
为例。
// MethodBeforeAdviceAdapter.java
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();
// 创建 MethodBeforeAdviceInterceptor 拦截器
return new MethodBeforeAdviceInterceptor(advice);
}
}
2.1.2 启动拦截器链
ReflectiveMethodInvocation
-
ReflectiveMethodInvocation
贯穿于拦截器链执行的始终,proceed()
方法用于启动启动拦截器链。
// ReflectiveMethodInvocation.java
public class ReflectiveMethodInvocation implements ProxyMethodInvocation {
private int currentInterceptorIndex = -1;
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;
/*
* 调用具有三个参数(3-args)的 matches 方法动态匹配目标方法,
* 两个参数(2-args)的 matches 方法用于静态匹配
*/
if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
// 调用拦截器逻辑
return dm.interceptor.invoke(this);
}
else {
// 如果匹配失败,则忽略当前的拦截器
return proceed();
}
}
else {
// 调用拦截器逻辑,并传递 ReflectiveMethodInvocation 对象
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
}
- 根据 currentInterceptorIndex 确定当前应该执行的拦截器,并在调用拦截器的
invoke()
方法时,将自身作为参数传递给该方法。
AspectJAfterAdvice
- 以后置拦截器为例。
// AspectJAfterAdvice.java
public class AspectJAfterAdvice extends AbstractAspectJAdvice
implements MethodInterceptor, AfterAdvice, Serializable {
public AspectJAfterAdvice(
Method aspectJBeforeAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aif) {
super(aspectJBeforeAdviceMethod, pointcut, aif);
}
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
try {
// 调用 proceed
return mi.proceed();
}
finally {
// 调用后置通知逻辑
invokeAdviceMethod(getJoinPointMatch(), null, null);
}
}
//...
}
- 由于后置通知需要在目标方法返回后执行,所以
AspectJAfterAdvice
先调用mi.proceed()
执行下一个拦截器逻辑,待下一个拦截器返回后,再执行后置通知逻辑。 - 假设目标方法
method()
在执行前,需要执行两个前置通知和一个后置通知,如下图所示。
invokeJoinpoint
- 目标方法的执行过程。
// ReflectiveMethodInvocation.java
protected Object invokeJoinpoint() throws Throwable {
return AopUtils.invokeJoinpointUsingReflection(this.target, this.method, this.arguments);
}
// AopUtils.java
public abstract class AopUtils {
public static Object invokeJoinpointUsingReflection(Object target, Method method, Object[] args)
throws Throwable {
try {
ReflectionUtils.makeAccessible(method);
// 通过反射执行目标方法
return method.invoke(target, args);
}
catch (InvocationTargetException ex) {...}
catch (IllegalArgumentException ex) {...}
catch (IllegalAccessException ex) {...}
}
}
- 目标方法通过反射执行。
网友评论