一文吃透 Spring 中的 AOP 編程
AOP 概述
AOP 為 Aspect Oriented Programming 的縮寫(xiě),是面向切面編程,通過(guò)預(yù)編譯方式和運(yùn)行期動(dòng)態(tài)代理實(shí)現(xiàn)程序功能的統(tǒng)一維護(hù)的一種技術(shù)。AOP 是 OOP 的延續(xù),是軟件開(kāi)發(fā)中的一個(gè)熱點(diǎn),也是 Spring 框架中的一個(gè)重要內(nèi)容,是函數(shù)式編程的一種衍生范型
AOP 可以分離業(yè)務(wù)代碼和關(guān)注點(diǎn)代碼(重復(fù)代碼),在執(zhí)行業(yè)務(wù)代碼時(shí),動(dòng)態(tài)的注入關(guān)注點(diǎn)代碼。切面就是關(guān)注點(diǎn)代碼形成的類。Spring AOP 中的動(dòng)態(tài)代理主要有兩種方式,JDK 動(dòng)態(tài)代理和 CGLIB 動(dòng)態(tài)代理。JDK 動(dòng)態(tài)代理通過(guò)反射來(lái)接收被代理的類,并且要求被代理的類必須實(shí)現(xiàn)一個(gè)接口
AOP 實(shí)現(xiàn)分類
AOP 要達(dá)到的效果是,保證開(kāi)發(fā)者不修改源代碼的前提下,去為系統(tǒng)中的業(yè)務(wù)組件添加某種通用功能,按照 AOP 框架修改源代碼的時(shí)機(jī),可以將其分為兩類:
- 靜態(tài) AOP 實(shí)現(xiàn), AOP 框架在編譯階段對(duì)程序源代碼進(jìn)行修改,生成了靜態(tài)的 AOP 代理類(生成的 *.class 文件已經(jīng)被改掉了,需要使用特定的編譯器),比如 AspectJ。
- 動(dòng)態(tài) AOP 實(shí)現(xiàn), AOP 框架在運(yùn)行階段對(duì)動(dòng)態(tài)生成代理對(duì)象(在內(nèi)存中以 JDK 動(dòng)態(tài)代理,或 CGlib 動(dòng)態(tài)地生成 AOP 代理類),如 SpringAOP
AOP 術(shù)語(yǔ)
- 連接點(diǎn)(JointPoint):與切入點(diǎn)匹配的執(zhí)行點(diǎn),在程序整個(gè)執(zhí)行流程中,可以織入切面的位置,方法的執(zhí)行前后,異常拋出的位置
- 切點(diǎn)(PointCut):在程序執(zhí)行流程中,真正織入切面的方法。
- 切面(ASPECT):切點(diǎn)+通知就是切面
- 通知(Advice):切面必須要完成的工作,也叫增強(qiáng)。即,它是類中的一個(gè)方法,方法中編寫(xiě)織入的代碼。
- 前置通知 后置通知
- 環(huán)繞通知 異常通知
- 最終通知
- 目標(biāo)對(duì)象(Target):被織入通知的對(duì)象
- 代理對(duì)象(Proxy):目標(biāo)對(duì)象被織入通知之后創(chuàng)建的新對(duì)象
通知的類型
Spring 方面可以使用下面提到的五種通知工作:
通知 | 描述 |
---|---|
前置通知 | 在一個(gè)方法執(zhí)行之前,執(zhí)行通知。 |
最終通知 | 在一個(gè)方法執(zhí)行之后,不考慮其結(jié)果,執(zhí)行通知。 |
后置通知 | 在一個(gè)方法執(zhí)行之后,只有在方法成功完成時(shí),才能執(zhí)行通知。 |
異常通知 | 在一個(gè)方法執(zhí)行之后,只有在方法退出拋出異常時(shí),才能執(zhí)行通知。 |
環(huán)繞通知 | 在一個(gè)方法調(diào)用之前和之后,執(zhí)行通知。 |
基于 Aspectj 實(shí)現(xiàn) AOP 操作
基于 Aspectj 實(shí)現(xiàn) AOP 操作,經(jīng)歷了下面三個(gè)版本的變化,注解版是我們最常用的。
切入點(diǎn)表達(dá)式
作用:聲明對(duì)哪個(gè)類中的哪個(gè)方法進(jìn)行增強(qiáng)
語(yǔ)法:
execution([訪問(wèn)權(quán)限修飾符] 返回值 [ 類的全路徑名 ] 方法名 (參數(shù)列表)[異常])
- 訪問(wèn)權(quán)限修飾符:
- 可選項(xiàng),不寫(xiě)就是四個(gè)權(quán)限都包含
- 寫(xiě)public就表示只包括公開(kāi)的方法
- 返回值類型
- 必填項(xiàng) * 標(biāo)識(shí)返回值任意
- 全限定類名
- 可選項(xiàng),兩個(gè)點(diǎn) … 表示當(dāng)前包以及子包下的所有類,省略表示所有類
- 方法名
- 必填項(xiàng) * 表示所有的方法 set*表示所有的set方法
- 形參列表
- 必填項(xiàng)
- ()表示沒(méi)有參數(shù)的方法
- (…)參數(shù)類型和參數(shù)個(gè)數(shù)隨意的方法
- (*)只有一個(gè)參數(shù)的方法
- (*,String) 第一個(gè)參數(shù)類型隨意,第二個(gè)參數(shù)String類型
- 異常信息
- 可選項(xiàng) 省略時(shí)標(biāo)識(shí)任何異常信息
第一版:基于xml(aop:config)配置文件
使用 Spring AOP 接口方式實(shí)現(xiàn) AOP, 可以通過(guò)自定義通知來(lái)供 Spring AOP 識(shí)別對(duì)應(yīng)實(shí)現(xiàn)的接口是:
- 前置通知:MethodBeforeAdvice
- 返回通知:AfterReturningAdvice
- 異常通知:ThrowsAdvice
- 環(huán)繞通知:MethodInterceptor
實(shí)現(xiàn)步驟:
1、定義業(yè)務(wù)接口
/** * 使用接口方式實(shí)現(xiàn)AOP, 默認(rèn)通過(guò)JDK的動(dòng)態(tài)代理來(lái)實(shí)現(xiàn). 非接口方式, 使用的是cglib實(shí)現(xiàn)動(dòng)態(tài)代理 */ package cn.kgc.spring05.entity; public interface Teacher { String teachOnLine(String course); String teachOffLine(Integer course); }
2、定義實(shí)現(xiàn)類
package cn.kgc.spring05.entity; public class TeacherA implements Teacher{ @Override public String teachOnLine(String course) { System.out.println("TeacherA開(kāi)始"+course+"課程線上教學(xué)"); if(course.equals("java")){ throw new RuntimeException("入門(mén)到放棄!"); } return course+"課程線上教學(xué)"; } @Override public String teachOffLine(Integer course) { System.out.println("TeacherA開(kāi)始"+course+"課程線下教學(xué)"); return course+"課程線下教學(xué)"; } }
3、實(shí)現(xiàn)接口定義通知類
前置通知類
package cn.kgc.spring05.advice; import org.springframework.aop.MethodBeforeAdvice; import java.lang.reflect.Method; //前置通知 public class MyMethodBeforeAdvice implements MethodBeforeAdvice { @Override public void before(Method method, Object[] objects, Object o) throws Throwable { System.out.println("------------spring aop 前置通知------------"); } }
后置通知類
package cn.kgc.spring05.advice; import org.springframework.aop.AfterReturningAdvice; import java.lang.reflect.Method; public class MyAfterReturnAdvice implements AfterReturningAdvice { @Override public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable { System.out.println("------------spring aop 后置通知------------"); } }
4、XML 配置方式
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"> <!--托管通知--> <bean id="after" class="cn.kgc.spring05.advice.MyAfterReturnAdvice"></bean> <bean id="before" class="cn.kgc.spring05.advice.MyMethodBeforeAdvice"></bean> <bean id="teacherA" class="cn.kgc.spring05.entity.TeacherA"></bean> <!--AOP的配置--> <aop:config> <!--切點(diǎn)表達(dá)式--> <aop:pointcut id="pt" expression="execution(* *(..))"/> <aop:advisor advice-ref="before" pointcut-ref="pt"></aop:advisor> <aop:advisor advice-ref="after" pointcut-ref="pt"></aop:advisor> </aop:config> </beans>
5、測(cè)試
package cn.kgc.spring05; import cn.kgc.spring05.entity.Teacher; import junit.framework.TestCase; import junit.framework.TestSuite; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; /** * Unit test for simple App. */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:spring-config.xml") public class AppTest { @Autowired Teacher teacher; @Test public void teachOnLine() { System.out.println(teacher.getClass()); String s = teacher.teachOnLine("java"); System.out.println("s = " + s); } }
6、運(yùn)行結(jié)果
第二版:基于xml(aop:aspect)配置文件
基于 xml(aop:config) 配置文件的方式,增加幾個(gè)通知,就會(huì)創(chuàng)建幾個(gè)通知類,那我們能否將這些通知類寫(xiě)在一個(gè)類中呢?下面就讓我來(lái)帶你們找到解決之法!
配置 AspectJ 標(biāo)簽解讀表
實(shí)現(xiàn)步驟:
1、定義業(yè)務(wù)接口
/** * 使用接口方式實(shí)現(xiàn)AOP, 默認(rèn)通過(guò)JDK的動(dòng)態(tài)代理來(lái)實(shí)現(xiàn). 非接口方式, 使用的是cglib實(shí)現(xiàn)動(dòng)態(tài)代理 */ package cn.kgc.spring05.entity; public interface Teacher { String teachOnLine(String course); String teachOffLine(Integer course); }
2、定義實(shí)現(xiàn)類
package cn.kgc.spring05.entity; public class TeacherA implements Teacher{ @Override public String teachOnLine(String course) { System.out.println("TeacherA開(kāi)始"+course+"課程線上教學(xué)"); if(course.equals("java")){ throw new RuntimeException("入門(mén)到放棄!"); } return course+"課程線上教學(xué)"; } @Override public String teachOffLine(Integer course) { System.out.println("TeacherA開(kāi)始"+course+"課程線下教學(xué)"); return course+"課程線下教學(xué)"; } }
3、實(shí)現(xiàn)接口定義通知類
package cn.kgc.spring05.advice; public class AllAdvice { public void before(){System.out.println("------------前置通知--------------");} public void afterReturning(){System.out.println("------------后置通知--------------");} public void afterThrowing(){System.out.println("------------異常通知--------------");} public void after(){System.out.println("------------最終通知--------------");} public void around(){System.out.println("------------環(huán)繞通知--------------");} }
4、XML 配置方式
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"> <!--托管通知--> <bean id="all" class="cn.kgc.spring05.advice.AllAdvice"></bean> <bean id="teacherA" class="cn.kgc.spring05.entity.TeacherA"></bean> <!--AOP的配置--> <aop:config> <!--切點(diǎn)表達(dá)式--> <aop:pointcut id="pt" expression="execution(* *(String))"/> <aop:aspect ref="all"> <aop:before method="before" pointcut-ref="pt"></aop:before> <aop:after-returning method="afterReturning" pointcut-ref="pt"></aop:after-returning> <aop:after-throwing method="afterThrowing" pointcut-ref="pt"></aop:after-throwing> <aop:after method="after" pointcut-ref="pt"></aop:after> <!-- <aop:around method="around" pointcut-ref="pt"></aop:around>--> </aop:aspect> </aop:config> </beans>
5、測(cè)試
package cn.kgc.spring05.advice; import cn.kgc.spring05.entity.Teacher; import junit.framework.TestCase; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:spring-config2.xml") public class AllAdviceTest{ @Autowired Teacher teacher; @Test public void test01() { System.out.println(teacher.getClass()); String s = teacher.teachOnLine("java"); System.out.println("s = " + s); } }
6、運(yùn)行結(jié)果
第三版:基于注解實(shí)現(xiàn)通知
- 常用 “通知” 注解如下:
@Aspect 注解將此類定義為切面。
@Before 注解用于將目標(biāo)方法配置為前置增強(qiáng)(前置通知)。
@AfterReturning 注解用于將目標(biāo)方法配置為后置增強(qiáng)(后置通知)。
@Around 定義環(huán)繞增強(qiáng)(環(huán)繞通知)
@AfterThrowing 配置異常通知
@After 也是后置通知,與 @AfterReturning 很相似,區(qū)別在于 @AfterReturning 在方法執(zhí)行完畢后進(jìn)行返回,可以有返回值。@After 沒(méi)有返回值。
實(shí)現(xiàn)步驟:
1、定義業(yè)務(wù)接口
/** * 使用接口方式實(shí)現(xiàn)AOP, 默認(rèn)通過(guò)JDK的動(dòng)態(tài)代理來(lái)實(shí)現(xiàn). 非接口方式, 使用的是cglib實(shí)現(xiàn)動(dòng)態(tài)代理 */ package cn.kgc.spring05.entity; public interface Teacher { String teachOnLine(String course); String teachOffLine(Integer course); }
2、定義注解
package cn.kgc.spring05.advice; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface AnnoAdvice { }
3、定義實(shí)現(xiàn)類
package cn.kgc.spring05.entity; import cn.kgc.spring05.advice.AnnoAdvice; import org.springframework.stereotype.Component; @Component public class TeacherA implements Teacher{ @Override @AnnoAdvice public String teachOnLine(String course) { System.out.println("TeacherA開(kāi)始"+course+"課程線上教學(xué)"); if(course.equals("java")){ throw new RuntimeException("入門(mén)到放棄!"); } return course+"課程線上教學(xué)"; } @Override @AnnoAdvice public String teachOffLine(Integer course) { System.out.println("TeacherA開(kāi)始"+course+"課程線下教學(xué)"); return course+"課程線下教學(xué)"; } }
4、實(shí)現(xiàn)接口定義切面類
首先在類上面添加 @Aspect 注解,將該類轉(zhuǎn)化為切面類,再在類中的各個(gè)方法上面使用各自的 “通知” 注解即可實(shí)現(xiàn)。
package cn.kgc.spring05.advice; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.springframework.stereotype.Component; @Component @Aspect public class AllAdvice { @Pointcut("@annotation(AnnoAdvice)") public void point(){} @Before("point()") public void before(){System.out.println("------------前置通知--------------");} @AfterReturning("point()") public void afterReturning(){System.out.println("------------后置通知--------------");} @AfterThrowing("point()") public void afterThrowing(){System.out.println("------------異常通知--------------");} @After("point()") public void after(){System.out.println("------------最終通知--------------");} @Around("point()") public Object aroundAdvice(ProceedingJoinPoint joinPoint){ Object proceed = null; try { System.out.println("----------spring aop 環(huán)繞 前通知-----------"); proceed = joinPoint.proceed(); System.out.println("----------spring aop 環(huán)繞 后通知-----------"); } catch (Throwable throwable) { throwable.printStackTrace(); System.out.println("----------spring aop 環(huán)繞 異常通知-----------"); }finally { System.out.println("----------spring aop 環(huán)繞 最終通知-----------"); } return proceed; } }
5、XML 配置方式
開(kāi)啟包掃描和aspectj自動(dòng)代理
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"> <!--開(kāi)啟包掃描--> <context:component-scan base-package="cn.kgc.spring05"></context:component-scan> <!--開(kāi)啟aspectj自動(dòng)代理--> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> </beans>
6、測(cè)試
package cn.kgc.spring05.advice; import cn.kgc.spring05.entity.Teacher; import junit.framework.TestCase; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:spring-config3.xml") public class AllAdviceTest{ @Autowired Teacher teacher; @Test public void test01() { System.out.println(teacher.getClass()); String s = teacher.teachOnLine("html"); System.out.println("s = " + s); } }
7、運(yùn)行效果
到此這篇關(guān)于一文吃透 Spring 中的 AOP 編程的文章就介紹到這了,更多相關(guān)Java Spring AOP編程內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java運(yùn)算符的常見(jiàn)問(wèn)題與用法小結(jié)
這篇文章主要介紹了Java運(yùn)算符,結(jié)合實(shí)例形式總結(jié)分析了Java各種常見(jiàn)運(yùn)算符,包括算術(shù)運(yùn)算符、比較運(yùn)算符、邏輯運(yùn)算符、位運(yùn)算符等相關(guān)功能、原理與使用技巧,需要的朋友可以參考下2020-04-04解決SpringBoot引用別的模塊無(wú)法注入的問(wèn)題
這篇文章主要介紹了解決SpringBoot引用別的模塊無(wú)法注入的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-02-02Java中Date類和Calendar類的常用實(shí)例小結(jié)
這篇文章主要介紹了Java中Date類和Calendar類的常用實(shí)例小結(jié),是Java入門(mén)學(xué)習(xí)中的基礎(chǔ)知識(shí)的運(yùn)用,需要的朋友可以參考下2015-08-08Java中@ConfigurationProperties實(shí)現(xiàn)自定義配置綁定問(wèn)題分析
這篇文章主要介紹了@ConfigurationProperties實(shí)現(xiàn)自定義配置綁定問(wèn)題,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-08-08Javafx簡(jiǎn)單實(shí)現(xiàn)【我的電腦資源管理器】效果
這篇文章主要介紹了Javafx簡(jiǎn)單實(shí)現(xiàn)【我的電腦資源管理器】效果,涉及Javafx操作系統(tǒng)文件模擬資源管理器的實(shí)現(xiàn)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-09-09