代理類需實現同一接口,委托給真實對象,每新增一個被代理類就需寫一個代理類,代碼重復度高。
// 1. 定義調用處理器
class SubjectHandler implements InvocationHandler {
private Subject target;
public SubjectHandler(Subject target) { this.target = target; }
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("預處理..."); // 統一前置邏輯
Object result = method.invoke(target, args); // 反射調用目標
System.out.println("後處理..."); // 統一後置邏輯
return result;
}
}
// 2. 創建代理實例
Subject proxy = (Subject) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new SubjectHandler(target)
);
proxy.request(); // 觸發 invoke()
Proxy.newProxyInstance 根據接口動態生成實現類字節碼(繼承 Proxy、實現目標接口)。InvocationHandler.invoke(),實現統一攔截。裝飾類繼承組件接口,持有組件引用,構造函數傳入具體裝飾類,靜態組合,擴展需新增裝飾類。
// 能力接口
interface Feature { void load(); }
class FlyFeature implements Feature { public void load() { System.out.println("加翼"); } }
class DigFeature implements Feature { public void load() { System.out.println("鑽地"); } }
// 通用裝飾動作類
class DecorateAnimal implements Animal {
private Animal animal;
private Class<? extends Feature> featureClz;
public DecorateAnimal(Animal animal, Class<? extends Feature> clz) {
this.animal = animal; this.featureClz = clz;
}
@Override public void doStuff() {
InvocationHandler handler = (proxy, method, args) -> {
if (Modifier.isPublic(method.getModifiers())) {
method.invoke(featureClz.newInstance(), args); // 動態實例化能力並調用
}
animal.doStuff(); // 委托核心業務
return null;
};
Feature proxy = (Feature) Proxy.newProxyInstance(
getClass().getClassLoader(), featureClz.getInterfaces(), handler);
proxy.load(); // 觸發能力加載
}
}
// 客戶端疊加能力
Animal jerry = new Rat();
jerry = new DecorateAnimal(jerry, FlyFeature.class);
jerry = new DecorateAnimal(jerry, DigFeature.class);
jerry.doStuff();
InvocationHandler 動態控制,可讀持久化配置決定裝飾邏輯。父類定義 final templateMethod() 調用 abstract primitiveOps(),子類必須實現所有抽象方法,擴展受限。
public abstract class AbsPopulator {
// 模板方法:掃描所有 public 方法,匹配規則自動調用
public final void dataInitialing() throws Exception {
for (Method m : getClass().getMethods()) {
if (isInitDataMethod(m)) m.invoke(this);
}
}
// 基本方法鑒別器:命名規約 + 修飾符 + 簽名約束
private boolean isInitDataMethod(Method m) {
return m.getName().startsWith("init")
&& Modifier.isPublic(m.getModifiers())
&& m.getReturnType() == void.class
&& m.getParameterCount() == 0
&& !Modifier.isAbstract(m.getModifiers());
}
}
// 子類自由定義任意數量 initXxx() 方法
public class UserPopulator extends AbsPopulator {
public void initUser() { /* 建表、導數據 */ }
public void initPassword() { /* 密碼策略 */ }
public void initJobs() { /* 定時任務 */ }
}
JUnit 3/4 早期要求測試方法以 test 開頭、public、無參、無返回,本質相同。JUnit 5 改用註解(@Test),但反射掃描機制未變。
abstract class BaseDao<T> {
// 運行期獲取 T 的實際 Class
private Class<T> entityClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass())
.getActualTypeArguments()[0];
public T get(Serializable id) {
return session.get(entityClass, id); // Hibernate 需要 Class 對象
}
}
class UserDao extends BaseDao<User> { } // 編譯期確定 T=User
get() 傳入 User.class,大量重復代碼。80% 的性能消耗集中在 20% 的代碼中。優化應針對熱點路徑(經 Profiling 證實),而非預防性地拒絕反射等強大工具。
Method 對象、使用 MethodHandle、或代碼生成(ASM/ByteBuddy)替換。反射是 Java 動態性的基石,動態代理、裝飾模式增強、模板方法泛化、泛型類型擷取等高級模式均依賴它。效率低是真命題,因效率低而拒用是假命題。善用反射實現框架級抽象,業務層專注領域邏輯,才是高質量代碼的正道。