在 spring 中阻止来自控制器的 Dao 调用
Prevent dao call from controller in spring
我试图避免来自控制器的 dao 调用 class。如果调用是从服务包完成的,那么 dao 调用应该是成功的,否则我将抛出异常。我不想在dao的每个方法中都写这个逻辑class所以打算用aspectj来拦截dao的调用。我怎样才能阻止控制器使用 dao 并仅允许它使用 class 服务。我可以使用任何其他 api/approach 吗?任何建议
package com;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class DaoAspect {
@Before(value = "execution(* com.dao.*.*(..))")
public void beforeAdvice( JoinPoint joinPoint) {
// here I want to know the caller package/class
// if its com.service allow ,if com.controller reject
}
}
免责声明:我不是 Spring 用户。也许有一种更简单的机载方法可以通过拦截器、Spring 安全性或其他方式实现这一点。但是你要求一个AOP解决方案。
我们首先要区分
- AspectJ(快速、高效、无代理、更强大)和
- Spring AOP(基于代理,委托模式,"AOP light",仅方法拦截)。
使用 @DeclareError
对调用代码进行编译时检查
当使用 AspectJ 并使用 AspectJ 编译器重新编译遗留库时(使用 AOP 增强功能直接替换 Java 编译器),您可以使用 @DeclareError
以使编译失败,如果发现来自错误 class 或包模式的调用。这样你就不需要任何昂贵的运行时检查、反射或其他技巧。对于您的 Maven 构建,您可以使用 AspectJ Maven 插件。有关 @DeclareError
:
的更多信息,请在此处查看我的回答
这是我的建议:在构建时检测无效调用,修复代码以使其在运行时编译和无忧。
运行-time 检查使用 AspectJ 加载时编织 (LTW) 和 call()
切入点
如果您不想使用 AspectJ 编译器(即使您将问题标记为 aspectj 而不是 spring-aop ) 或者对调用代码没有编译时影响,您仍然可以使用 Spring 中的 AspectJ load-time weaving (LTW)。 Spring 如果你想避免为了分析调用堆栈以找到调用者而创建异常,AOP 绝对是不够的。相反,在完整的 AspectJ 中有一个名为 call()
的切入点,它在 Spring AOP 中不可用。您可以通过 LTW 编织到调用代码中,然后使用 EnclosingStaticPart
来查找调用者。唯一需要注意的是,在 Spring 中,您可能会使用代理而不是直接调用,这可能会通过间接方式弄乱调用者,但您可以尝试一下。
这里是 MCVE 的普通 Java + AspectJ(不涉及 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)
我试图避免来自控制器的 dao 调用 class。如果调用是从服务包完成的,那么 dao 调用应该是成功的,否则我将抛出异常。我不想在dao的每个方法中都写这个逻辑class所以打算用aspectj来拦截dao的调用。我怎样才能阻止控制器使用 dao 并仅允许它使用 class 服务。我可以使用任何其他 api/approach 吗?任何建议
package com;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class DaoAspect {
@Before(value = "execution(* com.dao.*.*(..))")
public void beforeAdvice( JoinPoint joinPoint) {
// here I want to know the caller package/class
// if its com.service allow ,if com.controller reject
}
}
免责声明:我不是 Spring 用户。也许有一种更简单的机载方法可以通过拦截器、Spring 安全性或其他方式实现这一点。但是你要求一个AOP解决方案。
我们首先要区分
- AspectJ(快速、高效、无代理、更强大)和
- Spring AOP(基于代理,委托模式,"AOP light",仅方法拦截)。
使用 @DeclareError
对调用代码进行编译时检查
当使用 AspectJ 并使用 AspectJ 编译器重新编译遗留库时(使用 AOP 增强功能直接替换 Java 编译器),您可以使用 @DeclareError
以使编译失败,如果发现来自错误 class 或包模式的调用。这样你就不需要任何昂贵的运行时检查、反射或其他技巧。对于您的 Maven 构建,您可以使用 AspectJ Maven 插件。有关 @DeclareError
:
这是我的建议:在构建时检测无效调用,修复代码以使其在运行时编译和无忧。
运行-time 检查使用 AspectJ 加载时编织 (LTW) 和 call()
切入点
如果您不想使用 AspectJ 编译器(即使您将问题标记为 aspectj 而不是 spring-aop ) 或者对调用代码没有编译时影响,您仍然可以使用 Spring 中的 AspectJ load-time weaving (LTW)。 Spring 如果你想避免为了分析调用堆栈以找到调用者而创建异常,AOP 绝对是不够的。相反,在完整的 AspectJ 中有一个名为 call()
的切入点,它在 Spring AOP 中不可用。您可以通过 LTW 编织到调用代码中,然后使用 EnclosingStaticPart
来查找调用者。唯一需要注意的是,在 Spring 中,您可能会使用代理而不是直接调用,这可能会通过间接方式弄乱调用者,但您可以尝试一下。
这里是 MCVE 的普通 Java + AspectJ(不涉及 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)