【问题标题】:AspectJ and CDIAspectJ 和 CDI
【发布时间】:2016-07-02 12:12:13
【问题描述】:

我正在尝试找出一种将 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 注入方面?

【问题讨论】:

  • provided scope 表示它是由其他人提供的,例如框架。你检查是不是这样?或者您是否尝试过没有特殊范围,例如将其添加到运行时类路径?
  • @Thomas 是的,事实上我试图将其移至compile,但并没有改变这种情况。您的意思是将 jar 添加到 Wildfly 类路径还是 Arquillian 的?
  • 你使用收缩包装吗?如果是,则必须添加依赖项: File[] files = Maven.resolver().loadPomFromFile("pom.xml") .importRuntimeDependencies().resolve().withTransitivity().asFile();
  • AFAIK compileprovided 类似,因为它不会添加到应用程序中。如果尝试将其添加到耳朵的 lib 文件夹中(不提供任何范围)。或者为 AspectJ 创建一个模块(可能已经存在一个)并为您的应用程序定义对该模块的依赖项(在 JBoss 7 中,这将在 MANIFEST.MF 或 jboss-deployment-structure.xml 中,不确定是否重命名它在 Wildfly 中)。
  • 我不认为你可以在一个方面使用@Inject。这不是一个 Java 类,AspectJ 编织器根据切入点将切面注入到您的 Java 类中。

标签: java maven cdi aspectj


【解决方案1】:

我对 CDI 不熟悉,但如果它没有将切面作为依赖注入的候选者,您应该手动设置它,最好在切面的依赖项准备好后立即进行设置。您可以使用AspectName.aspectOf() 访问一个方面(默认为单例)。

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

@Singleton
@Startup
public class GreeterAspectSetup {

    @Inject
    private Greeter greeter;

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

}

当然,您必须将 Greeter 的设置器添加到方面,或者更改该字段在方面的可见性并直接设置。

【讨论】:

  • 我对 CDI 也不是很熟悉。我使用您提到的aspectOf 方法作为Spring 上下文中这些方面的工厂方法(因此利用了Spring 依赖注入)。你知道是否有办法指示 CDI 使用不同的工厂方法而不是默认的构造函数?这样就可以解决问题了。
  • AFAI懂你,你需要@Producer method
  • @G.Demecki 尝试过,但 @Producer 方法需要在托管 bean 中(据我了解,生成的 bean 将被注入到同一个 bean 中)。如果在方面内部,则不会调用该方法。
  • @Produces 可以在 any 托管 bean 中声明...规范说 A producer method must be a (...) method of a managed bean class or session bean class。无论如何,我很高兴你解决了你的问题。
  • @G.Demecki 是的,你是对的。对制片人进行了快速测试,我肯定弄错了。但不幸的是,没有调用切面的生产者,我猜是因为对于 CDI,切面不是 bean。
【解决方案2】:

感谢社区的帮助,我设法为这两个问题找到了解决方案。离开这里。

第 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>

不需要&lt;version&gt;: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."));
}

成功了。

【讨论】:

  • 这是一个非常有趣的解决方案。然而,我想知道目标 CDI bean 的结构变化是否会产生任何不需要的副作用。
  • @NikosParaskevopoulos 不是我到目前为止遇到的(最近我在这个项目上写了很多代码)。虽然,我不在运行时使用 AspectJ,但在编译时使用(为了性能),因此方面被一劳永逸地编织到字节码中(粗略地说,我知道)。 CDI 稍后在执行期间工作,因此我预计两者之间不会发生任何冲突。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-04-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-04-18
相关资源
最近更新 更多