亚洲精品久久久中文字幕-亚洲精品久久片久久-亚洲精品久久青草-亚洲精品久久婷婷爱久久婷婷-亚洲精品久久午夜香蕉

您的位置:首頁技術(shù)文章
文章詳情頁

Spring AOP 與代理的概念與使用

瀏覽:13日期:2023-08-04 18:23:40

一、AOP 的基本概念

1.1 什么是 AOP

Aspect Oriented Programming,面向切面編程。

就跟我們說 OOP 是面向?qū)ο笠粯樱珹OP 是面向切面的。切面是分散在應(yīng)用中的一個標(biāo)準(zhǔn)代碼或功能。切面通常與實際的業(yè)務(wù)邏輯不同(例如,事務(wù)管理)。每個切面專注于一個特定的環(huán)切功能。

這里的切面呢,可以理解為橫切。比如在所有的 DAO 層方法上加上一個同樣的切面,功能是記錄日志;又或者在某個接口上應(yīng)用一個切面,作用是檢查權(quán)限。

AOP 是基于代理來實現(xiàn)的。而代理又分為靜態(tài)代理和動態(tài)代理。兩者的區(qū)別在于代理類于何時生成。

下面我們講講代理是怎么回事?

1.2 代理與 Spring AOP

代理分為靜態(tài)代理和動態(tài)代理:

靜態(tài)代理:代理類在編譯階段生成,程序運行前就存在。包括:AspectJ 靜態(tài)代理、JDK 靜態(tài)代理 動態(tài)代理:代理類在程序運行時創(chuàng)建。包括:JDK 動態(tài)代理、CGLib 動態(tài)代理

Spring AOP 原理:

JDK Proxy:interface based CGLib Proxy: class based

Spring AOP 與代理的概念與使用

Spring AOP 中默認(rèn)使用 JDK 動態(tài)代理,通過反射獲取被代理的類,這個類必須實現(xiàn)一個接口。如果目標(biāo)類沒有實現(xiàn)接口,就會默認(rèn)使用 CGLIB Proxy 來動態(tài)生成代理目標(biāo)類,后者是被代理類的子類。

可以通過獲取代理對象并打印的方式來查看其類型(JDK Proxy 下是 com.sun.prxy, CGlib 下是子類.

AspectJ: 用特定的編譯器和語法,在編譯時增強,實現(xiàn)了靜態(tài)代理技術(shù)。

1.3 Spring AOP 與 AspectJ 的區(qū)別

AspectJ 是一套完整的 AOP 解決方案,而 Spring AOP 并不是 —— 它只是在 Spring 框架下滿足其使用要求的一個解決方法,比如 Spring AOP 僅支持對方法使用切面。

二、靜態(tài)代理

2.1 AspectJ 靜態(tài)代理

基于特殊的編譯器和語法。這里不多介紹了。

IDEA 下編譯 AspectJ 可以參考這篇:https://blog.csdn.net/gavin_john/article/details/80156963

2.2 JDK 靜態(tài)代理

實際上是利用實現(xiàn)一個具體的代理類來調(diào)用業(yè)務(wù)類。代理類持有了一個業(yè)務(wù)類的引用。

更概況地說,JDK 靜態(tài)代理體現(xiàn)的是一種設(shè)計模式。

缺點很明顯,代碼冗余,難以維護(hù)。

這里以 借書 和 還書 這兩個行為來作為一個示例:

編寫一個 BookService 接口:

public interface BookService { boolean borrow(String id, String userName); boolean reBack(String id, String userName);}

然后實現(xiàn)這個接口:

public class BookServiceImpl implements BookService { @Override public boolean borrow(String id, String userName) {System.out.println(userName + ' 借書:' + id);return true; } @Override public boolean reBack(String id, String userName) {System.out.println(userName + ' 還書:' + id);return true; }}

下面我們來編寫 BookService 的代理類:

public class BookProxy implements BookService { private BookServiceImpl bookService; public BookProxy(BookServiceImpl bookService) {this.bookService = bookService; } @Override public boolean borrow(String id, String userName) {boolean res = false;if (check()) { res = bookService.borrow(id, userName);}addLog();return res; } @Override public boolean reBack(String id, String userName) {boolean res = false;if (check()) { res = bookService.reBack(id, userName);}addLog();return res; } // private boolean check() {System.out.println('檢查權(quán)限');return true; } private void addLog() {System.out.println('操作完成'); }}

編寫一個測試類:

public class MainTest { public static void main(String[] args) {BookProxy proxy = new BookProxy(new BookServiceImpl());proxy.borrow('123', 'eknown');proxy.reBack('234', 'java'); }}

這里我們可以看到,JDK 靜態(tài)代理就是說在原來的實現(xiàn)類上套一層 代理。它好像是體現(xiàn)了代理模式,但實際上并沒有帶來太多的好處。代碼相當(dāng)冗余,也不利于維護(hù)。

真正體現(xiàn)代理模式好處的還是動態(tài)代理,下面我們來看看動態(tài)代理的原理。

三、動態(tài)代理

動態(tài)代理是程序運行時,由 JVM 根據(jù)反射等機制動態(tài)生成代理類的。

也就是說,程序運行前,我們僅僅定義了代理的規(guī)則,而不知道代理類具體長什么樣,這不像上面的靜態(tài)代理里,我們完整地定義了代理對象。

3.1 JDK 動態(tài)代理

JDK 動態(tài)代理是基于接口的。

我們可以通過實現(xiàn) InvocationHandler 接口來手動創(chuàng)建一個 JDK 代理類。

首先需要定義一個接口,讓業(yè)務(wù)類和代理類都實現(xiàn)這個接口。

然后編寫一個 InvocationHandler 接口的實現(xiàn)類:

public class BookProxy implements InvocationHandler { // 被該代理類處理的業(yè)務(wù)類 private BookService bookService; public BookProxy(BookService bookService) {this.bookService = bookService; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Object res = null;if (check()) { // 調(diào)用實際的 method,參數(shù)是 接口 + 參數(shù) res = method.invoke(bookService, args);}addLog();return res; }private boolean check() {System.out.println('檢查權(quán)限');return true; } private void addLog() {System.out.println('操作完成'); }}

測試:

public class MainTest { public static void main(String[] args) {// 創(chuàng)建被代理的實際業(yè)務(wù)類BookServiceImpl bookServiceImpl = new BookServiceImpl();ClassLoader classLoader = bookServiceImpl.getClass().getClassLoader();// 獲取所有的接口方法Class[] interfaces = bookServiceImpl.getClass().getInterfaces();// 構(gòu)造 HandlerInvocationHandler invocationHandler = new BookProxy(bookServiceImpl);// 創(chuàng)建代理Object obj = Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);BookService bookService = (BookService) obj;bookService.borrow('abc', 'eknown');bookService.reBack('c23', 'py'); }}

3.2 CGLIB 動態(tài)代理

CGLIB 代理的原理是:讓代理類繼承業(yè)務(wù)類(也就自動擁有了業(yè)務(wù)類的所有非 final 的 public 方法)

我們這里手動編寫一個 CGLIB 的代理試試看。

首先我們有一個 BookServiceImpl 業(yè)務(wù)類,這個業(yè)務(wù)類可以實現(xiàn)接口,也可以就是單純的一個業(yè)務(wù)類。

然后我們定義一個 BookCglibProxy 類:

public class BookCglibProxy implements MethodInterceptor { @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {check();// 調(diào)用實際的 methodObject obj = methodProxy.invokeSuper(o, objects);addLog();return obj; } private boolean check() {System.out.println('檢查權(quán)限');return true; } private void addLog() {System.out.println('操作完成'); }}

測試類:

public class CglibTest { public static void main(String[] args) {BookServiceImpl bookServiceImpl = new BookServiceImpl();BookCglibProxy proxy = new BookCglibProxy();// cjlib 中的增強器,用于創(chuàng)建動態(tài)代理(被代理類的子類)Enhancer enhancer = new Enhancer();// 設(shè)置要被代理的類enhancer.setSuperclass(bookServiceImpl.getClass());// 設(shè)置回調(diào)enhancer.setCallback(proxy);// 強轉(zhuǎn)成父類BookServiceImpl proxyResult = (BookServiceImpl) enhancer.create();proxyResult.borrow('12333', 'ye');proxyResult.reBack('123', 'fe'); }}

在第一節(jié)我們提到過 Spring AOP 是基于 JDK 動態(tài)代理和 CGLIB 動態(tài)代理的。下面我們來 Spring AOP 的一些基本案例。

四、Spring AOP 實例

AOP 中一些概念詞匯,通過這些詞匯,我們可以對 AOP 有更高一層的抽象。

Aspect - 切面,分散在應(yīng)用中的一個標(biāo)準(zhǔn)代碼或功能。切面通常與實際的業(yè)務(wù)邏輯不同(例如,事務(wù)管理)。每個切面專注于一個特定的環(huán)切功能。 Joinpoint - 連接點,是程序執(zhí)行過程中的特定點,比如方法執(zhí)行、構(gòu)造器調(diào)用、字段賦值 Advice - 通知,切面在某個連接點采取的操作。Advice 有 5 種類型。 Pointcut - 切入點,一個匹配連接點的正則表達(dá)式。每當(dāng)連接點匹配了一個切入點時,一個特定的通知就會被執(zhí)行。 Weaving - 織入,指的是將切面和目標(biāo)對象連接起來以創(chuàng)建代理對象的過程。

Spring AOP 有兩種實現(xiàn)方式:基于 XML 或基于注解。更流行、更方便的是后者。(阿 sir,不會還有人用 XML 來做 Bean 的配置文件吧?)

4.1 基于 XML 的實例

首先定義一下接口和實現(xiàn)類(沒有注解的!)。再編寫一個代理類:

這里的代理類方法以 JoinPoint 為參數(shù)即可:

public class BookAspect { public void checkUser(JoinPoint point) {System.out.println('-----before-----');Object[] args = point.getArgs();for (Object arg : args) { System.out.println(arg);}System.out.println('檢查用戶權(quán)限...'); } public void saveLog(JoinPoint point) {System.out.println('-----after-----');Object[] args = point.getArgs();for (Object arg : args) { System.out.println(arg);}System.out.println('請求完畢,記錄日志...'); }}

然后編寫 Spring 的配置文件:

<?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/beanshttp://www.springframework.org/schema/beans/spring-beans-4.2.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-4.2.xsd'> <!-- 定義 bean --> <bean id='bookService' class='com.example.springaopdemo.basicxml.BookServiceImpl' /> <bean id='bookAspect' class='com.example.springaopdemo.basicxml.BookAspect' /> <aop:config><!-- 這是定義一個切面,切面是切點和通知的集合--><aop:aspect id='do' ref='bookAspect'> <!-- 定義切點 ,后面是 expression 語言,表示包括該接口中定義的所有方法都會被執(zhí)行 --> <aop:pointcut id='point' expression='execution(* com.example.springaopdemo.basicxml.BookService.*(..))' /> <!-- 定義通知 --> <aop:before method='checkUser' pointcut-ref='point' /> <aop:after method='saveLog' pointcut-ref='point' /></aop:aspect> </aop:config></beans>

運行測試:

public class AopXMLTest { public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext('SpringAop.xml');BookService bookService = context.getBean('bookService', BookService.class);bookService.borrow('123', 'eknown');bookService.reback('123', 'eknown'); }}

基于 XML 配置的 Spring 現(xiàn)在已經(jīng)很少使用了。下面我們來看看如何基于注解使用 Spring AOP

4.2 基于注解的實例

這里以一個使用 SpringBoot 框架的 Web 項目作為簡單的實例。

首先創(chuàng)建一個 SpringBoot 項目,寫好 Controller、Service、DAO 層的基本類。(示例源碼中沒有使用 Mybatis 等持久層框架,而是用 Map 來模擬數(shù)據(jù)的存取)

下面我們針對 UserService 接口類,添加切面。

@Aspect@Componentpublic class UserAspect { @Before(value = 'execution(* com.example.springaopdemo.boot.UserService.*(..))') public void checkUser(JoinPoint point) {System.out.println('-----before-----');Object[] args = point.getArgs();for (Object arg : args) { System.out.println(arg);}System.out.println('檢查...' + point); } @After(value = 'execution(* com.example.springaopdemo.boot.UserService.*(..))') public void saveLog(JoinPoint point) {System.out.println('-----after-----');Object[] args = point.getArgs();for (Object arg : args) { System.out.println(arg);}// 這里可以使用 point.getTarget() 獲取到切面對應(yīng)的 bean//Object target = point.getTarget();//UserService userService = (UserService) target;//List<User> userList = userService.findAll();System.out.println('請求完畢,記錄日志...' + point); } @Around(value = 'execution(* com.example.springaopdemo.boot.UserService.save(..))') public Object saveAround(ProceedingJoinPoint point) {System.out.println('around-before');Object obj = null;try { obj = point.proceed();} catch (Throwable throwable) { throwable.printStackTrace();}System.out.println('around-after');return obj; }}

示例中使用了 @Before/@After/@Aroud 三個注解,value 中使用切點表達(dá)式,分別匹配了 UserService 接口的所有方法和單個 save 方法。

我們還可以通過切點表達(dá)式匹配自定義的注解,比如實現(xiàn)一個 UserMonitor 注解,然后定義其切點方法:

public @interface UserMonitor { String value() default ''; int roleLimit() default 0;}

切點:

@Around('@annotation(com.example.springaopdemo.boot.UserMonitor)') public Object userRolePointCut(ProceedingJoinPoint point) {System.out.println('檢查用戶權(quán)限...'); // 獲取參數(shù)Object[] args = point.getArgs();Class<?>[] argTypes = new Class[point.getArgs().length];for (int i = 0; i < args.length; i++) { argTypes[i] = args[i].getClass();} // 獲取方法Method method = null;try { method = point.getTarget().getClass() .getMethod(point.getSignature().getName(), argTypes);} catch (NoSuchMethodException | SecurityException e) { e.printStackTrace();}// 獲取方法上的該注解,之后可以根據(jù)注解中的值進(jìn)行一些操作,比如判定是否具有權(quán)限UserMonitor monitor = method.getAnnotation(UserMonitor.class);System.out.println(monitor);Object obj = null;try { obj = point.proceed();} catch (Throwable throwable) { throwable.printStackTrace();}return obj; }

以上就是Spring AOP 與代理的概念與使用的詳細(xì)內(nèi)容,更多關(guān)于Spring AOP 與代理的資料請關(guān)注好吧啦網(wǎng)其它相關(guān)文章!

標(biāo)簽: Spring
相關(guān)文章:
主站蜘蛛池模板: 国产综合婷婷 | 高清国产一级毛片国语 | 亚洲成人黄色在线观看 | 国产视频在线观看免费 | 高h喷水荡肉爽文np欲 | 亚洲欧美日韩综合一区久久 | 黄色看片网址 | 日本va视频| 欧美成人免费高清视频 | 国产在线观看不卡免费高清 | 久99久女女精品免费观看69堂 | 日韩国产欧美一区二区三区在线 | 国产成人精品男人免费 | 日本久久影视 | 亚洲欧洲精品一区二区三区 | 亚洲一二三四 | 男女午夜爱爱久久无遮挡 | 午夜激情福利 | 久久国产精品老人性 | 91精品全国免费观看含羞草 | 免费人成网站尤物在线观看 | 欧美v亚洲| 污污网站在线 | 大学生a级毛片免费观看 | 成人午夜久久 | 日韩欧美不卡片 | 黄色婷婷 | 日日夜夜免费精品视频 | 亚洲精品区一区二区三区四 | 香蕉影视在线观看 | 特黄特a级特别特级特毛片 特黄特色一级aa毛片免费观看 | 欧美特黄级乱色毛片 | 伊人中文字幕在线观看 | 日韩欧国产精品一区综合无码 | 国内自拍在线视频高清 | 亚洲一区毛片 | 成人看片毛片免费播放器 | 男人和女人做a免费视频 | 久久www免费人成看国产片 | 黄色免费看网站 | 亚洲欧美日韩在线不卡 |