什么是AOP編程
- Aspect Oriented Programming 面向切面編程
- 主要應(yīng)用場(chǎng)景是日志記錄,權(quán)限控制,性能監(jiān)控,事務(wù)處理,異常處理等
- 主要意圖是將日志記錄,權(quán)限控制,性能監(jiān)控,事務(wù)處理,異常處理等代碼從業(yè)務(wù)邏輯代碼中抽取出來,降低耦合性并解決代碼復(fù)用的問題
- 底層實(shí)現(xiàn)原理是代理設(shè)計(jì)模式,可以通過預(yù)編譯方式和運(yùn)行期動(dòng)態(tài)代理實(shí)現(xiàn)在不修改源代碼的情況下給程序動(dòng)態(tài)統(tǒng)一添加功能.假設(shè)把應(yīng)用程序想成一個(gè)立體結(jié)構(gòu)的話,OOP的利刃是縱向切入系統(tǒng),把系統(tǒng)劃分為很多個(gè)模塊(如: 用戶模塊,文章模塊等等),而AOP的利刃是橫向切入系統(tǒng),提取各個(gè)模塊可能都要重復(fù)操作的部分(如: 權(quán)限檢查,日志記錄等等).由此可見,AOP是OOP的一個(gè)有效補(bǔ)充
名詞解釋
- 關(guān)注點(diǎn): 重復(fù)代碼(如打印日志的代碼)
- 切面/切面類: 關(guān)注點(diǎn)形成的類,將重復(fù)代碼抽取出來,在運(yùn)行的時(shí)候在業(yè)務(wù)方法上動(dòng)態(tài)植入
- 切入點(diǎn): 執(zhí)行目標(biāo)對(duì)象方法,動(dòng)態(tài)植入切面代碼(可以通過切入點(diǎn)表達(dá)式,指定攔截哪些類的哪些方法;給指定的類在運(yùn)行的時(shí)候植入切面類代碼)
AOP底層實(shí)現(xiàn)原理
代理的分類:
靜態(tài)代理和動(dòng)態(tài)代理
靜態(tài)代理和動(dòng)態(tài)代理的區(qū)別:
靜態(tài)代理是靜態(tài)定義代理類(需要手寫代理類);動(dòng)態(tài)代理是動(dòng)態(tài)生成代理類(虛擬生成代理類)
動(dòng)態(tài)代理分為:
jdk動(dòng)態(tài)代理和cglib動(dòng)態(tài)代理
jdk動(dòng)態(tài)代理和cglib的區(qū)別:
jdk是基于反射機(jī)制實(shí)現(xiàn)的;
cglib是基于ASM字節(jié)碼包裝的一個(gè)類庫(性能比反射要高一些)(ASM是一個(gè)java字節(jié)碼操控框架,它能被用來動(dòng)態(tài)生成類或者增強(qiáng)既有類的功能.ASM可以直接產(chǎn)生二進(jìn)制class文件,也可以在類被加載進(jìn)java虛擬機(jī)之前動(dòng)態(tài)改變類的行為.)
AOP實(shí)現(xiàn)原理: 靜態(tài)代理和動(dòng)態(tài)代理
-
什么是靜態(tài)代理: 由程序員創(chuàng)建或工具生成代理類的源碼,再編譯代理類.所謂靜態(tài)也就是在程序運(yùn)行前就已經(jīng)存在代理類的字節(jié)碼文件,代理類和委托類的關(guān)系在運(yùn)行前就確定了.
//接口 public interface IUserDao { void save(); }//被代理類 public class UserDaoImpl implements IUserDao { @Override public void save() { System.out.println("假裝把數(shù)據(jù)入庫了..."); } }//代理類 public class UserDaoProxy implements IUserDao { private IUserDao target; public UserDaoProxy(IUserDao userDao) { this.target = userDao; } @Override public void save() { System.out.println("開啟事務(wù)..."); target.save(); System.out.println("提交事務(wù)..."); } } 什么是動(dòng)態(tài)代理: 代理類不需要實(shí)現(xiàn)接口,動(dòng)態(tài)的在內(nèi)存中構(gòu)建代理對(duì)象(需要我們指定創(chuàng)建代理對(duì)象/目標(biāo)對(duì)象實(shí)現(xiàn)的接口的類型)
-
jdk動(dòng)態(tài)代理(原理是根據(jù)類加載器和接口創(chuàng)建代理類,此代理類是接口的實(shí)現(xiàn)類,所以必須使用接口,面向接口生成代理,接口位于java.lang.reflect包下)
public class InvocationHandlerImpl implements InvocationHandler{ // 這其實(shí)是業(yè)務(wù)實(shí)現(xiàn)類對(duì)象,用來調(diào)用具體的業(yè)務(wù)方法 private Object target; // 通過構(gòu)造函數(shù)傳入目標(biāo)對(duì)象 public InvocationHandlerImpl(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result = null; System.out.println("調(diào)用開始處理---"); result = method.invoke(target, args); System.out.println("調(diào)用結(jié)束處理---"); return result; } public static void main(String[] args){ // 被代理對(duì)象 IUserDao userDao = new UserDaoImpl(); InvocationHandlerImpl invocationHandlerImpl = new InvocationHandlerImpl(userDao); ClassLoader loader = userDao.getClass().getClassLoader(); Class<?>[] interfaces = userDao.getClass().getInterfaces(); // 主要裝載器、一組接口及調(diào)用處理動(dòng)態(tài)代理實(shí)例 IUserDao newProxyInstance = (IUserDao) Proxy.newProxyInstance(loader, interfaces, invocationHandlerImpl); newProxyInstance.save(); } } -
cglib動(dòng)態(tài)代理(原理是利用asm開源包,對(duì)代理對(duì)象類的class文件加載進(jìn)來,通過修改其字節(jié)碼生成子類來處理)
public class CglibProxy implements MethodInterceptor { private Object targetObject; // 這里的目標(biāo)類型為Object,則可以接受任意一種參數(shù)作為被代理類,實(shí)現(xiàn)了動(dòng)態(tài)代理 public Object getInstance(Object target) { // 設(shè)置需要?jiǎng)?chuàng)建子類的類 this.targetObject = target; Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(target.getClass()); enhancer.setCallback(this); return enhancer.create(); } @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("開啟事務(wù).."); Object result = proxy.invoke(targetObject, args); System.out.println("關(guān)閉事務(wù).."); // 返回代理對(duì)象 return result; } public static void main(String[] args) { CglibProxy cglibProxy = new CglibProxy(); UserDaoImpl userDao = (UserDaoImpl) cglibProxy.getInstance(new UserDaoImpl()); userDao.save(); } }
-
jdk動(dòng)態(tài)代理和cglib動(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開源包,對(duì)代理對(duì)象類的class文件加載進(jìn)來,通過修改其字節(jié)碼生成子類來處理。
Spring中:
如果目標(biāo)對(duì)象實(shí)現(xiàn)了接口,默認(rèn)情況下會(huì)采用JDK的動(dòng)態(tài)代理實(shí)現(xiàn)AOP ;
如果目標(biāo)對(duì)象實(shí)現(xiàn)了接口,可以強(qiáng)制使用CGLIB實(shí)現(xiàn)AOP ;
如果目標(biāo)對(duì)象沒有實(shí)現(xiàn)了接口,必須采用CGLIB庫,spring會(huì)自動(dòng)在JDK動(dòng)態(tài)代理和CGLIB之間轉(zhuǎn)換 ;
JDK動(dòng)態(tài)代理只能對(duì)實(shí)現(xiàn)了接口的類生成代理,而不能針對(duì)類 ;
CGLIB是針對(duì)類實(shí)現(xiàn)代理,主要是對(duì)指定的類生成一個(gè)子類,覆蓋其中的方法 ;
因?yàn)槭抢^承,所以該類或方法最好不要聲明成final ,final可以阻止繼承和多態(tài)。
AOP編程使用
-
注解版本實(shí)現(xiàn)AOP
<aop:aspectj-autoproxy></aop:aspectj-autoproxy> 開啟事物注解權(quán)限@Component //交給Spring來處理 @Aspect //指定一個(gè)類為切面類 public class AopLog { // 前置通知 @Before("execution(* com.pliu.service.UserService.add(..))") public void begin() { System.out.println("前置通知"); } // 后置通知 @After("execution(* com.pliu.service.UserService.add(..))") public void commit() { System.out.println("后置通知"); } // 運(yùn)行通知 @AfterReturning("execution(* com.pliu.service.UserService.add(..))") public void returning() { System.out.println("運(yùn)行通知"); } // 異常通知 @AfterThrowing("execution(* com.pliu.service.UserService.add(..))") public void afterThrowing() { System.out.println("異常通知"); } // 環(huán)繞通知 @Around("execution(* com.pliu.service.UserService.add(..))") public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { System.out.println("環(huán)繞通知開始"); proceedingJoinPoint.proceed(); System.out.println("環(huán)繞通知結(jié)束"); } }
- xml版本實(shí)現(xiàn)AOP(不好意思,感覺麻煩懶得寫,如有必要還請(qǐng)百度吧)
Spring事務(wù)使用
-
事務(wù)基本特性
⑴ 原子性(Atomicity)
原子性是指事務(wù)包含的所有操作要么全部成功,要么全部失敗回滾,因此事務(wù)的操作如果成功就必須要完全應(yīng)用到數(shù)據(jù)庫,如果操作失敗則不能對(duì)數(shù)據(jù)庫有任何影響。⑵ 一致性(Consistency)
一致性是指事務(wù)必須使數(shù)據(jù)庫從一個(gè)一致性狀態(tài)變換到另一個(gè)一致性狀態(tài),也就是說一個(gè)事務(wù)執(zhí)行之前和執(zhí)行之后都必須處于一致性狀態(tài)。
拿轉(zhuǎn)賬來說,假設(shè)用戶A和用戶B兩者的錢加起來一共是5000,那么不管A和B之間如何轉(zhuǎn)賬,轉(zhuǎn)幾次賬,事務(wù)結(jié)束后兩個(gè)用戶的錢相加起來應(yīng)該還得是5000,這就是事務(wù)的一致性。⑶ 隔離性(Isolation)
隔離性是當(dāng)多個(gè)用戶并發(fā)訪問數(shù)據(jù)庫時(shí),比如操作同一張表時(shí),數(shù)據(jù)庫為每一個(gè)用戶開啟的事務(wù),不能被其他事務(wù)的操作所干擾,多個(gè)并發(fā)事務(wù)之間要相互隔離。
即要達(dá)到這么一種效果:對(duì)于任意兩個(gè)并發(fā)的事務(wù)T1和T2,在事務(wù)T1看來,T2要么在T1開始之前就已經(jīng)結(jié)束,要么在T1結(jié)束之后才開始,這樣每個(gè)事務(wù)都感覺不到有其他事務(wù)在并發(fā)地執(zhí)行。⑷ 持久性(Durability)
持久性是指一個(gè)事務(wù)一旦被提交了,那么對(duì)數(shù)據(jù)庫中的數(shù)據(jù)的改變就是永久性的,即便是在數(shù)據(jù)庫系統(tǒng)遇到故障的情況下也不會(huì)丟失提交事務(wù)的操作。
例如我們?cè)谑褂肑DBC操作數(shù)據(jù)庫時(shí),在提交事務(wù)方法后,提示用戶事務(wù)操作完成,當(dāng)我們程序執(zhí)行完成直到看到提示后,就可以認(rèn)定事務(wù)以及正確提交,即使這時(shí)候數(shù)據(jù)庫出現(xiàn)了問題,也必須要將我們的事務(wù)完全執(zhí)行完成,否則就會(huì)造成我們看到提示事務(wù)處理完畢,但是數(shù)據(jù)庫因?yàn)楣收隙鴽]有執(zhí)行事務(wù)的重大錯(cuò)誤。
- 事務(wù)控制分類
編程式事務(wù): 手動(dòng)事務(wù)(自己去begin,commit,rollback),靈活但繁瑣
聲明式事務(wù): 自動(dòng)事務(wù),原理是使用編程式事務(wù)+反射機(jī)制進(jìn)行包裝.分注解版本和xml版本