Spring?AOP原理及動(dòng)態(tài)代理
一、什么是代理?
指為一個(gè)目標(biāo)對(duì)象提供一個(gè)代理對(duì)象, 并由代理對(duì)象控制對(duì)目標(biāo)對(duì)象的引用. 使用代理對(duì)象, 是為了在不修改目標(biāo)對(duì)象的基礎(chǔ)上,增強(qiáng)目標(biāo)對(duì)象的業(yè)務(wù)邏輯.
1、靜態(tài)代理
靜態(tài)代理的特點(diǎn)是, 為每一個(gè)業(yè)務(wù)增強(qiáng)都提供一個(gè)代理類, 由代理類來創(chuàng)建代理對(duì)象. 下面我們通過靜態(tài)代理來實(shí)現(xiàn)對(duì)轉(zhuǎn)賬業(yè)務(wù)進(jìn)行身份驗(yàn)證.
(1) 轉(zhuǎn)賬業(yè)務(wù)
public interface IAccountService { //主業(yè)務(wù)邏輯: 轉(zhuǎn)賬 void transfer(); } public class AccountServiceImpl implements IAccountService { @Override public void transfer() { System.out.println("調(diào)用dao層,完成轉(zhuǎn)賬主業(yè)務(wù)."); } }
(2) 代理類
public class AccountProxy implements IAccountService { //目標(biāo)對(duì)象 private IAccountService target; public AccountProxy(IAccountService target) { this.target = target; } /** * 代理方法,實(shí)現(xiàn)對(duì)目標(biāo)方法的功能增強(qiáng) */ @Override public void transfer() { before(); target.transfer(); } /** * 前置增強(qiáng) */ private void before() { System.out.println("對(duì)轉(zhuǎn)賬人身份進(jìn)行驗(yàn)證."); } }
(3) 測(cè)試
public class Client { public static void main(String[] args) { //創(chuàng)建目標(biāo)對(duì)象 IAccountService target = new AccountServiceImpl(); //創(chuàng)建代理對(duì)象 AccountProxy proxy = new AccountProxy(target); proxy.transfer(); } }
結(jié)果: 對(duì)轉(zhuǎn)賬人身份進(jìn)行驗(yàn)證.調(diào)用dao層,完成轉(zhuǎn)賬主業(yè)務(wù).
2、動(dòng)態(tài)代理
靜態(tài)代理會(huì)為每一個(gè)業(yè)務(wù)增強(qiáng)都提供一個(gè)代理類, 由代理類來創(chuàng)建代理對(duì)象, 而動(dòng)態(tài)代理并不存在代理類, 代理對(duì)象直接由代理生成工具動(dòng)態(tài)生成.
2.1、JDK動(dòng)態(tài)代理
JDK動(dòng)態(tài)代理是使用 java.lang.reflect 包下的代理類來實(shí)現(xiàn). JDK動(dòng)態(tài)代理動(dòng)態(tài)代理必須要有接口.
(1) 轉(zhuǎn)賬業(yè)務(wù)
public interface IAccountService { //主業(yè)務(wù)邏輯: 轉(zhuǎn)賬 void transfer(); } public class AccountServiceImpl implements IAccountService { @Override public void transfer() { System.out.println("調(diào)用dao層,完成轉(zhuǎn)賬主業(yè)務(wù)."); } }
(2) 增強(qiáng)
因?yàn)檫@里沒有配置切入點(diǎn), 稱為切面會(huì)有點(diǎn)奇怪, 所以稱為增強(qiáng).
public class AccountAdvice implements InvocationHandler { //目標(biāo)對(duì)象 private IAccountService target; public AccountAdvice(IAccountService target) { this.target = target; } /** * 代理方法, 每次調(diào)用目標(biāo)方法時(shí)都會(huì)進(jìn)到這里 */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { before(); return method.invoke(target, args); } /** * 前置增強(qiáng) */ private void before() { System.out.println("對(duì)轉(zhuǎn)賬人身份進(jìn)行驗(yàn)證."); } }
(3) 測(cè)試
public class Client { public static void main(String[] args) { //創(chuàng)建目標(biāo)對(duì)象 IAccountService target = new AccountServiceImpl(); //創(chuàng)建代理對(duì)象 IAccountService proxy = (IAccountService) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new AccountAdvice(target) ); proxy.transfer(); } }
結(jié)果: 對(duì)轉(zhuǎn)賬人身份進(jìn)行驗(yàn)證.調(diào)用dao層,完成轉(zhuǎn)賬主業(yè)務(wù).
2.2、 CGLIB
動(dòng)態(tài)代理
JDK動(dòng)態(tài)代理必須要有接口, 但如果要代理一個(gè)沒有接口的類該怎么辦呢? 這時(shí)我們可以使用CGLIB動(dòng)態(tài)代理. CGLIB動(dòng)態(tài)代理的原理是生成目標(biāo)類的子類, 這個(gè)子類對(duì)象就是代理對(duì)象, 代理對(duì)象是被增強(qiáng)過的.
注意: 不管有沒有接口都可以使用CGLIB動(dòng)態(tài)代理, 而不是只有在無接口的情況下才能使用
(1) 轉(zhuǎn)賬業(yè)務(wù)
public class AccountService { public void transfer() { System.out.println("調(diào)用dao層,完成轉(zhuǎn)賬主業(yè)務(wù)."); } }
(2) 增強(qiáng)
因?yàn)檫@里沒有配置切入點(diǎn), 稱為切面會(huì)有點(diǎn)奇怪, 所以稱為增強(qiáng).
public class AccountAdvice implements MethodInterceptor { /** * 代理方法, 每次調(diào)用目標(biāo)方法時(shí)都會(huì)進(jìn)到這里 */ @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { before(); return methodProxy.invokeSuper(obj, args); // return method.invoke(obj, args); 這種也行 } /** * 前置增強(qiáng) */ private void before() { System.out.println("對(duì)轉(zhuǎn)賬人身份進(jìn)行驗(yàn)證."); } }
(3) 測(cè)試
public class Client { public static void main(String[] args) { //創(chuàng)建目標(biāo)對(duì)象 AccountService target = new AccountService(); // //創(chuàng)建代理對(duì)象 AccountService proxy = (AccountService) Enhancer.create(target.getClass(), new AccountAdvice()); proxy.transfer(); } }
結(jié)果: 對(duì)轉(zhuǎn)賬人身份進(jìn)行驗(yàn)證.調(diào)用dao層,完成轉(zhuǎn)賬主業(yè)務(wù).
二、模擬Spring AOP場(chǎng)景
了解了動(dòng)態(tài)代理后, 我們就可以自己來實(shí)現(xiàn)Spring AOP功能了, 所以下面我們來模擬下Spring AOP場(chǎng)景.
(1) 轉(zhuǎn)賬業(yè)務(wù)
public interface IAccountService { //主業(yè)務(wù)邏輯: 轉(zhuǎn)賬 void transfer(); } public class AccountServiceImpl implements IAccountService { @Override public void transfer() { System.out.println("調(diào)用dao層,完成轉(zhuǎn)賬主業(yè)務(wù)."); } }
(2) 切面抽象類
定義一個(gè)切面抽象類, 該類使用了模板方法的設(shè)計(jì)模式, 為開始, 結(jié)束, 異常, 前置增強(qiáng), 后置增強(qiáng)提供了默認(rèn)實(shí)現(xiàn), 當(dāng)我們定義切面類時(shí)只需要按需重寫它們就行. isIntercept() 方法用來判斷切入點(diǎn)是否正確, 切面類需要重寫這個(gè)方法.
public abstract class BaseAspect implements MethodInterceptor { private static final Logger logger = LoggerFactory.getLogger(BaseAspect.class); @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { Object result = null; begin(); try { if (isIntercept(method, args)) { before(); result = methodProxy.invokeSuper(obj, args); after(); } else { result = methodProxy.invokeSuper(obj,args); } } catch (Exception e) { logger.error("proxy failure", e); error(e); throw e; } finally { end(); } return result; } /** * 開始增強(qiáng) */ public void begin() { } /** * 切入點(diǎn)判斷 */ public boolean isIntercept(Method method, Object[] args) throws Throwable { return true; } /** * 前置增強(qiáng) */ public void before() throws Throwable { } /** * 后置增強(qiáng) */ public void after() throws Throwable { } /** * 異常增強(qiáng) */ public void error(Throwable e) { } /** * 最終增強(qiáng) */ public void end() { } }
(3) 切面類
創(chuàng)建一個(gè)切面類, 類中配置切入點(diǎn)和增強(qiáng).
public class AccountAspect extends BaseAspect { /** * 切入點(diǎn) */ public boolean isIntercept(Method method, Object[] args) throws Throwable { return method.getName().equals("transfer"); } /** * 前置增強(qiáng) */ public void before() throws Throwable { System.out.println("對(duì)轉(zhuǎn)賬人身份進(jìn)行驗(yàn)證."); } }
(4) 代理工廠類
定義一個(gè)工廠類來創(chuàng)建代理, 其實(shí)不創(chuàng)建這個(gè)類也行, 但為了模仿Spring還是創(chuàng)建了. @SuppressWarnings是為了抑制警告, 就是編譯器上面的黃線.
public class ProxyFactory { @SuppressWarnings("unchecked") public static <T> T createProxy(final Class<?> targetClass, final MethodInterceptor methodInterceptor) { return (T) Enhancer.create(targetClass,methodInterceptor); } }
(5) 測(cè)試
public class Client { public static void main(String[] args) { //創(chuàng)建目標(biāo)對(duì)象 IAccountService target = new AccountServiceImpl(); //切面 BaseAspect accountAspect = new AccountAspect(); //創(chuàng)建代理對(duì)象 IAccountService proxy = (IAccountService) ProxyFactory.createProxy(target.getClass(), accountAspect); proxy.transfer(); } }
結(jié)果:對(duì)轉(zhuǎn)賬人身份進(jìn)行驗(yàn)證.調(diào)用dao層,完成轉(zhuǎn)賬主業(yè)務(wù).
到此這篇關(guān)于Spring AOP原理及動(dòng)態(tài)代理的文章就介紹到這了,更多相關(guān)Spring AOP 內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
maven利用tomcat插件部署遠(yuǎn)程Linux服務(wù)器的步驟詳解
Maven已經(jīng)是Java的項(xiàng)目管理常用方式,下面這篇文章主要給大家介紹了關(guān)于maven利用tomcat插件部署遠(yuǎn)程Linux服務(wù)器的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。2017-11-11Java編程通過list接口實(shí)現(xiàn)數(shù)據(jù)的增刪改查代碼示例
這篇文章是介紹Java編程基礎(chǔ)方面的內(nèi)容,涉及l(fā)ist接口的操作,通過list接口實(shí)現(xiàn)對(duì)數(shù)據(jù)的增刪改查的相關(guān)代碼,具有一定參考價(jià)值,需要的朋友可以了解下。2017-10-10java wait()/notify() 實(shí)現(xiàn)生產(chǎn)者消費(fèi)者模式詳解
這篇文章主要介紹了java wait()/notify() 實(shí)現(xiàn)生產(chǎn)者消費(fèi)者模式詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07java&javascript自定義加密數(shù)據(jù)傳輸代碼示例
這篇文章主要介紹了java&javascript自定義加密數(shù)據(jù)傳輸代碼示例,具有一定參考價(jià)值,需要的朋友可以了解下。2017-11-11Java使用wait/notify實(shí)現(xiàn)線程間通信下篇
wait()和notify()是直接隸屬于Object類,也就是說所有對(duì)象都擁有這一對(duì)方法,下面這篇文章主要給大家介紹了關(guān)于使用wait/notify實(shí)現(xiàn)線程間通信的相關(guān)資料,需要的朋友可以參考下2022-12-12解決Spring Boot和Feign中使用Java 8時(shí)間日期API(LocalDate等)的序列化問題
這篇文章主要介紹了解決Spring Boot和Feign中使用Java 8時(shí)間日期API(LocalDate等)的序列化問題,需要的朋友可以參考下2018-03-03