1 回答

TA貢獻1898條經驗 獲得超8個贊
我們首先要區分
AspectJ(快速、高效、無代理、更強大)和
Spring AOP(基于代理、委托模式、“輕AOP”、僅方法攔截)。
編譯時檢查調用代碼@DeclareError
當使用 AspectJ 并使用 AspectJ 編譯器重新編譯遺留庫(具有 AOP 增強功能的 Java 編譯器的直接替代品)時,@DeclareError
如果發現來自錯誤的類或包模式的調用,您可以使用 AspectJ 使編譯失敗。這樣你就不需要任何昂貴的運行時檢查、反射或其他技巧。對于 Maven 構建,您可以使用 AspectJ Maven 插件。
這就是我的建議:在構建時檢測無效調用,修復代碼以使其能夠編譯并在運行時無憂無慮。
call()
使用帶有切入點的AspectJ 加載時編織 (LTW) 進行運行時檢查
然而,如果您不想使用 AspectJ 編譯器(即使您標記了問題spectj而不是spring-aop)或者對調用代碼沒有編譯時影響,您仍然可以使用AspectJ 加載時編織(LTW )來自春天。如果您想避免創建異常的唯一目的是分析其調用堆棧以找到調用者,那么 Spring AOP 絕對不夠。相反,在完整的 AspectJ 中,有一個名為call()
Spring AOP 的切入點。您可以通過 LTW 編織到調用代碼中,然后使用它EnclosingStaticPart
來查找調用者。唯一需要注意的是,在 Spring 中,您可能會使用代理而不是直接調用,這可能會間接地擾亂調用者,但您可以嘗試一下。
這是純 Java + AspectJ 中的MCVE(不涉及 Spring):
DAO、服務、控制器:
package com.dao.ddd;
public class MyDao {
? public void doSomething() {
? ? System.out.println("Doing something in DAO");
? }
}
package com.service.sss;
import com.dao.ddd.MyDao;
public class MyService {
? public void doSomething() {
? ? System.out.println("Doing something in service");
? ? new MyDao().doSomething();
? }
}
package com.controller.ccc;
import com.dao.ddd.MyDao;
public class MyController {
? public void doSomething() {
? ? System.out.println("Doing something in controller");
? ? new MyDao().doSomething();
? }
}
package de.scrum_master.app;
import com.controller.ccc.MyController;
import com.service.sss.MyService;
public class Application {
? public static void main(String[] args) {
? ? // Allowed
? ? new MyService().doSomething();
? ? // Forbidden
? ? new MyController().doSomething();
? }
}
驅動程序應用:
package de.scrum_master.app;
import com.controller.ccc.MyController;
import com.service.sss.MyService;
public class Application {
? public static void main(String[] args) {
? ? // Allowed
? ? new MyService().doSomething();
? ? // Forbidden
? ? new MyController().doSomething();
? }
}
不帶方面的控制臺日志:
Doing something in service
Doing something in DAO
Doing something in controller
Doing something in DAO
實際上,列表行不應該被打印,因為從控制器到 DAO 的調用是被禁止的。
方面:
package de.scrum_master.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.JoinPoint.EnclosingStaticPart;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class ContractEnforcerAspect {
? @Before("call(* com.dao..*(..))")
? public void beforeAdvice(JoinPoint joinPoint, EnclosingStaticPart enclosingStaticPart) {
? ? System.out.println("? Callee = " + joinPoint.getSignature());
? ? System.out.println("? Caller = " + enclosingStaticPart.getSignature());
? ? if (enclosingStaticPart.getSignature().getDeclaringType().getPackageName().startsWith("com.controller"))
? ? ? throw new RuntimeException("DAO must not be called from controller");
? }
}
控制臺日志與方面:
Doing something in service
? Callee = void com.dao.ddd.MyDao.doSomething()
? Caller = void com.service.sss.MyService.doSomething()
Doing something in DAO
Doing something in controller
? Callee = void com.dao.ddd.MyDao.doSomething()
? Caller = void com.controller.ccc.MyController.doSomething()
Exception in thread "main" java.lang.RuntimeException: DAO must not be called from controller
? ? at de.scrum_master.aspect.ContractEnforcerAspect.beforeAdvice(ContractEnforcerAspect.aj:17)
? ? at com.controller.ccc.MyController.doSomething(MyController.java:8)
? ? at de.scrum_master.app.Application.main(Application.java:11)
添加回答
舉報