日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
圖文詳解Spring AOP,你學(xué)會(huì)了嗎?

本篇主要會(huì)詳解以下六點(diǎn):

創(chuàng)新互聯(lián)是一家專業(yè)提供北林企業(yè)網(wǎng)站建設(shè),專注與成都網(wǎng)站設(shè)計(jì)、成都網(wǎng)站建設(shè)、H5建站、小程序制作等業(yè)務(wù)。10年已為北林眾多企業(yè)、政府機(jī)構(gòu)等服務(wù)。創(chuàng)新互聯(lián)專業(yè)的建站公司優(yōu)惠進(jìn)行中。

1.AOP的定義

2.AOP的作用

3.AOP的應(yīng)用場(chǎng)景

4.Spring AOP的術(shù)語

  • AOP核心概念
  • Spring AOP 通知分類
  • Spring AOP 織入時(shí)期

5.Spring AOP三種使用方式

  1. 方式1:使用Spring自帶的AOP
  2. 方式2:使用Aspectj實(shí)現(xiàn)切面(普通POJO的實(shí)現(xiàn)方式)
  3. 方式3:使用Aspectj實(shí)現(xiàn)切面(基于注解的實(shí)現(xiàn)方式)

6.Spring AOP的實(shí)現(xiàn)原理

  1. JDK動(dòng)態(tài)代理
  2. JDK動(dòng)態(tài)代理優(yōu)缺
  3. CGLib代理
  4. CGLIB組成結(jié)構(gòu)

AOP的定義

AOP (Aspect Orient Programming),直譯過來就是 面向切面編程,AOP 是一種編程思想,是面向?qū)ο缶幊?OOP)的一種補(bǔ)充。

面向切面編程,實(shí)現(xiàn)在不修改源代碼的情況下給程序動(dòng)態(tài)統(tǒng)一添加額外功能的一種技術(shù),如下圖所示:

AOP可以攔截指定的方法并且對(duì)方法增強(qiáng),而且無需侵入到業(yè)務(wù)代碼中,使業(yè)務(wù)與非業(yè)務(wù)處理邏輯分離,比如Spring的事務(wù),通過事務(wù)的注解配置,Spring會(huì)自動(dòng)在業(yè)務(wù)方法中開啟、提交業(yè)務(wù),并且在業(yè)務(wù)處理失敗時(shí),執(zhí)行相應(yīng)的回滾策略。

AOP的作用

AOP 采取橫向抽取機(jī)制(動(dòng)態(tài)代理),取代了傳統(tǒng)縱向繼承機(jī)制的重復(fù)性代碼,其應(yīng)用主要體現(xiàn)在事務(wù)處理、日志管理、權(quán)限控制、異常處理等方面。

主要作用是分離功能性需求和非功能性需求,使開發(fā)人員可以集中處理某一個(gè)關(guān)注點(diǎn)或者橫切邏輯,減少對(duì)業(yè)務(wù)代碼的侵入,增強(qiáng)代碼的可讀性和可維護(hù)性。

簡(jiǎn)單的說,AOP 的作用就是保證開發(fā)者在不修改源代碼的前提下,為系統(tǒng)中的業(yè)務(wù)組件添加某種通用功能。

AOP的應(yīng)用場(chǎng)景

比如典型的AOP的應(yīng)用場(chǎng)景:

  • 日志記錄
  • 事務(wù)管理
  • 權(quán)限驗(yàn)證
  • 性能監(jiān)測(cè)

AOP可以攔截指定的方法,并且對(duì)方法增強(qiáng),比如:事務(wù)、日志、權(quán)限、性能監(jiān)測(cè)等增強(qiáng),而且無需侵入到業(yè)務(wù)代碼中,使業(yè)務(wù)與非業(yè)務(wù)處理邏輯分離。

Spring AOP的術(shù)語

在深入學(xué)習(xí)SpringAOP 之前,讓我們先對(duì)AOP的幾個(gè)基本術(shù)語有個(gè)大致的概念。

AOP核心概念

Spring AOP 通知分類

Spring AOP 織入時(shí)期

Spring AOP三種使用方式

AOP編程其實(shí)是很簡(jiǎn)單的事情,縱觀AOP編程,程序員只需要參與三個(gè)部分:

1.定義普通業(yè)務(wù)組件。

2.定義切入點(diǎn),一個(gè)切入點(diǎn)可能橫切多個(gè)業(yè)務(wù)組件。

3.定義增強(qiáng)處理,增強(qiáng)處理就是在AOP框架為普通業(yè)務(wù)組件織入的處理動(dòng)作。

所以進(jìn)行AOP編程的關(guān)鍵就是定義切入點(diǎn)和定義增強(qiáng)處理,一旦定義了合適的切入點(diǎn)和增強(qiáng)處理,AOP框架將自動(dòng)生成AOP代理,即:代理對(duì)象的方法=增強(qiáng)處理+被代理對(duì)象的方法。

方式1:使用Spring自帶的AOP

public class LogAdvice implements MethodBeforeAdvice, AfterReturningAdvice,MethodInterceptor {

@Override

public void before(Method method, Object[] objects, Object target) throws Throwable {

//前置通知

}



@Override

public void afterReturning(Object result, Method method, Object[] objects, Object target) throws Throwable {

//后置通知

}

@Override

public Object invoke(MethodInvocation methodInvocation) throws Throwable {

//環(huán)繞通知

//目標(biāo)方法之前執(zhí)行

methodInvocation.proceed(); //目標(biāo)方法



//目標(biāo)方法之后執(zhí)行

return resultVal;

}

}

配置通知時(shí)需實(shí)現(xiàn)org.springframework.aop包下的一些接口:

  • 前置通知:MethodBeforeAdvice。
  • 后置通知:AfterReturningAdvice。
  • 環(huán)繞通知:MethodInterceptor。
  • 異常通知:ThrowsAdvice。

創(chuàng)建被代理對(duì)象



通知(Advice)



切入點(diǎn)(Pointcut):通過正則表達(dá)式描述指定切入點(diǎn)(某些 指定方法)







Advisor(高級(jí)通知) = Advice(通知) + Pointcut(切入點(diǎn))













創(chuàng)建自動(dòng)代理









*ServiceBean

*TaskBean













logAdviceBean

performanceAdvisorBean





方式2:使用Aspectj實(shí)現(xiàn)切面(普通POJO的實(shí)現(xiàn)方式)

導(dǎo)入Aspectj相關(guān)依賴。





org.aspectj

aspectjrt

1.9.5









org.aspectj

aspectjweaver

1.9.5

通知方法名隨便起,沒有限制。

public class LogAspectj {

//前置通知

public void beforeAdvice(JoinPoint joinPoint){

System.out.println("========== 【Aspectj前置通知】 ==========");

}



//后置通知:方法正常執(zhí)行后,有返回值,執(zhí)行該后置通知:如果該方法執(zhí)行出現(xiàn)異常,則不執(zhí)行該后置通知

public void afterReturningAdvice(JoinPoint joinPoint,Object returnVal){

System.out.println("========== 【Aspectj后置通知】 ==========");

}

public void afterAdvice(JoinPoint joinPoint){

System.out.println("========== 【Aspectj后置通知】 ==========");

}



//環(huán)繞通知

public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {

System.out.println("##########【環(huán)繞通知中的前置通知】##########");

Object returnVale = joinPoint.proceed();

System.out.println("##########【環(huán)繞通知中的后置通知】##########");

return returnVale;

}



/**

* 異常通知:方法出現(xiàn)異常時(shí),執(zhí)行該通知

*/

public void throwAdvice(JoinPoint joinPoint, Exception ex){

System.out.println("出現(xiàn)異常:" + ex.getMessage());

}



}

使用Aspectj實(shí)現(xiàn)切面,使用Spring AOP進(jìn)行配置。


























expression="execution(* com.apesource.service.impl.*.create*(..))"/>
















method="afterReturningAdvice" pointcut-ref="pointcut"/>





















方式3:使用Aspectj實(shí)現(xiàn)切面(基于注解的實(shí)現(xiàn)方式)

//聲明當(dāng)前類為Aspect切面,并交給Spring容器管理

@Component

@Aspect

public class LogAnnotationAspectj {

private final static String EXPRESSION =

"execution(* com.apesource.service.impl.*.create*(..))";



//前置通知

@Before(EXPRESSION)

public void beforeAdvice(JoinPoint joinPoint){

System.out.println("========== 【Aspectj前置通知】 ==========");

}





//后置通知:方法正常執(zhí)行后,有返回值,執(zhí)行該后置通知:如果該方法執(zhí)行出現(xiàn)異常,則不執(zhí)行該后置通知

@AfterReturning(value = EXPRESSION,returning = "returnVal")

public void afterReturningAdvice(JoinPoint joinPoint,Object returnVal){

System.out.println("========== 【Aspectj后置通知】 ==========");

}



//后置通知

@After(EXPRESSION)

public void afterAdvice(JoinPoint joinPoint){

System.out.println("========== 【Aspectj后置通知】 ==========");

}



//環(huán)繞通知

@Around(EXPRESSION)

public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {

System.out.println("##########【環(huán)繞通知中的前置通知】##########");

Object returnVale = joinPoint.proceed();

System.out.println("##########【環(huán)繞通知中的后置通知】##########");

return returnVale;

}



// 異常通知:方法出現(xiàn)異常時(shí),執(zhí)行該通知

@AfterThrowing(value = EXPRESSION,throwing = "ex")

public void throwAdvice(JoinPoint joinPoint, Exception ex){

System.out.println("********** 【Aspectj異常通知】執(zhí)行開始 **********");

System.out.println("出現(xiàn)異常:" + ex.getMessage());

System.out.println("********** 【Aspectj異常通知】執(zhí)行結(jié)束 **********");

}



}








Spring AOP的實(shí)現(xiàn)原理

Spring的AOP實(shí)現(xiàn)原理其實(shí)很簡(jiǎn)單,就是通過動(dòng)態(tài)代理實(shí)現(xiàn)的。

Spring AOP 采用了兩種混合的實(shí)現(xiàn)方式:JDK 動(dòng)態(tài)代理和 CGLib 動(dòng)態(tài)代理。

  • JDK動(dòng)態(tài)代理:Spring AOP的首選方法。每當(dāng)目標(biāo)對(duì)象實(shí)現(xiàn)一個(gè)接口時(shí),就會(huì)使用JDK動(dòng)態(tài)代理。目標(biāo)對(duì)象必須實(shí)現(xiàn)接口
  • CGLIB代理:如果目標(biāo)對(duì)象沒有實(shí)現(xiàn)接口,則可以使用CGLIB代理。

JDK動(dòng)態(tài)代理

Spring默認(rèn)使用JDK的動(dòng)態(tài)代理實(shí)現(xiàn)AOP,類如果實(shí)現(xiàn)了接口,Spring就會(huì)使用這種方式實(shí)現(xiàn)動(dòng)態(tài)代理。

JDK實(shí)現(xiàn)動(dòng)態(tài)代理需要兩個(gè)組件,首先第一個(gè)就是InvocationHandler接口。

我們?cè)谑褂肑DK的動(dòng)態(tài)代理時(shí),需要編寫一個(gè)類,去實(shí)現(xiàn)這個(gè)接口,然后重寫invoke方法,這個(gè)方法其實(shí)就是我們提供的代理方法。

如下源碼所示:

/**

* 動(dòng)態(tài)代理

*

* @author mikechen

*/

public class JdkProxySubject implements InvocationHandler {



private Subject subject;



public JdkProxySubject(Subject subject) {

this.subject = subject;

}



@Override

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {



System.out.println("before 前置通知");

Object result = null;



try {

result = method.invoke(subject, args);

}catch (Exception ex) {

System.out.println("ex: " + ex.getMessage());

throw ex;

}finally {

System.out.println("after 后置通知");

}

return result;

}

}

然后JDK動(dòng)態(tài)代理需要使用的第二個(gè)組件就是Proxy這個(gè)類,我們可以通過這個(gè)類的newProxyInstance方法,返回一個(gè)代理對(duì)象。

生成的代理類實(shí)現(xiàn)了原來那個(gè)類的所有接口,并對(duì)接口的方法進(jìn)行了代理,我們通過代理對(duì)象調(diào)用這些方法時(shí),底層將通過反射,調(diào)用我們實(shí)現(xiàn)的invoke方法。

public class Main { public static void main(String[] args) { 

//獲取InvocationHandler對(duì)象 在構(gòu)造方法中注入目標(biāo)對(duì)象

InvocationHandler handler = new JdkProxySubject(new RealSubject());



//獲取代理類對(duì)象

Subject proxySubject = (Subject)Proxy.newProxyInstance(Main.class.getClassLoader(), new Class[]{Subject.class}, handler);



//調(diào)用目標(biāo)方法

proxySubject.request(); proxySubject.response();



}

運(yùn)行結(jié)果:

before 前置通知

執(zhí)行目標(biāo)對(duì)象的request方法......

after 后置通知

before 前置通知

執(zhí)行目標(biāo)對(duì)象的response方法......

after 后置通知


JDK動(dòng)態(tài)代理優(yōu)缺

優(yōu)點(diǎn)

JDK動(dòng)態(tài)代理是JDK原生的,不需要任何依賴即可使用;

通過反射機(jī)制生成代理類的速度要比CGLib操作字節(jié)碼生成代理類的速度更快;

缺點(diǎn)

如果要使用JDK動(dòng)態(tài)代理,被代理的類必須實(shí)現(xiàn)了接口,否則無法代理;

JDK動(dòng)態(tài)代理無法為沒有在接口中定義的方法實(shí)現(xiàn)代理,假設(shè)我們有一個(gè)實(shí)現(xiàn)了接口的類,我們?yōu)樗囊粋€(gè)不屬于接口中的方法配置了切面,Spring仍然會(huì)使用JDK的動(dòng)態(tài)代理,但是由于配置了切面的方法不屬于接口,為這個(gè)方法配置的切面將不會(huì)被織入。

JDK動(dòng)態(tài)代理執(zhí)行代理方法時(shí),需要通過反射機(jī)制進(jìn)行回調(diào),此時(shí)方法執(zhí)行的效率比較低;

CGLib代理

CGLIB組成結(jié)構(gòu)

Cglib是一個(gè)強(qiáng)大的、高性能的代碼生成包,它廣泛被許多AOP框架使用,為他們提供方法的攔截,如下圖所示Cglib與Spring等應(yīng)用的關(guān)系:

  • 最底層的是字節(jié)碼Bytecode,字節(jié)碼是Java為了保證“一次編譯、到處運(yùn)行”而產(chǎn)生的一種虛擬指令格式,例如iload_0、iconst_1、if_icmpne、dup等。
  • 位于字節(jié)碼之上的是ASM,這是一種直接操作字節(jié)碼的框架,應(yīng)用ASM需要對(duì)Java字節(jié)碼、Class結(jié)構(gòu)比較熟悉。
  • 位于ASM之上的是CGLIB、Groovy、BeanShell,后兩種并不是Java體系中的內(nèi)容而是腳本語言,它們通過ASM框架生成字節(jié)碼變相執(zhí)行Java代碼,這說明在JVM中執(zhí)行程序并不一定非要寫Java代碼—-只要你能生成Java字節(jié)碼,JVM并不關(guān)心字節(jié)碼的來源,當(dāng)然通過Java代碼生成的JVM字節(jié)碼是通過編譯器直接生成的,算是最“正統(tǒng)”的JVM字節(jié)碼。
  • 位于CGLIB、Groovy、BeanShell之上的就是Hibernate、Spring AOP這些框架了,這一層大家都比較熟悉
  • 最上層的是Applications,即具體應(yīng)用,一般都是一個(gè)Web項(xiàng)目或者本地跑一個(gè)程序。

所以,Cglib的實(shí)現(xiàn)是在字節(jié)碼的基礎(chǔ)上的,并且使用了開源的ASM讀取字節(jié)碼,對(duì)類實(shí)現(xiàn)增強(qiáng)功能的。


新聞名稱:圖文詳解Spring AOP,你學(xué)會(huì)了嗎?
標(biāo)題路徑:http://www.dlmjj.cn/article/djehiee.html