目錄
1. Web MVC發(fā)展史歷程
2.Spring概要
3.Spring-依賴注入概要(IOC)
4.屬性注入的三種實(shí)現(xiàn)方式
5.Spring-IoC XML裝配
6.Spring-XML設(shè)置Bean的值
7.Spring-IoC 注解(1)
8.Spring-IoC 注解(2)
9.Spring-AOP切面編程(1)
10.Spring-AOP切面編程(2)
11.Spring-AOP切面編程(3)
未完待續(xù)...
一、概要
軟件開發(fā)一直在尋求一種高效匿醒、護(hù)展戏蔑、維護(hù)的方式椭赋。
面向?qū)ο蟮奶攸c(diǎn)是繼承摩桶、多態(tài)和封裝赴精。而封裝的核心就是將功能分散到不同的對象中去胖喳,這在軟件設(shè)計(jì)中往往稱為職責(zé)分配以清。也就是說儿普,讓不同的類設(shè)計(jì)不同的方法。這樣代碼就分散到一個(gè)個(gè)的類中去了掷倔。這樣做的好處是降低了代碼的復(fù)雜程度眉孩,使類的復(fù)用性增加。但是新的問題又來了勒葱,在分散代碼的同時(shí)浪汪,也增加了代碼的重復(fù)性。什么意思呢凛虽?比如說死遭,我們在兩個(gè)類中,可能都需要在每個(gè)方法中做日志凯旋。按面向?qū)ο蟮脑O(shè)計(jì)方法呀潭,我們就必須在兩個(gè)類的方法中都加入日志的內(nèi)容。也許他們是完全相同的至非,但就是因?yàn)槊嫦驅(qū)ο蟮脑O(shè)計(jì)讓類與類之間無法聯(lián)系钠署,而不能將這些重復(fù)的代碼統(tǒng)一起來。
也許有人會(huì)說荒椭,那好辦啊谐鼎,我們可以將這段代碼寫在一個(gè)獨(dú)立的類獨(dú)立的方法里,然后再在這兩個(gè)類中調(diào)用趣惠。但是狸棍,這樣一來,這兩個(gè)類跟我們上面提到的獨(dú)立的類就有耦合了味悄,它的改變會(huì)影響這兩個(gè)類草戈。那么,有沒有什么辦法侍瑟,能讓我們在需要的時(shí)候猾瘸,隨意地加入代碼呢?這種在運(yùn)行時(shí),動(dòng)態(tài)地將代碼切入到類的指定方法牵触、指定位置上的編程思想就是面向切面的編程,我們把切入到指定類指定方法的代碼片段稱為切面咐低,而切入到哪些類揽思、哪些方法則叫切入點(diǎn)。有了AOP见擦,我們就可以把幾個(gè)類共有的代碼钉汗,抽取到一個(gè)切片中,等到需要時(shí)再切入對象中去鲤屡,從而改變其原有的行為损痰。
二、面向切面編程
1酒来、什么切面編程
面向切面編程(也叫面向方面):Aspect Oriented Programming(AOP)卢未。利用AOP可以對業(yè)務(wù)邏輯的各個(gè)部分進(jìn)行隔離,從而使得業(yè)務(wù)邏輯各部分之間的耦合度降低堰汉,提高程序的可重用性辽社,同時(shí)提高了開發(fā)的效率。AOP是OOP的補(bǔ)充和完善
2翘鸭、主要解決的問題
解決代碼復(fù)用的問題
解決關(guān)注點(diǎn)分離
水平分離: 例如 控制層—服務(wù)層—數(shù)據(jù)庫
垂直分離: 例如 用戶模塊—商品模塊
關(guān)注點(diǎn)分離(功能): 業(yè)務(wù)和非業(yè)務(wù)
3滴铅、主要的應(yīng)用場景
- 日志記錄
- 性能統(tǒng)計(jì)
- 安全控制
- 事務(wù)處理
- 異常處理
- 權(quán)限處理等等
三、實(shí)例講解
1就乓、原始代碼
- 需求
比如我們要寫一個(gè)UserService汉匙,在調(diào)用保存用戶和刪除用戶的時(shí)候需要去打開數(shù)據(jù)庫和開啟事務(wù) -
示例代碼
image - 說明
通過上面的代碼我們發(fā)現(xiàn)打開數(shù)據(jù),開啟事務(wù),提交事務(wù)生蚁,關(guān)閉數(shù)據(jù)庫是重復(fù)的代碼噩翠,有重復(fù)的代碼我們怎么辦?
通過類封裝成方法提取重復(fù)的代碼啊!(抽取成類的方式我們稱之為:縱向抽取)
2、提取類封裝成方法
- 說明
這個(gè)時(shí)候我們新建一個(gè)DBManager類 定義四個(gè)方法 - 示例代碼
public class DBManager { public void open() { System.out.println("打開數(shù)據(jù)庫..."); } public void colse() { System.out.println("關(guān)閉數(shù)據(jù)庫..."); } public void begin() { System.out.println("開啟事務(wù)"); } public void commit() { System.out.println("提交事務(wù)"); } }
public class UserService { private DBManager manager = new DBManager(); public void save() { manager.open(); manager.begin(); System.out.println("保存用戶信息"); manager.commit(); manager.colse(); } public void delete() { manager.open(); manager.begin(); System.out.println("刪除用戶"); manager.commit(); manager.colse(); } }
- 說明
通過上面的案例解決了代碼重復(fù)性的問題,但同時(shí)也會(huì)帶來另外兩個(gè)問題:- 耦合度:會(huì)造成封裝類和業(yè)務(wù)類的耦合,
- 侵入性強(qiáng):被我們提取的邏輯代碼還是融合到業(yè)務(wù)邏輯中
但是守伸,這種做法相比最原始的代碼寫法绎秒,已經(jīng)有了一些的改進(jìn),那么有沒有一種方案能解決耦合度,侵入性強(qiáng)的問題,
答案就是代理模式
四尼摹、代理模式
1见芹、代理模式概要
代理模式是一種非常好理解的一種設(shè)計(jì)模式,舉幾個(gè)簡單的代理的例子:
- 比如玩游戲升級太麻煩了,這個(gè)是時(shí)候我們可以去請代練幫我們升級,那這個(gè)代練其實(shí)就是一個(gè)代理
- 比如我們回家過年買不到火車票,通常加價(jià)請第三方的一些黃牛幫我們?nèi)ベI
- 歌星或者明星都有一個(gè)自己的經(jīng)紀(jì)人蠢涝,這個(gè)經(jīng)紀(jì)人就是他們的代理人玄呛,當(dāng)我們需要找明星表演時(shí),不能直接找到該明星和二,只能是找明星的經(jīng)紀(jì)人
讓代練幫我們升級,我們可能就想下副本,讓黃牛幫我們買票,我們就的目的就是想回家,明星讓經(jīng)紀(jì)人接拍電影,明星只是需要去拍電影就行了徘铝。無論是游戲代練,黃牛,還是經(jīng)紀(jì)人其它他們其實(shí)都是在幫我們在做事(做一些我們不想做,或者做不來的事情),但并不能把所有的事情都幫我們做了,比如黃牛幫我們買點(diǎn)票了,回家你還的自己回吧,
2、分類
- 靜態(tài)
由程序員創(chuàng)建代理類或特定工具自動(dòng)生成源代碼再對其編譯。在程序運(yùn)行前代理類的.class文件就已經(jīng)存在了惕它。 - 動(dòng)態(tài)
在程序運(yùn)行時(shí)運(yùn)用反射機(jī)制動(dòng)態(tài)創(chuàng)建而成怕午。
3、作用
代理解決的問題當(dāng)兩個(gè)類需要通信時(shí)淹魄,引入第三方代理類郁惜,將兩個(gè)類的關(guān)系解耦,讓我們只了解代理類即可甲锡,而且代理的出現(xiàn)還可以讓我們完成與另一個(gè)類之間的關(guān)系的統(tǒng)一管理兆蕉,但必須,代理類和委托類要實(shí)現(xiàn)相同的接口缤沦,因?yàn)榇碚嬲{(diào)用的還是委托類的方法
五虎韵、靜態(tài)代理
1、說明
2缸废、示例代碼
- 業(yè)務(wù)代碼
/** * 簡單業(yè)務(wù)層接口 */ public interface UserService{ public void saveUser(); } /** * 業(yè)務(wù)層實(shí)現(xiàn)類,實(shí)現(xiàn)save方法 */ public class UserServiceImpl implements UserService{ @Override public void saveUser() { System.out.println("2:保存用戶信息"); } }
- 代理類
/** * 代理類 */ public class UserServiceProxy implements UserService{ private UserService userService; public UserServiceProxy(UserService userService) { super(); this.userService = userService; } public void open(){ System.out.println("1:打開數(shù)據(jù)庫連接"); } public void close(){ System.out.println("3:關(guān)閉數(shù)據(jù)庫連接"); } @Override public void saveUser() { this.open(); userService.saveUser(); this.close(); } }
- 測試代碼
/** * 測試類 */ public class TestProxy { public static void main(String[] args) { UserService userService =new UserServiceProxy(new UserServiceImpl()); userService.saveUser(); } }
3包蓝、分析
1、優(yōu)點(diǎn)
- 代理使客戶端不需要知道實(shí)現(xiàn)類是什么呆奕,怎么做的养晋,而客戶端只需知道代理即可(解耦合)
2、缺點(diǎn)
- 代理類和委托類實(shí)現(xiàn)了相同的接口梁钾,代理類通過委托類實(shí)現(xiàn)了相同的方法绳泉。這樣就出現(xiàn)了大量的代碼重復(fù)。如果接口增加一個(gè)方法姆泻,除了所有實(shí)現(xiàn)類需要實(shí)現(xiàn)這個(gè)方法外零酪,所有代理類也需要實(shí)現(xiàn)此方法。增加了代碼維護(hù)的復(fù)雜度
- 代理對象只服務(wù)于一種類型的對象拇勃,如果要服務(wù)多類型的對象四苇。勢必要為每一種對象都進(jìn)行代理,靜態(tài)代理在程序規(guī)模稍大時(shí)就無法勝任了
由于每個(gè)代理類只能為一個(gè)接口服務(wù)方咆,這樣程序開發(fā)中必然會(huì)產(chǎn)生許多的代理類
所以我們就會(huì)想能不能通過一個(gè)代理類完成全部的代理功能?月腋,那么我們就需要用動(dòng)態(tài)代理
六、JDK動(dòng)態(tài)代理
1瓣赂、概念
JDK動(dòng)態(tài)代理所用到的代理類在程序調(diào)用到代理類對象時(shí)才由JVM真正創(chuàng)建榆骚,JVM根據(jù)傳進(jìn)來的業(yè)務(wù)實(shí)現(xiàn)類對象 以及 方法名 ,動(dòng)態(tài)地創(chuàng)建了一個(gè)代理類并執(zhí)行煌集,然后通過該代理類對象進(jìn)行方法調(diào)用妓肢。我們需要做的,只需指定代理類的預(yù)處理苫纤、調(diào)用后操作即可
目前Java開發(fā)包中包含了對動(dòng)態(tài)代理的支持碉钠,但是其實(shí)現(xiàn)只支持對接口的的實(shí)現(xiàn)纲缓。 其實(shí)現(xiàn)主要通過java.lang.reflect.Proxy類和java.lang.reflect.InvocationHandler接口。
Proxy類主要用來獲取動(dòng)態(tài)代理對象喊废,
InvocationHandler接口用來約束調(diào)用者實(shí)現(xiàn)
2祝高、重要類介紹
2.1、Proxy
- 說明
這是 Java 動(dòng)態(tài)代理機(jī)制的主類污筷,它提供了一組靜態(tài)方法來為一組接口動(dòng)態(tài)地生成代理類及其對象 - 核心方法
返回值 方法 說明 static InvocationHandler getInvocationHandler(Object proxy) 方法返回指定接口的代理類的實(shí)例褂策,這些接口將調(diào)用方法調(diào)用到指定的調(diào)用處理程序。 static Class getProxyClass(ClassLoader loader, Class[] interfaces) 用于獲取關(guān)聯(lián)于指定類裝載器和一組接口的動(dòng)態(tài)代理類的類對象 static boolean isProxyClass(Class cls) 該方法用于判斷指定類對象是否是一個(gè)動(dòng)態(tài)代理類 staticObject newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) 用于為指定類裝載器颓屑、一組接口及調(diào)用處理器生成動(dòng)態(tài)代理類實(shí)例
2.2、InvocationHandler
- 說明
這是調(diào)用處理器接口耿焊,它自定義了一個(gè) invoke 方法揪惦,用于集中處理在動(dòng)態(tài)代理類對象上的方法調(diào)用,通常在該方法中實(shí)現(xiàn)對委托類的代理訪問 - 語法
// 該方法負(fù)責(zé)集中處理動(dòng)態(tài)代理類上的所有方法調(diào)用罗侯。器腋,第二個(gè)參數(shù)是被調(diào)用的方法對象 // 第三個(gè)方法是調(diào)用參數(shù)。調(diào)用處理器根據(jù)這三個(gè)參數(shù)進(jìn)行預(yù)處理或分派到委托類實(shí)例上發(fā)射執(zhí)行 Object invoke(Object proxy, Method method, Object[] args)
- 參數(shù)說明
- proxy
第一個(gè)參數(shù)既是代理類實(shí)例 - method
被調(diào)用的方法對象 - args
調(diào)用參數(shù)钩杰。調(diào)用處理器根據(jù)這三個(gè)參數(shù)進(jìn)行預(yù)處理或分派到委托類實(shí)例上發(fā)射執(zhí)行
- proxy
3纫塌、示例代碼
3.1、聲明代理類
- 說明
動(dòng)態(tài)代理類只能代理接口(不支持抽象類)讲弄,代理類都需要實(shí)現(xiàn)InvocationHandler類措左,實(shí)現(xiàn)invoke方法。該invoke方法就是調(diào)用被代理接口的所有方法時(shí)需要調(diào)用的避除,該invoke方法返回的值是被代理接口的一個(gè)實(shí)現(xiàn)類 - 示例代碼
// 第一步 定義代理類,實(shí)現(xiàn)InvocationHandler接口 public class DynamicProxy implements InvocationHandler { //代理目標(biāo)對象 private Object target; public Object newProxyInstance(Object object) { this.target = object; return Proxy.newProxyInstance(object.getClass().getClassLoader() , object.getClass().getInterfaces(), this); } /** * 關(guān)聯(lián)的這個(gè)實(shí)現(xiàn)類的方法被調(diào)用時(shí)將被執(zhí)行 * @param proxy 定義代理類的類的實(shí)例 * @param method 代理類要實(shí)現(xiàn)的接口列表 * @param args 指派方法調(diào)用的調(diào)用處理程序 * @return 返回執(zhí)行的目標(biāo)對象 * @throws Throwable */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("====start===="); /** * 打印方法的參數(shù) */ if (args != null && 0 != args.length) { for (Object arg : args) { System.out.println(arg); } } System.out.println("====方法被執(zhí)行前===="); Object invoke = null; try { String name = method.getName(); if (name.equals("add") || name.equals("delete")) { // 核心方法被執(zhí)行 invoke = method.invoke(target, args); System.out.println("====方法被執(zhí)行后===="); } } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { System.out.println("====方法出錯(cuò)誤了===="); } System.out.println("====end===="); return invoke; } }
3.2 聲明代理接口與代理類
- 說明
jdk動(dòng)態(tài)代理只能代理接口,所以第一步先聲明接口,然后在定義個(gè)實(shí)現(xiàn)類 - 示例代碼
//代理接口類 public interface UserDao { void add(String name); void delete(String uid); } public interface ShopDao { boolean addShop(); }
//代理接口的實(shí)現(xiàn)類 public class UserDaoImpl implements UserDao { @Override public void add(String name) { System.out.println("add:===" + name); } @Override public void delete(String uid) { System.out.println("delete" + uid); } public class ShopDaoImpl implements ShopDao { @Override public boolean addShop() { System.out.println("核心方法====>添加商品信息"); return false; } }
- 測試代碼
public class TestProxy { public static void main(String[] args) { DynamicProxy proxy = new DynamicProxy(); UserDao userDao = (UserDao) proxy.newProxyInstance(new UserDaoImpl()); userDao.add(); userDao.delete(); ShopDao shopDao = (ShopDao) proxy.newProxyInstance(new ShopDaoImpl()); shopDao.addShop(); } }
3.3怎披、總結(jié)
可以看到,我們可以通過DynamicProxy代理不同類型的對象瓶摆,如果我們把對外的接口都通過動(dòng)態(tài)代理來實(shí)現(xiàn)凉逛,那么所有的函數(shù)調(diào)用最終都會(huì)經(jīng)過invoke函數(shù)的轉(zhuǎn)發(fā),因此我們就可以在這里做一些自己想做的操作
七群井、CGLib動(dòng)態(tài)代理
1状飞、說明
JDK實(shí)現(xiàn)動(dòng)態(tài)代理需要實(shí)現(xiàn)類通過接口定義業(yè)務(wù)方法,對于沒有接口的類书斜,如何實(shí)現(xiàn)動(dòng)態(tài)代理呢诬辈,這就需要CGLib了。CGLib采用了非常底層的字節(jié)碼技術(shù)菩佑,其原理是通過字節(jié)碼技術(shù)為一個(gè)類創(chuàng)建子類自晰,并在子類中采用方法攔截的技術(shù)攔截所有父類方法的調(diào)用,順勢織入橫切邏輯稍坯。JDK動(dòng)態(tài)代理與CGLib動(dòng)態(tài)代理均是實(shí)現(xiàn)Spring AOP的基礎(chǔ)酬荞。
因?yàn)槭堑谌降乃行枰獙?dǎo)入第三方的jar包
<!-- https://mvnrepository.com/artifact/cglib/cglib --> <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.2.10</version> </dependency>
2搓劫、示例代碼
- 定義代理類
//代理對象 public class AccountDaoImpl{ public void add(String name) { System.out.println("add:===" + name); } }
- 定義動(dòng)態(tài)代理類代理類
import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; /** * 實(shí)現(xiàn)了一個(gè)方法攔截器接口 */ public class CglibProxy implements MethodInterceptor { // 實(shí)例化動(dòng)態(tài)代碼生成器 private Enhancer enhancer = new Enhancer(); /** * 動(dòng)態(tài)生成一個(gè)新的類,使用父類的無參構(gòu)造方法創(chuàng)建一個(gè)指定了特定回調(diào)的代理實(shí)例 * * @param clazz 被代理對象 * @return */ public Object getProxy(Class clazz) { //設(shè)置需要?jiǎng)?chuàng)建子類的類 enhancer.setSuperclass(clazz); //設(shè)置回調(diào)方法 enhancer.setCallback(this); //通過字節(jié)碼技術(shù)動(dòng)態(tài)創(chuàng)建子類實(shí)例 return enhancer.create(); } /** * 實(shí)現(xiàn)MethodInterceptor接口方法 * 攔截被代理對象的方法 * * @param obj 代理類實(shí)例 * @param method 被代理類所調(diào)用的被代理的方法 * @param args 參數(shù)值列表 * @param proxy 生成的代理類對方法的代理引用 * @return * @throws Throwable */ @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println(obj.getClass().getName()); System.out.println("前置代理"); //通過代理類調(diào)用父類中的方法 Object result = proxy.invokeSuper(obj, args); System.out.println("后置代理"); System.out.println(proxy.getClass().getName()); return result; } }
- 測試
public class TestCglib { public static void main(String[] args) { CglibProxy proxy = new CglibProxy(); //通過生成子類的方式創(chuàng)建代理類 UserDaoImpl dao = (UserDaoImpl) proxy.getProxy(UserDaoImpl.class); dao.add(); } }
八混巧、總結(jié)
1枪向、動(dòng)態(tài)代理和靜態(tài)代理相比較,
最大的好處就是接口中聲明的所有的方法都被轉(zhuǎn)移到一個(gè)集中的方法中去處理咧党,就是invocke()方法.這樣在接口中聲明的方法比較多的情況下我們可以進(jìn)行靈活處理秘蛔,而不需要像靜態(tài)代理那樣每一個(gè)方法進(jìn)行中轉(zhuǎn)。
2傍衡、CGLib與JDK動(dòng)態(tài)代理的區(qū)別
- 原理
- java動(dòng)態(tài)代理是利用反射機(jī)制生成一個(gè)實(shí)現(xiàn)代理接口的匿名類深员,在調(diào)用具體方法前調(diào)用InvokeHandler來處理。
- 而cglib動(dòng)態(tài)代理是利用asm開源包蛙埂,對代理對象類的class文件加載進(jìn)來倦畅,通過修改其字節(jié)碼生成子類來處理
- JDK動(dòng)態(tài)代理和CGLIB字節(jié)碼生成的區(qū)別
- JDK動(dòng)態(tài)代理只能對實(shí)現(xiàn)了接口的類生成代理,而不能針對類
- CGLIB是針對類實(shí)現(xiàn)代理绣的,主要是對指定的類生成一個(gè)子類叠赐,覆蓋其中的方法,因?yàn)槭抢^承屡江,所以該類或方法最好不要聲明成final
- 速度上
- 在jdk6以后sun公司對JDK動(dòng)態(tài)代理進(jìn)行優(yōu)化,在調(diào)用次數(shù)較少的情況下芭概,JDK代理效率高于CGLIB代理效率
- 只有當(dāng)進(jìn)行大量調(diào)用的時(shí)候,jdk6和jdk7比CGLIB代理效率低一點(diǎn)惩嘉,但是到j(luò)dk8的時(shí)候罢洲,jdk代理效率高于CGLIB代理
3、實(shí)現(xiàn)原理
- 通過實(shí)現(xiàn) InvocationHandler 接口創(chuàng)建自己的調(diào)用處理器宏怔;
- 通過為 Proxy 類指定 ClassLoader 對象和一組 interface 來創(chuàng)建動(dòng)態(tài)代理類奏路;
- 通過反射機(jī)制獲得動(dòng)態(tài)代理類的構(gòu)造函數(shù),其唯一參數(shù)類型是調(diào)用處理器接口類型臊诊;
- 通過構(gòu)造函數(shù)創(chuàng)建動(dòng)態(tài)代理類實(shí)例鸽粉,構(gòu)造時(shí)調(diào)用處理器對象作為參數(shù)被傳入。
// InvocationHandlerImpl 實(shí)現(xiàn)了 InvocationHandler 接口抓艳,并能實(shí)現(xiàn)方法調(diào)用從代理類到委托類的分派轉(zhuǎn)發(fā) // 其內(nèi)部通常包含指向委托類實(shí)例的引用触机,用于真正執(zhí)行分派轉(zhuǎn)發(fā)過來的方法調(diào)用 InvocationHandler handler = new InvocationHandlerImpl(..); // 通過 Proxy 為包括 Interface 接口在內(nèi)的一組接口動(dòng)態(tài)創(chuàng)建代理類的類對象 Class clazz = Proxy.getProxyClass(classLoader, new Class[] { Interface.class, ... }); // 通過反射從生成的類對象獲得構(gòu)造函數(shù)對象 Constructor constructor = clazz.getConstructor(new Class[] { InvocationHandler.class }); // 通過構(gòu)造函數(shù)對象創(chuàng)建動(dòng)態(tài)代理類實(shí)例 Interface Proxy = (Interface)constructor.newInstance(new Object[] { handler });
4、注意事項(xiàng)
- 包:如果所代理的接口都是 public 的玷或,那么它將被定義在頂層包(即包路徑為空)儡首,如果所代理的接口中有非 public 的接口(因?yàn)榻涌诓荒鼙欢x為 protect 或 private,所以除 public 之外就是默認(rèn)的 package 訪問級別)偏友,那么它將被定義在該接口所在包(假設(shè)代理了 com.xxx.xxx 包中的某非 public 接口 A蔬胯,那么新生成的代理類所在的包就是 com.xxx.xxx),這樣設(shè)計(jì)的目的是為了最大程度的保證動(dòng)態(tài)代理類不會(huì)因?yàn)榘芾淼膯栴}而無法被成功定義并訪問位他;
- 類修飾符:該代理類具有 final 和 public 修飾符氛濒,意味著它可以被所有的類訪問产场,但是不能被再度繼承;
- 類名:格式是“proxyN”舞竿,其中 N 是一個(gè)逐一遞增的阿拉伯?dāng)?shù)字京景,代表 Proxy 類第 N 次生成的動(dòng)態(tài)代理類,值得注意的一點(diǎn)是骗奖,并不是每次調(diào)用 Proxy 的靜態(tài)方法創(chuàng)建動(dòng)態(tài)代理類都會(huì)使得 N 值增加确徙,原因是如果對同一組接口(包括接口排列的順序相同)試圖重復(fù)創(chuàng)建動(dòng)態(tài)代理類,它會(huì)很聰明地返回先前已經(jīng)創(chuàng)建好的代理類的類對象执桌,而不會(huì)再嘗試去創(chuàng)建一個(gè)全新的代理類鄙皇,這樣可以節(jié)省不必要的代碼重復(fù)生成,提高了代理類的創(chuàng)建效率仰挣。
三育苟、AOP實(shí)現(xiàn)原理
實(shí)現(xiàn)AOP的技術(shù),主要分為兩大類:
一是采用動(dòng)態(tài)代理技術(shù)椎木,代理能干嘛?代理可以幫我們增強(qiáng)對象的行為博烂!使用動(dòng)態(tài)代理實(shí)質(zhì)上就是調(diào)用時(shí)攔截對象方法香椎,對方法進(jìn)行改造、增強(qiáng)禽篱!用通俗的話來講就是在核心方法執(zhí)行前后,執(zhí)行特定的代碼
二是采用靜態(tài)織入的方式畜伐,引入特定的語法創(chuàng)建“方面”,從而使得編譯器可以在編譯期間織入有關(guān)“方面”的代碼躺率。