gpt4 book ai didi

java - 触发 @Before 建议的字段上的 AspectJ 注释

转载 作者:行者123 更新时间:2023-12-01 12:30:48 25 4
gpt4 key购买 nike

我已经编写了 AspectJ 方面,它们执行由方法注释触发的 @Around 建议。现在我想做同样的事情,但是注释字段而不是方法。因此,对于下面类的每个方法调用,都必须将 accountSummary 字段设置为正确的实现。有办法做到这一点吗?我认为使用 @Before 建议将是解决此问题的最佳方法。使用 CDI 不是一种选择 - 解决方案必须使用 AspectJ。

public class PoolableBusinessLogic {
@InjectServiceClientAdapter(legacy=LegacyAccountSummary.class,new=NewAccountSummary.class)
private AccountSummary accountSummary;

public void foo() {
// use correct accountSummary impl, decided in @Before code
}

public void bar() {
// use correct accountSummary impl, decided in @Before code
}
}

最佳答案

我不确定您到底想要实现什么,因此我提出了两种替代解决方案。

首先,让我们创建一些应用程序类以获得完全可编译的示例:

package de.scrum_master.app;

public interface AccountSummary {
void doSomething();
}
package de.scrum_master.app;

public class LegacyAccountSummary implements AccountSummary {
@Override
public void doSomething() {
System.out.println("I am " + this);
}
}
package de.scrum_master.app;

public class NewAccountSummary implements AccountSummary {
@Override
public void doSomething() {
System.out.println("I am " + this);
}
}
package de.scrum_master.app;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
public @interface InjectServiceClientAdapter {
Class<?> legacyImpl();
Class<?> newImpl();
}
package de.scrum_master.app;

public class PoolableBusinessLogic {
@InjectServiceClientAdapter(legacyImpl = LegacyAccountSummary.class, newImpl = NewAccountSummary.class)
private AccountSummary accountSummary;

public void foo() {
accountSummary.doSomething();
}

public void bar() {
System.out.println("Account summary is " + accountSummary);
}
}

现在我们需要一个入口点:

package de.scrum_master.app;

public class Application {
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
PoolableBusinessLogic businessLogic = new PoolableBusinessLogic();
businessLogic.foo();
businessLogic.bar();
System.out.println();
}
}
}

显然这会产生错误,因为成员accountSummary尚未初始化:

Exception in thread "main" java.lang.NullPointerException
at de.scrum_master.app.PoolableBusinessLogic.foo(PoolableBusinessLogic.java:8)
at de.scrum_master.app.Application.main(Application.java:7)

现在我们有两个选择,具体取决于您想要实现的目标:

选项A:动态注入(inject)

场景:对于每个字段访问(即使在同一个 PoolableBusinessLogic 实例中),动态决定返回什么类型的对象实例。在此示例中,我将进行随机化以模拟另一个 if-else 标准。

顺便说一句,我希望我可以使用更具表现力的原生 AspectJ 语法。您可以轻松地将方面转换为注释样式。

package de.scrum_master.aspect;

import java.util.Random;
import org.aspectj.lang.SoftException;
import de.scrum_master.app.InjectServiceClientAdapter;

public aspect DynamicInjectionAspect {
private static final Random RANDOM = new Random();

Object around(InjectServiceClientAdapter adapterAnn) :
get(* *) && @annotation(adapterAnn)
{
try {
Class<?> implClass = RANDOM.nextBoolean() ? adapterAnn.legacyImpl() : adapterAnn.newImpl();
return implClass.newInstance();
} catch (Exception e) {
throw new SoftException(e);
}
}
}

这会产生以下输出:

I am de.scrum_master.app.LegacyAccountSummary@4d9cfefb
Account summary is de.scrum_master.app.NewAccountSummary@7e28388b

I am de.scrum_master.app.NewAccountSummary@2986e62
Account summary is de.scrum_master.app.LegacyAccountSummary@6576e542

I am de.scrum_master.app.NewAccountSummary@60c58418
Account summary is de.scrum_master.app.LegacyAccountSummary@4763754a

I am de.scrum_master.app.NewAccountSummary@52a971e3
Account summary is de.scrum_master.app.NewAccountSummary@7274187a

I am de.scrum_master.app.LegacyAccountSummary@23f32c4a
Account summary is de.scrum_master.app.LegacyAccountSummary@31e0c0b6

正如您所看到的,在五个输出组中的每一个(即对于每个 PoolableBusinessLogic 实例)中,都有不同的帐户摘要对象 ID,有时(并非总是)甚至有不同的类名称。

选项B:静态注入(inject)

场景:每个PoolableBusinessLogic实例动态决定静态分配给带注释的成员的对象实例类型(如果其值为null)。之后,不再覆盖该成员,而是返回之前初始化的值。我将再次进行随机化,以模拟另一个 if-else 标准。

注意:不要忘记停用第一个方面,例如通过将 if(false) && 添加到其切入点。否则这两个方面就会互相冲突。

package de.scrum_master.aspect;

import java.lang.reflect.Field;
import java.util.Random;
import org.aspectj.lang.SoftException;
import de.scrum_master.app.InjectServiceClientAdapter;

public aspect StaticInjectionAspect {
private static final Random RANDOM = new Random();

before(InjectServiceClientAdapter adapterAnn, Object targetObj) :
get(* *) && @annotation(adapterAnn) && target(targetObj)
{
try {
Field field = targetObj.getClass().getDeclaredField(thisJoinPoint.getSignature().getName());
field.setAccessible(true);
if (field.get(targetObj) != null)
return;
Class<?> implClass = RANDOM.nextBoolean() ? adapterAnn.legacyImpl() : adapterAnn.newImpl();
field.set(targetObj,implClass.newInstance());
} catch (Exception e) {
throw new SoftException(e);
}
}
}

这有点难看,因为它涉及使用反射来查找成员字段。因为它可能是(在我们的示例中确实是)私有(private)的,所以我们需要在对其进行任何操作之前使其可访问。

这会产生以下输出:

I am de.scrum_master.app.NewAccountSummary@20d1fa4
Account summary is de.scrum_master.app.NewAccountSummary@20d1fa4

I am de.scrum_master.app.NewAccountSummary@2b984909
Account summary is de.scrum_master.app.NewAccountSummary@2b984909

I am de.scrum_master.app.LegacyAccountSummary@1ae3043b
Account summary is de.scrum_master.app.LegacyAccountSummary@1ae3043b

I am de.scrum_master.app.LegacyAccountSummary@2e2acb47
Account summary is de.scrum_master.app.LegacyAccountSummary@2e2acb47

I am de.scrum_master.app.LegacyAccountSummary@7b87b9fe
Account summary is de.scrum_master.app.LegacyAccountSummary@7b87b9fe

现在输出看起来有所不同:在五个输出组中的每一个中(即对于每个 PoolableBusinessLogic 实例),两个输出行都显示完全相同的对象 ID。

关于java - 触发 @Before 建议的字段上的 AspectJ 注释,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25930192/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com