CDI:跨不同模块/ bean归档使用拦截器

时间:2010-10-29 10:43:58

标签: java-ee interceptor cdi ejb-3.1

我的Java EE 6应用程序由war文件和ejb模块组成,包含在ear文件中。我正在使用CDI for DI(即我在两个模块中都有一个beans.xml文件)。我想使用war模块中的ejb模块中定义的日志拦截器。我在ejb的beans.xml中启用了拦截器:

<beans>
    <interceptors>
        <class>com.test.interceptor.LoggingInterceptor</class>
    </interceptors>
</beans>

这仅适用于使用 ejb模块中的拦截器注释的类。战争模块中的类不被截获(尽管它们也被拦截器注释)。我认为解决方案是在战争的拦截器中启用拦截器(如上所述)。但是无法使用以下消息部署应用程序:

SEVERE:加载应用程序时出现异常:WELD-001417启用拦截器类类com.test.interceptor.LoggingInterceptor既没有注释@Interceptor也没有通过可移植扩展注册

My LoggingInterceptor如下所示:

@Log
@Interceptor
public class LoggingInterceptor {
    private static final Logger logger =  Logger.getLogger(LoggingInterceptor.class.getName());

    static {
        logger.setLevel(Level.ALL);
    }

    @AroundInvoke
    public Object logMethod(InvocationContext ctx) throws Exception {
        logger.log(Level.FINE, "ENTRY {0} {1}",
                new Object[]{ ctx.getTarget().getClass().getName(), ctx.getMethod().getName() });
        long startTime = System.nanoTime();
        try {
            return ctx.proceed();
        } finally {
            long diffTime = System.nanoTime() - startTime;
            logger.log(Level.FINE, "RETURN {0} {1}",
                new Object[]{ ctx.getTarget().getClass().getName(), ctx.getMethod().getName() });
            logger.log(Level.FINE, "{0} took {1} ms", new Object[]{ ctx.getMethod(),
                    TimeUnit.MILLISECONDS.convert(diffTime, TimeUnit.NANOSECONDS)});
        }
    }

}

拦截器绑定:

@InterceptorBinding
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface Log {}

如何为两个模块使用拦截器?

6 个答案:

答案 0 :(得分:10)

J2EE 7规范说(reference):

  

您在beans.xml文件中指定的拦截器仅适用于   同一档案中的课程。使用@Priority批注指定   全局拦截器,用于由多个应用程序组成的应用程序   模块

此解决方案具有独立于供应商的优势。

一个例子:

@Logged
@Interceptor
@Priority(Interceptor.Priority.APPLICATION)
public class LoggedInterceptor implements Serializable { ... }

答案 1 :(得分:5)

现在为时已晚,但如果有人还有这个问题。 两个模块都应该由同一个类加载器加载,以便跨越不同的模块使用拦截器,至少在WebSphere 8b2中是这样。 在WebSphere中,可以在管理控制台中切换此设置:应用程序&gt;应用类型&gt; WebSphere企业应用程序&gt; [您的应用名称]&gt; <类加载和更新检测> WAR类加载器策略=应用程序的单个类加载器。
必须仅在beans.xml中启用Interceptor。

答案 2 :(得分:1)

我想知道你的WAR是否缺少对你的ejb-jar的类加载器可见性?我认为理想情况下,299个拦截器将位于自己的jar中,对EJB和Web模块都可见,并在其两个beans.xml中启用。

答案 3 :(得分:0)

我在JBoss AS 6.0 / 6.1(每晚构建)上遇到了同样的问题并通过disabling separate classloaders修复了它(选项1),但要非常小心。没有任何理由没有引入类加载器的分离,所以显然前方的道路上存在新问题......

This是jira报告,请将其投票: - )

答案 4 :(得分:0)

我在JBoss 7上遇到与logging interceptor完全相同的问题,并通过完整拦截器的jar重叠到应用程序中来修复它。

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-war-plugin</artifactId>
            <version>2.4</version>
            <configuration>
                <overlays>
                    <overlay>
                        <groupId>com.github.t1</groupId>
                        <artifactId>logging-interceptor</artifactId>
                        <type>jar</type>
                        <targetPath>WEB-INF/classes</targetPath>
                    </overlay>
                </overlays>
            </configuration>
        </plugin>
    </plugins>
</build>

<dependencies>
    <dependency>
        <groupId>com.github.t1</groupId>
        <artifactId>logging-interceptor</artifactId>
        <version>1.1</version>
        <optional>true</optional>
    </dependency>
</dependencies>

您仍然需要在应用程序的breans.xml中激活拦截器。

不好,但确实有效。在Java EE 7中,它通过将拦截器注释为@Priority而无需激活。

答案 5 :(得分:0)

如果您无法控制外部依赖项,并且仍然想启用没有bean.xml的拦截器,则可以编写CDI扩展名:

package my.package;

import javax.enterprise.event.Observes;
import javax.enterprise.inject.spi.AfterTypeDiscovery;
import javax.enterprise.inject.spi.Extension;

public class MyCdiExtension implements Extension {

    public void observeAfterTypeDiscovery(@Observes AfterTypeDiscovery afterTypeDiscovery) {
        afterTypeDiscovery.getInterceptors().add(SomeExternalInterceptor.class);
    }
}

添加文件resources/META-INF/services/javax.enterprise.inject.spi.Extension 内容:

my.package.MyCdiExtension