亚洲精品中文免费|亚洲日韩中文字幕制服|久久精品亚洲免费|一本之道久久免费

      
      

            <dl id="hur0q"><div id="hur0q"></div></dl>

                手把手教會「Spring」從面向?qū)ο笤俚矫嫦蚯忻妫苯右锥?/h1>

                手把手教會「Spring」從面向?qū)ο笤俚矫嫦蚯忻?,直接易? src=

                Object object = new Object();

                世間萬物的本質(zhì)都可看作類的對象,面向?qū)ο?OOP)的模式讓程序易維護、易復(fù)用、易擴展,而面向切面(AOP)則是面向?qū)ο蟮难a充,讓對象的功能更加強大

                對比前面的日志框架技術(shù)二者非常相似,他的特點就是在不影響業(yè)務(wù)的前提下將程序的運行情況輸出到控制臺,總體來看是起一個輔助的作用,所謂的AOP亦是如此——是在不改原有代碼的前提下對其進行增強

                一.OOP&AOP

                OOP將組件視為對象,AOP將對象的切面視為“對象”

                OOP&AOP讓程序通過極其簡單的方式變得更加全面、強大

                AOP(Aspect Oriented Programming)面向切面編程、OOP(Object Oriented Programming)面向?qū)ο缶幊?/p>

                OOP是一種編程思想,AOP也是一種編程思想,編程思想主要的內(nèi)容就是指導(dǎo)程序員該如何編寫程序,兩者都是不同的編程范式各有特色

                二.AOP核心

                通過以下一個計算程序運行時間的功能,引出AOP相關(guān)概念

                @Repositorypublic class AImpl implements A { public void save() { //記錄程序當前執(zhí)行執(zhí)行(開始時間) Long startTime = System.currentTimeMillis(); //業(yè)務(wù)執(zhí)行萬次 for (int i = 0;i<10000;i++) { System.out.println("START …"); } //記錄程序當前執(zhí)行時間(結(jié)束時間) Long endTime = System.currentTimeMillis(); //計算時間差 Long totalTime = endTime-startTime; //輸出信息 System.out.println("執(zhí)行萬次程序消耗時間:" + totalTime + "ms"); } public void m1(){ System.out.println(" m1 …"); } public void m2(){ System.out.println(" m2 …"); }}

                (1) save , m1 和 m2 方法,這些方法我們給起了一個名字叫連接點

                (2)對于需要增強的方法我們給起了一個名字叫切入點

                (3)將功能抽取到一個方法中,換句話說就是存放 共性功能 的方法,我們給起了個名字叫通知

                (4)通知是要增強的內(nèi)容,會有多個,切入點是 需要被增強的方法 ,也會有多個,那哪個切入點需要添加哪個通知,就需要提前將它們之間的關(guān)系描述清楚,那么對于通知和切入點之間的關(guān)系描述,我們給起了個名字叫切面

                (5) 通知是一個方法 ,方法不能獨立存在需要被寫在一個類中,這個類我們也給起了個名字叫通知類

                三.第一個AOP案例

                1.環(huán)境準備

                • 創(chuàng)建一個Maven項目
                • pom.xml添加Spring依賴 spring-context
                • 添加A和AImpl類public interface A { public void save(); public void m1(); } @Repository public class AImpl implements A { public void save() { System.out.println(System.currentTimeMillis()); System.out.println(“book dao save …”); } public void m1(){ System.out.println(“book dao m1 …”); } }
                • 創(chuàng)建Spring的配置類@Configuration @ComponentScan(“yu7daily”) public class Config { }
                • 編寫Show運行類public class Show { public static void main(String[] args) { ShowlicationContext ctx = new AnnotationConfigShowlicationContext(Config.class); A A = ctx.getBean(A.class); A.save(); } }

                2.AOP實現(xiàn)步驟

                1.@EnableAspectJAutoProxy開啟注解格式AOP功能

                2.@Aspect設(shè)置當前類為AOP切面類

                3.@Pointcut設(shè)置切入點方法

                4.@Before

                **1.添加依賴

                pom.xml

                org.aspectj aspectjweaver 1.9.4

                因為 spring-context 中已經(jīng)導(dǎo)入了 spring-aop ,所以不需要再單獨導(dǎo)入 spring-aop .

                導(dǎo)入AspectJ的jar包,AspectJ是AOP思想的一個具體實現(xiàn),Spring有自己的AOP實現(xiàn),但是相比于AspectJ來說比較麻煩,所以我們直接采用Spring整合ApsectJ的方式進行AOP開發(fā)。

                2.定義接口與實現(xiàn)類:環(huán)境準備的時候,AImpl已經(jīng)準備好,不需要做任何修改

                3.定義通知類和通知

                通知就是將共性功能抽取出來后形成的方法,共性功能指的就是當前系統(tǒng)時間的打印

                public class Test { public void method(){ System.out.println(System.currentTimeMillis()); }}

                類名和方法名沒有要求,可以任意。

                4.定義切入點

                AImpl中有兩個方法,分別是save和m1,我們要增強的是m1方法,該如何定義呢?

                public class Test { @Pointcut(“execution(void yu7daily.dao.A.m1())”) private void po1(){ } public void method(){ System.out.println(System.currentTimeMillis()); }}

                說明:

                切入點定義依托一個不具有實際意義的方法進行,即無參數(shù)、無返回值、方法體無實際邏輯。

                execution及后面編寫的內(nèi)容

                5.制作切面

                切面是用來描述通知和切入點之間的關(guān)系,如何進行關(guān)系的綁定?

                public class Test { @Pointcut(“execution(void yu7daily.dao.A.m1())”) private void po1(){ } @Before(“po1()”) public void method(){ System.out.println(System.currentTimeMillis()); }}

                綁定切入點與通知關(guān)系,并指定通知添加到原始連接點的具體執(zhí)行位置

                說明:@Before翻譯過來是之前,也就是說通知會在切入點方法執(zhí)行之前執(zhí)行,除此之前還有其他四種類型

                6.將通知類配給容器并標識其為切面類

                @Component@Aspectpublic class Test { @Pointcut(“execution(void yu7daily.dao.A.m1())”) private void po1(){ } @Before(“po1()”) public void method(){ System.out.println(System.currentTimeMillis()); }}

                7.開啟注解格式AOP功能

                @Configuration@ComponentScan(“yu7daily”)@EnableAspectJAutoProxypublic class Config { }

                8.運行程序

                public class Show { public static void main(String[] args) { ShowlicationContext ctx = new AnnotationConfigShowlicationContext(Config.class); A A = ctx.getBean(A.class); A.m1(); }}

                看到在執(zhí)行m1方法之前打印了系統(tǒng)時間戳,說明對原始方法進行了增強,AOP編程成功!!!

                四.切入點表達式

                前面的案例中,有涉及到如下內(nèi)容:

                對于AOP中切入點表達式,我們總共會學(xué)習三個內(nèi)容,分別是 語法格式 、 通配符 和 書寫技巧 。

                1.語法格式

                首先我們先要明確兩個概念:

                切入點:要進行增強的方法

                切入點表達式:要進行增強的方法的描述方式

                描述方式一:執(zhí)行yu7daily.dao包下的A接口中的無參數(shù)m1方法

                execution(void yu7daily.dao.A.m1())

                描述方式二:執(zhí)行yu7daily.dao.impl包下的AImpl類中的無參數(shù)m1方法

                execution(void yu7daily.dao.impl.AImpl.m1())

                因為調(diào)用接口方法的時候最終運行的還是其實現(xiàn)類的方法,所以上面兩種描述方式都是可以的。

                對于切入點表達式的語法為:

                • 切入點表達式標準格式:動作關(guān)鍵字(訪問修飾符 返回值 包名.類/接口名.方法名(參數(shù)) 異常名)

                execution(public User yu7daily.service.UserService.findById(int))

                切入點表達式就是要找到需要增強的方法,所以它就是對一個具體方法的描述,但是方法的定義會有很多,所以如果每一個方法對應(yīng)一個切入點表達式,極其復(fù)雜可以通過以下方式進行簡化

                2.通配符

                使用通配符描述切入點,主要的目的就是簡化之前的配置

                * : 單個獨立的任意符號,可以獨立出現(xiàn),也可以作為前綴或者后綴的匹配符出現(xiàn)

                execution(public * yu7daily.*.UserService.find*(*))

                匹配yu7daily包下的任意包中的UserService類或接口中所有find開頭的帶有一個參數(shù)的方法.. : 多個連續(xù)的任意符號,可以獨立出現(xiàn),常用于簡化包名與參數(shù)的書寫

                execution(public User com..UserService.findById(..))

                匹配com包下的任意包中的UserService類或接口中所有名稱為findById的方法+ :專用于匹配子類類型

                execution(* *..*Service+.*(..))

                使用切入點表達式來分析下:

                execution(void yu7daily.dao.A.m1())匹配接口,能匹配到execution(void yu7daily.dao.impl.AImpl.m1())匹配實現(xiàn)類,能匹配到execution(* yu7daily.dao.impl.AImpl.m1())返回值任意,能匹配到execution(* yu7daily.dao.impl.AImpl.m1(*))返回值任意,但是m1方法必須要有一個參數(shù),無法匹配,要想匹配需要在m1接口和實現(xiàn)類添加參數(shù)execution(void com.*.*.*.*.m1())返回值為void,com包下的任意包三層包下的任意類的m1方法,匹配到的是實現(xiàn)類,能匹配execution(void com.*.*.*.m1())返回值為void,com包下的任意兩層包下的任意類的m1方法,匹配到的是接口,能匹配execution(void *..m1())返回值為void,方法名是m1的任意包下的任意類,能匹配execution(* *..*(..))匹配項目中任意類的任意方法,能匹配,但是不建議使用這種方式,影響范圍廣execution(* *..u*(..))匹配項目中任意包任意類下只要以u開頭的方法,m1方法能滿足,能匹配execution(* *..*e(..))匹配項目中任意包任意類下只要以e結(jié)尾的方法,m1和save方法能滿足,能匹配execution(void com..*())返回值為void,com包下的任意包任意類任意方法,能匹配,*代表的是方法execution(* yu7daily.*.*Service.find*(..))將項目中所有業(yè)務(wù)層方法的以find開頭的方法匹配execution(* yu7daily.*.*Service.save*(..))將項目中所有業(yè)務(wù)層方法的以save開頭的方法匹配

                五.AOP通知類型

                它所代表的含義是將 通知 添加到 切入點 方法執(zhí)行的前面。

                除了這個注解外,還有沒有其他的注解,換個問題就是除了可以在前面加,能不能在其他的地方加?

                (1)前置通知,追加功能到方法執(zhí)行前,類似于在代碼1或者代碼2添加內(nèi)容

                (2)后置通知,追加功能到方法執(zhí)行后,不管方法執(zhí)行的過程中有沒有拋出異常都會執(zhí)行,類似于在代碼5添加內(nèi)容

                (3)返回后通知,追加功能到方法執(zhí)行后,只有方法正常執(zhí)行結(jié)束后才進行,類似于在代碼3添加內(nèi)容,如果方法執(zhí)行拋出異常,返回后通知將不會被添加

                (4)拋出異常后通知,追加功能到方法拋出異常后,只有方法執(zhí)行出異常才進行,類似于在代碼4添加內(nèi)容,只有方法拋出異常后才會被添加

                (5)環(huán)繞通知,環(huán)繞通知功能比較強大,它可以追加功能到方法執(zhí)行的前后,這也是比較常用的方式,它可以實現(xiàn)其他四種通知類型的功能

                環(huán)境準備

                1.pom.xml添加Spring依賴 spring-context、aspectjweaver

                2.添加A和AImpl類

                public interface A { public void m1(); public int m2();}@Repositorypublic class AImpl implements A { public void m1(){ System.out.println(” m1 …”); } public int m2() { System.out.println(” m2 is running …”); return 1; }}

                • 創(chuàng)建Spring的配置類
                • @Configuration @ComponentScan(“yu7daily”) @EnableAspectJAutoProxy public class Config { }
                • 創(chuàng)建通知類
                • @Component @Aspect public class Test { @Pointcut(“execution(void yu7daily.dao.A.m1())”) private void po1(){ } public void around(){ System.out.println(“around before advice …”); System.out.println(“around after advice …”); } }
                • 編寫Show運行類
                • public class Show { public static void main(String[] args) { ShowlicationContext ctx = new AnnotationConfigShowlicationContext(Config.class); A A = ctx.getBean(A.class); A.m1(); } }

                環(huán)繞通知

                (1)原始方法有返回值的處理

                • 修改Test,對A中的m2方法添加環(huán)繞通知,

                @Component@Aspectpublic class Test { @Pointcut(“execution(void yu7daily.dao.A.m1())”) private void po1(){ } @Pointcut(“execution(int yu7daily.dao.A.m2())”) private void po2(){ } @Around(“po2()”) public void aroundM2(ProceedingJoinPoint pjp) throws Throwable { System.out.println(“around before advice …”); //表示對原始操作的調(diào)用 pjp.proceed(); System.out.println(“around after advice …”); }}

                • 修改Show類,調(diào)用m2方法

                @Component@Aspectpublic class Test { @Pointcut(“execution(void yu7daily.dao.A.m1())”) private void po1(){ } @Pointcut(“execution(int yu7daily.dao.A.m2())”) private void po2(){ } @Around(“po2()”) public Object aroundM2(ProceedingJoinPoint pjp) throws Throwable { System.out.println(“around before advice …”); //表示對原始操作的調(diào)用 Object ret = pjp.proceed(); System.out.println(“around after advice …”); return ret; }}

                說明:

                返回的是Object而不是int的主要原因是 Object類型更通用隨時可以轉(zhuǎn)型

                在環(huán)繞通知中是可以對原始方法返回值就行修改的

                1.返回后通知

                @Component@Aspectpublic class Test { @Pointcut(“execution(void yu7daily.dao.A.m1())”) private void po1(){ } @Pointcut(“execution(int yu7daily.dao.A.m2())”) private void po2(){ } @AfterReturning(“po2()”) public void afterReturning() { System.out.println(“afterReturning advice …”); }}

                注意 :返回后通知是需要在原始方法 m2 正常執(zhí)行后才會被執(zhí)行,如果 m2() 方法執(zhí)行的過程中出現(xiàn)了異常,那么返回后通知是不會被執(zhí)行。后置通知則是不管原始方法有沒有拋出異常都會被執(zhí)行

                2.異常后通知

                @Component@Aspectpublic class Test { @Pointcut(“execution(void yu7daily.dao.A.m1())”) private void po1(){ } @Pointcut(“execution(int yu7daily.dao.A.m2())”) private void po2(){ } @AfterReturning(“po2()”) public void afterThrowing() { System.out.println(“afterThrowing advice …”); }}

                環(huán)繞通知注意事項

                1. 環(huán)繞通知必須依賴形參ProceedingJoinPoint才能實現(xiàn)對原始方法的調(diào)用,進而實現(xiàn)原始方法調(diào)用前后同時添加通知

                2. 通知中如果未使用ProceedingJoinPoint對原始方法進行調(diào)用將跳過原始方法的執(zhí)行

                3. 對原始方法的調(diào)用可以不接收返回值,通知方法設(shè)置成void即可,如果接收返回值,最好設(shè)定為Object類型

                4. 原始方法的返回值如果是void類型,通知方法的返回值類型可以設(shè)置成void,也可以設(shè)置成Object

                5. 由于無法預(yù)知原始方法運行后是否會拋出異常,因此環(huán)繞通知方法必須要處理Throwable異常

                鄭重聲明:本文內(nèi)容及圖片均整理自互聯(lián)網(wǎng),不代表本站立場,版權(quán)歸原作者所有,如有侵權(quán)請聯(lián)系管理員(admin#wlmqw.com)刪除。
                用戶投稿
                上一篇 2022年8月6日 13:15
                下一篇 2022年8月6日 13:15

                相關(guān)推薦

                聯(lián)系我們

                聯(lián)系郵箱:admin#wlmqw.com
                工作時間:周一至周五,10:30-18:30,節(jié)假日休息