首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

JDK 動態代理深度解析(一):原理、實現與核心類分析

     編輯:Java 綜合教學

1. 什麼是代理模式

代理即代替主角完成額外操作。如明星有經紀人:洽談片酬、安排行程(前置操作),明星拍戲(核心業務),拍完後經紀人清算片酬(後置操作)。

Java 代理機制:在目標方法執行前後插入額外邏輯(安全檢查、日誌、事務等)。分為靜態代理和動態代理。

2. 靜態代理

// 目標接口
public interface LoginService { void login(); }

// 目標實現
public class LoginServiceImpl implements LoginService {
    @Override public void login() { System.out.println("login"); }
}

// 靜態代理類(手寫)
public class LoginServiceProxy implements LoginService {
    private LoginService loginService;
    public LoginServiceProxy(LoginService loginService) { this.loginService = loginService; }
    
    @Override public void login() {
        beforeLogin();
        loginService.login();
        afterLogin();
    }
    private void beforeLogin() { System.out.println("before login"); }
    private void afterLogin() { System.out.println("after login"); }
}

// 客戶端
public class Client {
    @Test public void test() {
        LoginService loginService = new LoginServiceImpl();
        LoginService proxy = new LoginServiceProxy(loginService);
        proxy.login();
    }
}
// 輸出:before login → login → after login

缺點

  • 代理類編譯期即存在,生成 .class 文件
  • 接口有 N 個方法需寫 N 個代理方法,大量重複樣板代碼
  • 違反開閉原則,擴展性差

3. 核心概念

  • 目標接口:操作抽象(LoginService)
  • 目標類:接口實現(LoginServiceImpl)
  • 目標對象:目標類實例
  • 代理類:目標類代理(動態生成)
  • 代理對象:代理類實例

4. JDK 動態代理

運行時根據目標接口動態生成代理類字節碼,加載至 JVM,編譯期無 .class 文件。

// 代理處理器(核心邏輯)
public class ProxyInvocationHandler implements InvocationHandler {
    private LoginService loginService;
    public ProxyInvocationHandler(LoginService loginService) { this.loginService = loginService; }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        beforeLogin();
        Object result = method.invoke(loginService, args); // 反射調用目標方法
        afterLogin();
        return result;
    }
    private void beforeLogin() { System.out.println("before login"); }
    private void afterLogin() { System.out.println("after login"); }
}

// 客戶端創建代理
public class Client {
    @Test public void test() {
        LoginService target = new LoginServiceImpl();
        ProxyInvocationHandler handler = new ProxyInvocationHandler(target);
        
        // 動態生成代理對象
        LoginService proxy = (LoginService) Proxy.newProxyInstance(
            target.getClass().getClassLoader(),
            target.getClass().getInterfaces(),
            handler
        );
        proxy.login();
        
        // 可選:導出生成的代理類字節碼用於分析
        createProxyClassFile();
    }
    
    public static void createProxyClassFile() {
        String name = "LoginServiceProxy";
        byte[] data = ProxyGenerator.generateProxyClass(name, new Class[]{LoginService.class});
        try (FileOutputStream out = new FileOutputStream("/Users/" + name + ".class")) {
            out.write(data);
        } catch (Exception e) { e.printStackTrace(); }
    }
}
// 輸出:before login → login → after login

5. 核心類解析

InvocationHandler

代理調用處理器接口,每個代理對象關聯一個處理器。核心方法:

Object invoke(Object proxy, Method method, Object[] args) throws Throwable;

代理對象調用任何接口方法時,均委派給此 invoke 方法。

Proxy

提供靜態方法創建動態代理類和實例。生成的代理類均繼承 Proxy。

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
  • loader:加載代理類的類加載器
  • interfaces:代理類實現的接口列表
  • h:調用處理器

6. 動態代理實現步驟總結

  1. 定義目標接口
  2. 實現目標類(核心業務邏輯)
  3. 實現 InvocationHandler,重寫 invoke 定義代理邏輯
  4. 調用 Proxy.newProxyInstance() 創建代理對象
  5. 使用代理對象調用方法

7. 下一節預告

《深入淺出 JDK 動態代理(二)》將深入源碼分析:

  • 動態生成的代理類結構長什麼樣?
  • 為什麼調用任何方法必定進入 invoke?
  • ProxyGenerator 生成字節碼的細節

敬請期待!

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved