仅当调用方为特定类型时才允许执行方法



我们有一个扩展JpaRepositoryXRepository。由于删除X实体最近变得非常特别,我们创建了一个XDeletionService,它包含许多。。。stuff:-(并使用CCD_ 5。

现在我们有一个有趣的想法,禁止在XRepository中执行任何delete方法,除非这些方法是从XDeletionService中调用的。例如,如果同事直接错误地从TheirService中调用XRepository.delete(..),则会引发异常。

我们仍然无法为这个想法找到一个优雅的解决方案。到目前为止,我们所做的是创建一个带有切入点表达式的方面,该表达式与存储库的删除方法相匹配。此方面通过咨询堆栈跟踪抛出异常,例如:

boolean calledFromXDeletionService = Arrays.stream(Thread.currentThread().getStackTrace())
.anyMatch(stackTraceElement ->
XDeletionService.class.getName().equals(stackTraceElement.getClassName())); 
if (!calledFromXDeletionService)
throw new ....

这看起来很难看。你有更好的想法如何实现这个";特征";?

我建议从Spring AOP切换到AspectJ,它可以与Spring一起使用,也可以完全不使用Spring。我将发布一个独立的Java示例,但Spring手册也解释了如何为Spring配置AspectJ LTW(加载时编织(。

示例类+驱动程序应用程序:

package de.scrum_master.app;
public class NormalType {
public void callTargetMethod(Application application) {
System.out.println("Normal caller");
application.doSomething();
}
}
package de.scrum_master.app;
public class SpecialType {
public void callTargetMethod(Application application) {
System.out.println("Special caller");
application.doSomething();
}
}
package de.scrum_master.app;
public class Application {
public static void main(String[] args) {
Application application = new Application();
application.callTargetMethod(application);
callTargetMethodStatic(application);
new NormalType().callTargetMethod(application);
new SpecialType().callTargetMethod(application);
}
public void callTargetMethod(Application application) {
System.out.println("Normal caller");
application.doSomething();
}
public static void callTargetMethodStatic(Application application) {
System.out.println("Static caller");
application.doSomething();
}
public void doSomething() {
System.out.println("Doing something");
}
}

期望的是,当运行小驱动程序应用程序时,只有从实例方法SpecialType.callTargetMethod(..)内部发出的对Application.doSomething()的调用会被实际拦截,而不是来自其他类的实例方法的调用,也不是来自静态方法的调用(与Spring AOP相比,可以在AspectJ中拦截(。

解决方案是使用call()切入点,它是execution()的一种对应物,在Spring AOP中不可用。它拦截调用方类内部的方法调用,而不是被调用方中的相应执行。这就是我们想要的,因为这样我们就可以使用this()来确定或缩小调用者类的范围。

仅对于call()this()(调用者(和target()(被调用者(的值之间存在差异。对于execution(),这两个值是相同的,这就是为什么如果不采用堆栈跟踪检查或更优雅有效的stack Walking API,Spring AOP就不能用于此目的(https://www.baeldung.com/java-9-stackwalking-api)在Java 9+中。

方面:

package de.scrum_master.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class MyAspect {
@Before(
"call(void de.scrum_master.app.Application.doSomething()) && " +
"this(de.scrum_master.app.SpecialType)"
)
public void myAdvice(JoinPoint joinPoint) {
System.out.println(joinPoint);
}
}

控制台日志:

Normal caller
Doing something
Static caller
Doing something
Normal caller
Doing something
Special caller
call(void de.scrum_master.app.Application.doSomething())
Doing something

如果您还想记录调用方实例,可以这样修改方面,将其绑定到一个建议方法参数:

package de.scrum_master.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import de.scrum_master.app.SpecialType;
@Aspect
public class MyAspect {
@Before(
"call(void de.scrum_master.app.Application.doSomething()) && " +
"this(specialType)"
)
public void myAdvice(JoinPoint joinPoint, SpecialType specialType) {
System.out.println(joinPoint + " -> " + specialType);
}
}

控制台日志将是:

Normal caller
Doing something
Static caller
Doing something
Normal caller
Doing something
Special caller
call(void de.scrum_master.app.Application.doSomething()) -> de.scrum_master.app.SpecialType@402a079c
Doing something

更新:您可能还想尝试将JoinPoint.EnclosingStaticPart enclosingStaticPart参数添加到建议中,然后打印和/或检查它。它可以帮助您查找有关call()切入点调用方的更多信息,而无需使用堆栈跟踪或堆栈遍历API。

TheirService应该使用一个没有delete方法的接口,这被称为接口分离原则。

最新更新