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)
添加回答
举报