1. 程式人生 > >Java JDK中的靜態代理、動態代理&Cglib動態代理

Java JDK中的靜態代理、動態代理&Cglib動態代理

代理模式

代理模式是常用的java設計模式,他的特徵是代理類與委託類有同樣的介面,代理類主要負責為委託 類預處理訊息、過濾訊息、把訊息轉發給委託類,以及事後處理訊息等。代理類與委託類之間通常會存在關聯關係,一個代理類的物件與一個委託類的物件關聯,代理類的物件本身並不真正實現服務,而是通過呼叫委託類的物件的相關方法,來提供特定的服務。

按照代理的建立時期,代理類可以分為兩種。

  • 靜態代理:由程式設計師建立或特定工具自動生成原始碼,再對其編譯。在程式執行前,代理類的.class檔案就已經存在了。
  • 動態代理:動態代理的代理類。沒有直接由原始碼生成。動態代理類的物件是在程式執行時由Java反射機制動態生成,不需要手工編寫原始碼。從而提高了軟體的可擴充套件性。JAVA反射機制可以生成任意型別的動態代理類。

靜態代理

  • 介面:
public interface UserManager {

    public void addUser(String userId,String userName);  

    public void delUser(String userId);  

    public void modifyUser(String userId,String userName);  

    public String findUser(String userId);  

}
  • 介面實現類:
/**
 * 實現類
 * @author Lzx
 *
 */
public class UserManagerImpl implements UserManager { @Override public void addUser(String userId, String userName) { System.out.println("UserManagerImpl.addUser() userId = " + userId); } @Override public void delUser(String userId) { System.out.println("UserManagerImpl.delUser() userId = "
+ userId); } @Override public void modifyUser(String userId, String userName) { System.out.println("UserManagerImpl.modifyUser() userId = " + userId); } @Override public String findUser(String userId) { System.out.println("UserManagerImpl.findUser() userId = " + userId); return null; } }
  • 靜態代理類:(只持有物件的引用)
/**
 * 靜態代理類(只持有物件的引用)
 * @author Lzx
 *
 */
public class UserManagerImplProxy implements UserManager {

    private UserManager userManager;

    public UserManagerImplProxy(UserManager userManager) {
        this.userManager = userManager;
    }

    @Override
    public void addUser(String userId, String userName) {
        userManager.addUser(userId, userName);
    }

    @Override
    public void delUser(String userId) {
        userManager.delUser(userId);
    }

    @Override
    public void modifyUser(String userId, String userName) {
        userManager.modifyUser(userId, userName);
    }

    @Override
    public String findUser(String userId) {
        return userManager.findUser(userId);
    }

}
  • 測試
public static void main(String[] args) {
    // 靜態代理:客戶端例項化代理,通過代理取 子類的引用
    UserManager userManager = new UserManagerImplProxy(new UserManagerImpl());
    userManager.addUser("1", "李湘");

}

輸出結果如下:

UserManagerImpl.addUser()  userId = 1

觀察程式碼可以發現每一個代理類只能為一個介面服務,這樣一來程式開發中必然會產生過多的代理,而且,所有的代理操作除了呼叫的方法不一樣之外,其他的操作 都一樣,則此時肯定是重複程式碼。解決這一問題最好的做法是可以通過一個代理類完成全部的代理功能,那麼此時就必須使用動態代理完成。

再來看一下動態代理。

動態代理

JDK動態代理中包含一個類和一個介面。

InvocationHandler介面

public interface InvocationHandler { 
public Object invoke(Object proxy,Method method,Object[] args) throws Throwable; 
}

引數說明:
Object proxy:指被代理的物件。
Method method:要呼叫的方法
Object[] args:方法呼叫時所需要的引數

可以將InvocationHandler介面的子類想象成一個代理的最終操作類,替換掉ProxySubject。

Proxy類

Proxy類是專門完成代理的操作類,可以通過此類為一個或多個介面動態地生成實現類,此類提供瞭如下的操作方法:

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, 
InvocationHandler h) throws IllegalArgumentException

引數說明:
ClassLoader loader:類載入器
Class< ?>[] interfaces:得到全部的介面
InvocationHandler h:得到InvocationHandler介面的子類例項

關於類載入器

在Proxy類中的newProxyInstance()方法中需要一個ClassLoader類的例項,ClassLoader實際上對應的是類載入器,在Java中主要有一下三種類載入器:

Booststrap ClassLoader:此載入器採用C++編寫,一般開發中是看不到的;
Extendsion ClassLoader:用來進行擴充套件類的載入,一般對應的是jre\lib\ext目錄中的類;
AppClassLoader:(預設)載入classpath指定的類,是最常使用的是一種載入器。

動態代理類的位元組碼在程式執行時由Java反射機制動態生成,無需程式設計師手工編寫它的原始碼。 動態代理類不僅簡化了程式設計工作,而且提高了軟體系統的可擴充套件性,因為Java 反射機制可以生成任意型別的動態代理類。java.lang.reflect 包中的Proxy類和InvocationHandler 介面提供了生成動態代理類的能力。

實現動態代理步驟

  1. 建立一個實現介面InvocationHandler的類,它必須實現invoke方法
  2. 建立被代理的類以及介面
  3. 通過Proxy的靜態方法
    newProxyInstance(ClassLoaderloader, Class[] interfaces, InvocationHandler h)建立一個代理
  4. 通過代理呼叫方法

具體實現如下。

  • 介面:

同靜態代理。

  • 介面實現類:

同靜態代理。

  • 動態代理生成類:
public class ProxyHandler implements InvocationHandler {

     private Object targetObject;  

     public Object newProxyInstance(Object targetObject) {
         this.targetObject = targetObject;
         return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(), targetObject.getClass().getInterfaces(), this);
     }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        return method.invoke(targetObject, args);
    }

}
  • 測試
public static void main(String[] args) {
    ProxyHandler proxyHandler = new ProxyHandler();
    UserManager userManager  = (UserManager) proxyHandler.newProxyInstance(new UserManagerImpl());
    userManager.addUser("2", "趙雅芝");

}

輸出結果如下:

UserManagerImpl.addUser()  userId = 2

動態代理類,在程式中沒有體現。只有在程式執行的時候採用建立相應的代理類。這樣就可以少些大量的代理類。

Cglib動態代理

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

準備環境

實現

  • 沒有實現介面的類

UserManagerCglib.java

/**
 * 沒有實現介面的實現類
 * @author Lzx
 *
 */
public class UserManagerCglib {

    public void addUser(String userId, String userName) {
        System.out.println("UserManagerCglib.addUser()  userId = " + userId);  
    }

    public void delUser(String userId) {
        System.out.println("UserManagerCglib.delUser()  userId = " + userId); 
    }

    public void modifyUser(String userId, String userName) {
        System.out.println("UserManagerCglib.modifyUser()  userId = " + userId); 
    }

    public String findUser(String userId) {
        System.out.println("UserManagerCglib.findUser()  userId = " + userId); 
        return null;
    }
}
  • cglib動態代理

UserManagerCglibProxy.java

/**
 * 使用cglib動態代理 
 * @author Lzx
 *
 */
public class UserManagerCglibProxy implements MethodInterceptor{

    private Object targetObject;  
    private Enhancer enhancer = new Enhancer();
    public Object newProxyInstance(Object targetObject) {
         this.targetObject = targetObject;

        //設定需要建立子類的類
         enhancer.setSuperclass(this.targetObject.getClass());
         // 回撥方法  
         enhancer.setCallback(this);
         // 建立代理物件 
         return enhancer.create();
     }

    @Override
    public Object intercept(Object obj, Method method, Object[] args,  
            MethodProxy proxy) throws Throwable { 
        System.out.println("事物開始");  
        Object result = proxy.invokeSuper(obj, args);
        System.out.println("事物結束");  
        return null;
    }

}
  • 測試
/**
 * @author Lzx
 *
 */
final class TestCglibProxy {

    /**
     * @param args
     */
    public static void main(String[] args) {
        UserManagerCglibProxy proxy = new UserManagerCglibProxy();
        UserManagerCglib userManagerCglib = (UserManagerCglib) proxy.newProxyInstance(new UserManagerCglib());
        userManagerCglib.addUser("3", "李宇春");

    }

}

輸出結果如下:

事物開始
UserManagerCglib.addUser()  userId = 3
事物結束

相關推薦

Java JDK靜態代理動態代理&Cglib動態代理

代理模式 代理模式是常用的java設計模式,他的特徵是代理類與委託類有同樣的介面,代理類主要負責為委託 類預處理訊息、過濾訊息、把訊息轉發給委託類,以及事後處理訊息等。代理類與委託類之間通常會存在關聯關係,一個代理類的物件與一個委託類的物件關聯,代理類的物件本

面試題--JAVA靜態靜態變數載入順序詳解

public class test { //1.第一步,準備載入類 public static void main(String[] args)

Java靜態變數常量方法

由static修飾的變數、常量和方法被稱做靜態變數、常量和方法。 靜態成員屬於類所有,區別與個別物件,可以在本類或其他類使用類名和“.”運算子呼叫靜態成員。 語法 類名.靜態類成員 pub

Java靜態屬性靜態方法繼承重寫的那點破事

public class A { public static String staticStr = "A's static field"; public String nonStaticStr = "A's nonstatic field"

java靜態類的屬性變數ClassA classA = new ClassA(); 建構函式的執行順序

1.先看如下程式,判斷執行的 結果:package com.dbzhang.demo; /** * 驗證類在被初始化的時候的執行順序 * 靜態程式碼塊:static{...} * 類的屬性變數:ClassA classA = new ClassA(); * 構造方法:

python靜態方法類方法屬性方法區別

ref self 使用 lan com 通過 場景 UNC cme 在python中,靜態方法、類方法、屬性方法,剛接觸對於它們之間的區別確實讓人疑惑。 類方法(@classmethod) 是一個函數修飾符,表是該函數是一個類方法 類方法第一個參數是cls,而實例方法第

C#靜態變數 靜態方法的說明

1.靜態變數在C#程式中,沒有全域性變數的概念,這意味著所有的成員變數只有該類的例項才能操作這些資料,這起到了“資訊隱藏”的作用。但有些時候,這樣做卻不是個明智的選擇。假設我們要定義一個圖書類,要求該類能儲存圖書的數量,即每增加一本圖書(定義一個例項),圖書的數量應該加1。如果沒有靜態變數,我們需要將圖書的數

java JDK的包及其基本功能

(1)java.awt:包含構成抽象視窗工具集的多個類,用來構建和管理應用程式的圖形使用者介面,主要用於編寫GUI程式,包括按鈕、標籤等常用元件以及相應的事件類。 (2) java.lang:java的語言包,java程式設計的基礎類,核心包,預設匯入到使用者程式,包中有object類,資料型別

java NIO的channel分散聚集(二)

Java NIO中的通道(channel)介紹、通道的實現方式、直接緩衝區和非直接緩衝區、nio中的分散聚集。 /**  *   *   * 一.通道(channel):用於源節點和目標節點的連線。在Java nio中負責資料的傳輸, &n

Day01---計算機語言發展史與環境安裝配置及Java語言的識別符號關鍵字

【每日一句:不積跬步無以至千里,不積小流無以成江海】 【1】Java語言的特性: 1.跨平臺:藉助虛擬機器,程式不經修改即可在不同硬體或者軟體平臺上執行 2.以物件為基本單位,使得程式開發變得簡單易用,拓展更方便 3.Java是一門強型別的語言,摒棄了指標,擁有

代理模式三:CGLib動態代理

1、定義一個類,不實現任何介面,如下: package aop.demo4; public class GreetingImpl { public void sayHello(String

Java Web的ActionDaoServiceModel學習筆記-----阿冬專欄

SSH 框架學習之初識Java中的Action、Dao、Service、Model-----------------------------學到就要查,自己動手動腦!!! 首先這是現在最基本的分層方式,結合了SSH架構。modle層就是對應的資料庫表的實體類。Da

java/c++內部類匿名類

1.java中的內部類: class Outside{ private String nameString ; private int age; Outside() {

GCC與靜態共享庫以及動態載入庫

1.      GCC中不同型別的檔案: 字尾 內容 .a  靜態物件庫檔案 .i 已經進行預處理的C原始檔 .o 物件檔案(-c) .s 組合語言程式碼(-S) .so 共享物件庫檔案 2.      常見命令列選項與結果

ElasticSearch學習(十)在Java應用range查詢prefix查詢wildcard查詢fuzzy查詢type查詢id查詢

//range查詢(限定範圍查詢) @Test public void test14() throws Exception { //指定ES叢集 Settings settings = Settings.builder(

Java集合:ListSetmap的區別和具體的使用場景和高頻問題解析

1. Interface Iterable 迭代器介面,這是Collection類的父介面。實現這個Iterable介面的物件允許使用foreach進行遍歷,也就是說,所有的Collection集合物件都具有"foreach可遍歷性"。這個Iterable介面只有一個方法: iterator()。它返回一個代

javascript靜態方法例項方法內部方法和原型的一點見解

1、靜態方法的定義 Js程式碼   var BaseClass = function() {}; // var BaseClass=new Function(); BaseClass.f1 = function(){//定義靜態方法      alert('

Java靜態成員的初始化

//: initialization/StaticInitialization.java// Specifying initial values in a class definition./* *   2018年3月20日10:09:27 *   程式碼位置: java

網路運維靜態路由 三層交換技術 動態路由 OSPF協議的配置

一、路由器原理及靜態路由1、路由跨越從源主機到目標主機的一個網際網路絡來轉發資料包的過程2、路由表路由器根據路由表做路徑選擇3、路由表的獲得1)直連路由:配置IP地址,埠UP狀態,形成直連路由。2)非直連網段:需要靜態路由或動態路由,將網段新增到路由表中。4、靜態路由1)、特

java web 持久層業務層表現層域模型層理解

許多設計良好的web應用,可以被按職責分為四層。這些層次是表現層、持久層、業務層、和域模型層。每一個層次都有其獨特的職責,不能把各自的功能與其它層次相混合。每一個應用層都應該和其它層隔離開來,但允許使用介面在層間進行通訊。我們開始來看看每個層,並討論一下它們各自都應該提供