AspectJ 和 CDI

AspectJ and CDI

我正在尝试找出一种将 bean 注入方面的方法。

我是说

public class Greeter {
    public String greet(String name) {....}
}

...

public aspect GreeterAspect {
    @Inject
    private Greeter greeter

    ...
}

使用 Arquillian + Wildfly 8.2.1(托管和远程)将其作为 JUnit 测试执行,我得到以下日志行:

WELD-000119: Not generating any bean definitions from x.y.z.Greeter because of underlying class loading error: Type org.aspectj.runtime.internal.AroundClosure from [Module "deployment.test.war:main" from Service Module Loader] not found.
WELD-000119: Not generating any bean definitions from x.y.z.GreeterAspect because of underlying class loading error: Type org.aspectj.lang.NoAspectBoundException from [Module "deployment.test.war:main" from Service Module Loader] not found.

我收到错误后不久

WELD-001474: Class x.y.z.Greeter is on the classpath, but was ignored because a class it references was not found: org.aspectj.runtime.internal.AroundClosure from [Module "deployment.test.war:main" from Service Module Loader].

如果我做对了,它会抱怨 aspectjrt.jar 不在类路径中,尽管我已经检查并在依赖项中得到它(使用 Maven 构建)。在 provided 范围内,尝试切换到 compile 但没有任何改变。

谁能帮我解决这个问题?

编辑:解决了最初的问题,现在是 NullPointerException

按照 simas_ch 的建议,通过将 aspectjrt.jar 添加到 Arquillian 部署来解决最初的问题。

不过,在执行时,我收到一个 NullPointerException

public class Greeter {
    public String greet(String name) {....}
}

...

public aspect GreeterAspect {
    @Inject
    private Greeter greeter;

    private pointcut pc() : execution(* x.y.z.SomeClass.someMethod(..));

    String around() : pc() {
        log.debug("Aspect is about to say something...");
        String result = greeter.greet("Stefano");
        log.debug("Aspect said: " + result);
        return proceed();
    }
}

我可以看到第一行日志 (Aspect is about to say something...),然后我得到 NullPointerException,显然 Greeter bean 没有被注入。

我做错了什么?或者是否有可能将 bean 注入方面?

我不熟悉 CDI,但如果它没有选择方面作为依赖项注入的候选者,您应该手动设置它,最好是在方面的依赖项准备就绪后立即设置。您可以使用 AspectName.aspectOf().

访问一个方面(默认为单例)

也许是一个类似于这个的启动单例 bean:

@Singleton
@Startup
public class GreeterAspectSetup {

    @Inject
    private Greeter greeter;

    @PostConstruct
    private void setupGreeterAspect() {
        GreeterAspect.aspectOf().setGreeter(greeter);
    }

}

当然,你得把Greeter的setter加到切面,或者改变切面的字段可见性,直接设置。

感谢社区的帮助,我设法找到了解决这两个问题的方法。在这里留下足迹。

第 1 部分 - aspectjrt.jar 部署中

首先,将 Shrinkwrap 添加到我的依赖项中:

<dependency>
    <groupId>org.jboss.shrinkwrap.resolver</groupId>
    <artifactId>shrinkwrap-resolver-api-maven</artifactId>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.jboss.shrinkwrap.resolver</groupId>
    <artifactId>shrinkwrap-resolver-impl-maven</artifactId>
          <scope>test</scope>
</dependency>
<dependency>
       <groupId>org.jboss.shrinkwrap.resolver</groupId>
       <artifactId>shrinkwrap-resolver-impl-maven-archive</artifactId>
      <scope>test</scope>
</dependency>
不需要

<version>:Arquillian 的 BOM - 已经包括在内 - 会解决这个问题。

然后将 aspectj 添加到部署类路径:

@RunWith(Arquillian.class)
public class ArquillianTest {
    private static final String[] DEPENDENCIES = {
        "org.aspectj:aspectjrt:1.8.7"
    };

    @Deployment
    public static JavaArchive createEnvironement() {
        JavaArchive lib = ShrinkWrap.create(JavaArchive.class, "libs.jar");
        for (String dependency : DEPENDENCIES) {
            lib.merge(Maven.resolver().resolve(dependency).withTransitivity().asSingle(JavaArchive.class));
        }

        JavaArchive jar = ShrinkWrap.create(JavaArchive.class)
            // create you deployment here
            .as(JavaArchive.class);

        JavaArchive toBeDeployed = jar.merge(lib);

        return toBeDeployed;
    }

    // other stuff, like tests

}

第二部分:将 bean 注入方面

经过进一步询问,我认为 simas_ch 说 CDI 不将 bean 注入方面是正确的。

提出了一个解决方法:通过方面将 @Injected 成员添加到 bean 中。

public interface Advised {
    String buildGreeting(String name);
}

public class AdvisedImpl implements Advised {
    String buildGreeting(String name) {
        return "ADVISED";
    }
}

public class Greeter {
    public String greet(String name) {
        return "Hello, " + name + ".";
    }
}

...

public aspect GreeterAspect {
    @Inject
    private Greeter Advised.greeter; // adding the member to the interface / class. No need for getters / setters

    private pointcut pc() : execution(* x.y.z.Advised.buildGreeting(String));

    String around(Advised adv, String name) : pc() && target(adv) && args(name) {
        log.debug("Aspect is about to say something...");
        String result = proceed(adv, name) + " - " + adv.greeter.greet(name);
        log.debug("Aspect said: '" + result + "'");
        return result;
    }
}

鉴于测试

@Test
public void test() {
    assertThat(advised, not(is(nullValue())));
    assertThat(advised.buildGreeting("Stefano"), equalToIgnoringCase("advised - hello, stefano."));
}

成功了。