AOP簡介spring
Spring AOP的2種代理編程
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是經過代理(proxy)實現的AOP。有2種代理方式:JDK動態代理、CGLIB代理。
一、新建包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(業務自己)中的方法。
由於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代理不須要目標類實現接口。