代理模式:為其他對象提供一個代理以控制對該對象的訪問。核心價值在於關注點分離:目標對象專注業務邏輯,代理對象處理橫切關注點 (事務、日誌、權限、緩存、監控)。
| 類型 | 實現方式 | 代理類生成時機 | 目標類要求 | 性能 | 典型場景 |
|---|---|---|---|---|---|
| 靜態代理 | 手寫/工具生成 .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, "張三"));
// 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-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, "王五"));
@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());
}
}
MethodProxy.invokeSuper 避免反射開銷代理模式是 AOP 的基石。靜態代理 簡單但擴展性差;JDK 動態代理 要求接口,反射調用有開銷;CGLIB 通過繼承實現,性能更好但無法代理 final 類/方法。Spring 智能選擇:有接口用 JDK,無接口或強制配置用 CGLIB。實際開發中優先面向接口編程,配合 @EnableAspectJAutoProxy 享受聲明式事務、緩存、鎖等企業級能力。