为什么EJB bean方法未被检测到,如果它是封装私有的?
这一个令我困惑。我创建了一个基于在Wildfly 10.1上运行的JavaEE7的最小JAX-RS应用程序。为什么EJB bean方法未被检测到,如果它是封装私有的?
@ApplicationPath("")
public class JAXRSConfiguration extends Application {
@Override
public Set<Class<?>> getClasses() {
return new HashSet<Class<?>>(Arrays.asList(Resource.class));
}
}
资源注入一个单独的虚拟无状态的bean:
@Path("")
public class Resource {
@Inject
Manager manager;
@GET
@Path("/go")
public void go() {
manager.call();
}
}
这是豆:
@Stateless
public class Manager {
@PostConstruct
private void init() {
System.out.println("POST CONSTRUCT ");
}
void call() {
System.out.println("called ");
}
}
使用浏览器来执行GET导致以下错误:
org.jboss.resteasy.spi.UnhandledException: org.jboss.weld.exceptions.WeldException: Class org.jboss.weld.util.reflection.Reflections can not access a member of class com.a.b.Manager with modifiers ""
Can pos t完整的堆栈跟踪,但所有引起的消息都是相同的。
我搜索了这个错误,当注入的bean是而不是 public,但是我的是。我决定尝试删除公众,看看它会抱怨什么,以及......它的工作原理。注入bean,注入任何注入,调用post构造方法,打印所有打印。
这与Does ejb stateless class has to be public?完全相反。这里发生了什么?
更新
Oliv37促使我做一些测试,下面是调查结果:
- 如果
call
为package
,则只有当Manager
是package
工作。 - 如果
call
是public
,它无论如何工作。 - 如果
call
是final
,则只有在Manager
为package
时才会调用@PostConstruct
方法。
现在问题变成了:为什么该方法需要公开以便CDI检测工作以及为什么使其最终导致post构造方法在类为公共时不会被调用?
UPDATE2
完整的堆栈跟踪:
org.jboss.resteasy.spi.UnhandledException: org.jboss.weld.exceptions.WeldException: Class org.jboss.weld.util.reflection.Reflections can not access a member of class com.a.b.Manager with modifiers ""
at org.jboss.resteasy.core.ExceptionHandler.handleApplicationException(ExceptionHandler.java:77)
at org.jboss.resteasy.core.ExceptionHandler.handleException(ExceptionHandler.java:220)
at org.jboss.resteasy.core.SynchronousDispatcher.writeException(SynchronousDispatcher.java:175)
at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:418)
at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:209)
at org.jboss.resteasy.plugins.server.servlet.ServletContainerDispatcher.service(ServletContainerDispatcher.java:221)
at org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(HttpServletDispatcher.java:56)
at org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(HttpServletDispatcher.java:51)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:790)
at io.undertow.servlet.handlers.ServletHandler.handleRequest(ServletHandler.java:85)
at io.undertow.servlet.handlers.security.ServletSecurityRoleHandler.handleRequest(ServletSecurityRoleHandler.java:62)
at io.undertow.servlet.handlers.ServletDispatchingHandler.handleRequest(ServletDispatchingHandler.java:36)
at org.wildfly.extension.undertow.security.SecurityContextAssociationHandler.handleRequest(SecurityContextAssociationHandler.java:78)
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at io.undertow.servlet.handlers.security.SSLInformationAssociationHandler.handleRequest(SSLInformationAssociationHandler.java:131)
at io.undertow.servlet.handlers.security.ServletAuthenticationCallHandler.handleRequest(ServletAuthenticationCallHandler.java:57)
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at io.undertow.security.handlers.AbstractConfidentialityHandler.handleRequest(AbstractConfidentialityHandler.java:46)
at io.undertow.servlet.handlers.security.ServletConfidentialityConstraintHandler.handleRequest(ServletConfidentialityConstraintHandler.java:64)
at io.undertow.security.handlers.AuthenticationMechanismsHandler.handleRequest(AuthenticationMechanismsHandler.java:60)
at io.undertow.servlet.handlers.security.CachedAuthenticatedSessionHandler.handleRequest(CachedAuthenticatedSessionHandler.java:77)
at io.undertow.security.handlers.NotificationReceiverHandler.handleRequest(NotificationReceiverHandler.java:50)
at io.undertow.security.handlers.AbstractSecurityContextAssociationHandler.handleRequest(AbstractSecurityContextAssociationHandler.java:43)
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at org.wildfly.extension.undertow.security.jacc.JACCContextIdHandler.handleRequest(JACCContextIdHandler.java:61)
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at io.undertow.servlet.handlers.ServletInitialHandler.handleFirstRequest(ServletInitialHandler.java:292)
at io.undertow.servlet.handlers.ServletInitialHandler.access$100(ServletInitialHandler.java:81)
at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:138)
at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:135)
at io.undertow.servlet.core.ServletRequestContextThreadSetupAction$1.call(ServletRequestContextThreadSetupAction.java:48)
at io.undertow.servlet.core.ContextClassLoaderSetupAction$1.call(ContextClassLoaderSetupAction.java:43)
at io.undertow.servlet.api.LegacyThreadSetupActionWrapper$1.call(LegacyThreadSetupActionWrapper.java:44)
at io.undertow.servlet.api.LegacyThreadSetupActionWrapper$1.call(LegacyThreadSetupActionWrapper.java:44)
at io.undertow.servlet.api.LegacyThreadSetupActionWrapper$1.call(LegacyThreadSetupActionWrapper.java:44)
at io.undertow.servlet.api.LegacyThreadSetupActionWrapper$1.call(LegacyThreadSetupActionWrapper.java:44)
at io.undertow.servlet.api.LegacyThreadSetupActionWrapper$1.call(LegacyThreadSetupActionWrapper.java:44)
at io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:272)
at io.undertow.servlet.handlers.ServletInitialHandler.access$000(ServletInitialHandler.java:81)
at io.undertow.servlet.handlers.ServletInitialHandler$1.handleRequest(ServletInitialHandler.java:104)
at io.undertow.server.Connectors.executeRootHandler(Connectors.java:202)
at io.undertow.server.HttpServerExchange$1.run(HttpServerExchange.java:805)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
Caused by: org.jboss.weld.exceptions.WeldException: Class org.jboss.weld.util.reflection.Reflections can not access a member of class com.a.b.Manager with modifiers ""
at org.jboss.weld.util.reflection.Reflections.invokeAndUnwrap(Reflections.java:437)
at org.jboss.weld.bean.proxy.EnterpriseBeanProxyMethodHandler.invoke(EnterpriseBeanProxyMethodHandler.java:128)
at org.jboss.weld.bean.proxy.EnterpriseTargetBeanInstance.invoke(EnterpriseTargetBeanInstance.java:56)
at org.jboss.weld.bean.proxy.InjectionPointPropagatingEnterpriseTargetBeanInstance.invoke(InjectionPointPropagatingEnterpriseTargetBeanInstance.java:67)
at org.jboss.weld.bean.proxy.ProxyMethodHandler.invoke(ProxyMethodHandler.java:100)
at com.a.b.Manager$Proxy$_$$_Weld$EnterpriseProxy$.call(Unknown Source)
at com.airhacks.boundary.Resource.go(Resource.java:16)
at com.airhacks.boundary.Resource$Proxy$_$$_WeldClientProxy.go(Unknown Source)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:139)
at org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTarget(ResourceMethodInvoker.java:295)
at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:249)
at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:236)
at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:402)
... 42 more
Caused by: java.lang.IllegalAccessException: Class org.jboss.weld.util.reflection.Reflections can not access a member of class com.a.b.Manager with modifiers ""
at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:102)
at java.lang.reflect.AccessibleObject.slowCheckMemberAccess(AccessibleObject.java:296)
at java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:288)
at java.lang.reflect.Method.invoke(Method.java:491)
at org.jboss.weld.util.reflection.Reflections.invokeAndUnwrap(Reflections.java:433)
... 58 more
简短的回答是 - 让你call
方法public
较长的一个是这样的 - 这里的问题是你的call
方法受封装保护。因此到EJB规范,章会话Bean的无接口视图(从3.1 EJB规范引用):
Only public methods of the bean class (and any super-classes) may be invoked through the no-interface view. Attempted invocations of methods with any other access modifiers via the no-interface view reference must result in a
javax.ejb.EJBException
.
通过采用非公修改你违反了规范和事实,即在不引发EJBException
可要么在WFLY中忽略EJB impl,要么忽略一个特性。不管怎样,你都处于灰色未指定的区域。
现在至于焊接 - 它试图使用反射来访问该方法并调用它,deliberately not making it accessible prior to invocation。这是显而易见的地方,因为您无法绕过它而访问其他方法(使用Method.setAccessible()
)。
至于你的问题final
方法和@PostConstruct
没有被调用 - 你打破了另一个EJB规则。这一个是4.9.8 Session Bean’s No-Interface View
和破折号一个有说:
Only private methods of the bean class and any superclasses except
java.lang.Object
may be declaredfinal
.
我不能说为什么没有异常,但是这是你头痛的原因。至于为什么这个规则存在 - 正如我在评论中所述,Weld需要创建代理,并且如果该方法不能被覆盖,则不可能在@PostConstruct
中编织并拦截它。
希望这回答您的查询。
也创建[WFLY问题](https://issues.jboss.org/browse/WFLY-9425)来检查这种行为 - 事实证明它会抛出正确的异常(调用非公共EJB方法 - 请参阅我在该问题中的评论),但焊接速度更快,并且在IllegalAccess反射问题上失败 - 这可能(应该)在焊接方面得到改进。 – Siliarus
最后一个修饰符会不会调用post构造呢? – Mark
Uf,与我们在这里整理的内容相比,这是完全不同的问题。我想这个问题会出现在Weld创建bean的代理对象 - 但是根据CDI规范,这不适用于最终方法。但是,真的,这个问题应该得到一个单独的“线程”,您可以在其中指定其失败的具体部署方案。在这里,它与所有选项都纠结在一起。 – Siliarus
关于该类不公开的一个评论,它的工作原理:正如你的链接问题中的一个评论所说:它在你的环境中与你选择的容器一起工作,但不能保证在每个EE容器中工作,因为它是没有被规范定义。 – dunni
@dunni好的,但如果这是规范,为什么它不与公众一起工作?我明白,如果我做的东西不在规范中,它可能工作,可能不会,但如果我在规范中做事情,他们必须工作... – Mark
哼,你是否尝试为方法'调用'添加'public'修饰符在'经理'类里面? –