仅在调用者为特定类型时才允许执行方法 [英] Allow method execution only if the caller is of certain type

查看:119
本文介绍了仅在调用者为特定类型时才允许执行方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们有一个 XRepository ,它扩展了 JpaRepository .由于删除 X 实体最近变得很特别,我们创建了一个 XDeletionService ,其中包含很多...::)并使用 XRepository

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

对于这个想法,我们仍然找不到合适的解决方案.到目前为止,我们要做的是创建一个切入点表达式的方面,该切入点表达式与存储库的delete方法相匹配.通过查询堆栈跟踪,这方面会引发异常,例如:

  boolean namedFromXDeletionService = Arrays.stream(Thread.currentThread().getStackTrace()).anyMatch(stackTraceElement->XDeletionService.class.getName().equals(stackTraceElement.getClassName()));如果(!扔新的.... 

这似乎很难看.您是否有一个更好的主意,如何实现此功能"?

解决方案

我建议从Spring AOP切换到AspectJ,可以与Spring一起使用,也可以完全不使用Spring.我将发布一个独立的Java示例,但是Spring手册还说明了如何配置 程序包de.scrum_master.app;公共类NormalType {public void callTargetMethod(Application application){System.out.println(普通呼叫者");application.doSomething();}}

 程序包de.scrum_master.app;公共类SpecialType {public void callTargetMethod(Application application){System.out.println(特殊呼叫者");application.doSomething();}} 

 程序包de.scrum_master.app;公共类申请{公共静态void main(String [] args){应用程序应用程序= new Application();application.callTargetMethod(application);callTargetMethodStatic(application);新的NormalType().callTargetMethod(application);新的SpecialType().callTargetMethod(application);}public void callTargetMethod(Application application){System.out.println(普通呼叫者");application.doSomething();}公共静态无效callTargetMethodStatic(应用程序应用){System.out.println(静态呼叫者");application.doSomething();}公共无效doSomething(){System.out.println(做某事");}} 

期望是,在运行小驱动程序应用程序时,仅对实例方法 SpecialType.callTargetMethod(..)中发出的对 Application.doSomething()的调用实际上将被拦截,不会被其他类的实例方法调用,也不会被静态方法调用(与Spring AOP相比,可以在AspectJ中被拦截).<​​/p>

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

仅对于 call()()()()的值与 target()(被调用者)的值之间存在差异.对于 execution()来说,两个值都相同,这就是为什么不能不借助堆栈跟踪检查或更优雅,高效地使用Stack Walking API( package de.scrum_master.aspect;导入org.aspectj.lang.JoinPoint;导入org.aspectj.lang.annotation.Aspect;导入org.aspectj.lang.annotation.Before;@方面公共类MyAspect {@前(" call(void de.scrum_master.app.Application.doSomething())&&"+"this(de.scrum_master.app.SpecialType)")公共无效myAdvice(JoinPoint joinPoint){System.out.println(joinPoint);}}

控制台日志:

 普通呼叫者做某事静态呼叫者做某事普通来电者做某事特别来电呼叫(避免de.scrum_master.app.Application.doSomething())做某事 

如果您还想记录调用者实例,则可以像这样修改方面,将其绑定到通知方法参数:

  package de.scrum_master.aspect;导入org.aspectj.lang.JoinPoint;导入org.aspectj.lang.annotation.Aspect;导入org.aspectj.lang.annotation.Before;导入de.scrum_master.app.SpecialType;@方面公共课程MyAspect {@前(" call(void de.scrum_master.app.Application.doSomething())&&"+"this(specialType)")public void myAdvice(JoinPoint joinPoint,SpecialType specialType){System.out.println(joinPoint +->" + specialType);}} 

控制台日志将为:

 普通呼叫者做某事静态呼叫者做某事普通来电者做某事特别来电呼叫(void de.scrum_master.app.Application.doSomething())->de.scrum_master.app.SpecialType@402a079c做某事 


更新:您可能还想尝试在建议中添加 JoinPoint.EnclosingStaticPart enclosingStaticPart 参数,然后打印和/或检查它.它可以帮助您找到有关 call()切入点的调用者的更多信息,而不必求助于堆栈跟踪或堆栈漫游API.

We have an XRepository which extends JpaRepository. Since deleting X entities got pretty ad-hoc recently we created an XDeletionService which contains lots of... stuff :-) and uses the XRepository.

Now we have an interesting idea to forbid the execution of any delete methods in the XRepository unless these are called from within the XDeletionService. E.g. if a colleague calls directly by mistake XRepository.delete(..) from within TheirService it will throw an exception.

We still cannot find an elegant solution for this idea. What we did so far is to create an aspect with a pointcut expression which matches the delete methods of the repository. This aspect throws an exception by consulting the stacktrace, e.g.:

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

This seems pretty ugly though. Do you have a better idea how to implement this "feature"?

解决方案

I suggest to switch from Spring AOP to AspectJ, which can be used with or completely without Spring. I am going to post a stand-alone Java example, but the Spring manual also explains how you can configure AspectJ LTW (load-time weaving) for Spring.

Sample classes + driver application:

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");
  }
}

The expectation is that when running the little driver application, only the call to Application.doSomething() issued from within the instance method SpecialType.callTargetMethod(..) will actually be intercepted, not calls from other classes' instance methods and also not calls from static methods (which can be intercepted in AspectJ in contrast to Spring AOP).

The solution is to use a call() pointcut which is a kind of counterpart to execution() and unavailable in Spring AOP. It intercepts a method call inside the caller class, not the corresponding execution in the callee. This is what we want because then we can use this() in order to determine or narrow down the caller class.

Only for call() there is a difference between the values of this() (caller) and target() (callee). For execution() both values are the same, which is why Spring AOP cannot be used for this purpose without resorting to stack trace inspection or more elegantly and efficiently the Stack Walking API (https://www.baeldung.com/java-9-stackwalking-api) in Java 9+.

Aspect:

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);
  }
}

Console log:

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

If you also want to log the caller instance, you can modify the aspect like this, binding it to an advice method parameter:

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);
  }
}

The console log would then be:

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


Update: You may also want to experiment with adding a JoinPoint.EnclosingStaticPart enclosingStaticPart parameter to your advice, then print and/or inspect it. It helps you find out more information about the caller for call() pointcuts without having to resort to stack traces or stack walking API.

这篇关于仅在调用者为特定类型时才允许执行方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆