4.Spring的AOP:
①AspectJ
Aspectj是什么?
官方網(wǎng)站的的介紹是這樣的:
<li>a seamless aspect-oriented extension to the Javatm programming language(一種基于Java平臺的面向切面編程的語言)
<li>Java platform compatible(兼容Java平臺慢睡,可以無縫擴(kuò)展)
<li>easy to learn and use(易學(xué)易用)
Aspectj能做什么?
clean modularization of crosscutting concerns, such as error checking and handling, synchronization, context-sensitive behavior, performance optimizations, monitoring and logging, debugging support, and multi-object protocols烫葬。
大意是說:干凈的模塊化橫切關(guān)注點(diǎn)(也就是說單純杨蛋,基本上無侵入),如錯(cuò)誤檢查和處理,同步哩照,上下文敏感的行為蓄髓,性能優(yōu)化贡未,監(jiān)控和記錄汹族,調(diào)試支持讼昆,多目標(biāo)的協(xié)議托享。
還有那些常用的Aop,以及他們的區(qū)別
<li>boss Aop:我基本上沒有用過,所以沒有發(fā)言權(quán).
<li>Spring Aop:Spring自己原生的Aop闰围,只能用一個(gè)詞來形容:難用赃绊。 你需要實(shí)現(xiàn)大量的接口羡榴,繼承大量的類碧查,所以spring aop一度被千夫所指。這于他的無侵入校仑,低耦合完全沖突忠售。不過Spring對開源的優(yōu)秀框架,組建向來是采用兼容迄沫,并入的態(tài)度稻扬。所以,后來的Spring 就提供了Aspectj支持羊瘩,也就是我們后來所說的基于純POJO的Aop泰佳。
區(qū)別:springAop采用的動態(tài)織入,而Aspectj是靜態(tài)織入尘吗。靜態(tài)織入:指在編譯時(shí)期就織入逝她,即:編譯出來的class文件,字節(jié)碼就已經(jīng)被織入了摇予。動態(tài)織入又分靜動兩種汽绢,靜則指織入過程只在第一次調(diào)用時(shí)執(zhí)行吗跋;動則指根據(jù)代碼動態(tài)運(yùn)行的中間狀態(tài)來決定如何操作侧戴,每次調(diào)用Target的時(shí)候都執(zhí)行。有不清楚的同學(xué)跌宛,可以自己補(bǔ)下基礎(chǔ)的代理知識酗宋。
②AOP的基本概念:
AOP框架不與特定的代碼耦合.下面是一些面向切面編程的術(shù)語:
切面(Aspect):業(yè)務(wù)流程運(yùn)行到某個(gè)特定的步驟,也就是一個(gè)應(yīng)用運(yùn)行的關(guān)注點(diǎn)疆拘,關(guān)注點(diǎn)可能橫切多個(gè)對象蜕猫,所以常常也被稱為橫切關(guān)注點(diǎn)。
連接點(diǎn)(Joinpoint):程序執(zhí)行過程明確的點(diǎn)哎迄,如方法的調(diào)用回右、異常的拋出,Spring AOP中連接點(diǎn)總是方法的調(diào)用漱挚。
增強(qiáng)處理(Advice):AOP框架在特定的切入點(diǎn)執(zhí)行的增強(qiáng)處理翔烁,處理有around、before和After等類型旨涝。
切入點(diǎn)(Pointcut):可以插入增強(qiáng)處理的連接點(diǎn)蹬屹,簡而言之,當(dāng)某個(gè)連接點(diǎn)滿足指定要求時(shí),該連接點(diǎn)將被添加增強(qiáng)處理慨默,該連接點(diǎn)也就變成了切入點(diǎn).
例如一下代碼:
pointcut xxxPointcut(){
:execution(void H*.say*());
}
每個(gè)方法的調(diào)用都只是連接點(diǎn)贩耐,但如果該方法屬于H開頭的類,且方法名以say開頭厦取,按照方法的執(zhí)行將變成切入點(diǎn)潮太。
引入:將方法添加到被處理的類中,Spring允許引入新的接口到任何被處理的對象蒜胖。例如:你可以使用一個(gè)引用消别,使任何對象實(shí)現(xiàn)IsModified接口,以此簡化緩存台谢。
目標(biāo)對象:被AOP增強(qiáng)處理的對象寻狂,也被稱為被增強(qiáng)的對象,如果AOP框架是通過運(yùn)行時(shí)代理來實(shí)現(xiàn)的朋沮,那么這個(gè)對象將是一個(gè)被代理的對象蛇券。
AOP代理:AOP框架創(chuàng)建的 對象,簡單的說樊拓,代理就是對目標(biāo)對象的加強(qiáng)纠亚,Spring的AOP代理可以使JDK動態(tài)代理,也可以是CGLIB代理筋夏,前者為實(shí)現(xiàn)接口的目標(biāo)對象的代理后者為不實(shí)現(xiàn)接口的目標(biāo)對象的代理蒂胞。
織入(Weaving):將增強(qiáng)處理增加到目標(biāo)對象中,并創(chuàng)建一個(gè)被增強(qiáng)的對象(AOP代理)的過程就是織入条篷∑妫織入有兩種實(shí)現(xiàn)方式:編譯時(shí)增強(qiáng)(例如AspectJ)和運(yùn)行時(shí)增強(qiáng)(例如CGLIB)
目前Spring只支持將方法調(diào)用作為連接點(diǎn),如果
需要把Field的訪問和更新也作為增強(qiáng)處理的連接點(diǎn)赴叹,可以
使用AspectJ鸿染。
一旦我們掌握了上面的AOP的相關(guān)概念,不難發(fā)現(xiàn)進(jìn)行AOP的編程其實(shí)是一件很簡單的事情乞巧,縱觀AOP編程需要程序員參與的只有
3個(gè)部分:
1.定義普通業(yè)務(wù)組件涨椒;
2.定義切入點(diǎn),一個(gè)切入點(diǎn)橫切多個(gè)業(yè)務(wù)組件绽媒;
3.定義增強(qiáng)處理蚕冬,增強(qiáng)處理就是現(xiàn)在AOP框架為普通業(yè)務(wù)組件織入的處理動作。
③AOP是辕,基于Annotation的零配置方式:
定義Before增強(qiáng)處理:
當(dāng)我們在一個(gè)切面類里使用@Before來標(biāo)注一個(gè)方法時(shí)囤热,該方法將作為Before增強(qiáng)處理,使用@Before標(biāo)注時(shí)通常要指定一個(gè)value屬性值免糕,用來指定一個(gè)切入點(diǎn)表達(dá)式(既可以是一個(gè)已有的切入點(diǎn)赢乓,也可以直接定義切入點(diǎn)表達(dá)式)忧侧,用于指定該增強(qiáng)處理將被織入那些切入點(diǎn)。
注意:("execution( * cn.huaxia.spring.*.*(..))")第一個(gè)星號后面一定要有一個(gè)空格牌芋。
//定義一個(gè)切面
@Aspect
public class BeforeAdviceTest {
// 執(zhí)行cn.huaxia.spring包下的所有方法都做為切入點(diǎn)
@Before("execution(* cn.hb.spring..(..))")
public void authority() {
System.out.println("模擬執(zhí)行權(quán)限檢查...");
}
}
>上面**@Aspect**標(biāo)注BeforeAdviceTest 表明該類是一個(gè)切面類蚓炬,在該切面里定義了一個(gè)authority()方法,這個(gè)方法本來沒有什么特別之處躺屁,但是因?yàn)槭褂?*@Before**來標(biāo)注該方法肯夏,這就將該方法轉(zhuǎn)換成一個(gè)增強(qiáng)處理。上面程序中使用**@Before Annotation**標(biāo)注時(shí)犀暑,直接指定切入點(diǎn)表達(dá)式驯击,指定cn.hb.spring下的所有方法都作為切入點(diǎn)。
>```
@Component
public class Chinese implements Person {
@Override
public String sayHello(String word) {
return word;
}
public void eat(String food) {
System.out.println("我正在吃" + food);
}
}
從上面Chinese類的代碼來看耐亏,他是一個(gè)如此純凈的Java類徊都,他絲毫不知將被誰來進(jìn)行增強(qiáng)處理,也不知道將被怎樣增強(qiáng)處理---正式這種無知才是"AOP"最大的魅力:目標(biāo)類可以被無限增強(qiáng)广辰。
在Spring配置文件中配置自動搜索Bean組件暇矫,配置自動搜索切面類,Spring AOP自動對Bean組件進(jìn)行增強(qiáng)择吊。配置文件如下:
<context:component-scanbase-package="cn.hb.spring">
<context:include-filtertype="annotation"
expression="org.aspectj.lang.annotation.Aspect"/>
</context:component-scan>
<!-- 啟動AspectJ支持-->
<aop:aspectj-autoproxy/>
編寫Junit類:
public class TestBefore {
static ApplicationContext ac =null;
@BeforeClass
public static void setUpBeforeClass() throws Exception {
ac =new ClassPathXmlApplicationContext("beans.xml");
}
@Test
public void testBefore() {
//Chinese c = (Chinese)ac.getBean("chinese");//會出錯(cuò)李根,不能轉(zhuǎn)換為Chinese
Person c = (Person)ac.getBean("chinese");
c.eat("西瓜");
System.out.println(c.sayHello("你好--Before"));
}
}
注意:Chinese c = (Chinese)ac.getBean("chinese");//會出錯(cuò),不能轉(zhuǎn)換為Chinese這里出錯(cuò)的原因是:
在默認(rèn)情況下几睛,如果一個(gè)目標(biāo)對象如果實(shí)現(xiàn)了接口Spring則會選擇JDK動態(tài)代理策略動態(tài)的創(chuàng)建一個(gè)接口實(shí)現(xiàn)類(動態(tài)代理類)來代理目標(biāo)對象房轿,可以通俗的理解這個(gè)動態(tài)代理類是目標(biāo)對象的另外一個(gè)版本,所以這兩者之間在強(qiáng)制轉(zhuǎn)換的時(shí)候會拋出java.lang.ClassCastException所森。而所以在默認(rèn)情況下囱持,如果目標(biāo)對象沒有實(shí)現(xiàn)任何接口,Spring會選擇CGLIB代理必峰, 其生成的動態(tài)代理對象是目標(biāo)類的子類洪唐。