@InterceptorBinding / CDI/ EJB 3.2 - 注入问题
@InterceptorBinding / CDI/ EJB 3.2 - problems with injection
我的开发环境是:WildFly 8.1、CDI、EJB 3.2、JDK1.7。应用程序打包为一个 ear 存档(一个 ejb + 一个 war),因为将来它可能会有其他网络模块。
我正在努力处理在我的 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 {
}
然后开始工作。但是为什么??
为什么我不能在方法上放置注释?有人知道吗?
顺便说一句:更奇怪的是,当我没有使用 @InterceptorBinding
,而是普通的旧版本时:
@Override
// @DetectIntegrityConstraintsViolation
@Interceptors(ReferentialIntegrityConstraintViolationInterceptor.class)
public User updateUser(final User user) {
// ...
}
拦截器即使在方法级别也能完美运行。
我发现 EJB 和 Weld 非常适合使用 awkward...
中描述了您的一条错误消息
An interceptor for lifecycle callbacks may only declare interceptor
binding types that are defined as @Target(TYPE). If an interceptor for
lifecycle callbacks declares an interceptor binding type that is
defined @Target({TYPE, METHOD}), the container automatically detects
the problem and treats it as a definition error.
您已经使用 @PostConstruct init(InvocationContext ctx)
创建了一个生命周期回调。此回调旨在 运行 构建您正在拦截的 bean,因此将其应用于方法没有意义。
至于为什么旧的 @Interceptor
有效,documentation 中也有描述:
An around-invoke interceptor may be defined to apply only to a
specific method of the target class. Likewise, an around-timeout
interceptor may be defined to apply only to a specific timeout method
of the target class. However, if an interceptor class that defines
lifecycle callback interceptor methods is defined to apply to a target
class at the method level, the lifecycle callback interceptor methods
are not invoked.
至于这个:
I find EJB and Weld so awkward to use...
如果你放慢速度并边学边学,你会过得更轻松。您似乎在尝试随机的事情并且对结果感到困惑,如果您不熟悉 CDI 和 EJB,这是可以预料的。
我还担心您正在使用 @ManagedBean
注释。第一,它实际上是 deprecated,第二,它用于 JSF,您没有说您正在使用它。
多亏了@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 似乎有一些错误,因为它拒绝在没有 @javax.annotation.ManagedBean
. 的情况下工作
- 一些Java EE 资源注入(如 EJB SessionContext)在拦截器中是通过 JNDI 完成的
我的开发环境是:WildFly 8.1、CDI、EJB 3.2、JDK1.7。应用程序打包为一个 ear 存档(一个 ejb + 一个 war),因为将来它可能会有其他网络模块。
我正在努力处理在我的 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 {
}
然后开始工作。但是为什么??
为什么我不能在方法上放置注释?有人知道吗?
顺便说一句:更奇怪的是,当我没有使用 @InterceptorBinding
,而是普通的旧版本时:
@Override
// @DetectIntegrityConstraintsViolation
@Interceptors(ReferentialIntegrityConstraintViolationInterceptor.class)
public User updateUser(final User user) {
// ...
}
拦截器即使在方法级别也能完美运行。
我发现 EJB 和 Weld 非常适合使用 awkward...
An interceptor for lifecycle callbacks may only declare interceptor binding types that are defined as @Target(TYPE). If an interceptor for lifecycle callbacks declares an interceptor binding type that is defined @Target({TYPE, METHOD}), the container automatically detects the problem and treats it as a definition error.
您已经使用 @PostConstruct init(InvocationContext ctx)
创建了一个生命周期回调。此回调旨在 运行 构建您正在拦截的 bean,因此将其应用于方法没有意义。
至于为什么旧的 @Interceptor
有效,documentation 中也有描述:
An around-invoke interceptor may be defined to apply only to a specific method of the target class. Likewise, an around-timeout interceptor may be defined to apply only to a specific timeout method of the target class. However, if an interceptor class that defines lifecycle callback interceptor methods is defined to apply to a target class at the method level, the lifecycle callback interceptor methods are not invoked.
至于这个:
I find EJB and Weld so awkward to use...
如果你放慢速度并边学边学,你会过得更轻松。您似乎在尝试随机的事情并且对结果感到困惑,如果您不熟悉 CDI 和 EJB,这是可以预料的。
我还担心您正在使用 @ManagedBean
注释。第一,它实际上是 deprecated,第二,它用于 JSF,您没有说您正在使用它。
多亏了@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 似乎有一些错误,因为它拒绝在没有@javax.annotation.ManagedBean
. 的情况下工作
- 一些Java EE 资源注入(如 EJB SessionContext)在拦截器中是通过 JNDI 完成的