我已经编写了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
}
}
答案 0 :(得分:1)
我不确定你想要达到什么目标,所以我提出了两种替代解决方案。
首先,让我们创建一些应用程序类,以获得完全可编译的样本:
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:动态注入
场景:对于每个字段访问(即使在相同的PoolableBusinessLogic
实例中),动态决定返回哪种类型的对象实例。在这个例子中,我将随机化以模拟另一个if-else标准。
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:静态注入
场景:如果PoolableBusinessLogic
实例的值为null
,则每个if(false) &&
实例动态决定静态分配给注释成员的对象实例类型。之后,不要再覆盖该成员,而是返回先前初始化的值。我将再次随机化以模拟另一个if-else标准。
注意:不要忘记停用第一个方面,例如:通过将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);
}
}
}
添加到其切入点。否则这两个方面将相互冲突。
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
现在输出看起来不同:在五个输出组中的每一个(即每个{{1}}实例)中,两个输出行都显示完全相同的对象ID。
答案 1 :(得分:0)
对于选项A:动态注入在kriegaex的回答中,注释风格的方面将如下所示:
@Aspect
public class InjectServiceClientAdapterAspect {
@Pointcut("get(* *) && @annotation(injectAnnotation)")
public void getServiceClientAdapter(InjectServiceClientAdapter injectAnnotation) {
}
@Around("getServiceClientAdapter(injectAnnotation)")
public Object injectServiceClientAdapter(final ProceedingJoinPoint joinPoint, final InjectServiceClientAdapter injectAnnotation) {
// injection code goes here
}