JAVA的動態代理
代理模式
代理模式是常用的java設計模式,他的特徵是代理類與委託類有同樣的接口,代理類主要負責為委託類預處理消息、過濾消息、把消息轉發給委託類,以及事後處理訊息等。代理類別與委託類別之間通常會存在關聯關係,一個代理類別的物件與一個委託類別的物件關聯,代理類別的物件本身並不真正實現服務,而是透過呼叫委託類別的物件的相關方法,來提供特定的服務。
依照代理人的創建時期,代理類別可以分為兩種。
靜態代理程式:由程式設計師建立或特定工具自動產生原始程式碼,再對其編譯。在程式運行前,代理類別的.class檔案就已經存在了。
動態代理:在程式運作時,運用反射機制動態創建而成。
首先看一下靜態代理:
1、Count.java
package net.battier.dao; /** * 定义一个账户接口 * * @author Administrator * */ public interface Count { // 查看账户方法 public void queryCount(); // 修改账户方法 public void updateCount(); }
2、CountImpl.java
package net.battier.dao.impl; import net.battier.dao.Count; /** * 委托类(包含业务逻辑) * * @author Administrator * */ public class CountImpl implements Count { @Override public void queryCount() { System.out.println("查看账户方法..."); } @Override public void updateCount() { System.out.println("修改账户方法..."); } } 、CountProxy.java package net.battier.dao.impl; import net.battier.dao.Count; /** * 这是一个代理类(增强CountImpl实现类) * * @author Administrator * */ public class CountProxy implements Count { private CountImpl countImpl; /** * 覆盖默认构造器 * * @param countImpl */ public CountProxy(CountImpl countImpl) { this.countImpl = countImpl; } @Override public void queryCount() { System.out.println("事务处理之前"); // 调用委托类的方法; countImpl.queryCount(); System.out.println("事务处理之后"); } @Override public void updateCount() { System.out.println("事务处理之前"); // 调用委托类的方法; countImpl.updateCount(); System.out.println("事务处理之后"); } }
3、TestCount.java ,這樣一來程式開發中必然會產生過多的代理,而且,所有的代理操作除了呼叫的方法不一樣之外,其他的操作都一樣,則此時肯定是重複程式碼。解決這問題最好的做法是可以透過一個代理類別完成全部的代理功能,那麼此時就必須使用動態代理來完成。
再看一下動態代理:JDK動態代理程式中包含一個類別和一個介面:
InvocationHandler介面:
package net.battier.test; import net.battier.dao.impl.CountImpl; import net.battier.dao.impl.CountProxy; /** *测试Count类 * * @author Administrator * */ public class TestCount { public static void main(String[] args) { CountImpl countImpl = new CountImpl(); CountProxy countProxy = new CountProxy(countImpl); countProxy.updateCount(); countProxy.queryCount(); } }
Method method:要呼叫的方法
Object[] args:方法呼叫時所需要的參數
可以將InvocationHandler介面的子類別想像成一個代理的最終操作類,替換掉ProxySubject。
Proxy類別:
Proxy類別是專門完成代理的操作類,可以透過此類為一個或多個介面動態地產生實作類,此類提供瞭如下的操作方法:public static Object newProxyInstance(ClassLoader loader, Class>[] interfaces,
InvocationHandler h)
loader:類別載入器
Class>[] interfaces:得到全部的介面
InvocationHandler h:得到InvocationHandler介面的子類別實例
Ps:類別載入器
在Proxy類別中的newProxyInstance()方法需要一個ClassLoader類別的實例,ClassLoader實際上對應的是類別載入器,在Java中主要有一下三種類載入器;
Booststrap ClassLoader:此載入器採用C++編寫,一般開發中是看不到的;
AppClassLoader:(預設)載入classpath指定的類,是最常使用的是一種載入器。
動態代理
與靜態代理類別對照的是動態代理類,動態代理類的字節碼在程式運行時由Java反射機制動態生成,無需程式設計師手動編寫它的源代碼。 動態代理類別不僅簡化了程式設計工作,而且提高了軟體系統的可擴展性,因為Java 反射機制可以產生任意類型的動態代理類別。 java.lang.reflect 套件中的Proxy類別和InvocationHandler 介面提供了產生動態代理類別的能力。
1、BookFacade.java
public interface InvocationHandler { public Object invoke(Object proxy,Method method,Object[] args) throws Throwable; }
2、BookFacadeImpl.java
package net.battier.dao; public interface BookFacade { public void addBook(); }
3、TestProxy.java 則不能使用JDK代理,這就要使用cglib動態代理了。
Cglib動態代理
JDK的動態代理機制只能代理實現了接口的類,而不能實現接口的類就不能實現JDK的動態代理,cglib是針對類來實現代理的,他的原理是對指定的目標類別產生子類,並覆寫其中方法實作增強,但因為採用的是繼承,所以不能對final修飾的類別進行代理。範例
1、BookFacadeCglib.java
package net.battier.dao.impl; import net.battier.dao.BookFacade; public class BookFacadeImpl implements BookFacade { @Override public void addBook() { System.out.println("增加图书方法。。。"); } } 、BookFacadeProxy.java package net.battier.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * JDK动态代理代理类 * * @author student * */ public class BookFacadeProxy implements InvocationHandler { private Object target; /** * 绑定委托对象并返回一个代理类 * @param target * @return */ public Object bind(Object target) { this.target = target; //取得代理对象 return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this); //要绑定接口(这是一个缺陷,cglib弥补了这一缺陷) } @Override /** * 调用方法 */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result=null; System.out.println("事物开始"); //执行方法 result=method.invoke(target, args); System.out.println("事物结束"); return result; } }
2、BookCadeImpl1.java
package net.battier.test; import net.battier.dao.BookFacade; import net.battier.dao.impl.BookFacadeImpl; import net.battier.proxy.BookFacadeProxy; public class TestProxy { public static void main(String[] args) { BookFacadeProxy proxy = new BookFacadeProxy(); BookFacade bookProxy = (BookFacade) proxy.bind(new BookFacadeImpl()); bookProxy.addBook(); } }