java AOP原理以及實(shí)例用法總結(jié)
AOP :
面向切面編程
在程序設(shè)計(jì)中,我們需要滿足高耦合低內(nèi)聚,所以編程需滿足六大原則,一個(gè)法則.
AOP面向切面編程正是為了滿足這些原則的一種編程思想.
一.裝飾者模式:
當(dāng)我們需要給對(duì)象增加功能時(shí),為了滿足單一職責(zé)原則,可利用裝飾者模式編程,創(chuàng)建一個(gè)類用來(lái)裝飾原來(lái)的類,這個(gè)類寫需要在原來(lái)的功能上增加的功能.
比如:一個(gè)類里面有一個(gè)增加圖書(shū)的功能,
@Service public class BookSericeImpl implements BookSerice { @Override public void addOne(BokBean bokBean) { System.out.println("執(zhí)行邏輯:插入一本書(shū)"); } @Override public void deletOne(Long bookId) { System.out.println("執(zhí)行邏輯:刪除一本書(shū)"); } }
我們需要在這個(gè)基礎(chǔ)上新增打印日志的功能,
public class BooklogServiceImpl implements BookSerice { private BookSerice bookSerice; public BooklogServiceImpl(BookSerice bookSerice) { this.bookSerice = bookSerice; } @Override public void addOne(BokBean bokBean) { System.out.println("準(zhǔn)備新增一本書(shū)"); this.bookSerice.addOne(bokBean); System.out.println("新增一本書(shū)完成"); } @Override public void deletOne(Long bookId) { System.out.println("準(zhǔn)備刪除一本書(shū)"); this.bookSerice.deletOne(323L); System.out.println("刪除一本書(shū)完成"); } }
下面我們調(diào)用這個(gè)增強(qiáng)過(guò)后的的對(duì)象
public void test1(){ //Aop :面向切面編程 //使用裝飾者模式設(shè)計(jì)對(duì)象 BookSerice bookSerice = new BookSericeImpl(); //把原來(lái)功能的對(duì)象通過(guò)構(gòu)造方傳給新增功能的類,并把新增功能類的對(duì)象賦給原來(lái)對(duì)象 //這里新增功能類和原來(lái)的類都是實(shí)現(xiàn)了同一個(gè)接口. bookSerice = new BooklogServiceImpl(bookSerice); //調(diào)用新增功能類的方法,在這個(gè)方法里讓構(gòu)造方法傳過(guò)去的對(duì)象調(diào)用原來(lái)的功能 bookSerice.addOne(new BokBean()); }
這樣我們就在不改變?cè)瓉?lái)代碼的基礎(chǔ)上新增了功能,并且也滿足單一職責(zé)的原則,降低了代碼的耦合性.
但是如果接口里面有很多方法,如果每個(gè)方法都需要增加日志功能,這樣就會(huì)出現(xiàn)很多重復(fù)代碼,并且裝飾者模式不能同時(shí)為多個(gè)沒(méi)有關(guān)系的類同時(shí)增強(qiáng)
所以java引入動(dòng)態(tài)代理技術(shù)來(lái)增加功能.
二.動(dòng)態(tài)代理
在java里動(dòng)態(tài)代理有兩個(gè)實(shí)現(xiàn)方式:
①針對(duì)有接口的類的代理,使用jdk中反射包下的動(dòng)態(tài)代理
②針對(duì)沒(méi)有接口的類的代理,使用第三方的jar包Enhancer
如果一個(gè)類既沒(méi)有接口,又是final,那么不能進(jìn)行增強(qiáng)
1.第一種實(shí)現(xiàn):
基于接口的動(dòng)態(tài)代理,使用java內(nèi)部反射包增強(qiáng)
這種方式創(chuàng)建對(duì)象是目標(biāo)對(duì)象的兄弟對(duì)象.
同樣上面是實(shí)現(xiàn)了接口的兩個(gè)功能的類:
@Service public class BookSericeImpl implements BookSerice { @Override public void addOne(BokBean bokBean) { System.out.println("執(zhí)行邏輯:插入一本書(shū)"); } @Override public void deletOne(Long bookId) { System.out.println("執(zhí)行邏輯:刪除一本書(shū)"); } }
調(diào)用通過(guò)對(duì)象調(diào)用上面兩個(gè)方法:
public void test2(){ //創(chuàng)建需要代理的對(duì)象 BookSerice bookSerice = new BookSericeImpl(); //根據(jù)對(duì)象的類獲取類加載器 ClassLoader classLoader = bookSerice.getClass().getClassLoader(); //獲取被代理對(duì)象說(shuō)實(shí)現(xiàn)的所有接口 Class<?>[] interfaces = bookSerice.getClass().getInterfaces(); //新建代理對(duì)象,里面參數(shù)需要(類加載器,一個(gè)對(duì)象所實(shí)現(xiàn)的接口,InvocationHandler接口類的對(duì)象) bookSerice = (BookSerice) Proxy.newProxyInstance(classLoader, interfaces, new LogHandler(bookSerice)); bookSerice.addOne(new BokBean()); bookSerice.deletOne(232L); }
在創(chuàng)建代理對(duì)象的時(shí)候需要一個(gè)InvocationHandler接口類的對(duì)象,下面創(chuàng)建一個(gè)該類的實(shí)現(xiàn)類
public class LogHandler implements InvocationHandler { //通過(guò)構(gòu)造方法接受一個(gè)沒(méi)有被代理的原來(lái)的對(duì)象 //通過(guò)下面的方法名的反射找到這個(gè)對(duì)象對(duì)應(yīng)方法 private Object target; public LogHandler(Object target) { this.target = target; } //當(dāng)代理對(duì)象調(diào)用原方法的時(shí)候,就會(huì)調(diào)用這個(gè)invoke方法 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String classname = target.getClass().getName(); String methodName = method.getName(); System.out.println(classname+"."+methodName+"方法開(kāi)始執(zhí)行"); //這里實(shí)際是Method類通過(guò)方法名反射調(diào)用了原方法(addone) Object value = method.invoke(target, args); System.out.println(classname+"."+methodName+"方法執(zhí)行完畢"); return value; } }
這樣實(shí)現(xiàn)了動(dòng)態(tài)代理之后,不管實(shí)現(xiàn)的接口里有多少個(gè)方法,你只需調(diào)用該方法,就會(huì)增強(qiáng)該方法,而不需要針對(duì)每個(gè)方法寫一遍增強(qiáng)功能,
并且這個(gè)增強(qiáng)類LogHandler類和原來(lái)的實(shí)現(xiàn)類BookSericeImpl類并沒(méi)有耦合性,這就是說(shuō)不管你是什么接口類的實(shí)現(xiàn)類,只需要對(duì)該類的對(duì)象進(jìn)行代理即可,就能對(duì)該類的方法添加上這個(gè)新增的功能
總的來(lái)說(shuō),這種動(dòng)態(tài)代理實(shí)現(xiàn)方式就是利用反射技術(shù),找到調(diào)用的方法名,針對(duì)這個(gè)方法進(jìn)行增強(qiáng).
如果當(dāng)不需要對(duì)某方法增加功能時(shí),就不用不帶.
2.第二種實(shí)現(xiàn):
基于類的動(dòng)態(tài)代理,使用cglib框架.
這種方式創(chuàng)建的代理對(duì)象是目標(biāo)對(duì)象的子類對(duì)象
第二種方式是利用第三方j(luò)ar包來(lái)實(shí)現(xiàn),下載CGLIB包:
利用jar包中的Enhancer類創(chuàng)建增強(qiáng)對(duì)象.
創(chuàng)建增強(qiáng)對(duì)象需要根據(jù)原對(duì)象的類名創(chuàng)建類增強(qiáng)器,還需要根據(jù)原對(duì)象的類型創(chuàng)建子類代理對(duì)象
屬性通過(guò)增強(qiáng)對(duì)象set方法賦值,上一種方式是通過(guò)調(diào)用方法Proxy.newProxyInstance傳參.
public void test3(){ //創(chuàng)建需要代理增強(qiáng)的對(duì)象 BookSerice bookSerice = new BookSericeImpl(); Enhancer enhancer = new Enhancer(); //用增強(qiáng)器對(duì)象創(chuàng)建類增強(qiáng)器 enhancer.setClassLoader(bookSerice.getClass().getClassLoader()); //因?yàn)閯?chuàng)建的代理對(duì)象是目標(biāo)對(duì)象的子類,所以這里填的就是目標(biāo)對(duì)象的類 enhancer.setSuperclass(bookSerice.getClass()); //創(chuàng)建代理對(duì)象,這里需要的參數(shù)是Callback接口的對(duì)象,所以需要?jiǎng)?chuàng)建一個(gè)接口的實(shí)現(xiàn)類. enhancer.setCallback(new TimeMethodInterceptor(bookSerice)); //把代理對(duì)象賦給原對(duì)象 bookSerice = (BookSerice) enhancer.create(); bookSerice.addOne(new BokBean()); bookSerice.deletOne(1l); }
創(chuàng)建Callback接口的實(shí)現(xiàn)類,也就是功能增強(qiáng)部分,
這一部分跟第一種方式的實(shí)現(xiàn)是一樣的,都是通過(guò)反射在添加功能過(guò)程中調(diào)用原方法.
//Callback接口沒(méi)有實(shí)現(xiàn)方法,所以這里實(shí)現(xiàn)的是他的子接口 public class TimeMethodInterceptor implements MethodInterceptor { private Object target; public TimeMethodInterceptor(Object target) { this.target = target; } @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { //記錄當(dāng)前系統(tǒng)時(shí)間 //這個(gè)時(shí)間是從1970年1月1日 0點(diǎn)0分到現(xiàn)在的毫秒數(shù) long start = System.currentTimeMillis(); Object value = method.invoke(target, objects); long time = System.currentTimeMillis() - start; System.out.println("當(dāng)前時(shí)長(zhǎng)"+time+"毫秒"); return null; } }
總結(jié):
兩種方法的區(qū)別:
第一種是用jdk內(nèi)部方法創(chuàng)建代理對(duì)象,由于創(chuàng)建過(guò)程中需要一個(gè)對(duì)象的接口,所以只能針對(duì)有接口類的對(duì)象進(jìn)行代理.
第二種是利用第三方j(luò)ar包中的增強(qiáng)器(Enhancer)創(chuàng)建代理對(duì)象,通過(guò)set方法給需要的屬性賦值.由于沒(méi)有接口實(shí)現(xiàn),所以創(chuàng)建的是對(duì)象的子類代理對(duì)象.
相關(guān)文章
IDEA下SpringBoot指定環(huán)境、配置文件啟動(dòng)操作過(guò)程
這篇文章主要介紹了IDEA下SpringBoot指定環(huán)境、配置文件啟動(dòng)過(guò)程,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-08-08詳解Lombok安裝及Spring Boot集成Lombok
這篇文章主要介紹了詳解Lombok安裝及Spring Boot集成Lombok,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2019-03-03SpringBoot實(shí)現(xiàn)對(duì)Http接口進(jìn)行監(jiān)控的代碼
Spring Boot Actuator是Spring Boot提供的一個(gè)模塊,用于監(jiān)控和管理Spring Boot應(yīng)用程序的運(yùn)行時(shí)信息,本文將介紹一下Spring Boot Actuator以及代碼示例,以及如何進(jìn)行接口請(qǐng)求監(jiān)控,需要的朋友可以參考下2024-07-07java數(shù)據(jù)結(jié)構(gòu)與算法之簡(jiǎn)單選擇排序詳解
這篇文章主要介紹了java數(shù)據(jù)結(jié)構(gòu)與算法之簡(jiǎn)單選擇排序,結(jié)合實(shí)例形式分析了選擇排序的原理、實(shí)現(xiàn)方法與相關(guān)操作技巧,需要的朋友可以參考下2017-05-05Spring Boot中使用Activiti的方法教程(二)
工作流(Workflow),就是“業(yè)務(wù)過(guò)程的部分或整體在計(jì)算機(jī)應(yīng)用環(huán)境下的自動(dòng)化”,下面這篇文章主要給大家介紹了關(guān)于Spring Boot中使用Activiti的相關(guān)資料,需要的朋友可以參考下2018-08-08Java實(shí)現(xiàn)文件切割拼接的實(shí)現(xiàn)代碼
這篇文章主要介紹了Java實(shí)現(xiàn)文件切割拼接的實(shí)現(xiàn)代碼,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-11-11