Spring AOP

 

目錄

AOP簡介spring

Spring  AOP的2種代理編程

 

 

 

AOP簡介

AOP全稱Aspect-Oriented  Programming,即面向切面編程,它是面向對象編程(OOP)的一種補充。框架

 

在一般的業務處理中,都會進行事務處理、日誌記錄等操做,好比:ide

 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()中一部分是用戶操做,一部分是日誌操做,你要先找到哪些是日誌操做,而後改。後面的方法也是如此,很繁瑣。this

 

AOP解決了此問題。AOP是一種新的編程思想,是OOP的一種補充。OOP專心負責核心業務,AOP負責其它雜七雜八的業務。spa

OOP比如是經理,AOP比如是助理。原先全部事兒,什麼批文件、見客戶、通知下級來開會、向下級傳達指示,全部事兒都是本身作,很繁瑣,搞得精疲力竭,還容易出問題。3d

如今招了助理AOP,總經理OOP能夠專心處理核心的業務,批示下文件、見見客戶就好了。傳遞指示、通知下級開會,這些事兒助理AOP來作。分工明確,效率代理

高不少。日誌

 

 

這些操做每每能夠被多個類使用的,因此叫作一個切面(Aspect)。

 

目前最流行的AOP框架有2個:Spring  AOP和AspectJ。

 

 

 

 

Spring  AOP

在Spring AOP中,spring是經過代理(proxy)實現的AOP。有2種代理方式:JDK動態代理、CGLIB代理。

 

JDK動態代理

一、新建包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 }

 

這些是要被代理的類。

 

 

二、新建包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 }

 

 

 

三、在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接口。

 

 

四、 新建一個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 }

 

 

五、運行,看到控制檯輸出以下

正在檢查權限
權限已夠
正在添加用戶
添加用戶成功
正在寫入日誌
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代理。

 

一、新建一個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 }

 

 

 

二、新建包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 }

 

 

 

三、在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 }

 

 

 

四、新建包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 }

 

 

 

五、運行,控制檯輸出以下

正在檢查權限
權限已夠
正在添加用戶
添加用戶成功
正在寫入日誌
xxx時間,xxx進行xxx操做,操做結果:xxx
日誌寫入完畢

 

 

 

說明:JDK動態代理須要目標類(被代理的類)實現接口,CGLIB代理不須要目標類實現接口。

相關文章
相關標籤/搜索