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

Java 代理模式全解析:靜態代理、動態代理、CGLIB 與 Spring AOP 核心原理

     編輯:Java 相關知識

模式定義與核心價值

代理模式:為其他對象提供一個代理以控制對該對象的訪問。核心價值在於關注點分離:目標對象專注業務邏輯,代理對象處理橫切關注點 (事務、日誌、權限、緩存、監控)。

三大代理類型對比

類型實現方式代理類生成時機目標類要求性能典型場景
靜態代理手寫/工具生成 .java 文件編譯期實現接口最高 (直接調用)簡單包裝、適配器
JDK 動態代理反射 + Proxy.newProxyInstance運行時必須實現接口中 (反射調用)Spring AOP 默認、接口編程
CGLIB 動態代理字節碼操作 (ASM) 生成子類運行時非 final 類、非 final 方法較高 (FastClass 機制)Spring AOP 無接口、第三方類增強

靜態代理代碼示範

// 1. 業務接口
interface UserDao {
    void save(User user);
    User findById(Long id);
}

// 2. 目標對象
class UserDaoImpl implements UserDao {
    @Override public void save(User user) { System.out.println("保存用戶: " + user); }
    @Override public User findById(Long id) { return new User(id, "用戶" + id); }
}

// 3. 靜態代理類 (手寫)
class UserDaoProxy implements UserDao {
    private final UserDao target;
    
    public UserDaoProxy(UserDao target) { this.target = target; }
    
    @Override
    public void save(User user) {
        System.out.println("[日誌] 開始保存...");
        long start = System.currentTimeMillis();
        try {
            target.save(user); // 委託目標對象
        } finally {
            System.out.printf("[日誌] 保存耗時: %dms%n", System.currentTimeMillis() - start);
        }
    }
    
    @Override public User findById(Long id) { return target.findById(id); }
}

// 4. 使用
UserDao dao = new UserDaoProxy(new UserDaoImpl());
dao.save(new User(1L, "張三"));

JDK 動態代理核心實現

// 1. InvocationHandler 實現攔截邏輯
class LoggingHandler implements InvocationHandler {
    private final Object target;
    
    public LoggingHandler(Object target) { this.target = target; }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("[動態代理] 調用: " + method.getName());
        long start = System.nanoTime();
        try {
            return method.invoke(target, args); // 反射調用目標方法
        } finally {
            System.out.printf("[動態代理] 耗時: %d ns%n", System.nanoTime() - start);
        }
    }
}

// 2. 創建代理對象
UserDao target = new UserDaoImpl();
UserDao proxy = (UserDao) Proxy.newProxyInstance(
    target.getClass().getClassLoader(),     // 類加載器
    target.getClass().getInterfaces(),      // 實現的接口數組
    new LoggingHandler(target)              // 處理器
);

proxy.save(new User(1L, "李四")); // 觸發 invoke()

CGLIB 動態代理核心實現

// 需依賴: cglib:cglib-nodep:3.3.0
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

class CglibLoggingInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("[CGLIB] 調用: " + method.getName());
        // proxy.invokeSuper 調用父類(目標類)方法,比反射快
        return proxy.invokeSuper(obj, args);
    }
}

// 創建代理 (目標類無需實現接口)
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserDaoImpl.class); // 設置父類
enhancer.setCallback(new CglibLoggingInterceptor());
UserDao proxy = (UserDao) enhancer.create();

proxy.save(new User(2L, "王五"));

Spring AOP 代理選擇策略

@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true) // true=強制 CGLIB, false=優先 JDK
public class AopConfig { }

// 決策邏輯 (Spring 源碼簡化)
if (proxyTargetClass || targetClass.isInterface() || 
    Proxy.isProxyClass(targetClass) || hasNoUserMethods(targetClass)) {
    // 使用 CGLIB
} else {
    // 使用 JDK 動態代理
}

進階:自定義註解 + 切面實現聲明式事務

// 1. 自定義註解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Transactional {
    int timeout() default 30;
    boolean readOnly() default false;
}

// 2. 切面類
@Aspect
@Component
public class TransactionAspect {
    @Around("@annotation(tx)")
    public Object manageTransaction(ProceedingJoinPoint pjp, Transactional tx) throws Throwable {
        Connection conn = DataSourceUtils.getConnection();
        boolean originalAutoCommit = conn.getAutoCommit();
        conn.setAutoCommit(false);
        try {
            Object result = pjp.proceed(); // 執行目標方法
            conn.commit();
            return result;
        } catch (Exception e) {
            conn.rollback();
            throw e;
        } finally {
            conn.setAutoCommit(originalAutoCommit);
            DataSourceUtils.releaseConnection(conn);
        }
    }
}

// 3. 業務代碼
@Service
public class OrderService {
    @Transactional(timeout = 10)
    public void createOrder(Order order) {
        orderDao.insert(order);
        inventoryService.deduct(order.getItems());
    }
}

性能優化建議

  • 緩存代理對象:同一目標對象重複創建代理開銷大,Spring 單例 Bean 自動緩存
  • 避免過度代理:僅對需要橫切邏輯的方法/類代理
  • 優先接口編程:JDK 代理比 CGLIB 輕量,無第三方依賴
  • 方法調用優化:CGLIB 使用 MethodProxy.invokeSuper 避免反射開銷

總結

代理模式是 AOP 的基石。靜態代理 簡單但擴展性差;JDK 動態代理 要求接口,反射調用有開銷;CGLIB 通過繼承實現,性能更好但無法代理 final 類/方法。Spring 智能選擇:有接口用 JDK,無接口或強制配置用 CGLIB。實際開發中優先面向接口編程,配合 @EnableAspectJAutoProxy 享受聲明式事務、緩存、鎖等企業級能力。

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