第一節(jié):AOP簡(jiǎn)介

第二節(jié):AOP的作用
對(duì)程序進(jìn)行增強(qiáng),不修改源碼的情況下AOP可以進(jìn)行權(quán)限校驗(yàn)、日志記錄、性能監(jiān)控、事務(wù)管理。
第三節(jié):Spring底層的AOP實(shí)現(xiàn)原理
動(dòng)態(tài)代理:
- JDK動(dòng)態(tài)代理:只能對(duì)實(shí)現(xiàn)了接口的類產(chǎn)生代理。
- Cglib動(dòng)態(tài)代理(類似于Javassist第三方代理技術(shù)):對(duì)沒(méi)有實(shí)現(xiàn)接口的類產(chǎn)生代理對(duì)象,生成子類對(duì)象。
第四節(jié):Spring中AOP的開(kāi)發(fā)(AspectJ的XML方式)
AOP思想最早是由AOP聯(lián)盟組織提出的,Spring是使用該思想最好的框架之一。
Spring的AOP有自己的實(shí)現(xiàn)方式(非常繁瑣)。AspectJ是一個(gè)AOP的框架。Spring引入AspectJ作為自身AOP的開(kāi)發(fā)。
Spring擁有兩套AOP開(kāi)發(fā)方式:
- Spring傳統(tǒng)方式(棄用);
- Spring基于AspectJ的AOP的開(kāi)發(fā);
4.1 AOP的7個(gè)專業(yè)術(shù)語(yǔ)
- 4.1.1 增強(qiáng)(Advice)
增強(qiáng)就很好理解了,AOP(切面編程)是用來(lái)給某一類特殊的連接點(diǎn),添加一些特殊的功能,那么我們添加的功能也就是增強(qiáng)啦~
比如:添加日志、管理事務(wù)。
不過(guò)增強(qiáng)不僅僅包含需要增加的功能代碼而已,它還包含了方位信息。
那什么是方位信息呢?
方位信息就是相對(duì)于方法的位置信息,如:方法前、方法后、方法環(huán)繞
為什么要方位信息呢?切點(diǎn)不是確定了需要增強(qiáng)的位置了嗎?
切點(diǎn)定位的是“在什么類的什么方法上”,也就是說(shuō),切點(diǎn)只是定位到了方法本身(也叫執(zhí)行點(diǎn),特殊的連接點(diǎn)),但是我們?cè)鰪?qiáng)的內(nèi)容是放在該方法的前面呢、后面呢?還是前后都要呢?這些切點(diǎn)卻沒(méi)有告訴我們,那么我們?cè)撊绾未_定具體位置呢?
所以,我們才需要用到方位信息,進(jìn)一步的定位到具體的增強(qiáng)代碼放置的位置。
咦?增強(qiáng)即包含了【功能】又包含了【方位】,那我是不是不用切點(diǎn)就可以匹配哪些方法,并添加功能了呢?
恩,確實(shí)如此,因?yàn)橥ㄟ^(guò)方位信息,雖然只是簡(jiǎn)單的描述了【功能】需要放在方法前、后、還是前后都要等信息,但是我們還是可以通過(guò)方位定位到位置。只不過(guò),是匹配到所有類的所有方法!因?yàn)榉轿恢皇钦f(shuō)明在方法前還是方法后,并沒(méi)有要求是哪些類?哪些方法?
— So,我們可以直接使用增強(qiáng)來(lái)生成一個(gè)切面,而不需要切點(diǎn),但這并不怎么推薦,因?yàn)樗瞧ヅ渌蟹椒ǖ摹K?,我們才需要用切點(diǎn)來(lái)進(jìn)一步確認(rèn)位置。 - 4.4.2 切點(diǎn)(Pointcut)
一個(gè)項(xiàng)目中有很多的類,一個(gè)類有很多個(gè)連接點(diǎn),當(dāng)我們需要在某個(gè)方法前插入一段增強(qiáng)(advice)代碼時(shí),我們就需要使用切點(diǎn)信息來(lái)確定,要在哪些連接點(diǎn)上添加增強(qiáng)。
那么切點(diǎn)是什么?
如果把連接點(diǎn)當(dāng)做數(shù)據(jù)庫(kù)中的記錄,那么切點(diǎn)就是查找該記錄的查詢條件。
所以,一般我們要實(shí)現(xiàn)一個(gè)切點(diǎn)時(shí),那么我們需要判斷哪些連接點(diǎn)是符合我們的條件的,如:方法名是否匹配、類是否是某個(gè)類、以及子類等。 - 4.4.3 連接點(diǎn)(Joinpoint)
連接點(diǎn)就是程序執(zhí)行的某個(gè)特定的位置,如:類開(kāi)始初始化前、類初始化后、類的某個(gè)方法調(diào)用前、類的某個(gè)方法調(diào)用后、方法拋出異常后等。Spring 只支持類的方法前、后、拋出異常后的連接點(diǎn)。 - 4.4.4 切面(Aspect)
切面由切點(diǎn)和增強(qiáng)(或引介)組成,或者只由增強(qiáng)(或引介)實(shí)現(xiàn)。 - 4.4.5 目標(biāo)對(duì)象(Target)
目標(biāo)對(duì)象就是我們需要對(duì)它進(jìn)行增強(qiáng)的業(yè)務(wù)類~
如果沒(méi)有AOP,那么該業(yè)務(wù)類就得自己實(shí)現(xiàn)需要的功能。 - 4.4.6 AOP代理(AOP proxy)
一個(gè)類被AOP織入后生成出了一個(gè)結(jié)果類,它是融合了原類和增強(qiáng)邏輯的代理類。 -
4.4.7 織入(Weaving)
織入就是將增強(qiáng)添加到目標(biāo)類具體連接點(diǎn)上的過(guò)程。
編譯期織入,這要求使用特殊java編譯器
類裝載期織入,這要求使用特殊的類裝載器
動(dòng)態(tài)代理織入,在運(yùn)行期為目標(biāo)類添加增強(qiáng)生成子類的方式
Spring采用的是動(dòng)態(tài)代理織入,而AspectJ采用編譯期織入和類裝載期織入。
第五節(jié):Spring的AOP開(kāi)發(fā)入門(mén)
5.1 Spring測(cè)試類的使用
測(cè)試類中整合Spring框架的注入功能, 需引入Jar包“spring-test-4.2.4.RELEASE.jar”從而減少測(cè)試類中對(duì)Spring工廠對(duì)象的創(chuàng)建
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import javax.annotation.Resource;
//結(jié)合Spring對(duì)測(cè)試類進(jìn)行屬性注入
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class UserDaoServiceTest {
@Resource(name = "userDaoService")
private UserDaoService userDaoService;
@Test
public void demo01(){
userDaoService.save();
}
}
5.2 AOP切面編程的準(zhǔn)備工作:
-
引入jar包(使用AspectJ的需求前需引入所需的jar包)
- 編寫(xiě)基礎(chǔ)測(cè)試類以接口實(shí)現(xiàn)
public class ProductImpl implements Product {
@Override
public void save() {
System.out.println("保存商品");
}
@Override
public void update() {
System.out.println("更新商品");
}
@Override
public void search() {
System.out.println("查詢商品");
}
@Override
public void del() {
System.out.println("刪除商品");
}
}
- 通過(guò)配置文件,將該類交給Spring管理
<!--配置目標(biāo)對(duì)象:被增強(qiáng)的對(duì)象-->
<bean id="product" class="com.seapp.spring02.ProductImpl"/>
- 編寫(xiě)切面類,并建立增強(qiáng)方法
public class MyAspect {
public void logInfo(){
System.out.println("實(shí)現(xiàn)日志增強(qiáng)");
}
}
- 將切面類交給Spring管理,并對(duì)目標(biāo)對(duì)象配置增強(qiáng)
<!--將切面類交給Spring去管理-->
<bean id="myAspect" class="com.seapp.spring02.MyAspect"/>
<!--AOP配置-->
<aop:config>
<!--expression:是一個(gè)表達(dá)式,配置那些類的那些方法需要增強(qiáng)-->
<aop:pointcut id="pointcut01" expression="execution(* com.seapp.spring02.ProductImpl.save(..))"/>
<!--配置切面-->
<aop:aspect ref="myAspect">
<aop:before method="logInfo" pointcut-ref="pointcut01"/>
</aop:aspect>
</aop:config>
第六節(jié):Spring的通知類型
- 前置通知:
在目標(biāo)方法執(zhí)行之前進(jìn)行操作。 - 后置通知:
在目標(biāo)方法執(zhí)行之后進(jìn)行操作。 - 環(huán)繞通知:
在目標(biāo)方法執(zhí)行之前和之后進(jìn)行操作。 - 異常拋出通知:
在程序出現(xiàn)異常的時(shí)候進(jìn)行的操作。 - 最終通知:
程序執(zhí)行完成后進(jìn)行操作。無(wú)論有無(wú)異常都會(huì)執(zhí)行。 - 引介通知:(不常用)
//6.1 切面類的定義以及增強(qiáng)
public class MyAspect {
/**
* 前置通知
*/
public void logInfo(){
System.out.println("實(shí)現(xiàn)日志增強(qiáng)");
}
/**
* 后置通知
* @param result
*/
public void writeLog(Object result){
System.out.println("log日志獲取" + result);
}
/**
* 環(huán)繞通知
*/
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("環(huán)繞前通知");
Object proceed = joinPoint.proceed();
System.out.println("環(huán)繞后通知");
return proceed;
}
/**
* 異常拋出通知
*/
public void afterThrowing(Throwable ex){
System.out.println("異常拋出信息" + ex );
}
/**
* 最終通知
*/
public void after(){
System.out.println("最終通知信息");
}
}
//接口實(shí)現(xiàn)類:
public class ProductImpl implements Product {
@Override
public void save() {
System.out.println("保存商品");
}
@Override
public String update() {
System.out.println("更新商品");
return "更新成功" ;
}
@Override
public void search() {
System.out.println("查詢商品");
}
@Override
public void del() {
System.out.println("刪除商品");
int i = 10/0;
}
}
//6.3 核心,在applicationContext.xml中關(guān)于切面的配置
<!--配置目標(biāo)對(duì)象:被增強(qiáng)的對(duì)象-->
<bean id="product" class="com.seapp.spring02.ProductImpl"/>
<!--將切面類交給Spring去管理-->
<bean id="myAspect" class="com.seapp.spring02.MyAspect"/>
<!--AOP配置-->
<aop:config>
<!--expression:是一個(gè)表達(dá)式,配置那些類的那些方法需要增強(qiáng)-->
<aop:pointcut id="pointcut01" expression="execution(* com.seapp.spring02.ProductImpl.save(..))"/>
<aop:pointcut id="pointcut02" expression="execution(* com.seapp.spring02.ProductImpl.update(..))"/>
<aop:pointcut id="pointcut03" expression="execution(* com.seapp.spring02.ProductImpl.search(..))"/>
<aop:pointcut id="pointcut04" expression="execution(* com.seapp.spring02.ProductImpl.del(..))"/>
<!--配置切面-->
<aop:aspect ref="myAspect">
<!--前置通知配置-->
<aop:before method="logInfo" pointcut-ref="pointcut01"/>
<!--后置通知配置-->
<aop:after-returning method="writeLog" pointcut-ref="pointcut02" returning="result"/>
<!--環(huán)繞通知-->
<aop:around method="around" pointcut-ref="pointcut03"/>
<!--異常拋出信息-->
<aop:after-throwing method="afterThrowing" pointcut-ref="pointcut04" throwing="ex"/>
<!--最終通知信息-->
<aop:after method="after" pointcut-ref="pointcut04"/>
</aop:aspect>
</aop:config>
第七節(jié):Spring切入點(diǎn)表達(dá)式
- 基于execution的函數(shù)完成的,語(yǔ)法定義如下:
[訪問(wèn)修飾符] 方法返回值 包名.類名.方法名(參數(shù))
public void com.seapp.product.save(..)
//其中除以為其余字段都可以使用*號(hào)代替
* com.seapp.product.save(..)
* *.*.product.*(..)
* *.*.*.*save(..)//以
...
第八節(jié):Spring使用AspectJ進(jìn)行AOP注解開(kāi)發(fā)方式
@Aspect//定義切面類的注解
/**通知類型:
*@Before :前置通知
*@AfterReturing :后置通知
*@Around :環(huán)繞通知
*@After:最終通知
*@AfterThrowing:異常拋出通知
**/
@poincut//定義切入點(diǎn)注解
切面類:
@Aspect
public class MyAspectAnno {
/**
* 前置通知
*/
@Before("MyAspectAnno.pointcut1()")
public void logInfo(){
System.out.println("實(shí)現(xiàn)日志增強(qiáng)");
}
/**
* 后置通知
*/
@AfterReturning("MyAspectAnno.pointcut4()" )
public void writeLog(){
System.out.println("log日志獲取");
}
/**
* 環(huán)繞通知
*/
@Around("MyAspectAnno.pointcut2()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("環(huán)繞前通知");
Object proceed = joinPoint.proceed();
System.out.println("環(huán)繞后通知");
return proceed;
}
/**
* 異常拋出通知
*/
@AfterThrowing("MyAspectAnno.pointcut3()")
public void afterThrowing(){
System.out.println("異常拋出信息");
}
/**
* 最終通知
*/
@After("MyAspectAnno.pointcut4()")
public void after(){
System.out.println("最終通知信息");
}
/**
* 定義切入點(diǎn)
*/
@Pointcut("execution(* com.seapp.spring02.ProductDaoImpl.search(..))")
private void pointcut1(){
}
@Pointcut("execution(* com.seapp.spring02.ProductDaoImpl.save(..))")
private void pointcut2(){
}
@Pointcut("execution(* com.seapp.spring02.ProductDaoImpl.del(..))")
private void pointcut3(){
}
@Pointcut("execution(* com.seapp.spring02.ProductDaoImpl.update(..))")
private void pointcut4(){
}
}
Spring中applicationContext.xml中的配置項(xiàng):
<!--使用注解實(shí)現(xiàn)Spring的AspectJ AOP開(kāi)發(fā)-->
<!--配置目標(biāo)類-->
<bean id="productDao" class="com.seapp.spring02.ProductDaoImpl"/>
<!--配置切面類-->
<bean id="MyAspectAnno" class="com.seapp.spring02.MyAspectAnno"/>
<!--開(kāi)啟AOP注解的自動(dòng)代理-->
<aop:aspectj-autoproxy/>

