主要想了解一下Spring中如何通過切面去動態在方法前後切入多個切入點去實現的。
需要關注的幾個點:
- 切入點和通知是如何去註冊的?(後續補充)
- 代理過程中是如何植入這些攔截的?
佈置場景
log 日誌切入點實現類
/**
* 日誌切面
*
* @author Liukx
* @create 2017-12-14 11:21
* @email [email protected]
**/
public class LogAspect {
public LogAspect(){
System.out.println("加載==============logAspect");
}
Logger logger = LoggerFactory.getLogger(LogAspect.class);
public void before(JoinPoint point) {
logger.info("=============before==================");
System.out.println("---------------before---------------");
}
public void after(JoinPoint point, Object retValue) {
logger.info("=============after==================");
System.out.println("---------------after---------------");
}
}
配置文件: spring-service.xml
這裏只列舉相關的關鍵配置,其他註解掃描的就沒加了
<!-- log 切面類 -->
<bean id="logAspect" class="com.aop.LogAspect" />
<!-- log 的Aop配置 -->
<aop:config proxy-target-class="true">
<aop:aspect ref="logAspect">
<aop:before method="before" pointcut="execution(* com.service..*.*(..))"></aop:before>
<aop:after-returning pointcut="execution(* com.service..*.*(..))" arg-names="point,retValue" returning="retValue" method="after"/>
</aop:aspect>
</aop:config>
測試用例:
@Autowired
@Qualifier("transactionalService")
private ITransactionalService transactionalService;
/**
* 用於測試事物是否提交
*
* @throws Exception
*/
@Test
public void testTransactionalCommit() throws Exception {
transactionalService.testQuery();
logger.debug("test---------");
}
上面的配置就是說 通知com.service包下面的類將會被LogAspect切入,before方法表示方法執行之前切入,after方法在方法之後之後切入
處理流程
我們先看下代理中做了些啥事?
- 直接debug打到transactionalService.testQuery();看處理的代理是個什麼樣子的類
CglibAopProxy.class : 這是一個Cglib代理的類,具體看他的攔截方法
@Override
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...
// 這裏是獲取要執行的目標對象,就是我們的ITransactionalService實現類
target = getTarget();
if (target != null) {
targetClass = target.getClass();
}
// 這裏會獲得一個攔截鏈,也就是一系列的advised對象,相當於設計模式中的責任鏈模式
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...
// 創造一個方法調用,也就是具體責任鏈的執行類
// 這個方法裏面非常關鍵,這裏執行chain裏面的所有代理方法
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);
}
}
}
CglibMethodInvocation類的結構
new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
CglibMethodInvocation的process()方法其實是委託父類去執行的 也就是ReflectiveMethodInvocation
ReflectiveMethodInvocation類
// 這裏只列舉關鍵方法,因爲上面已經拿到了代理的chain
public class ReflectiveMethodInvocation implements ProxyMethodInvocation, Cloneable {
// 攔截器列表 裏面包裝的都是advised
protected final List<?> interceptorsAndDynamicMethodMatchers;
// 計數器
private int currentInterceptorIndex = -1;
@Override
public Object proceed() throws Throwable {
// We start with an index of -1 and increment early.
// 從這裏如果大小相等,表示interceptorsAndDynamicMethodMatchers裏面的advised已經執行完了.. 就開始執行最終的目標方法
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
// 執行目標方法
return invokeJoinpoint();
}
// 拿到下一個advised
Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
// 判斷是否是InterceptorAndDynamicMethodMatcher這個類型的,這裏不用關注
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.
// 執行這個advised,這裏可能是AfterReturningAdviceInterceptor可能是MethodBeforeAdviceInterceptor
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
}
/**
* Implementation of AOP Alliance MethodInvocation used by this AOP proxy.
*/
// 這個類的目的就是爲了執行最終的方法而設定的,具體的攔截鏈路交給了父類的proceed方法處理,只有當父類的proceed方法執行完畢之後,纔會回調這個類的invokeJoinpoint方法
private static class CglibMethodInvocation extends ReflectiveMethodInvocation {
private final MethodProxy methodProxy;
private final boolean publicMethod;
public CglibMethodInvocation(Object proxy, Object target, Method method, Object[] arguments,
Class<?> targetClass, List<Object> interceptorsAndDynamicMethodMatchers, MethodProxy methodProxy) {
super(proxy, target, method, arguments, targetClass, interceptorsAndDynamicMethodMatchers);
this.methodProxy = methodProxy;
this.publicMethod = Modifier.isPublic(method.getModifiers());
}
/**
* Gives a marginal performance improvement versus using reflection to
* invoke the target when invoking public methods.
*/
@Override
// 最終的執行目標方法
protected Object invokeJoinpoint() throws Throwable {
// 如果執行的目標類的方法是public的,則直接反射調用
if (this.publicMethod) {
return this.methodProxy.invoke(this.target, this.arguments);
}
else {
// 如果執行的目標方法非public的則會交給父類處理
// 父類會調用AopUtils.invokeJoinpointUsingReflection方法
// 其實反射的時候設置了method.setAccessible(true);
return super.invokeJoinpoint();
}
}
}
我們看下具體的advised對象
- AfterReturningAdviceInterceptor - 目標方法之後執行
- MethodBeforeAdviceInterceptor - 目標方法執行
其實這兩個方法實現方式是差不多的,都實現了MethodInterceptor接口,只是切入點執行的順序上做了調整而已
public class AfterReturningAdviceInterceptor implements MethodInterceptor, AfterAdvice, Serializable {
private final AfterReturningAdvice advice;
/**
* Create a new AfterReturningAdviceInterceptor for the given advice.
* @param advice the AfterReturningAdvice to wrap
*/
public AfterReturningAdviceInterceptor(AfterReturningAdvice advice) {
Assert.notNull(advice, "Advice must not be null");
this.advice = advice;
}
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
// 目標方法,也可以說是責任鏈對象 因爲上面是通過this傳遞進來的,相當於又執行上面的ReflectiveMethodInvocation的process()方法.去找下一個攔截器這樣一個循環
Object retVal = mi.proceed();
// 後置切入點
this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
return retVal;
}
}
public class MethodBeforeAdviceInterceptor implements MethodInterceptor, Serializable {
private MethodBeforeAdvice advice;
/**
* Create a new MethodBeforeAdviceInterceptor for the given advice.
* @param advice the MethodBeforeAdvice to wrap
*/
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() );
// 目標方法執行
return mi.proceed();
}
}
梳理一下:
- 通過Cglib代理拿到具體的代理的對象(CglibAopProxy)
- 在Cglib中的攔截(intercept)處理中,先獲取所有切入點的對象(chain)並且構建了一個責任鏈類(CglibMethodInvocation),這個責任鏈類(實際執行過程類:ReflectiveMethodInvocation)包含了所有攔截鏈(advised集合)對象
- 通過這個責任鏈類開始遞歸下面所有的攔截類去執行每個advised方法
- 執行完所有advised鏈條方法之後,會到達這個最終的目標方法CglibMethodInvocation.invokeJoinpoint().調用方法這部分都是通過反射去執行的。
- 如果被代理的方法不是public類型的則會在反射的時候設置setAccessible爲true,破壞了對象封裝屬性強制調用!