Spring AOP 預設使用 CGLib 代理類(非介面情況)。CGLib 透過繼承目標類並覆寫非 final 方法實現代理。若業務方法被標記 final,代理類無法覆寫,導致呼叫未被攔截、依賴注入的欄位(如 logger)為 null。
public abstract class BaseServlet extends HttpServlet {
protected Log logger;
@Override
public final void doGet(HttpServletRequest req, HttpServletResponse res) // ❌ final
throws IOException {
response(req, res);
logger.debug("@@@@@@doGet"); // NPE:logger 為 null
}
public void setLogger(Log logger) { this.logger = logger; }
// ...
}
// 目標類
class A {
protected String name = null;
public final void finalMethod() { System.out.println(name); }
public void notFinalMethod() { System.out.println(name); }
public void setName(String name) { this.name = name; }
}
// CGLib 生成的代理類(簡化)
class B extends A {
private A target = new A(); // 內部持有目標實例
@Override
public void notFinalMethod() { target.notFinalMethod(); } // 覆寫並委派
@Override
public void setName(String name) { target.setName(name); } // 覆寫並委派
// finalMethod 無法覆寫!繼承自 A,操作 A 的 name(永遠為 null)
}
B,注入 logger 到 target (A 實例)proxy.doGet()doGet 為 final,代理類 B 無法覆寫 → 直接執行 A.doGet()A.doGet() 存取 this.logger → A 實例的 logger 為 null(注入給了 target)NullPointerExceptionproxy-target-class="false",代理介面而非類