Java 代理實現詳解
阿新 • • 發佈:2020-12-21
所謂代理模式,是指客戶端(Client)並不直接呼叫實際的物件,而是通過呼叫代理(Proxy),來間接的呼叫實際的物件。
代理模式的使用場合,一般是由於客戶端不想直接訪問實際物件,或者訪問實際的物件存在技術上的障礙,因而通過代理物件作為橋樑,來完成間接訪問。
靜態代理
//賬戶介面
public interface AccountDao {
public void queryCount();//查詢賬戶
public void updateCount();//修改賬戶
}
/**
* 委託類(代理類跟委託類繼承同一個介面)
* 賬戶實現類
*/
public class AccountDaoImpl implements AccountDao{
@Override
public void queryCount() {
System.out.println("********檢視賬戶方法...");
}
@Override
public void updateCount() {
System.out.println("*********修改賬戶方法...");
}
}
//代理類
public class AccountProxy implements AccountDao{
private AccountDao accountDao; //目標類,因為真正執行任務的還是目標類
public AccountProxy(AccountDao accountDao) {
this.accountDao = accountDao;
}
@Override
public void queryCount() { //名字最好跟目標類的方法一樣
System.out.println("開啟資料庫");
accountDao.queryCount(); //執行目標類的方法
System.out.println("關閉資料庫" );
}
@Override
public void updateCount() {
System.out.println("開啟資料庫");
System.out.println("開啟事務處理");
accountDao.updateCount(); //執行目標類
System.out.println("提交事務");
System.out.println("關閉資料庫");
}
}
//測試類
public class TestStatic_Proxy {
public static void main(String[] args) {
AccountDao accountDao = new AccountDaoImpl(); //建立目標類物件
AccountDao accountProxy = new AccountProxy(accountDao); //把目標類物件傳遞給代理類
accountProxy.updateCount();
accountProxy.queryCount();
}
}
執行結果:
優點:
- 易於理解和實現;
- 代理類和真實類的關係是編譯期靜態決定的,和下文馬上要介紹的動態代理比較起來,執行時沒有任何額外開銷;
缺點:
觀察程式碼可以發現每一個代理類只能為一個介面服務,這樣一來程式開發中必然會產生過多的代理,而且,所有的代理操作除了呼叫的方法不一樣之外,其他的操作都一樣,則此時肯定是重複程式碼。
解決這一問題最好的做法是可以通過一個代理類完成全部的代理功能,那麼此時就必須使用動態代理完成。
動態代理:
- JDK動態代理
//介面類
public interface BookDao {
public void addBook();
public void updateBook();
}
目標實現類:
public class BookDaoImpl implements BookDao{
@Override
public void addBook() {
System.out.println("*************增加圖書方法。。。");
}
@Override
public void updateBook(){
System.out.println("*************更新圖書方法。。。");
}
}
/**
* jdk動態代理類
*/
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class BookProxy implements InvocationHandler{ //動態代理必須實現這個類
private Object target;//目標物件
public Object bind(Object target){ //讓目標類繫結,即給目標類生成代理物件
this.target = target;
//建立代理物件,引數:(類載入器:通過目標類建立位元組碼檔案.類載入器,得到所有的介面,當前類 )
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),this);
}
@Override //這個方法雖然沒有呼叫,但是當執行目標類的方法時會自動呼叫
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result=null;
System.out.println("打'開'資料庫");
System.out.println("jdk事務開始");
result=method.invoke(target, args); //執行被代理物件中的方法
System.out.println("jdk事務結束");
System.out.println("'關'閉資料庫");
return result;
}
}
動態代理:在程式執行時,運用反射機制動態建立而成。
動態代理類的位元組碼在程式執行時由Java反射機制動態生成,無需程式設計師手工編寫它的原始碼。
動態代理類不僅簡化了程式設計工作,而且提高了軟體系統的可擴充套件性,因為Java 反射機制可以生成任意型別的動態代理類。
java.lang.reflect 包中的Proxy類和InvocationHandler 介面提供了生成動態代理類的能力。
//測試類
public class TestDynamic_Proxy {
public static void main(String[] args) {
BookProxy proxy = new BookProxy();
BookDao bookDao = (BookDao) proxy.bind(new BookDaoImpl()); //因為JDK是代理實現了介面的實現類
bookDao.addBook(); //當執行這個方法時就自動呼叫代理類的invoke方法,雖然這裡沒有呼叫它
bookDao.updateBook();
}
}
執行結果:
動態代理在許多框架中都有用到。
- cglib代理
JDK 的動態代理機制只能代理實現了介面的類,而沒有實現介面的類就不能實現 JDK 的動態代理,cglib是針對類來實現代理的,他的原理是對指定的目標類生成一個子類,並覆蓋其中方法實現增強,但因為採用的是繼承,所以不能對 final 修飾的類進行代理。
跟上面相同的介面跟實現類:
public interface BookDao {
public void addBook();
public void updateBook();
}
public class BookDaoImpl implements BookDao{
@Override
public void addBook() {
System.out.println("*************增加圖書方法。。。");
}
@Override
public void updateBook(){
System.out.println("*************更新圖書方法。。。");
}
}
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
/**
*代理類
*/
public class BookCglib implements MethodInterceptor{
private Object target;
public Object getInstance(Object target) { //返回例項
this.target = target;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.target.getClass());// 設定代理目標
enhancer.setCallback(this); //回撥方法
return enhancer.create(); //建立代理物件
}
@Override
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
System.out.println("cglib事務開始");
proxy.invokeSuper(obj, args);
System.out.println("cglib事務結束");
return null;
}
}
測試類:
import com.icss.proxy.dynamic_proxy.BookCglib;
import com.icss.proxy.dynamic_proxy.BookDaoImpl;
public class TestCglib {
public static void main(String[] args) {
BookCglib cglib=new BookCglib();
BookDaoImpl bookCglib=(BookDaoImpl)cglib.getInstance(new BookDaoImpl()); //cglib代理的是沒有實現介面的實現類,詳情見PPT
bookCglib.addBook();
bookCglib.updateBook();
}
}
執行結果:
通過CGLIB成功建立的動態代理,實際是被代理類的一個子類。那麼如果被代理類被標記成final,也就無法通過CGLIB去建立動態代理。