說到spring的Transactional,必須先了解spring AOP的原理,先看個簡單的例子
//一個普通的類
public class CyclicA {
public void printlnMethod(){
System.out.println("i am method");
}
}
/**
* 切面,包含了一個Pointcut和多個Advice
*/
@Aspect
public class aspectjTest {
//切點
@Pointcut("execution(* *.printlnMethod(..))")
public void test(){
}
//前置通知
@Before("test()")
public void before(JoinPoint joinPoint){
System.out.println("傳入參數:"+ Arrays.asList(joinPoint.getArgs()));
System.out.println("before");
}
//後置通知
@After("test()")
public void after(){
System.out.println("after");
}
//環繞通知
@Around("test()")
public Object around(ProceedingJoinPoint proceedingJoinPoint){
System.out.println("aroundBefore");
Object o = null;
try {
//執行攔截器方法鏈,如果是最後一個攔截器,這裏執行原對象方法
proceedingJoinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
System.out.println("aroundAfter");
return o;
}
}
<!--spring-core.xml-->
<bean class="spring.aspectjTest"></bean>
<aop:aspectj-autoproxy expose-proxy="true" proxy-target-class="true"/>
//測試方法
public static void main(String[] args){
ClassPathXmlApplicationContext factory = new ClassPathXmlApplicationContext("spring-core.xml");
CyclicA cyclicA = (CyclicA) factory.getBean("cyclicA");
cyclicA.printlnMethod();
}
打印結果
aroundBefore
傳入參數:[]
before
i am method
aroundAfter
after
爲了更好的理解,先介紹一下AOP相關的術語和對應的接口類
1、JoinPoint(連接點)
public interface JoinPoint {
//執行方法鏈,該方法鏈會被攔截器攔截
Object proceed() throws Throwable;
Object getThis();
AccessibleObject getStaticPart();
}
簡單的理解,連接點就是目標對象可以被代理的部分,SpringAOP代理是方法級,所以目標對象的每個方法都可以認爲是一個連接點.
上面只是列舉了JoinPoint接口的常用方法,在前置通知中,可以通過JoinPoint獲取參數進行預處理或者記錄日誌等
//前置通知
@Before("test()")
public void before(JoinPoint joinPoint){
System.out.println("傳入參數:"+ Arrays.asList(joinPoint.getArgs()));
System.out.println("before");
}
2、Pointcut(切點)
public interface Pointcut {
ClassFilter getClassFilter();
MethodMatcher getMethodMatcher();
}
實際運用中,並不是每一個連接點都需要去代理,那麼如何去選擇需要的連接點,這就用到了切點Pointcut,從接口方法的名稱可以看出來,切點主要的工作就是根據制定的規則匹配連接點,例如上面的例子
//切點
@Pointcut("execution(* *.printlnMethod(..))")
public void test(){
}
3、Advice(通知)
public interface Advice {
}
//前置通知
public interface MethodBeforeAdvice extends BeforeAdvice {
void before(Method method, Object[] args, @Nullable Object target) throws Throwable;
}
//後置通知
public interface AfterReturningAdvice extends AfterAdvice {
void afterReturning(@Nullable Object returnValue, Method method, Object[] args, @Nullable Object target) throws Throwable;
}
匹配到指定的連接點,我們就可以做一些邏輯處理了,而這些邏輯處理就是通知,分爲
前置通知(Before advice)- 在目標方法調用前執行通知
後置通知(After advice)- 在目標方法完成後執行通知
返回通知(After returning advice)- 在目標方法執行成功後,調用通知(異常則不調用)
異常通知(After throwing advice)- 在目標方法拋出異常後,執行通知
環繞通知(Around advice)- 在目標方法調用前後均可執行自定義邏輯
4、顧問(Advisor)
可以認爲是Advice的裝配器,讓advice能正確的匹配到目標方法
public interface Advisor {
Advice getAdvice();
}
public interface PointcutAdvisor extends Advisor {
Pointcut getPointcut();
}
5、攔截器(Interceptor)
通常一個目標方法可能存在多個通知乃至多個切面,並且通知的時機也不相同,例如前置通知、後置通知. springAop通過JoinPoint.proceed()執行方法鏈,通過攔截器對方法鏈進行攔截,在此過程中決定advice執行的時機,當所有攔截器執行完畢,將通過反射執行目標方法
public interface Interceptor extends Advice {
}
public interface MethodInterceptor extends Interceptor {
//核心方法,通過該方法執行攔截器的邏輯
Object invoke(MethodInvocation invocation) throws Throwable;
}
//看一個例子,前置通知攔截器
public class MethodBeforeAdviceInterceptor implements MethodInterceptor, BeforeAdvice, Serializable {
//攔截器包裝了前置通知
private final 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();
}
}
6、織入(Weaving)
把切面(aspect)連接到其它的應用程序類型或者對象上,並創建一個被通知(advised)的對象,這樣的行爲叫做織入,上面Advice、Advisor、Interceptor的邏輯就是織入的具體實現
下面通過一個簡單的時序圖來說明下spring AOP的一個大概實現過程
1、通過beanFactory.getBean(String beanName)獲取bean的時候,代理流程就開始了
2、AbstractAutoProxyCreator實現了BeanPostProcessor接口,所以可以通過postProcessAfterInitialization方法在bean初始化之後進行處理
3、獲取匹配目標bean的所有通知Advice 並且包裝成Advisor列表
4、對步驟3的分解,查找所有類型是Advisor的bean, 子類AnnotationAwareAspectJAutoProxyCreator對該方法進行了擴展,可以查找所有加了@Aspect註解的bean,並且將其中的PointCut 和 Advice包裝成Advisor
5、對步驟3的分解,對Advisor列表進行分析,尋找匹配目標bean的Advisor
8、如果有匹配的Advisor,開始針對目標bean創建代理類,這個實現最終交給DefaultAopProxyFactory
9、默認如果目標對象實現了接口,那麼交給JdkDynamicAopProxy實現JDK動態代理
否則交給ObjenesisCglibAopProxy使用Cglib創建代理
至此一個代理類就順利創建了,那麼接下來看看這個代理類執行方法的時候springAop是如何實現織入的
以一開始的例子,假設只注入了前置通知和後置通知,通過JDK動態代理
2、假設是JDK動態代理,執行方法的時候,調用invoke方法進行攔截
3、如果配置了exposeProxy屬性爲true,則將當前代理緩存到ThreadLocal中
4、通過Advisor包裝的advice創建相應的攔截器,加入到攔截器數組中,例如前面提到的MethodBeforeAdviceInterceptor以及後置通知AspectJAfterAdvice,這裏的AspectJAfterAdvice自身就實現了MethodInterceptor,所以他既是Advice又是Interceptor
//在下一個方法執行之前執行前置通知邏輯 MethodBeforeAdviceInterceptor
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
return mi.proceed();
}
//在方法鏈調用結束後再執行後置通知邏輯 AspectJAfterAdvice
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
try {
return mi.proceed();
}
finally {
invokeAdviceMethod(getJoinPointMatch(), null, null);
}
}
7、通過ReflectiveMethodInvocation類調用方法鏈proceed,主要邏輯就是從攔截器數組中按順序取出攔截器執行invoke,並且將自身也當作參數傳入, 由攔截器決定下一個proceed和advice的調用順序,以此正確的織入前置通知、後置通知. 當所有的攔截器全部調用完畢,則通過反射執行目標方法返回
看一下這部分代碼
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) {
// Evaluate dynamic method matcher here: static part will already have
// been evaluated and found to match.
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 {
// 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);
}
}
Spring AOP還是相對比較簡單的,代理的級別也只是方法級,但是對於絕大多數應用已經足夠了