Spring超詳細講解AOP面向切面_第1頁
Spring超詳細講解AOP面向切面_第2頁
Spring超詳細講解AOP面向切面_第3頁
Spring超詳細講解AOP面向切面_第4頁
Spring超詳細講解AOP面向切面_第5頁
已閱讀5頁,還剩10頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

第Spring超詳細講解AOP面向切面目錄簡介AOP底層原理代理概念JDK動態(tài)代理實現Spring中的AOP相關術語AspectJ實現AOP不同通知類型實現相同的切入點抽取增強類優(yōu)先級完全使用注解開發(fā)說明:基于atguigu學習筆記。

簡介

AOP(AspectOrientedProgramming)是一種面向切面的編程思想。不同于面向對象里的繼承思想,當需要為多個不具有繼承關系的對象引人同一個公共行為時,也就是把程序橫向看,尋找切面,插入公共行為。

AOP目的是為了些把影響了多個類的公共行為抽取到一個可重用模塊里,不通過修改源代碼方式,在主干功能里面添加新功能,降低模塊間的耦合度,增強代碼的可操作性和可維護性。

例如,每次用戶請求我們的服務接口,都要進行權限認證,看看是否登錄,就可以在不改變原來接口代碼的情況下,假如認證這個新功能。

SpringAOP底層使用了代理模式。下面具體了解一下。

AOP底層原理

代理概念

所謂代理,也就是讓我們的代理對象持有原對象,在執(zhí)行原對象目標方法的前后可以執(zhí)行額外的增強代碼。

代理對象需要是原對象接口的實現或原對象的子類,這樣就可以在對象引用處直接替換原對象。

代理方式分靜態(tài)代理和動態(tài)代理,區(qū)別在于代理對象生成方式不同

靜態(tài)代理:在編譯期增強,生成可見的代理class,使用代理類替換原有類進行調用。

動態(tài)代理:在運行期增強,內存中動態(tài)生成代理類,使用反射動態(tài)調用原對象方法。

在spring中使用的是JDK、CGLIB動態(tài)代理對象。

JDK動態(tài)代理:必須基于接口,即生成的代理對象是對原對象接口的實現,相當于替換了實現類,面向對象中接口可以替換實現類。

CGLIB動態(tài)代:理基于繼承,即生成的代理對象是原對象的子類,面向對象中子類可以替換父類。

JDK動態(tài)代理實現

使用JDK動態(tài)代理,使用反射包里java.lang.refelft.Proxy類的newProxyInstance方法創(chuàng)建代理對象。

源碼如下

@CallerSensitive

publicstaticObjectnewProxyInstance(ClassLoaderloader,

Class[]interfaces,

InvocationHandlerh){

Objects.requireNonNull(h);

finalClasscaller=System.getSecurityManager()==null

null

:Reflection.getCallerClass();

*Lookuporgeneratethedesignatedproxyclassanditsconstructor.

Constructorcons=getProxyConstructor(caller,loader,interfaces);

returnnewProxyInstance(caller,cons,h);

}

方法有三個參數:

第一參數,類加載器

第二參數,增強方法所在的類,這個類實現的接口,支持多個接口

第三參數,實現這個接口InvocationHandler,創(chuàng)建代理對象,寫增強的部分

下面以JDK動態(tài)代理為例,具體步驟。

1.創(chuàng)建接口,定義方法

publicinterfaceUserDao{

publicintadd(inta,intb);

publicStringupdate(Stringid);

}

2.創(chuàng)建接口實現類,實現方法

publicclassUserDaoImplimplementsUserDao{

@Override

publicintadd(inta,intb){

returna+b;

@Override

publicStringupdate(Stringid){

returnid;

}

3.使用Proxy類創(chuàng)建接口代理對象

publicclassJDKProxy{

publicstaticvoidmain(String[]args){

//創(chuàng)建接口實現類代理對象

Class[]interfaces={UserDao.class};

//Proxy.newProxyInstance(JDKProxy.class.getClassLoader(),interfaces,newInvocationHandler(){

//@Override

//publicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{

//returnnull;

//}

//});

UserDaoImpluserDao=newUserDaoImpl();

UserDaodao=(UserDao)Proxy.newProxyInstance(JDKProxy.class.getClassLoader(),interfaces,newUserDaoProxy(userDao));

intresult=dao.add(1,2);

System.out.println("result:"+result);

//創(chuàng)建代理對象代碼

classUserDaoProxyimplementsInvocationHandler{

//1把創(chuàng)建的是誰的代理對象,把誰傳遞過來

//有參數構造傳遞

privateObjectobj;

publicUserDaoProxy(Objectobj){

this.obj=obj;

//增強的邏輯

@Override

publicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{

//方法之前

System.out.println("方法之前執(zhí)行...."+method.getName()+":傳遞的參數..."+Arrays.toString(args));

//被增強的方法執(zhí)行

Objectres=method.invoke(obj,args);

//方法之后

System.out.println("方法之后執(zhí)行...."+obj);

returnres;

}

Spring中的AOP

相關術語

1.連接點(Joinpoint):類里面可以被增強的方法。

2.切入點:真正被增強的方法。

3.通知:實際增強處理的邏輯。

AOP框架匯總通知分為以下幾種:

前置通知@Before后置通知@AfterReturning環(huán)繞通知@Around異常通知@AfterThrowing最終通知@After

4.切面:把通知應用到切入點的過程,是一個動作。

AspectJ

AspectJ不是Spring組成部分,獨立AOP框架,一般把AspectJ和Spirng框架一起使用,進行AOP操作。

基于AspectJ實現AOP操作可以有兩種方式:基于xml配置文件、基于注解。

要使用AspectJ,首先要引入相關依賴:

dependency

groupIdorg.aspectj/groupId

artifactIdaspectjweaver/artifactId

/dependency

dependency

groupIdorg.springframework/groupId

artifactIdspring-aop/artifactId

/dependency

使用AspectJ時,會尋找切入點,這時候會用到切入點表示,為了知道對哪個類里面的哪個方法進行增強。

語法結構:execution([權限修飾符][返回類型][類全路徑][方法名稱]([參數列表]))

舉例1:對com.example.dao.BookDao類里面的add進行增強

execution(*com.example.dao.BookDao.add(..))

舉例2:對com.example.dao.BookDao類里面的所有的方法進行增強

execution(*com.example.dao.BookDao.*(..))

舉例3:對com.example.dao包里面所有類,類里面所有方法進行增強

execution(*com.example.dao.*.*(..))

實現AOP

1.創(chuàng)建項目,引入依賴

依賴如下:

xmlversion="1.0"encoding="UTF-8"

projectxmlns="/POM/4.0.0"

xmlns:xsi="/2001/XMLSchema-instance"

xsi:schemaLocation="/POM/4.0.0/xsd/maven-4.0.0.xsd"

modelVersion4.0.0/modelVersion

groupIdorg.example/groupId

artifactIdspring-demo02/artifactId

version1.0-SNAPSHOT/version

properties

piler.source11/piler.source

piler.target11/piler.target

/properties

dependencies

dependency

groupIdorg.springframework/groupId

artifactIdspring-core/artifactId

version5.2.6.RELEASE/version

/dependency

dependency

groupIdorg.springframework/groupId

artifactIdspring-beans/artifactId

version5.2.6.RELEASE/version

/dependency

dependency

groupIdorg.springframework/groupId

artifactIdspring-context/artifactId

version5.2.6.RELEASE/version

/dependency

dependency

groupIdorg.aspectj/groupId

artifactIdaspectjweaver/artifactId

version1.8.1/version

/dependency

dependency

groupIdorg.springframework/groupId

artifactIdspring-aop/artifactId

version5.2.6.RELEASE/version

/dependency

/dependencies

/project

2.創(chuàng)建類

創(chuàng)建一個自己的類,寫一個要增強的方法,并使用注解管理bean

packagecom.example;

importorg.springframework.stereotype.Component;

@Component

publicclassUser{

publicvoidadd(){

System.out.println("useraddmethod...");

}

3.創(chuàng)建代理增強類

創(chuàng)建增強類,使用@Aspect注解。

在增強類里面,創(chuàng)建方法,讓不同方法代表不同通知類型,此例創(chuàng)建前置通知使用@Before

packagecom.example;

importorg.aspectj.lang.annotation.Aspect;

importorg.aspectj.lang.annotation.Before;

importorg.springframework.stereotype.Component;

@Component

@Aspect

publicclassUserProxy{

@Before(value="execution(*com.example.User.add())")

publicvoidbefore(){

System.out.println("proxybefore...");

4.xml配置

開啟注解掃描和Aspect生成代理對象

xmlversion="1.0"encoding="UTF-8"

beansxmlns="/schema/beans"

xmlns:xsi="/2001/XMLSchema-instance"

xmlns:context="/schema/context"

xmlns:aop="/schema/aop"

xsi:schemaLocation="/schema/beans

/schema/beans/spring-beans.xsd

/schema/context

/schema/context/spring-context.xsd

/schema/aop

/schema/aop/spring-aop.xsd"

!--開啟注解掃描--

context:component-scanbase-package="com.example"/context:component-scan

!--開啟Aspect生成代理對象--

aop:aspectj-autoproxy/aop:aspectj-autoproxy

/beans

5.測試類

packagecom.example;

importorg.springframework.context.support.ClassPathXmlApplicationContext;

publicclassAopTest{

publicstaticvoidmain(String[]args){

ClassPathXmlApplicationContextap=newClassPathXmlApplicationContext("bean1.xml");

Useruser=ap.getBean("user",User.class);

user.add();

}

結果先輸出proxybefore,再輸出useraddmethod。說明我們的前置通知確實再被增強方法之前執(zhí)行成功。

不同通知類型實現

下面把五種通知都實現看一下順序,修改我們的代理類如下:

packagecom.example;

importorg.aspectj.lang.ProceedingJoinPoint;

importorg.aspectj.lang.annotation.*;

importorg.springframework.stereotype.Component;

@Component

@Aspect

publicclassUserProxy{

*前置通知

@Before(value="execution(*com.example.User.add())")

publicvoidbefore(){

System.out.println("proxybefore...");

*后置通知

@AfterReturning(value="execution(*com.example.User.add())")

publicvoidafterReturning(){

System.out.println("proxyafterReturning...");

*最終通知

@After(value="execution(*com.example.User.add())")

publicvoidafter(){

System.out.println("proxyafter...");

*異常通知

@AfterThrowing(value="execution(*com.example.User.add())")

publicvoidafterThrowing(){

System.out.println("proxyafterThrowing...");

*環(huán)繞通知

@Around(value="execution(*com.example.User.add())")

publicvoidaround(ProceedingJoinPointproceedingJoinPoint)throwsThrowable{

//環(huán)繞之前

System.out.println("proxyaroundbefore...");

proceedingJoinPceed();

//環(huán)繞之后

System.out.println("proxyaroundafter...");

}

執(zhí)行結果如下:

proxyaroundbefore...

proxybefore...

useraddmethod...

proxyaroundafter...

proxyafter...

proxyafterReturning...

相同的切入點抽取

上面代碼可以看到我們的通知value是相同的,這時候可以抽取出來公用,改寫代理類如下代碼如下:

packagecom.example;

importorg.aspectj.lang.ProceedingJoinPoint;

importorg.aspectj.lang.annotation.*;

importorg.springframework.stereotype.Component;

@Component

@Aspect

publicclassUserProxy{

@Pointcut(value="execution(*com.example.User.add())")

publicvoidpointDemo(){}

*前置通知

@Before(value="pointDemo()")

publicvoidbefore(){

System.out.println("proxybefore...");

*后置通知

@AfterReturning(value="pointDemo()")

publicvoidafterReturning(){

System.out.println("proxyafterReturning...");

*最終通知

@After(value="pointDemo()")

publicvoidafter(){

System.out.println("proxyafter...");

*異常通知

@AfterThrowing(value="pointDemo()")

publicvoidafterThrowing(){

System.out.println("proxyafterThrowing...");

*環(huán)繞通知

@Around(value="pointDemo()")

publicvoidaround(ProceedingJoinPointproceedingJoinPoint)throwsThrowable{

//環(huán)繞之前

System.out.println("proxyaroun

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論