springAOP指的是在spring中的AOP,什麼是AOP,相對於java中的面向對象(oop),在面向對象中一些公共的行爲,像日誌記錄,權限驗證等若是都使用面向對象來作,會在每一個業務方法中都寫上重複的代碼,形成代碼的冗餘。而AOP指的是面向切面編程,定義一個切面,用切面去切相應的方法,就能夠織入相關的邏輯。面向切面編程使用代理模式java
代理模式做爲23種經典設計模式之一,其比較官方的定義爲「爲其餘對象提供一種代理以控制對這個對象的訪問」,簡單點說就是,以前A類本身作一件事,在使用代理以後,A類不直接去作,而是由A類的代理類B來去作。代理類實際上是在以前類的基礎上作了一層封裝。java中有靜態代理、JDK動態代理、CGLib動態代理的方式。靜態代理指的是代理類是在編譯期就存在的,相反動態代理則是在程序運行期動態生成的,spring
靜態代理,簡單點來講就是在程序運行以前,代理類和被代理類的關係已經肯定。靜態代理的實現方式通常有如下幾個步驟,首先要定義一個公共的接口,供代理類和被代理類實現,以下編程
package cn.com.staticProxy; /** * * *<p>Title: IUserDao</p> * <p>Description:公共的接口</p> * <p>Company: </p> * @author Administrator * @date 2019年4月24日 下午4:15:12 */ public interface IUserDao { void save(); void find(); }
而後是代理類和被代理類,先看被代理類,設計模式
package cn.com.staticProxy; public class UserDao implements IUserDao{ /** * 被代理類或者叫代理目標類 */ @Override public void save() { // TODO Auto-generated method stub System.out.println("模擬保存用戶"); } @Override public void find() { // TODO Auto-generated method stub System.out.println("模擬查找用戶"); } }
從被代理類上能夠看到,被代理類要作的就是save和find操做,若是不使用代理模式,那麼咱們的使用方式則是直接使用,以下ide
package cn.com.staticProxy; /** * * *<p>Title: Test2</p> * <p>Description:直接使用被代理類 </p> * <p>Company: </p> * @author Administrator * @date 2019年4月24日 下午4:18:39 */ public class Test2 { public static void main(String[] args) { // TODO Auto-generated method stub IUserDao udp=new UserDao(); udp.save(); System.out.println("-------------------"); udp.find(); } }
經過測試方法,能夠得出執行方法的結果以下,oop
模擬保存用戶
-------------------
模擬查找用戶
下面看代理類,測試
package cn.com.staticProxy; public class UserDaoProxy implements IUserDao { private UserDao ud = new UserDao(); @Override public void save() { // TODO Auto-generated method stub System.out.println("代理操做,開啓事務"); ud.save(); System.out.println("代理操做,關閉事務"); } @Override public void find() { // TODO Auto-generated method stub System.out.println("代理操做,開啓事務"); ud.find(); System.out.println("代理操做,關閉事務"); } }
能夠看到代理類的實現邏輯是在代理類中持有一個被代理類的實例,經過被代理類實例調用被代理對象的方法,另外在方法以前先後都可加入其它的方法處理邏輯,this
最後,看使用代理類的測試方法,spa
package cn.com.staticProxy; public class Test { public static void main(String[] args) { // TODO Auto-generated method stub IUserDao udp=new UserDaoProxy(); udp.save(); System.out.println("-------------------"); udp.find(); } }
返回的執行結果以下,設計
代理操做,開啓事務
模擬保存用戶
代理操做,關閉事務
-------------------
代理操做,開啓事務
模擬查找用戶
代理操做,關閉事務
對比,使用靜態代理和不使用靜態代理,能夠發現使用了代理以後,能夠在被代理方法的執行前或後加入別的代碼,實現諸如權限及日誌的操做。
但,靜態代理也存在必定的問題,若是被代理方法不少,就要爲每一個方法進行代理,增長了代碼維護的成本。有沒有其餘的方式能夠減小代碼的維護,那就是動態代理。
JDK提供了動態代理的方式,能夠生成代理類,被代理類和接口沿用靜態代理中的IUserDao和UserDao,UserDao爲被代理類,下面看代理類,
package cn.com.dynamicProxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import cn.com.staticProxy.IUserDao; import cn.com.staticProxy.UserDao; /** * * *<p>Title: DynamicProxy</p> * <p>Description: 動態代理類</p> * <p>Company: </p> * @author Administrator * @date 2019年4月24日 下午4:35:00 */ public class DynamicProxy implements InvocationHandler{ //被代理類的實例 private IUserDao iud=null; @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // TODO Auto-generated method stub Object result=null; System.out.println("開始JDK動態代理"); method.invoke(iud, args); System.out.println("結束JDK動態代理"); return result; } //構造方法 public DynamicProxy(IUserDao iud){ this.iud=iud; } }
須要實現JDK中的InvocationHandler接口,實現其中的invoke方法,在此方法中經過反射的方式調用被代理類的方法,能夠在方法執行前或後進行別的處理,下面看測試代碼
package cn.com.dynamicProxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import cn.com.staticProxy.IUserDao; import cn.com.staticProxy.UserDao; public class Test2 { public static void main(String[] args) { // TODO Auto-generated method stub UserDao ud=new UserDao(); DynamicProxy dp=new DynamicProxy(ud); //生成代理對象 IUserDao iud=(IUserDao)Proxy.newProxyInstance(ud.getClass().getClassLoader(), ud.getClass().getInterfaces(), dp); iud.save(); System.out.println("--------------"); iud.find(); } }
在測試代碼中經過Proxy類的newProxyInstance方法,生成代理類的實例iud,須要三個參數,第一個爲被代理類的類加載器,第二個爲被代理類實現的接口,第三個則爲invocationHandler的實現類,這樣就生成了代理對象,而後經過代理對象調用方法執行結果以下,
開始JDK動態代理
模擬保存用戶
結束JDK動態代理
--------------
開始JDK動態代理
模擬查找用戶
結束JDK動態代理
另外,因爲第三個參數爲一個接口,還可使用匿名內部類的方式進行書寫,其實現代碼以下,
package cn.com.dynamicProxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import cn.com.staticProxy.IUserDao; import cn.com.staticProxy.UserDao; public class Test { public static void main(String[] args) { // TODO Auto-generated method stub //被代理類的實例 UserDao ud = new UserDao(); IUserDao iud = (IUserDao) Proxy.newProxyInstance(ud.getClass().getClassLoader(), ud.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // TODO Auto-generated method stub Object result = null; if ("find".equals(method.getName())) { result = method.invoke(ud, args); } else { System.out.println("開始JDK代理"); result = method.invoke(ud, args); System.out.println("結束JDK代理"); } return result; } }); //使用代理類調用方法 iud.find(); System.out.println("----------------"); iud.save(); } }
執行結果以下,
模擬查找用戶 ---------------- 開始JDK代理 模擬保存用戶 結束JDK代理
從上面能夠看出代理類是由Proxy這個類經過newProxyInstance方法動態生成的,生成對象後使用「實例調用方法」的方式進行方法調用,那麼代理類的被代理類的關係只有在執行這行代碼的時候纔會生成,所以成爲動態代理。
JDK的動態代理也存在不足,即被代理類必需要有實現的接口,如沒有接口則沒法使用JDK動態代理(從newProxyInstance方法的第二個參數可得知,必須傳入被代理類的實現接口),那麼須要使用CGLib動態代理。
CGLib動態代理是一個第三方實現的動態代理類庫,不要求被代理類必須實現接口,它採用的是繼承被代理類,使用其子類的方式,彌補了被代理類沒有接口的不足,
要使用CGLib必需要引入第三方類庫,我這裏使用的類庫以下,
要使用CGLib代理須要實現其MethodInterceptor接口,
package cn.com.cglib; import java.lang.reflect.Method; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; public class MyMethodInterceptor implements MethodInterceptor { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { // TODO Auto-generated method stub System.out.println("開始CGLib動態代理"); Object object=proxy.invokeSuper(obj, args); System.out.println("結束CGLib動態代理"); return object; } }
其,測試代碼以下,
package cn.com.cglib; import cn.com.staticProxy.UserDao; import net.sf.cglib.proxy.Enhancer; public class Test { public static void main(String[] args) { // TODO Auto-generated method stub Enhancer enhancer=new Enhancer(); enhancer.setSuperclass(UserDao.class); enhancer.setCallback(new MyMethodInterceptor()); //生成代理類 UserDao ud=(UserDao) enhancer.create(); ud.save();
System.out.println("----------------"); ud.find(); } }
能夠看出使用Enhancer生成代理類,須要設置被代理類,也就是父類(從這裏能夠看出是使用繼承,生成的子類),設置回調方法。
開始CGLib動態代理
模擬保存用戶
結束CGLib動態代理
--------------------
開始CGLib動態代理
模擬查找用戶
結束CGLib動態代理
在設置回調enhancer.setCallback的時候須要傳入MethodInterceptor的實例,這裏可使用匿名內部類的方式,可參照上面的。
對靜態代理、JDK動態代理、CGLib動態代理作一個總結,靜態代理的維護成本比較高,有一個被代理類就須要建立一個代理類,並且須要實現相同的接口。JDK動態代理模式和CGLib動態代理的區別是JDK動態代理須要被代理類實現接口,而CGLib則是生成被代理類的子類,要求被代理類不能是final的,由於final類沒法被繼承。
下次會基於springAOP梳理相關內容。
有不正之處歡迎指正,感謝!