英文:
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);
}
}
编辑
看看你正在做什么。
- 你告诉 Guice 使用 @AfterReturn 包装一个方法,用 FirstInterceptor
- 然后你调用 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.
- You're telling Guice to wrap a method with @AfterReturn with the FirstInterceptor
- 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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论