Javal动态代理
Proxy設計模式
代理模式為其他對象提供一個代理以控制對某個對象的訪問。
?
為了行為的一致性,代理類與委托類通常會實現相同的接口。
代理模式與適配器、裝飾者模式的區別:
代理模式與適配器、裝飾者模式,都包裝了一個內部對象,對請求進行,但是他們有本質的區別:
-
裝飾者模式,擴展了包裝對象的行為和責任。包裝對象與被包裝對象實現了相同的接口,即有共同的行為。
-
適配器模式,擴展了包裝對象的行為和責任。但是進行了接口轉換。
-
代理模式,未擴展行為和責任,是控制對象的訪問。包裝對象與被包裝對象實現了相同的接口。
?
按照代理的創建時期,代理類可以分為兩種:?
靜態:由程序員創建代理類或特定工具自動生成源代碼再對其編譯。在程序運行前代理類的.class文件就已經存在了。
動態:在程序運行時運用反射機制動態創建而成
Java靜態代理
//real Subjectpublic class UserManagerImpl implements UserManager { @Override public void addUser(String userId, String userName) { System.out.println("UserManagerImpl.addUser"); } @Override public void delUser(String userId) { System.out.println("UserManagerImpl.delUser"); } @Override public String findUser(String userId) { System.out.println("UserManagerImpl.findUser"); return "張三"; } @Override public void modifyUser(String userId, String userName) { System.out.println("UserManagerImpl.modifyUser"); } } //Proxypublic class UserManagerImplProxy implements UserManager { // 目標對象 private UserManager userManager; // 通過構造方法傳入目標對象 public UserManagerImplProxy(UserManager userManager){ this.userManager=userManager; } @Override public void addUser(String userId, String userName) { try{ //添加打印日志的功能 //開始添加用戶 System.out.println("start-->addUser()"); userManager.addUser(userId, userName); //添加用戶成功 System.out.println("success-->addUser()"); }catch(Exception e){ //添加用戶失敗 System.out.println("error-->addUser()"); } } @Override public void delUser(String userId) { userManager.delUser(userId); } @Override public String findUser(String userId) { userManager.findUser(userId); return "張三"; } @Override public void modifyUser(String userId, String userName) { userManager.modifyUser(userId,userName); } } //Client 使用代理public class Client { public static void main(String[] args){ //UserManager userManager=new UserManagerImpl(); UserManager userManager=new UserManagerImplProxy(new UserManagerImpl()); userManager.addUser("1111", "張三"); } }代理模式的缺點
JDK動態代理
靜態代理一個代理只能代理一種類型,而且是在編譯器就已經確定被代理的對象。而動態代理是在運行時,通過反射機制實現動態代理,并且能夠代理各種類型的對象
在Java中要想實現動態代理機制,需要java.lang.reflect.InvocationHandler接口和 java.lang.reflect.Proxy 類的支持
//Object proxy:被代理的對象 //Method method:要調用的方法 //Object[] args:方法調用時所需要參數 public interface InvocationHandler { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable; } //java.lang.reflect.Proxy 類://CLassLoader loader:類的加載器 //Class<?> interfaces:得到全部的接口 //InvocationHandler h:得到InvocationHandler接口的子類的實例 public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException示例代碼:
public interface UserManager {void addUser(String userId, String userName);void delUser(String userId);String findUser(String userId);void modifyUser(String userId, String userName);}public class UserManagerImpl implements UserManager {@Override public void addUser(String userId, String userName) { System.out.println("UserManagerImpl.addUser"); } @Override public void delUser(String userId) { System.out.println("UserManagerImpl.delUser"); } @Override public String findUser(String userId) { System.out.println("UserManagerImpl.findUser"); return "張三"; } @Override public void modifyUser(String userId, String userName) { System.out.println("UserManagerImpl.modifyUser"); } }import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method;public class LogHandler implements InvocationHandler{// 目標對象 private Object targetObject; //綁定關系,也就是關聯到哪個接口(與具體的實現類綁定)的哪些方法將被調用時,執行invoke方法。 public void setTarget(Object targetObject){ this.targetObject=targetObject; } @Override //關聯的這個實現類的方法被調用時將被執行 /*InvocationHandler接口的方法,proxy表示代理,method表示原對象被調用的方法,args表示方法的參數*/ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("start-->>"); for(int i=0;i<args.length;i++){ System.out.println(args[i]); } Object ret=null; try{ /*原對象方法調用前處理日志信息*/ System.out.println("satrt-->>"); //調用目標方法 ret=method.invoke(targetObject, args); /*原對象方法調用后處理日志信息*/ System.out.println("success-->>"); }catch(Exception e){ e.printStackTrace(); System.out.println("error-->>"); throw e; } return ret; } }public class UserManagerTest {public static void main(String[] args) {// TODO Auto-generated method stubUserManager userManager = new UserManagerImpl();LogHandler logHandler=new LogHandler(); logHandler.setTarget(userManager);//該方法用于為指定類裝載器、一組接口及調用處理器生成動態代理類實例 //根據傳入的目標返回一個代理對象 UserManager proxy =(UserManager) Proxy.newProxyInstance(//第一個參數指定產生代理對象的類加載器,需要將其指定為和目標對象同一個類加載器 userManager.getClass().getClassLoader() //第二個參數要實現和目標對象一樣的接口,所以只需要拿到目標對象的實現接口 ,userManager.getClass().getInterfaces()//第三個參數表明這些被攔截的方法在被攔截時需要執行哪個InvocationHandler的invoke方法 ,logHandler); proxy.addUser("1111", "張三"); }}?cglib代理
Java靜態代理與JDK動態代理要求目標對象實現了一個接口,如果目標對象沒有實現接口,則不可以使用動態代理。此時可以使用Cglib代理來創建一個代理對象。
Cglib代理,也叫作子類代理,它是在內存中構建一個子類對象從而實現對目標對象功能的擴展.
- JDK的動態代理有一個限制,就是使用動態代理的對象必須實現一個或多個接口,如果想代理沒有實現接口的類,就可以使用Cglib實現.
- Cglib是一個強大的高性能的代碼生成包,它可以在運行期擴展java類與實現java接口.它廣泛的被許多AOP的框架使用,例如Spring AOP和synaop,為他們提供方法的interception(攔截)
- Cglib包的底層是通過使用一個小而塊的字節碼處理框架ASM來轉換字節碼并生成新的類.不鼓勵直接使用ASM,因為它要求你必須對JVM內部結構包括class文件的格式和指令集都很熟悉.
Cglib子類代理實現方法:
1.需要引入cglib的jar文件,但是Spring的核心包中已經包括了Cglib功能,所以直接引入pring-core-3.2.5.jar即可.
2.引入功能包后,就可以在內存中動態構建子類
3.代理的類不能為final,否則報錯
4.目標對象的方法如果為final/static,那么就不會被攔截,即不會執行目標對象額外的業務方法.
?
?
代碼示例:
目標對象類:UserDao.java
Cglib代理工廠:ProxyFactory.java
/*** Cglib子類代理工廠* 對UserDao在內存中動態構建一個子類對象*/ public class ProxyFactory implements MethodInterceptor{//維護目標對象private Object target;public ProxyFactory(Object target) {this.target = target;}//給目標對象創建一個代理對象public Object getProxyInstance(){//1.工具類Enhancer en = new Enhancer();//2.設置父類en.setSuperclass(target.getClass());//3.設置回調函數en.setCallback(this);//4.創建子類(代理對象)return en.create();}@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {System.out.println("開始事務...");//執行目標對象的方法Object returnValue = method.invoke(target, args);System.out.println("提交事務...");return returnValue;} }測試類:
/*** 測試類*/ public class App {@Testpublic void test(){//目標對象UserDao target = new UserDao();//代理對象UserDao proxy = (UserDao)new ProxyFactory(target).getProxyInstance();//執行代理對象的方法proxy.save();} }在Spring的AOP編程中:
如果加入容器的目標對象有實現接口,用JDK代理
如果目標對象沒有實現接口,用Cglib代理
?
?
總結
- 上一篇: Java平台扩展机制#3:SLF4J怪招
- 下一篇: Java反射综述