在某些狀況下,一個客戶不想或者不能直接引用一個對象,此時能夠經過一個稱之爲「代理」的第三者來實現間接引用。代理對象能夠在客戶端和目標對象之間起到 中介的做用,而且能夠經過代理對象去掉客戶不能看到 的內容和服務或者添加客戶須要的額外服務。java
簡單來講代理模式就是經過一個代理對象去訪問一個實際對象,而且能夠像裝飾模式同樣給對象添加一些功能。程序員
所謂靜態代理即在程序運行前代理類就已經存在,也就是說咱們編寫代碼的時候就已經把代理類的代碼寫好了,而動態代理則是在程序運行時自動生成代理類。編程
描述起來太過抽象,看一下代碼就明白是怎麼回事了測試
接口spa
public interface Subject { public void aa(); }
被代理類代理
public class Student implements Subject { public void aa() { System.out.println("添加學生"); } }
代理類code
//代理類 //與被代理類實現同一個接口
public class Proxy implements Subject {
private Student stu;
public void aa() {
if(stu==null){
stu=new Student();
}
//在執行被代理對象的方法前作一些事情
System.out.println("============before");
//執行被代理對象的方法
stu.aa();
//在執行被代理對象的方法後作一些事
System.out.println("============after");
}
}
測試類對象
public class MyTest { @Test public void test1(){ Proxy proxy=new Proxy(); proxy.aa(); } }
執行結果blog
============before
添加學生
============after
觀察以上代碼能夠發現每個代理類只能爲一個接口服務,這樣一來程序開發中必然會產生過多的代理,並且,全部的代理操做除了調用的方法不同以外,其餘的操做都同樣,則此時確定是重複代碼。解決這一問題最好的作法是能夠經過一個代理類完成所有的代理功能,那麼此時就必須使用動態代理完成。 繼承
再來看一下動態代理:
JDK動態代理中包含一個類和一個接口:
public interface IUserDao { public void add(); }
public class UserDaoImpl implements IUserDao { public void add() { System.out.println("添加學生"); } }
測試
//JDK動態代理 @Test public void testOne(){ final IUserDao dao=new UserDaoImpl(); InvocationHandler in=new InvocationHandler() { /** * * @param proxy 代理對象 * @param method 目標對象的方法 * @param args 目標對象方法的參數 * @return * @throws Throwable */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("==========before"); Object result = method.invoke(dao, args); System.out.println("==========after"); return result; } };
//Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
IUserDao proxy = (IUserDao)Proxy.newProxyInstance(dao.getClass().getClassLoader(), dao.getClass().getInterfaces(),in);
proxy.add(); }
參數說明:
Object proxy:指被代理的對象。
Method method:要調用的方法
Object[] args:方法調用時所須要的參數
能夠將InvocationHandler接口的子類想象成一個代理的最終操做類,
//Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)參數說明
ClassLoader loader:類加載器
Class<?>[] interfaces:獲得所有的接口
InvocationHandler h:獲得InvocationHandler接口的子類實例
Ps:類加載器
在Proxy類中的newProxyInstance()方法中須要一個ClassLoader類的實例,ClassLoader實際上對應的是類加載器,在Java中主要有一下三種類加載器;
Booststrap ClassLoader:此加載器採用C++編寫,通常開發中是看不到的;
Extendsion ClassLoader:用來進行擴展類的加載,通常對應的是jre\lib\ext目錄中的類;
AppClassLoader:(默認)加載classpath指定的類,是最常使用的是一種加載器。
動態代理
與靜態代理類對照的是動態代理類,動態代理類的字節碼在程序運行時由Java反射機制動態生成,無需程序員手工編寫它的源代碼。動態代理類不只簡化了編程工做,並且提升了軟件系統的可擴展性,由於Java 反射機制能夠生成任意類型的動態代理類。java.lang.reflect 包中的Proxy類和InvocationHandler 接口提供了生成動態代理類的能力。
可是,JDK的動態代理依靠接口實現,若是有些類並無實現接口,則不能使用JDK代理,這就要使用cglib動態代理了。
Cglib動態代理
JDK的動態代理機制只能代理實現了接口的類,而不能實現接口的類就不能實現JDK的動態代理,cglib是針對類來實現代理的,他的原理是對指定的目標類生成一個子類,並覆蓋其中方法實現加強,但由於採用的是繼承,因此不能對final修飾的類進行代理。
public interface IUserDao { public void add(); }
public class UserDaoImpl {
public void add() { System.out.println("添加學生"); } }
測試類
//Cglib動態代理 @Test public void test1(){ //建立目標對象 final UserDaoImpl impl=new UserDaoImpl(); //建立一個代理對象 Enhancer enhancer=new Enhancer(); enhancer.setSuperclass(impl.getClass()); enhancer.setCallback(new MethodInterceptor() { /** * * @param o 代理對象 * @param method 目標對象方法 * @param objects 目標對象方法參數 * @param methodProxy 代理類的方法 * @return * @throws Throwable */ public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("==============before"); Object result = method.invoke(impl, objects); System.out.println("==============after"); return result; } }); UserDaoImpl proxy = (UserDaoImpl) enhancer.create(); proxy.add(); }