@InterceptorBinding / CDI / EJB 3.2 - 注入问题

时间:2015-06-17 14:41:52

标签: java java-ee cdi wildfly-8 java-ee-7

我的开发环境是:WildFly 8.1,CDI,EJB 3.2,JDK 1.7。应用程序被打包为一个耳朵存档(一个ejb +一个战争),因为它可能会在未来拥有其他网络模块。

我正在努力使用EJB无状态bean中使用的自定义@InterceptorBinding类型。

@Inherited
@InterceptorBinding
@Target({ TYPE, METHOD })
@Retention(RUNTIME)
@Documented
public @interface DetectIntegrityConstraintsViolation {
}

@javax.annotation.ManagedBean  // make it a CDI bean. @Interceptor itself should be enough, but WildFly 8.1 seems to have a bug, since it doesn't work without this line.
@Interceptor
@DetectIntegrityConstraintsViolation
public class ReferentialIntegrityConstraintViolationInterceptor {

    @PersistenceContext
    private EntityManager em;

    @Resource
    private SessionContext sessionContext;

    // ....
 }

beans.xml中:

<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
       version="1.1" bean-discovery-mode="annotated">

  <interceptors>
    <class>com.xxx.ejb.ReferentialIntegrityConstraintViolationInterceptor</class>
  </interceptors>
</beans>

当我通过REST服务调用EJB方法时,我得到了Error injecting resource into CDI managed bean

javax.naming.NameNotFoundException: Caused by java.lang.IllegalStateException: JBAS011048: Failed to construct component instance  Caused by: java.lang.IllegalArgumentException: JBAS016081: Error injecting resource into CDI managed bean.
Can't find a resource named java:comp/env/com.xxx.ejb.ReferentialIntegrityConstraintViolationInterceptor/sessionContext defined on private javax.ejb.SessionContext com.xxx.ejb.ReferentialIntegrityConstraintViolationInterceptor.sessionContext   at org.jboss.as.weld.services.bootstrap.WeldResourceInjectionServices.resolveResource(WeldResourceInjectionServices.java:188) [wildfly-weld-8.1.0.Final.jar:8.1.0.Final]

所以走在黑暗中,我已经转向ResourceLookup方法:

@ManagedBean
@Interceptor
@DetectIntegrityConstraintsViolation
public class ReferentialIntegrityConstraintViolationInterceptor {

    @PersistenceContext
    private EntityManager em;

    private SessionContext sessionContext;

    @PostConstruct
    public void init(InvocationContext ctx) {
        try {
            InitialContext ic = new InitialContext();
            this.sessionContext = (SessionContext)ic.lookup("java:comp/EJBContext");
        } catch (NamingException ex) {
            throw new RuntimeException(ex.getMessage());
        }
    }

    // .....
}

然后注入开始工作,但我收到了一个新错误:

Caused by: org.jboss.weld.exceptions.DefinitionException: WELD-000619: An interceptor for lifecycle callbacks Interceptor [class com.xxx.ejb.ReferentialIntegrityConstraintViolationInterceptor intercepts @DetectIntegrityConstraintsViolation] declares and interceptor binding interface com.xxx.ejb.DetectIntegrityConstraintsViolation with METHOD as its @Target.

所以当从DetectIntegrityConstraintsViolation METHOD目标移除时

@Inherited
@InterceptorBinding
@Target({ TYPE /*, METHOD*/ }) // CRUCIAL
@Retention(RUNTIME)
@Documented
public @interface DetectIntegrityConstraintsViolation {
}

然后它开始工作。但为什么呢? 为什么我不能在方法上加注我的注释?有人知道吗?

BTW:当我没有使用@InterceptorBinding时,更奇怪的是,但是很平常:

@Override
// @DetectIntegrityConstraintsViolation
@Interceptors(ReferentialIntegrityConstraintViolationInterceptor.class)
public User updateUser(final User user) {
    // ...
}

拦截器即使在方法级别上也能完美运行。

我发现EJB和Weld使用起来很笨拙......

2 个答案:

答案 0 :(得分:4)

CDI spec

中描述了您的一条错误消息
  

生命周期回调的拦截器可能只声明拦截器   绑定类型,定义为@Target(TYPE)。如果是一个拦截器   生命周期回调声明了一个拦截器绑定类型   定义@Target({TYPE,METHOD}),容器自动检测   问题并将其视为定义错误。

您已使用@PostConstruct init(InvocationContext ctx)创建了生命周期回调。这个回调是为了在你正在拦截的bean的构造上运行,所以将它应用于一个方法是没有意义的。

至于为什么普通的@Interceptor工作正常,documentation中也有描述:

  

可以将around-invoke拦截器定义为仅应用于a   目标类的具体方法。同样,周围超时   可以将拦截器定义为仅应用于特定的超时方法   目标类。但是,如果一个拦截器类定义   生命周期回调拦截器方法被定义为应用于目标   类在方法级别,生命周期回调拦截器方法   没有被援引。

至于此:

  

我发现EJB和Weld使用起来很笨拙......

如果你放慢脚步并尝试学习,你会有更轻松的时间。您似乎正在尝试随机的事情,并对结果感到困惑,如果您不熟悉CDI和EJB,这是可以预期的。

我还担心你正在使用@ManagedBean注释。一个,它实际上是deprecated,两个,它是JSF,你没有说你正在使用它。

答案 1 :(得分:1)

感谢@DavidS,我设法让它发挥作用。感谢他。

他指出,@Interceptor里面的@PostConstruct与我想的意思不同。

下面带有注释的正确代码:

@Inherited
@InterceptorBinding
@Target({ TYPE, METHOD })
@Retention(RUNTIME)
@Documented
public @interface DetectIntegrityConstraintsViolation {
}


import javax.annotation.ManagedBean;
import javax.annotation.Resource;
import javax.ejb.SessionContext;
import javax.persistence.PersistenceContext;

@ManagedBean // make it a CDI bean. @Interceptor itself should be enough, but WildFly 8.1 seems to have a bug, since it doesn't work without this line.
@Interceptor
@DetectIntegrityConstraintsViolation
public class ReferentialIntegrityConstraintViolationInterceptor {

    @PersistenceContext
    private EntityManager em;

    @Resource(name = "java:comp/EJBContext") // injecting Java EE resource into CDI bean
    private SessionContext sessionContext;

    @AroundInvoke
    public Object processInvocation(InvocationContext ctx) throws Exception {
        // ...
    }
 }

所以现在我可以在我的EJB bean中使用:

@DetectIntegrityConstraintsViolation
public User updateUser(final User user) {
    // ...
}

而不是:

@Interceptors(ReferentialIntegrityConstraintViolationInterceptor.class)
public User updateUser(final User user) {
    // ...
}

未来来到这里的人们的解释:

@InterceptorBinding机制来自CDI世界,因此拦截器本身必须成为CDI bean。它带来了一些后果:

  • 必须在beans.xml
  • 中明确指定拦截器
  • 拦截器(如果bean-discovery-mode="annotated")必须成为CDI bean;所以用@javax.interceptor.Interceptor注释它。不幸的是,WildFly 8.1似乎有一些bug,因为它没有@javax.annotation.ManagedBean拒绝工作。
  • 拦截器内的一些Java EE资源注入(如EJB SessionContext)是通过JNDI
  • 完成的