深入淺出講解Spring框架中AOP及動(dòng)態(tài)代理的應(yīng)用
一. Spring AOP
面向切面編程(Aspect Oriented Programming,AOP)是軟件編程思想發(fā)展到一定階段的產(chǎn)物,是對(duì)面向?qū)ο缶幊?Object Oriented Programming,OOP)的有益補(bǔ)充, 目前已成為一種比較成熟的編程方式。AOP適用于具有橫向邏輯的場(chǎng)所,如訪問(wèn)控制、事務(wù)管理、性能監(jiān)測(cè)等。
1. 傳統(tǒng)問(wèn)題:
在傳統(tǒng)的業(yè)務(wù)處理代碼中,通常都會(huì)進(jìn)行事務(wù)處理、日志記錄等操作。雖然OOP可以通過(guò)組合或者繼承的方式來(lái)達(dá)到代碼的重用,但是比如實(shí)現(xiàn)日志記錄時(shí),代碼還是會(huì)分散到不同的方法中。這樣就會(huì)存在一個(gè)問(wèn)題,如果想要關(guān)閉某個(gè)功能或者修改時(shí),就必須要修改所有的相關(guān)方法。這不單單增加了開(kāi)發(fā)人員的工作量,而且提高了代碼的出錯(cuò)率。
2. 問(wèn)題的解決策略:
為了解決這個(gè)問(wèn)題,AOP思想隨之產(chǎn)生。AOP采取橫向抽取機(jī)制,將分散在各個(gè)方法中的重復(fù)代碼提取出來(lái),然后在程序編譯或運(yùn)行時(shí),再將這些提取出來(lái)的代碼應(yīng)用到需要執(zhí)行的地方。這種采用橫向抽取機(jī)制的方式,是傳統(tǒng)的OOP思想無(wú)法辦到的,因?yàn)镺OP只能實(shí)現(xiàn)父子關(guān)系的縱向的重用。雖然AOP是一種新的編程思想,卻不是OOP的替代品,它只是OOP的延申和補(bǔ)充。
3. AOP優(yōu)點(diǎn):
AOP的使用讓開(kāi)發(fā)人員在編寫業(yè)務(wù)邏輯時(shí)可以專心于核心業(yè)務(wù),而不用過(guò)多地關(guān)注于其他業(yè)務(wù)邏輯的實(shí)現(xiàn),這不但提高了開(kāi)發(fā)效率,而且增強(qiáng)了代碼的可維護(hù)性。
在AOP思想中,類于切面的關(guān)系如下圖所示。我們可以看出,通過(guò)Aspect(切面)分別在Class1和Class2的方法中加入了事務(wù)、日志、權(quán)限和異常等功能。
二. 動(dòng)態(tài)代理
通過(guò)學(xué)習(xí)我們知道了AOP中的代理就是由AOP框架動(dòng)態(tài)生成的一個(gè)對(duì)象,該對(duì)象可以作為目標(biāo)對(duì)象使用,對(duì)于面向切面編程,簡(jiǎn)單地說(shuō),就是在不改變?cè)绦虻幕A(chǔ)上為代碼段增加新的功能,對(duì)代碼段進(jìn)行增強(qiáng)處理。它的設(shè)計(jì)思想來(lái)源于代理設(shè)計(jì)模式,通常情況下調(diào)用對(duì)象的方法如下圖。
在代理模式中可以為該對(duì)象設(shè)置一個(gè)代理對(duì)象,代理對(duì)象為function()提供一個(gè)代理方法,當(dāng)通過(guò)代理對(duì)象的function()方法調(diào)用原對(duì)象的function()方法時(shí),就可以在代理方法中添加新的功能,即增強(qiáng)處理。增強(qiáng)的功能既可以插到原對(duì)象的function()前面,也可以插到其后面(如虛線)
1. JDK動(dòng)態(tài)代理
JDK動(dòng)態(tài)代理是通過(guò)java.lang.reflect.Proxy類來(lái)實(shí)現(xiàn)的,可以調(diào)用Proxy類的newProxyInstance()方法來(lái)創(chuàng)建代理對(duì)象。對(duì)于使用業(yè)務(wù)接口的類,Spring框架會(huì)默認(rèn)使用JDK動(dòng)態(tài)代理來(lái)實(shí)現(xiàn)AOP。通過(guò)一個(gè)案例來(lái)演示。
1. UserDao.java
package dao; public interface UserDao { public void addUserDao(); public void deleteUser(); }
2. UserDaoImpl.java
package dao; public class UserDaoImpl implements UserDao{ @Override public void addUserDao() { System.out.println("添加用戶"); } @Override public void deleteUser() { System.out.println("刪除用戶"); } }
3. MyAspect.java
package aspect; public class MyAspect { public void check_permission(){ System.out.println("----模擬檢查訪問(wèn)----"); } public void log(){ System.out.println("----模擬記錄日記----"); } }
4. JdkProxy.java
package jdk; import aspect.MyAspect; import dao.UserDao; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * Jdk代理類 */ public class JdkProxy implements InvocationHandler { //聲明目標(biāo)類接口 private UserDao userdao; // 創(chuàng)建代理方法 public Object createProxy(UserDao userdao){ this.userdao=userdao; //類加載器 ClassLoader classLoader=JdkProxy.class.getClassLoader(); //被代理對(duì)象實(shí)現(xiàn)的所有接口 Class[] clazz=userdao.getClass().getInterfaces(); //使用代理類進(jìn)行增強(qiáng),返回的是代理后的對(duì)象 return Proxy.newProxyInstance(classLoader,clazz,this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //聲明切面 MyAspect myAspect=new MyAspect(); //前增強(qiáng) myAspect.check_permission(); //在目標(biāo)上調(diào)用方法,并傳入?yún)?shù) Object obj=method.invoke(userdao,args); //后增強(qiáng) myAspect.log(); return obj; } }
5. Test.java
@Test public void shouldAnswerWithTrue() { JdkProxy jdkProxy=new JdkProxy(); UserDao userDao=new UserDaoImpl(); UserDao userDao1=(UserDao) jdkProxy.createProxy(userDao); userDao1.addUserDao(); System.out.println("\n-----------------------------分割線------------------------------------\n"); userDao1.deleteUser(); }
結(jié)果:
2. CGLIB代理
JDK 動(dòng)態(tài)代理的使用非常簡(jiǎn)單,但它具有一定的局限性(使用動(dòng)態(tài)代理的對(duì)象必須實(shí)現(xiàn)一個(gè)或多個(gè)接口)如果要對(duì)沒(méi)有實(shí)現(xiàn)接口的類進(jìn)行代理,那么可以使用CGLIB代理。
CGLIB(Code Generation Library)是一個(gè)高性能開(kāi)源的代碼生成包,它采用非常底層的字節(jié)碼技術(shù),對(duì)指定的目標(biāo)類生成一個(gè)子類,并對(duì)子類進(jìn)行增強(qiáng)。在Spring框架的核心包中已經(jīng)集成了CGLIB所需要的包,所以開(kāi)發(fā)中不需要另外導(dǎo)入jar包。
1. BookDao.java
package dao; public class BookDao { public void addBook(){ System.out.println("添加書本"); } public void deleteBook(){ System.out.println("刪除書本"); } }
2. CglibProxy.java
package jdk; import aspect.MyAspect; import org.springframework.cglib.proxy.Enhancer; import org.springframework.cglib.proxy.MethodInterceptor; import org.springframework.cglib.proxy.MethodProxy; import java.lang.reflect.Method; public class CglibProxy implements MethodInterceptor { //代理方法 public Object createProxy(Object target){ Enhancer enhancer=new Enhancer(); enhancer.setSuperclass(target.getClass()); enhancer.setCallback(this); return enhancer.create(); } @Override public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { MyAspect myAspect=new MyAspect(); myAspect.check_permission(); Object o1=methodProxy.invokeSuper(proxy,args); myAspect.log(); return o1; } }
結(jié)果:
到此這篇關(guān)于深入淺出講解Spring框架中AOP及動(dòng)態(tài)代理的應(yīng)用的文章就介紹到這了,更多相關(guān)Spring AOP及動(dòng)態(tài)代理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Spring中AOP概念與兩種動(dòng)態(tài)代理模式原理詳解
- Spring AOP里的靜態(tài)代理和動(dòng)態(tài)代理用法詳解
- Spring AOP手動(dòng)實(shí)現(xiàn)簡(jiǎn)單動(dòng)態(tài)代理的代碼
- Spring AOP中的JDK和CGLib動(dòng)態(tài)代理哪個(gè)效率更高?
- Spring AOP注解失效的坑及JDK動(dòng)態(tài)代理
- 利用spring aop實(shí)現(xiàn)動(dòng)態(tài)代理
- spring基礎(chǔ)概念A(yù)OP與動(dòng)態(tài)代理理解
- Spring?AOP原理及動(dòng)態(tài)代理
相關(guān)文章
Java參數(shù)傳遞實(shí)現(xiàn)代碼及過(guò)程圖解
這篇文章主要介紹了Java參數(shù)傳遞實(shí)現(xiàn)代碼及過(guò)程圖解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-11-11關(guān)于SpringBoot集成Lettuce連接Redis的方法和案例
這篇文章主要介紹了關(guān)于SpringBoot集成Lettuce連接Redis的方法和案例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-04-04Java BigDecimal使用及基本運(yùn)算(推薦)
Java在java.math包中提供的API類BigDecimal,用來(lái)對(duì)超過(guò)16位有效位的數(shù)進(jìn)行精確的運(yùn)算。這篇文章主要介紹了Java BigDecimal使用指南針(推薦),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-08-08spring boot使用sonarqube來(lái)檢查技術(shù)債務(wù)
今天小編就為大家分享一篇關(guān)于spring boot使用sonarqube來(lái)檢查技術(shù)債務(wù),小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2018-12-12@RequestBody 部分屬性沒(méi)有轉(zhuǎn)化成功的處理
這篇文章主要介紹了@RequestBody 部分屬性沒(méi)有轉(zhuǎn)化成功的處理方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-10-10詳解Java中Checked Exception與Runtime Exception 的區(qū)別
這篇文章主要介紹了詳解Java中Checked Exception與Runtime Exception 的區(qū)別的相關(guān)資料,這里提供實(shí)例幫助大家學(xué)習(xí)理解這部分內(nèi)容,需要的朋友可以參考下2017-08-08Mybatis錯(cuò)誤引起的程序啟動(dòng)卡死問(wèn)題及解決
這篇文章主要介紹了Mybatis錯(cuò)誤引起的程序啟動(dòng)卡死問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-02-02Spring注解驅(qū)動(dòng)之BeanFactoryPostProcessor原理解析
這篇文章主要介紹了Spring注解驅(qū)動(dòng)之BeanFactoryPostProcessor原理,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-09-09