使用Guice和AOP进行整合

huangapple go评论69阅读模式
英文:

Incorporating Guice and AOP

问题

我正在构建一个包,试图基于标志拦截函数的返回值。我的设计涉及一些面向切面编程(AOP)。想法是一个名为 FirstIntercept 的类拦截调用 firstCall 并将参数存储在 Parameters 对象中。然后稍后,第二个名为 SecondIntercept 的类拦截另一个调用 secondCall,并根据在 Parameters 中填充的内容执行一些逻辑:

// 伪代码
public class FirstIntercept {
   private Parameters param;

   @AfterReturning(pointcut = "execution(* ...firstCall(..))", returning = "payload")
   public void loadParam(Joinpoint joinPoint, Object payload) {
      // 处理从 firstCall() 返回的 payload 的逻辑
      // 逻辑提供了一个布尔标志
      this.param = new Parameters(flag);
   }
}

public class Parameters {
   @Getter
   private Boolean flag;

   public Parameters(Boolean flag) {
      this.flag = flag;
   }
}

public class SecondIntercept {
   private static Parameters params;

   @Around("execution(* ...secondCall(..))")
   public void handleSecondCallIntercept(ProceedingJoinPoint joinPoint) {
      // 基于 params 包含的内容在此处执行逻辑
   }
}

我想要实现的目标是,在通过 AOP 调用 FirstIntercept.loadParam 时,一次性加载并设置好 Parameters 对象。我不太确定如何实现这种持久性。我在网上搜索了一下,Google Guice 似乎是一个有前途的方案。我认为第一步是在 Parameters 上使用依赖注入,但我真的不确定。有人可以帮助指点我正确的方向吗?


编辑:

所以我尝试了这个设置:

public class FirstIntercept implements MethodInterceptor {
   public Object invoke(MethodInvocation invocation) throws Throwable {
      System.out.println("invoked!");
      return invocation.proceed();
   }

   @AfterReturning(pointcut = "execution(* ...firstCall(..))", returning = "payload")
   public void loadParam(Joinpoint joinPoint, Object payload) {
      // 做一些处理
   }

   public String firstCall() {
      return "hello";
   }
}

public class InterceptionModule extends AbstractModule {
   protected void configure() {
      FirstIntercept first = new FirstIntercept();
      bindInterceptor(Matchers.any(), Matchers.annotatedWith(AfterReturning.class), first);
   }
}

public class FirstIterceptTest {
   @Test
   public void dummy() {
      Injector injector = Guice.createInjector(new InterceptionModule());
      FirstIntercept intercept = injector.getInstance(FirstIntercept.class);

      intercept.firstCall();
   }
}

当我调用 .firstCall() 时,我可以看到 @AfterReturning 在运行,但 invoke 方法没有被调用。

英文:

I'm building a package that is trying to intercept a function's return value based on a flag. My design involves some AOP. The idea is that a class FirstIntercept intercepts a call firstCall and stores parameters in a Parameters object. Then later, a second class SecondIntercept intercepts another call secondCall and does some logic based on what is populated in Parameters:

// pseudoish code 
public class FirstIntercept {
   private Parameters param;

   @AfterReturning(pointcut = "execution(* ...firstCall(..))", returning = "payload")
   public void loadParam(Joinpoint joinPoint, Object payload) {
      // logic handling payload returned from firstCall()
      // logic provides a Boolean flag
      this.param = new Parameters(flag);
   }
}



public class Parameters {
   @Getter
   private Boolean flag;

   public Parameters(Boolean flag) {
      this.flag = flag;
   }
}



public class SecondIntercept {
   private static Parameters params;
   
   @Around("execution(* ...secondCall(..))")
   public void handleSecondCallIntercept(ProceedingJoinPoint joinPoint) {
      // want to do logic here based on what params contains
   }
}

What I want to achieve is that the Parameters object is loaded once and for all when FirstIntercept.loadParam is invoked through AOP. I'm not too sure how I can go about with this persistence. I looked online and Google guice seems to be promising. I believe a first step would to use dependency injection on the Parameters, but I'm really not sure. Can someone help point me in the right direction?


edit:

So I tried this setup:

public class FirstIntercept implements MethodInterceptor {
   public Object invoke(MethodInvocation invocation) throws Throwable {
      System.out.println("invoked!");
      return invocation.proceed();
   }

   @AfterReturning(pointcut = "execution(* ...firstCall(..))", returning = "payload")
   public void loadParam(Joinpoint joinPoint, Object payload) {
      // do stuff
   }

   public String firstCall() {
      return "hello";
   }
}

public class InterceptionModule extends AbstractModule {
   protected void configure() {
      FirstIntercept first = new FirstIntercept();
      bindInterceptor(Matchers.any(), Matchers.annotatedWith(AfterReturning.class), first);
   }
}

public class FirstIterceptTest {
   @Test
   public void dummy() {
      Injector injector = Guice.createInjector(new InterceptionModule());
      FirstIntercept intercept = injector.getInstance(FirstIntercept.class);

      intercept.firstCall();
   }
}

When I do .firstCall(), I can see the @AfterReturning running but the invoke is not being called.

答案1

得分: 0

如果你在AOP文档 https://github.com/google/guice/wiki/AOP 的基础上进行扩展,你应该会得到类似于以下的代码:

第一个拦截器
```java
public class FirstInterceptor implements MethodInterceptor {

  @Inject Parameters parameters;  // 使用单例的 Parameter 进行注入

  public Object invoke(MethodInvocation invocation) throws Throwable {
    Object result = invocation.proceed();
    // 基于 result 的逻辑来设置 parameters.setFlag()
    return result;
  }
}

然后是第二个拦截器:

public class SecondInterceptor implements MethodInterceptor {

  @Inject Parameters parameters;  // 使用单例的 Parameter 进行注入

  public Object invoke(MethodInvocation invocation) throws Throwable {
    boolean flag = parameters.getFlag();
    // 在这里实现你的逻辑
    return invocation.proceed(); // 也许是这样,也许不是这样?
  }
}

关键在于你的 parameters,你需要确保它是线程安全的,这是另一个话题。但要注入它们,你需要:

public class InterceptionModule extends AbstractModule {
  protected void configure() {
    // 确保只会有一个被注入的 Parameter 实例
    bind(Parameter.class).in(Scopes.SINGLETON);

    // 现在注入并绑定第一个拦截器
    FirstInterceptor firstInterceptor = new FirstInterceptor();
    requestInjection(firstInterceptor);
    bindInterceptor(Matchers.any(), Matchers.annotatedWith(AfterReturning.class),
        firstInterceptor);

    // 现在注入并绑定第二个拦截器
    SecondInterceptor secondInterceptor = new SecondInterceptor();
    requestInjection(secondInterceptor);
    bindInterceptor(Matchers.any(), Matchers.annotatedWith(AfterReturning.class),
        secondInterceptor);
  }
}

编辑
看看你正在做什么。

  1. 你告诉 Guice 使用 @AfterReturn 包装一个方法,用 FirstInterceptor
  2. 然后你调用 interceptor.firstCall()

firstCall 没有 @AfterReturn 注解,那么为什么它会与该配置匹配?

我猜想如果你调用了:

intercept.loadParam();

你会看到 invoke 方法。另外,在实际情况下,你希望一个 Service 层的类拥有 @AfterReturn,然后将其注入到另一个 Api/Job/Etc 中,该 Api/Job/Etc 将调用 loadParam。

编辑
糟糕。看看这一行

bindInterceptor(Matchers.any(),  // 匹配任意类
   Matchers.annotatedWith(AfterReturning.class),  // 匹配带有这个注解的方法
        firstInterceptor); 

这意味着注入器只会在 loadParams 上触发。你需要在你希望引发拦截的类的方法上加上 @AfterReturning 注解。而你希望 loadParams 成为 invoke 方法。


<details>
<summary>英文:</summary>

If you expand upon the documentation for AOP https://github.com/google/guice/wiki/AOP you should get something close to:

```java
public class FirstInterceptor implements MethodInterceptor {

  @Inject Parameters parameters;  // Injected with singleton Parameter

  public Object invoke(MethodInvocation invocation) throws Throwable {
    Object result = invocation.proceed();
    // your logic based on result to set parameters.setFlag()
    return result;
  }
}

Then the second:

public class SecondInterceptor implements MethodInterceptor {

  @Inject Parameters parameters;  // Injected with singleton Parameter

  public Object invoke(MethodInvocation invocation) throws Throwable {
    boolean flag = parameters.getFlag();
    // your logic here
    return invocation.proceed(); // maybe maybe not?
  }
}

Your parameters is the key, you'll need to ensure it's thread safe, which is another topic. But to inject these you need:

public class InterceptionModule extends AbstractModule {
  protected void configure() {
    // Ensure there is only ever one Parameter injected
    bind(Parameter.class).in(Scopes.SINGLETON);

    // Now inject and bind the first interceptor
    FirstInterceptor firstInterceptor = new FirstInterceptor();
    requestInjection(firstInterceptor );
    bindInterceptor(Matchers.any(), Matchers.annotatedWith(AfterReturning.class),
        firstInterceptor);

    // Now inject and bind the second interceptor
    SecondInterceptor SecondInterceptor = new SecondInterceptor ();
    requestInjection(firstInterceptor);
    bindInterceptor(Matchers.any(), Matchers.annotatedWith(AfterReturning.class),
        SecondInterceptor);
  }
}

Edit
Look at what you're doing.

  1. You're telling Guice to wrap a method with @AfterReturn with the FirstInterceptor
  2. Then you're calling interceptor.firstCall()

First call does not have @AfterReturn annotation, so why would it be matched against that configuration?

I'm guessing if you called:

intercept.loadParam();

you would see the invoke method. Also, this is great for a test, but in real life you want to have a Service level class have the @AfterReturn which is then Injected into another Api/Job/Etc that will call LoadParam.

edit
Oh no. Take a look at this line

bindInterceptor(Matchers.any(),  // a class with this matcher
   Matchers.annotatedWith(AfterReturning.class),  // a method with this 
        firstInterceptor); 

This means that the injector only fires on the loadParams. You need to annotate the method of the class youw ish to cause the interception with @AfterReturning. And you want the loadParams to be the invoke method.

huangapple
  • 本文由 发表于 2020年9月11日 15:21:13
  • 转载请务必保留本文链接:https://go.coder-hub.com/63842487.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定