目錄 AOP簡介 Spring AOP的2種代理 JDK動態代理 CGLIB代理 AOP簡介 AOP全稱Aspect-Oriented Programming,即面向切麵編程,它是面向對象編程(OOP)的一種補充。 在通常的業務處理中,都會進行事務處理、日誌記錄等操作,比如: 這是一個操作用戶的類, ...
目錄
Spring AOP的2種代理
AOP簡介
AOP全稱Aspect-Oriented Programming,即面向切麵編程,它是面向對象編程(OOP)的一種補充。
在通常的業務處理中,都會進行事務處理、日誌記錄等操作,比如:
1 class User{ 2 public void addUser(){ 3 ...... //添加用戶 4 ....... //記錄一條日誌:xxx時間添加xxx用戶,操作者:xxx,操作結果:xxx 5 } 6 7 public void alterUser(){ 8 ....... //修改用戶 9 ........//記錄一條日誌:xxx時間修改xxx用戶,操作者:xxx,操作結果:xxx 10 } 11 12 public void deleteUser(){ 13 .......//刪除用戶 14 .......//記錄一條日誌:xxx時間刪除xxx用戶,操作者:xxx,操作結果:xxx 15 }
這是一個操作用戶的類,是對用戶的抽象,日誌操作和用戶操作其實沒有半毛錢關係,上面的抽象並不好,把用戶操作和日誌操作雜糅在一起,應該把日誌操作分離出去,這樣才符合OOP的編程思想。
而且後期不好維護、升級,比如後面要修改日誌操作,你找到User類,在addUser()中一部分是用戶操作,一部分是日誌操作,你要先找到哪些是日誌操作,然後改。後面的方法也是如此,很繁瑣。
AOP解決了此問題。AOP是一種新的編程思想,是OOP的一種補充。OOP專心負責核心業務,AOP負責其它雜七雜八的業務。
OOP好比是經理,AOP好比是助理。原先所有事兒,什麼批文件、見客戶、通知下級來開會、向下級傳達指示,所有事兒都是自己做,很繁瑣,搞得精疲力竭,還容易出問題。
現在招了助理AOP,總經理OOP可以專心處理核心的業務,批示下文件、見見客戶就行了。傳遞指示、通知下級開會,這些事兒助理AOP來做。分工明確,效率
高很多。
這些操作往往可以被多個類使用的,所以叫做一個切麵(Aspect)。
目前最流行的AOP框架有2個:Spring AOP和AspectJ。
Spring AOP
在Spring AOP中,spring是通過代理(proxy)實現的AOP。有2種代理方式:JDK動態代理、CGLIB代理。
JDK動態代理
1、新建包jdk_proxy,用來寫代理類、被代理類。包下新建UserInterface介面
1 public interface UserInterface { 2 public void addUser(); 3 public void alterUser(); 4 public void deleteUser(); 5 }
包下新建一個實現類User
1 public class User implements UserInterface { 2 @Override 3 public void addUser() { 4 //模擬添加用戶 5 System.out.println("正在添加用戶"); 6 System.out.println("添加用戶成功"); 7 } 8 9 @Override 10 public void alterUser() { 11 //模擬修改用戶信息 12 System.out.println("正在修改用戶信息"); 13 System.out.println("修改用戶信息成功"); 14 } 15 16 @Override 17 public void deleteUser() { 18 //模擬刪除用戶 19 System.out.println("正在刪除用戶"); 20 System.out.println("刪除用戶成功"); 21 } 22 }
這些是要被代理的類。
2、新建包aspect,用來寫切麵中要使用方法(類)。包下新建類MyAspect
1 public class MyAspect { 2 //模擬檢查許可權 3 public boolean checkPermission(){ 4 System.out.println("正在檢查許可權"); 5 System.out.println("許可權已夠"); 6 return true; 7 } 8 9 //模擬日誌 10 public void log(){ 11 System.out.println("正在寫入日誌"); 12 System.out.println("xxx時間,xxx進行xxx操作,操作結果:xxx"); 13 System.out.println("日誌寫入完畢"); 14 } 15 }
3、在jdk_proxy包下,新建JdkProxy類,要實現InvocationHandler介面(只有invoke()方法)
1 public class JdkProxy implements InvocationHandler { 2 //聲明目標介面的實例 3 private UserInterface user; 4 5 //創建代理方法,參數是目標介面的實例 6 public Object createProxy(UserInterface user){ 7 this.user=user; //初始化目標介面實例 8 9 ClassLoader classLoader=JdkProxy.class.getClassLoader(); //獲取當前類的類載入器,當前類類名.class.getClassLoader() 10 Class[] classArr=user.getClass().getInterfaces(); //獲取目標介面實例實現的全部介面 11 12 //參數:當前類的類載入器,目標介面實例實現的所有介面,當前類的實例 13 return Proxy.newProxyInstance(classLoader,classArr,this); 14 } 15 16 @Override 17 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 18 //聲明切麵 19 MyAspect myAspect=new MyAspect(); 20 21 //前增強,執行切麵中的方法,在執行目標類中的方法之前做一些操作,比如檢查許可權。根據需要選用。 22 myAspect.checkPermission(); 23 24 //執行目標類中的方法。比如addUser()。第一個參數目標對象,第二個是固定的 25 Object object=method.invoke(user,args); 26 27 //後增強。執行切麵中的方法,在執行目標類中的方法之後做一些操作,比如記錄日誌。根據需要選用。 28 myAspect.log(); 29 30 return object; 31 } 32 }
這是代理類,用來代理User類/UserInterface介面。
4、 新建一個test包,用來寫測試類。包下新建類Test
1 public class Test { 2 public static void main(String[] args) { 3 //JdkProxy的實例 4 JdkProxy jdkProxy=new JdkProxy(); 5 6 //創建目標對象 7 UserInterface user=new User(); 8 9 //通過JdkProxy類的實例動態創建user的代理,因為返回值是Object,需要強制類型轉換,必須用目標介面轉換,不能用實現類轉換 10 UserInterface userProxy=(UserInterface) jdkProxy.createProxy(user); 11 12 //通過代理調用方法,會自動調用OOP、AOP中的相關方法。 13 userProxy.addUser(); 14 } 15 }
5、運行,看到控制台輸出如下
正在檢查許可權
許可權已夠
正在添加用戶
添加用戶成功
正在寫入日誌
xxx時間,xxx進行xxx操作,操作結果:xxx
日誌寫入完畢
已自動調用AOP(前增強+後增強)、OOP(業務本身)中的方法。
模型分析
AOP術語
- Aspect(切麵):用來寫AOP中要用的方法,比如上例中的MyAspect類。
- Pointcut(切入點):AOP、OOP交匯處,比如addUser()、alterUser()、deleteUser(),這三個方法是OOP中的方法,但使用時還會調用AOP中檢查許可權、記錄日誌的方法,這三個方法就是AOP切入OOP的切入點。
- Joinpoint(連接點):即切入點+AOP處理的點,比如checkPermission()、addUser()、log()就是三個連接點。
- Advice(通知/增強):英文意為建議,即做一些其他操作。比如前增強checkPermission()、後增強log()
- Target Object(目標對象):被代理的對象,比如UserInterface介面/User類的實例
- Proxy(代理):即上例的userProxy
- Weaving(織入):將AOP植入到OOP中,比如上例的JdkProxy類。
CGLIB代理
因為JDK動態代理的JdkProxy類中的createProxy()方法中 Class[] classArr=user.getClass().getInterfaces(); //獲取目標介面實例實現的全部介面 ,要用到目標對象的所實現的全部介面,就是說被代理的OOP的核心業務類必須要實現介面。
如果被代理的類沒有實現介面,則可以使用CGLIB代理。
1、新建一個cglib包,用來寫代理類、被代理類。包下新建User類(被代理類),不必實現介面
1 public class User{ 2 public void addUser() { 3 //模擬添加用戶 4 System.out.println("正在添加用戶"); 5 System.out.println("添加用戶成功"); 6 } 7 8 public void alterUser() { 9 //模擬修改用戶信息 10 System.out.println("正在修改用戶信息"); 11 System.out.println("修改用戶信息成功"); 12 } 13 14 public void deleteUser() { 15 //模擬刪除用戶 16 System.out.println("正在刪除用戶"); 17 System.out.println("刪除用戶成功"); 18 } 19 }
2、新建包aspect,包下新建切麵類MyAspect
1 public class MyAspect { 2 //模擬檢查許可權 3 public boolean checkPermission(){ 4 System.out.println("正在檢查許可權"); 5 System.out.println("許可權已夠"); 6 return true; 7 } 8 9 //模擬日誌 10 public void log(){ 11 System.out.println("正在寫入日誌"); 12 System.out.println("xxx時間,xxx進行xxx操作,操作結果:xxx"); 13 System.out.println("日誌寫入完畢"); 14 } 15 }
3、在cglib包下新建CglibProxy類(代理類),需實現MethodInterceptor介面(只有intercept()方法)。註意是org.springframework.cglib.proxy.MethodInterceptor介面,不是其他包下的MethodInterceptor介面。
1 public class CglibProxy implements MethodInterceptor { 2 //創建代理,參數是Object類型 3 public Object createProxy(Object target){ 4 Enhancer enhancer=new Enhancer(); //創建一個動態類對象 5 enhancer.setSuperclass(target.getClass()); //將這個動態類對象的父類/基類設置為目標類(需要增強的類) 6 enhancer.setCallback(this); //設置回調 7 return enhancer.create(); //返回創建的代理 8 } 9 10 @Override 11 public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { 12 //創建切麵實例 13 MyAspect myAspect=new MyAspect(); 14 15 //前增強 16 myAspect.checkPermission(); 17 18 //執行目標方法,參數:形參表的第一個參數、第三個參數 19 Object object=methodProxy.invokeSuper(o,objects); 20 21 //後增強 22 myAspect.log(); 23 24 //返回代理對象 25 return object; 26 } 27 }
4、新建包test,包下新建測試類Test
1 public class Test { 2 public static void main(String[] args) { 3 //創建CglibProxy的實例 4 CglibProxy cglibProxy=new CglibProxy(); 5 6 //創建目標對象 7 User user=new User(); 8 9 //通過CglibProxy類的實例創建user的代理,因為返回值是Object,需要強制類型轉換, 10 User userProxy=(User)cglibProxy.createProxy(user); 11 12 //通過代理調用方法,會自動調用OOP、AOP中的相關方法。 13 userProxy.addUser(); 14 } 15 }
5、運行,控制台輸出如下
正在檢查許可權
許可權已夠
正在添加用戶
添加用戶成功
正在寫入日誌
xxx時間,xxx進行xxx操作,操作結果:xxx
日誌寫入完畢
說明
- JDK動態代理需要目標類(被代理的類)實現介面,CGLIB代理不需要目標類實現介面。
- 需要spring-aop.jar的支持(Spring自帶的)。