1. 程式人生 > >java三種代理模式的實現

java三種代理模式的實現

1、什麼是代理模式
代理模式:就是為其他物件提供一種代理以控制對這個物件的訪問。

代理可以在不改動目標物件的基礎上,增加其他額外的功能(擴充套件功能)。
在這裡插入圖片描述

舉個例子來說明代理的作用: 一般我們想邀請明星來當我們的代言人,我們並不能直接聯絡到明星,而是通過其經紀人,來告訴經紀人我們需要和明星進行合作,然後通過經紀人來轉達給明星。,明星只需要做好代言工作就好,其他繁瑣的事情就交於經紀人就可以。這裡的經經紀人就是一個代理物件,明星就是一個目標物件。

用圖表示如下:

在這裡插入圖片描述

2、三種代理模式
2.1 靜態代理

靜態代理在使用時,需要定義介面或者父類,被代理物件(目標物件)與代理物件(Proxy)一起實現相同的介面或者是繼承相同父類。

下面通過程式碼演示下:

介面IUserDao:

複製程式碼
/**

  • 介面
    */
    public interface IUserDao {

    void save();

}

目標物件:UserDao:

/**

  • 實現介面

  • 目標物件
    */
    public class UserDao implements IUserDao {

    public void save() {
    System.out.println("----儲存資料成功!----");
    }

}

代理物件:UserDaoProxy

/**

  • 代理物件(靜態代理)
    */
    public class UserDaoProxy implements IUserDao{
    //接收儲存目標物件
    private IUserDao target;
    public UserDaoProxy(IUserDao target){
    this.target=target;
    }

    public void save() {
    System.out.println(“開始事務…”);
    target.save();//執行目標物件的方法
    System.out.println(“提交事務…”);
    }
    }

測試類:AppTest:

/**

  • 測試類
    */
    public class AppTest {
    public static void main(String[] args) {
    //目標物件
    UserDao target = new UserDao();

     //代理物件,把目標物件傳給代理物件,建立代理關係
     UserDaoProxy proxy = new UserDaoProxy(target);
    
     proxy.save();//執行的是代理的方法
    

    }
    }

靜態代理總結:

可以實現在不修改目標物件的基礎上,對目標物件的功能進行擴充套件。

但是由於代理物件需要與目標物件實現一樣的介面,所以會有很多代理類,類太多.同時,一旦介面增加方法,目標物件與代理物件都要維護.

可以使用動態代理方式來解決。

2.2 動態代理(JDK代理)

動態代理有以下特點:

1.代理物件,不需要實現介面
2.代理物件的生成,是利用JDK的API,動態的在記憶體中建立代理物件(需要我們指定建立代理物件/目標物件實現的介面的型別)
3.動態代理也叫做:JDK代理,介面代理

JDK中生成代理物件的API

代理類所在包:java.lang.reflect.Proxy
JDK實現代理只需要使用newProxyInstance方法,但是該方法需要接收三個引數,完整的寫法是:

static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h )
方法是在Proxy類中是靜態方法,且接收的三個引數依次為:

ClassLoader loader //指定當前目標物件使用類載入器
Class<?>[] interfaces //目標物件實現的介面的型別,使用泛型方式確認型別
InvocationHandler h //事件處理器
下面進行程式碼演示:

介面類IUserDao

/**

  • 介面
    */
    public interface IUserDao {

    void save();

}

目標物件UserDao

/**

  • 介面實現

  • 目標物件
    */
    public class UserDao implements IUserDao {

    public void save() {

     System.out.println("----儲存資料成功!----");
    

    }

}

代理工廠類:ProxyFactory

/**

  • 建立動態代理物件

  • 動態代理不需要實現介面,但是需要指定介面型別
    */
    public class ProxyFactory{

    //維護一個目標物件
    private Object target;
    public ProxyFactory(Object target){
    this.target=target;
    }

    //給目標物件生成代理物件
    public Object getProxyInstance(){
    return Proxy.newProxyInstance(
    target.getClass().getClassLoader(),
    target.getClass().getInterfaces(),
    new InvocationHandler() {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    System.out.println(“開始事務111”);
    //執行目標物件方法
    Object returnValue = method.invoke(target, args);
    System.out.println(“提交事務111”);
    return returnValue;
    }
    }
    );
    }

}
測試類:App:
/**

  • 測試類
    */
    public class App {
    public static void main(String[] args) {
    // 目標物件
    IUserDao target = new UserDao();
    // 【原始的型別 class com.zhong.UserDao】
    System.out.println(target.getClass());

     // 給目標物件,建立代理物件
     IUserDao proxy = (IUserDao) new ProxyFactory(target).getProxyInstance();
     // class $Proxy0   記憶體中動態生成的代理物件
     System.out.println(proxy.getClass());
    
     // 執行方法   【代理物件】
     proxy.save();
    

    }
    }

總結:

動態代理實現過程:

  1. 通過getProxyClass0()生成代理類。JDK生成的最終真正的代理類,它繼承自Proxy並實現了我們定義的介面.

  2. 通過Proxy.newProxyInstance()生成代理類的例項物件,建立物件時傳入InvocationHandler型別的例項。

  3. 呼叫新例項的方法,即此例中的save(),即原InvocationHandler類中的invoke()方法。

代理物件不需要實現介面,但是目標物件一定要實現介面,否則不能用動態代理

2.3.Cglib代理

JDK的動態代理機制只能代理實現了介面的類,而不能實現介面的類就不能實現JDK的動態代理,cglib是針對類來實現代理的,他的原理是對指定的目標類生成一個子類,並覆蓋其中方法實現增強,但因為採用的是繼承,所以不能對final修飾的類進行代理。

Cglib代理,也叫作子類代理,它是在記憶體中構建一個子類物件從而實現對目標物件功能的擴充套件

Cglib子類代理實現方法:
1.需要引入cglib的jar檔案,但是Spring的核心包中已經包括了Cglib功能,所以直接引入Spring-core.jar即可.
2.引入功能包後,就可以在記憶體中動態構建子類
3.代理的類不能為final,否則報錯
4.目標物件的方法如果為final/static,那麼就不會被攔截,即不會執行目標物件額外的業務方法.

程式碼演示如下:
/**

  • 目標物件,沒有實現任何介面
    */
    public class UserDao {

    public void save() {
    System.out.println("----儲存資料成功!----");
    }
    }

Cglib代理工廠:ProxyFactory
/**

  • 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();

    }

    @Override
    public 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;
    

    }
    }

測試類APPTest:
/**

  • 測試類
    */
    public class AppTest {

    @Test
    public void test(){
    //目標物件
    UserDao target = new UserDao();

     //代理物件
     UserDao proxy = (UserDao)new ProxyFactory(target).getProxyInstance();
    
     //執行代理物件的方法
     proxy.save();
    

    }
    }
    在Spring的AOP程式設計中:
    如果加入容器的目標物件有實現介面,用JDK代理
    如果目標物件沒有實現介面,用Cglib代理

連結:
詳細請檢視https://www.cnblogs.com/linzhong/p/7234051.html
原創請檢視http://www.cnblogs.com/cenyu/p/6289209.html;