如何解决仅在调用者为特定类型时才允许执行方法
我们有一个XRepository
,它扩展了JpaRepository
。自从最近删除X
实体以来,我们特别创建了一个XDeletionService
,其中包含很多...东西:-)并使用XRepository
。
现在,我们有一个有趣的想法,禁止在delete
中执行任何XRepository
方法,除非从XDeletionService
内部调用这些方法。例如。如果同事从XRepository.delete(..)
内部错误地直接致电Theirservice
打电话,将会抛出异常。
对于这个想法,我们仍然找不到合适的解决方案。到目前为止,我们要做的是创建一个切入点表达式的方面,该切入点表达式与存储库的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手册还将说明如何配置AspectJ LTW (load-time weaving) for Spring。
示例类+驱动程序应用程序
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");
}
}
期望是,在运行小驱动程序应用程序时,实际上只会拦截从实例方法Application.doSomething()
中发出的对SpecialType.callTargetMethod(..)
的调用,而不是其他类的实例方法的调用,也不会来自静态方法的调用(与Spring AOP相比,可以在AspectJ中进行拦截)。
解决方案是使用call()
切入点,该切入点与execution()
相对,并且在Spring AOP中不可用。它拦截了调用者类内部的方法调用,而不是被调用者中的相应执行。这就是我们想要的,因为这样我们就可以使用this()
来确定或缩小呼叫者类别。
仅对于call()
,this()
(呼叫者)和target()
(被呼叫者)的值之间存在差异。对于execution()
,这两个值是相同的,这就是为什么Spring AOP不能在没有使用堆栈跟踪检查或更优雅,更有效地使用Java 9+中的Stack Walking API(https://www.baeldung.com/java-9-stackwalking-api)的情况下用于此目的的原因。
方面:
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
方法的接口,这被称为Interface Segregation Principle。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。