java AOP原理以及實例用法總結(jié)
AOP :
面向切面編程
在程序設(shè)計中,我們需要滿足高耦合低內(nèi)聚,所以編程需滿足六大原則,一個法則.
AOP面向切面編程正是為了滿足這些原則的一種編程思想.
一.裝飾者模式:
當(dāng)我們需要給對象增加功能時,為了滿足單一職責(zé)原則,可利用裝飾者模式編程,創(chuàng)建一個類用來裝飾原來的類,這個類寫需要在原來的功能上增加的功能.
比如:一個類里面有一個增加圖書的功能,
@Service
public class BookSericeImpl implements BookSerice {
@Override
public void addOne(BokBean bokBean) {
System.out.println("執(zhí)行邏輯:插入一本書");
}
@Override
public void deletOne(Long bookId) {
System.out.println("執(zhí)行邏輯:刪除一本書");
}
}
我們需要在這個基礎(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)備新增一本書");
this.bookSerice.addOne(bokBean);
System.out.println("新增一本書完成");
}
@Override
public void deletOne(Long bookId) {
System.out.println("準(zhǔn)備刪除一本書");
this.bookSerice.deletOne(323L);
System.out.println("刪除一本書完成");
}
}
下面我們調(diào)用這個增強過后的的對象
public void test1(){
//Aop :面向切面編程
//使用裝飾者模式設(shè)計對象
BookSerice bookSerice = new BookSericeImpl();
//把原來功能的對象通過構(gòu)造方傳給新增功能的類,并把新增功能類的對象賦給原來對象
//這里新增功能類和原來的類都是實現(xiàn)了同一個接口.
bookSerice = new BooklogServiceImpl(bookSerice);
//調(diào)用新增功能類的方法,在這個方法里讓構(gòu)造方法傳過去的對象調(diào)用原來的功能
bookSerice.addOne(new BokBean());
}
這樣我們就在不改變原來代碼的基礎(chǔ)上新增了功能,并且也滿足單一職責(zé)的原則,降低了代碼的耦合性.
但是如果接口里面有很多方法,如果每個方法都需要增加日志功能,這樣就會出現(xiàn)很多重復(fù)代碼,并且裝飾者模式不能同時為多個沒有關(guān)系的類同時增強
所以java引入動態(tài)代理技術(shù)來增加功能.
二.動態(tài)代理
在java里動態(tài)代理有兩個實現(xiàn)方式:
①針對有接口的類的代理,使用jdk中反射包下的動態(tài)代理
②針對沒有接口的類的代理,使用第三方的jar包Enhancer
如果一個類既沒有接口,又是final,那么不能進行增強
1.第一種實現(xiàn):
基于接口的動態(tài)代理,使用java內(nèi)部反射包增強
這種方式創(chuàng)建對象是目標(biāo)對象的兄弟對象.
同樣上面是實現(xiàn)了接口的兩個功能的類:
@Service
public class BookSericeImpl implements BookSerice {
@Override
public void addOne(BokBean bokBean) {
System.out.println("執(zhí)行邏輯:插入一本書");
}
@Override
public void deletOne(Long bookId) {
System.out.println("執(zhí)行邏輯:刪除一本書");
}
}
調(diào)用通過對象調(diào)用上面兩個方法:
public void test2(){
//創(chuàng)建需要代理的對象
BookSerice bookSerice = new BookSericeImpl();
//根據(jù)對象的類獲取類加載器
ClassLoader classLoader = bookSerice.getClass().getClassLoader();
//獲取被代理對象說實現(xiàn)的所有接口
Class<?>[] interfaces = bookSerice.getClass().getInterfaces();
//新建代理對象,里面參數(shù)需要(類加載器,一個對象所實現(xiàn)的接口,InvocationHandler接口類的對象)
bookSerice = (BookSerice) Proxy.newProxyInstance(classLoader, interfaces, new LogHandler(bookSerice));
bookSerice.addOne(new BokBean());
bookSerice.deletOne(232L);
}
在創(chuàng)建代理對象的時候需要一個InvocationHandler接口類的對象,下面創(chuàng)建一個該類的實現(xiàn)類
public class LogHandler implements InvocationHandler {
//通過構(gòu)造方法接受一個沒有被代理的原來的對象
//通過下面的方法名的反射找到這個對象對應(yīng)方法
private Object target;
public LogHandler(Object target) {
this.target = target;
}
//當(dāng)代理對象調(diào)用原方法的時候,就會調(diào)用這個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+"方法開始執(zhí)行");
//這里實際是Method類通過方法名反射調(diào)用了原方法(addone)
Object value = method.invoke(target, args);
System.out.println(classname+"."+methodName+"方法執(zhí)行完畢");
return value;
}
}
這樣實現(xiàn)了動態(tài)代理之后,不管實現(xiàn)的接口里有多少個方法,你只需調(diào)用該方法,就會增強該方法,而不需要針對每個方法寫一遍增強功能,
并且這個增強類LogHandler類和原來的實現(xiàn)類BookSericeImpl類并沒有耦合性,這就是說不管你是什么接口類的實現(xiàn)類,只需要對該類的對象進行代理即可,就能對該類的方法添加上這個新增的功能
總的來說,這種動態(tài)代理實現(xiàn)方式就是利用反射技術(shù),找到調(diào)用的方法名,針對這個方法進行增強.
如果當(dāng)不需要對某方法增加功能時,就不用不帶.
2.第二種實現(xiàn):
基于類的動態(tài)代理,使用cglib框架.
這種方式創(chuàng)建的代理對象是目標(biāo)對象的子類對象
第二種方式是利用第三方j(luò)ar包來實現(xiàn),下載CGLIB包:

利用jar包中的Enhancer類創(chuàng)建增強對象.
創(chuàng)建增強對象需要根據(jù)原對象的類名創(chuàng)建類增強器,還需要根據(jù)原對象的類型創(chuàng)建子類代理對象
屬性通過增強對象set方法賦值,上一種方式是通過調(diào)用方法Proxy.newProxyInstance傳參.
public void test3(){
//創(chuàng)建需要代理增強的對象
BookSerice bookSerice = new BookSericeImpl();
Enhancer enhancer = new Enhancer();
//用增強器對象創(chuàng)建類增強器
enhancer.setClassLoader(bookSerice.getClass().getClassLoader());
//因為創(chuàng)建的代理對象是目標(biāo)對象的子類,所以這里填的就是目標(biāo)對象的類
enhancer.setSuperclass(bookSerice.getClass());
//創(chuàng)建代理對象,這里需要的參數(shù)是Callback接口的對象,所以需要創(chuàng)建一個接口的實現(xiàn)類.
enhancer.setCallback(new TimeMethodInterceptor(bookSerice));
//把代理對象賦給原對象
bookSerice = (BookSerice) enhancer.create();
bookSerice.addOne(new BokBean());
bookSerice.deletOne(1l);
}
創(chuàng)建Callback接口的實現(xiàn)類,也就是功能增強部分,
這一部分跟第一種方式的實現(xiàn)是一樣的,都是通過反射在添加功能過程中調(diào)用原方法.
//Callback接口沒有實現(xiàn)方法,所以這里實現(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)時間
//這個時間是從1970年1月1日 0點0分到現(xiàn)在的毫秒數(shù)
long start = System.currentTimeMillis();
Object value = method.invoke(target, objects);
long time = System.currentTimeMillis() - start;
System.out.println("當(dāng)前時長"+time+"毫秒");
return null;
}
}
總結(jié):
兩種方法的區(qū)別:
第一種是用jdk內(nèi)部方法創(chuàng)建代理對象,由于創(chuàng)建過程中需要一個對象的接口,所以只能針對有接口類的對象進行代理.
第二種是利用第三方j(luò)ar包中的增強器(Enhancer)創(chuàng)建代理對象,通過set方法給需要的屬性賦值.由于沒有接口實現(xiàn),所以創(chuàng)建的是對象的子類代理對象.
相關(guān)文章
Java基礎(chǔ)之JDBC的數(shù)據(jù)庫連接與基本操作
這篇文章主要介紹了Java基礎(chǔ)之JDBC的數(shù)據(jù)庫連接與基本操作,文中有非常詳細(xì)的代碼示例,對正在學(xué)習(xí)java基礎(chǔ)的小伙伴們也有很好的幫助,需要的朋友可以參考下2021-05-05
Java如何通過"枚舉的枚舉"表示二級分類的業(yè)務(wù)場景
這篇文章主要介紹了Java如何通過"枚舉的枚舉"表示二級分類的業(yè)務(wù)場景問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-06-06
mybatis-plus 新增/修改如何實現(xiàn)自動填充指定字段
這篇文章主要介紹了mybatis-plus 新增/修改實現(xiàn)自動填充指定字段方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-06-06
學(xué)習(xí)Java多線程之線程定義、狀態(tài)和屬性
這篇文章主要為大家詳細(xì)介紹了Java多線程之線程定義、狀態(tài)和屬性,感興趣的小伙伴們可以參考一下2016-02-02

