上一章節(jié)分析了靜態(tài)代理和JDK、CGLIB動態(tài)代理,接下來我們還要回顧一下AOP的一些相關(guān)知識,以方便為接下來分析AOP的源碼做好準(zhǔn)備。
1.先來回顧一下AOP中的一些術(shù)語。
- 連接點(Jointpoint):表示需要在程序中插入橫切關(guān)注點的擴展點,連接點可能是類初始化、方法執(zhí)行、方法調(diào)用、字段調(diào)用或處理異常等等,Spring只支持方法執(zhí)行連接點,在AOP中表示為“在哪里干”;
- 切入點(Pointcut):選擇一組相關(guān)連接點的模式,即可以認(rèn)為連接點的集合,Spring支持perl5正則表達式和AspectJ切入點模式,Spring默認(rèn)使用AspectJ語法,在AOP中表示為“在哪里干的集合”;
- 增強(Advice):很多地方理解為通知,但是理解為增強更為準(zhǔn)確,增強表示在連接點上執(zhí)行的行為,增強提供了在AOP中需要在切入點所選擇的連接點處進行擴展現(xiàn)有行為的手段;包括前置增強(before advice)、后置增強(after advice)、環(huán)繞增強(around advice),在Spring中通過代理模式實現(xiàn)AOP,并通過攔截器模式以環(huán)繞連接點的攔截器鏈織入增強;在AOP中表示為“干什么”;
- 方面/切面(Aspect):橫切關(guān)注點的模塊化,比如上邊提到的日志組件??梢哉J(rèn)為是增強、引入和切入點的組合;在Spring中可以使用Schema和@AspectJ方式進行組織實現(xiàn);在AOP中表示為“在哪干和干什么集合”;
- 引介增強(inter-type declaration):引介增強是一個比較特殊的增強,它不是在目標(biāo)方法周圍織入增強,而是為目標(biāo)類創(chuàng)建新的方法或?qū)傩裕砸樵鰪姷倪B接點是類級別的,而非方法級別的,Spring允許引入新的接口(必須對應(yīng)一個實現(xiàn))到所有被代理對象(目標(biāo)對象), 在AOP中表示為“干什么(引入什么)”;
- 目標(biāo)對象(Target Object):需要被織入橫切關(guān)注點的對象,即該對象是切入點選擇的對象,需要被增強的對象,從而也可稱為“被增強對象”;由于Spring AOP 通過代理模式實現(xiàn),從而這個對象永遠(yuǎn)是被代理對象,在AOP中表示為“對誰干”;
- AOP代理(AOP Proxy):AOP框架使用代理模式創(chuàng)建的對象,從而實現(xiàn)在連接點處插入增強(即應(yīng)用切面),就是通過代理來對目標(biāo)對象應(yīng)用切面。在Spring中,AOP代理可以用JDK動態(tài)代理或CGLIB代理實現(xiàn),而通過攔截器模型應(yīng)用切面。
- 織入(Weaving):織入是一個過程,是將切面應(yīng)用到目標(biāo)對象從而創(chuàng)建出AOP代理對象的過程,織入可以在編譯期、類裝載期、運行期進行。
上面所提到的概念,比較抽象,也比較枯燥,而且在實際的開發(fā)中使用切面編程只占很少一部分,但是如果大家對上面的概念無所了解的話,那么對接下來的源碼分析必然是一頭霧水,下面我們再通過幾個例子,讓大家對上面的概念有所了解,為源碼分析做好準(zhǔn)備。
2. 增強方式簡介
也有很多人將增強理解為通知,但是理解為增強會更加準(zhǔn)確,因為它表示在連接點上執(zhí)行的行為,這個行為是目標(biāo)類類所沒有的,是為目標(biāo)類增加了額外的方法或者其他的一些功能,所以理解為增強比通知更加貼切,當(dāng)然如果有的人已經(jīng)習(xí)慣了通知這個概念的話也無所謂,只要知道本文將的增強即是通知即可。
增強的類型有前置增強、后置返回增強、異常增強、環(huán)繞增強、引介增強、后置最終增強等。下面我們通過實例的例子來介紹一下,為了讓大家能夠更為深刻的理解SpringAop,我們不會一開始就講解基于@AspectJ或者基于Schema配置文件的方式,而是從最基礎(chǔ)開始講解,畢竟AOP在實際開發(fā)中并不占太大的比重,相信很多人并沒有深刻的理解。
接下來我們先講解基于Advice接口以編碼方式實現(xiàn)的增強。
3.MethodBeforeAdvice前置增強
- 目標(biāo)接口和實現(xiàn)類
package com.lyc.cn.v2.day04.advisor;
/**
* @author: LiYanChao
* @create: 2018-11-01 21:48
*/
public interface Animal {
void sayHello(String name,int age);
void sayException(String name,int age);
}
package com.lyc.cn.v2.day04.advisor;
/**
* @author: LiYanChao
* @create: 2018-11-01 21:48
*/
public class Dog implements Animal {
@Override
public void sayHello(String name, int age) {
System.out.println("==名字:" + name + " 年齡:" + age);
}
@Override
public void sayException(String name, int age) {
System.out.println("==名字:" + name + " 年齡:" + age);
System.out.println("==拋出異常:" + 1 / 0);
}
}
- 前置增強
package com.lyc.cn.v2.day04.advisor;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
/**
* 前置增強
* @author: LiYanChao
* @create: 2018-11-01 21:50
*/
public class MyMethodBeforeAdvice implements MethodBeforeAdvice {
/**
* Callback before a given method is invoked.
* @param method method being invoked
* @param args arguments to the method
* @param target target of the method invocation. May be {@code null}.
* @throws Throwable if this object wishes to abort the call.
* Any exception thrown will be returned to the caller if it's
* allowed by the method signature. Otherwise the exception
* will be wrapped as a runtime exception.
*/
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("==前置增強");
System.out.println("==方法名:" + method.getName());
if (null != args && args.length > 0) {
for (int i = 0; i < args.length; i++) {
System.out.println("==第" + (i + 1) + "參數(shù):" + args[i]);
}
}
System.out.println("==目標(biāo)類信息:" + target.toString());
}
}
4.AfterReturningAdvice后置增強
package com.lyc.cn.v2.day04.advisor;
import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;
/**
* 后置增強
* @author: LiYanChao
* @create: 2018-11-01 22:09
*/
public class MyAfterReturningAdvice implements AfterReturningAdvice {
/**
* Callback after a given method successfully returned.
* @param returnValue the value returned by the method, if any
* @param method method being invoked
* @param args arguments to the method
* @param target target of the method invocation. May be {@code null}.
* @throws Throwable if this object wishes to abort the call.
* Any exception thrown will be returned to the caller if it's
* allowed by the method signature. Otherwise the exception
* will be wrapped as a runtime exception.
*/
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("==后置增強");
System.out.println("==方法名:" + method.getName());
if (null != args && args.length > 0) {
for (int i = 0; i < args.length; i++) {
System.out.println("==第" + (i + 1) + "參數(shù):" + args[i]);
}
}
System.out.println("==目標(biāo)類信息:" + target.toString());
}
}
5.ThrowsAdvice異常增強
package com.lyc.cn.v2.day04.advisor;
import org.springframework.aop.ThrowsAdvice;
import java.lang.reflect.Method;
/**
* @author: LiYanChao
* @create: 2018-11-01 22:17
*/
public class MyThrowsAdvice implements ThrowsAdvice {
/**
* 異常增強
*/
public void afterThrowing(Method method, Object[] args, Object target, Exception ex) {
System.out.println("==異常增強");
System.out.println("==方法名:" + method.getName());
if (null != args && args.length > 0) {
for (int i = 0; i < args.length; i++) {
System.out.println("==第" + (i + 1) + "參數(shù):" + args[i]);
}
}
System.out.println("==目標(biāo)類信息:" + target.toString());
System.out.println("==異常信息:" + ex.toString());
}
}
6.MethodInterceptor環(huán)繞增強
package com.lyc.cn.v2.day04.advisor;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
/**
* 環(huán)繞增強
* @author: LiYanChao
* @create: 2018-11-01 22:29
*/
public class MyMethodInterceptor implements MethodInterceptor {
/**
* 環(huán)繞增強 這里的方法參數(shù)與之前的前置增強、后置增強明顯不同,只有一個MethodInvocation類型的參數(shù)
* Implement this method to perform extra treatments before and
* after the invocation. Polite implementations would certainly
* like to invoke {@link Joinpoint#proceed()}.
* @param invocation the method invocation joinpoint
* @return the result of the call to {@link Joinpoint#proceed()};
* might be intercepted by the interceptor
* @throws Throwable if the interceptors or the target object
* throws an exception
*/
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("==環(huán)繞增強開始");
System.out.println("==方法名:" + invocation.getMethod().getName());
Object[] args = invocation.getArguments();
if (null != args && args.length > 0) {
for (int i = 0; i < args.length; i++) {
System.out.println("==第" + (i + 1) + "參數(shù):" + args[i]);
}
}
Object proceed = invocation.proceed();
System.out.println("==環(huán)繞增強結(jié)束");
return proceed;
}
}
7.測試及結(jié)果
@Test
public void test5() {
// 前置增強
// 1、實例化bean和增強
Animal dog = new Dog();
MyMethodBeforeAdvice advice = new MyMethodBeforeAdvice();
// 2、創(chuàng)建ProxyFactory并設(shè)置代理目標(biāo)和增強
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setTarget(dog);
proxyFactory.addAdvice(advice);
// 3、生成代理實例
Animal proxyDog = (Animal) proxyFactory.getProxy();
proxyDog.sayHello("二哈", 3);
}
@Test
public void test6() {
// 后置增強
// 1、實例化bean和增強
Animal dog = new Dog();
MyAfterReturningAdvice advice = new MyAfterReturningAdvice();
// 2、創(chuàng)建ProxyFactory并設(shè)置代理目標(biāo)和增強
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setTarget(dog);
proxyFactory.addAdvice(advice);
// 3、生成代理實例
Animal proxyDog = (Animal) proxyFactory.getProxy();
proxyDog.sayHello("二哈", 3);
}
@Test
public void test7() {
// 異常增強
// 1、實例化bean和增強
Animal dog = new Dog();
MyThrowsAdvice advice = new MyThrowsAdvice();
// 2、創(chuàng)建ProxyFactory并設(shè)置代理目標(biāo)和增強
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setTarget(dog);
proxyFactory.addAdvice(advice);
// 3、生成代理實例
Animal proxyDog = (Animal) proxyFactory.getProxy();
proxyDog.sayException("二哈", 3);
}
@Test
public void test8() {
// 環(huán)繞增強
// 1、實例化bean和增強
Animal dog = new Dog();
MyMethodInterceptor advice = new MyMethodInterceptor();
// 2、創(chuàng)建ProxyFactory并設(shè)置代理目標(biāo)和增強
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setTarget(dog);
proxyFactory.addAdvice(advice);
// 3、生成代理實例
Animal proxyDog = (Animal) proxyFactory.getProxy();
proxyDog.sayHello("二哈", 3);
}
信息: Creating CGLIB proxy: target source is SingletonTargetSource for target object [com.lyc.cn.v2.day04.advisor.Dog@2280cdac]
==前置增強
==方法名:sayHello
==第1參數(shù):二哈
==第2參數(shù):3
==目標(biāo)類信息:com.lyc.cn.v2.day04.advisor.Dog@2280cdac
==名字:二哈 年齡:3
十一月 03, 2018 10:32:46 下午 org.springframework.aop.framework.CglibAopProxy getProxy
信息: Creating CGLIB proxy: target source is SingletonTargetSource for target object [com.lyc.cn.v2.day04.advisor.Dog@6b2fad11]
==名字:二哈 年齡:3
==后置增強
==方法名:sayHello
==第1參數(shù):二哈
==第2參數(shù):3
==目標(biāo)類信息:com.lyc.cn.v2.day04.advisor.Dog@6b2fad11
十一月 03, 2018 10:32:46 下午 org.springframework.aop.framework.CglibAopProxy getProxy
信息: Creating CGLIB proxy: target source is SingletonTargetSource for target object [com.lyc.cn.v2.day04.advisor.Dog@38082d64]
==名字:二哈 年齡:3
==異常增強
==方法名:sayException
==第1參數(shù):二哈
==第2參數(shù):3
==目標(biāo)類信息:com.lyc.cn.v2.day04.advisor.Dog@38082d64
==異常信息:java.lang.ArithmeticException: / by zero
十一月 03, 2018 10:32:46 下午 org.springframework.aop.framework.CglibAopProxy getProxy
信息: Creating CGLIB proxy: target source is SingletonTargetSource for target object [com.lyc.cn.v2.day04.advisor.Dog@3f2a3a5]
java.lang.ArithmeticException: / by zero
at com.lyc.cn.v2.day04.advisor.Dog.sayException(Dog.java:17)
at com.lyc.cn.v2.day04.advisor.Dog$$FastClassBySpringCGLIB$$a974b1ec.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
==環(huán)繞增強開始
==方法名:sayHello
==第1參數(shù):二哈
==第2參數(shù):3
==名字:二哈 年齡:3
==環(huán)繞增強結(jié)束
8.總結(jié)
以上簡單介紹了前置增強、后置增強、環(huán)繞增強、異常增強等以編碼實現(xiàn)的方式,當(dāng)然以上實現(xiàn)也可以通過配置文件的方式實現(xiàn)。本篇所介紹的知識點比較簡單,但是理解增強的概念是AOP的基礎(chǔ),其實本篇的各種增強方式用上一篇講解的動態(tài)代理是完全可以實現(xiàn)的,因為本篇使用的代理工廠ProxyFactory內(nèi)部使用的是CglibAopProxy或JdkDynamicAopProxy,其實其本質(zhì)還是JDK或CGLIB動態(tài)代理,在接下來的章節(jié)中會詳細(xì)講解CglibAopProxy或JdkDynamicAopProxy的實現(xiàn),本篇的分析就到這里了。