什么是Spring AOP
Spring AOP 是Spring的核心功能糜芳,因?yàn)镴ava是面向?qū)ο缶幊天酰嗟氖且粋€縱向的,但是遇到存在不存在關(guān)聯(lián)和繼承關(guān)系的橫向關(guān)系時峭竣,就無法處理塘辅。所以這時可以通過切入點(diǎn),將不相關(guān)的邏輯織入到當(dāng)前業(yè)務(wù)代碼中皆撩。
Spring AOP的類型
AOP的代理可分為
- 靜態(tài)代理:編譯時增強(qiáng)扣墩,使用AOP框架提供的命令進(jìn)行編譯,從而在編譯階段生成AOP代理類扛吞。
- 動態(tài)代理:運(yùn)行時增強(qiáng)呻惕,借助JDK動態(tài)代理、CGLIB在內(nèi)存中臨時生成AOP動態(tài)代理類滥比。
Spring 使用了同AspectJ 5 一樣的注解亚脆,但是并沒有 使用AspectJ編譯器和織入器(Weaver),底層依然使用的Spring AOP 來實(shí)現(xiàn)動態(tài)代理生成AOP。
使用Spring AOP 實(shí)現(xiàn)AOP
- 啟用注解模式
<!-- 啟動 @AspectJ 支持 -->
<bean class="org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator"/>
- 定義需要處理的Bean
// 使用 @Aspect 定義一個方面類
@Component
public class Chinese
{
public String sayHello(String name){
System.out.println("-- 正在執(zhí)行 sayHello 方法 --");
// 返回簡單的字符串
return name + " Hello , Spring AOP";
}
// 定義一個 eat() 方法
public void eat(String food){
System.out.println("我正在吃 :"+ food);
}
}
- 切入增加方法:事務(wù)控制盲泛、日志記錄
//定義一個切面
@Aspect
public class AfterReturnTest{
//匹配包下所有的方法執(zhí)行為切入點(diǎn)
@AfterReturning(return="rvt",pointcut="execution(* cn.com.bluemoon.service.impl.*.*")
public void log(Object rvt){
System.out.println("獲取目標(biāo)方法返回值:"+ rvt);
System.out.println("模擬日制記錄...");
}
}
在程序中增加System.out.println(p.getClass());
代碼可以輸入p變量所引用對象的實(shí)現(xiàn)類濒持,再次執(zhí)行將看到代碼產(chǎn)生class org...**.Chinese$EnhancerByCgLib$$290441d2的輸出,這才是p變量所引用的對象的實(shí)現(xiàn)類寺滚,同時也是Spring AOP動態(tài)生成的AOP代理類柑营。
若將上面程序稍作修改:讓業(yè)務(wù)羅積累Chinese類實(shí)現(xiàn)一個任意接口例如:
public interface Person
{
String sayHello(String name);
void eat(String food);
}
接下來Beantest類面向Person接口、而不是Chinese類編程村视。
public class BeanTest
{
public static void main(String[] args)
{
// 創(chuàng)建 Spring 容器
ApplicationContext ctx = new
ClassPathXmlApplicationContext("bean.xml");
Person p = ctx.getBean("chinese" ,Person.class);
System.out.println(p.sayHello("張三"));
p.eat("西瓜");
System.out.println(p.getClass());
}
}
執(zhí)行輸出的將會是class$Proxy7官套,說明AOP代理由JDK動態(tài)代理生成。
Spring AOP 動態(tài)代理原則
類型 | 實(shí)現(xiàn)方式 |
---|---|
CGLIB | 目標(biāo)對象實(shí)現(xiàn)類沒有實(shí)現(xiàn)接口 |
JDK | 目標(biāo)對象實(shí)現(xiàn)了接口 |
Spring AOP 原理
如圖所示: Spring AOP的代理是由Spring的Ioc容易負(fù)責(zé)生成蚁孔、管理奶赔,其依賴關(guān)系也由Ioc容器負(fù)責(zé)管理。因此勒虾,AOP代理可以直接使用容器中的其他bean實(shí)例座位目標(biāo)纺阔,這種關(guān)系可由IOC容器的依賴注入提供。
而我們需要做的是:
- 定義普通業(yè)務(wù)組件
- 定義切入點(diǎn)修然,一個切入點(diǎn)可能橫切多個業(yè)務(wù)組件
- 定義增強(qiáng)處理(After笛钝、Before质况、Around),AOP框架為普通業(yè)務(wù)組件織入(weaver)處理動作玻靡。
CGLIB 生成代理類
CGLIB(Code Generation Library)即代碼生成庫结榄。可以在運(yùn)行時動態(tài)的生成某個類的自雷囤捻。
AroundAdivice.java : 攔截器實(shí)現(xiàn)類
public class AroundAdvice implements MethodInterceptor
{
public Object intercept(Object target, Method method, Object[] args, MethodProxy proxy)throws
java.lang.Throwable {
System.out.println("執(zhí)行目標(biāo)方法之前臼朗,模擬開始事務(wù) ...");
// 執(zhí)行目標(biāo)方法,并保存目標(biāo)方法執(zhí)行后的返回值
Object rvt = proxy.invokeSuper(target, new String[]{"被改變的參數(shù)"});
System.out.println("執(zhí)行目標(biāo)方法之后蝎土,模擬結(jié)束事務(wù) ...");
return rvt + " 新增的內(nèi)容";
}
}
它的作可以在調(diào)用目標(biāo)方法之前视哑、調(diào)用目標(biāo)方法之后織入增強(qiáng)處理。
ChineseProxyFactory.java:工作類
public class ChineseProxyFactory
{
public static Chinese getAuthInstance(){
Enhancer en = new Enhancer();
// 設(shè)置要代理的目標(biāo)類
en.setSuperclass(Chinese.class);
// 設(shè)置要代理的攔截器
en.setCallback(new AroundAdvice());
// 生成代理類的實(shí)例
return (Chinese)en.create();
}
}
通過Enhancer 將Chinese類作為目標(biāo)誊涯,以AroundAdvice對象作為ADvice挡毅,程序?qū)梢粋€Chinese的子類,這個子類就是CGLIB生成的代理類暴构,作為Chinese對象使用跪呈,但它增強(qiáng)了Chinese類的方法。
測試效果:
public class Main
{
public static void main(String[] args)
{
Chinese chin = ChineseProxyFactory.getAuthInstance();
System.out.println(chin.sayHello("孫悟空"));
chin.eat("西瓜");
System.out.println(chin.getClass());
}
}
-- 正在執(zhí)行 sayHello 方法 --
執(zhí)行目標(biāo)方法之后取逾,模擬結(jié)束事務(wù) ...
被改變的參數(shù) Hello , CGLIB 新增的內(nèi)容
執(zhí)行目標(biāo)方法之前耗绿,模擬開始事務(wù) ...
我正在吃 : 被改變的參數(shù)
執(zhí)行目標(biāo)方法之后,模擬結(jié)束事務(wù) ...
class lee.Chinese$$EnhancerByCGLIB$$4bd097d9
小結(jié)
AOP廣泛應(yīng)用于處理一些具有橫切性質(zhì)的系統(tǒng)級服務(wù)砾隅,AOP的出現(xiàn)是對OOP的補(bǔ)充误阻,它是的開發(fā)者能用更優(yōu)雅的方式處理具有橫切性質(zhì)的服務(wù)。