如果 EJB bean 方法是包私有的,为什么检测不到它?
Why is the EJB bean method not detected if it is package private?
这个让我很困惑。我在 Wildfly 10.1 上基于 JavaEE7 运行 创建了一个最小的 JAX-RS 应用程序。
@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 ""
可以 post 完整堆栈跟踪,但所有 Caused By 消息都是相同的。
我搜索了这个错误,当注入的 bean 不是 public 时,它就会发生,但我的是。我决定尝试删除 public 以查看它会抱怨什么并且......它有效。注入 bean,注入它可能进行的任何注入,调用 post 构造方法并打印所有打印件。
这与Does ejb stateless class has to be public?形成鲜明对比。这是怎么回事?
更新
Oliv37 提示我做了一些测试,结果如下:
- 如果
call
为package
,则仅当Manager
为package
时有效。
- 如果
call
是 public
,则无论如何都有效。
- 如果
call
是final
,只有当Manager
是package
时才会调用@PostConstruct
方法。
现在问题变成了:为什么 CDI 检测的方法需要 public 以及为什么将其设置为 final 会导致 post 构造方法在 class 是 public?
更新2
完整堆栈跟踪:
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.access0(ServletInitialHandler.java:81)
at io.undertow.servlet.handlers.ServletInitialHandler.call(ServletInitialHandler.java:138)
at io.undertow.servlet.handlers.ServletInitialHandler.call(ServletInitialHandler.java:135)
at io.undertow.servlet.core.ServletRequestContextThreadSetupAction.call(ServletRequestContextThreadSetupAction.java:48)
at io.undertow.servlet.core.ContextClassLoaderSetupAction.call(ContextClassLoaderSetupAction.java:43)
at io.undertow.servlet.api.LegacyThreadSetupActionWrapper.call(LegacyThreadSetupActionWrapper.java:44)
at io.undertow.servlet.api.LegacyThreadSetupActionWrapper.call(LegacyThreadSetupActionWrapper.java:44)
at io.undertow.servlet.api.LegacyThreadSetupActionWrapper.call(LegacyThreadSetupActionWrapper.java:44)
at io.undertow.servlet.api.LegacyThreadSetupActionWrapper.call(LegacyThreadSetupActionWrapper.java:44)
at io.undertow.servlet.api.LegacyThreadSetupActionWrapper.call(LegacyThreadSetupActionWrapper.java:44)
at io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:272)
at io.undertow.servlet.handlers.ServletInitialHandler.access[=14=]0(ServletInitialHandler.java:81)
at io.undertow.servlet.handlers.ServletInitialHandler.handleRequest(ServletInitialHandler.java:104)
at io.undertow.server.Connectors.executeRootHandler(Connectors.java:202)
at io.undertow.server.HttpServerExchange.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 规范,章节 Session 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
.
通过使用非 public 修饰符,您违反了规范,并且 in 不会抛出 EJBException
的事实可能在 WFLY 中的 EJB impl 或功能中被忽略。无论哪种方式,您都处于灰色未指定区域。
现在对于 Weld - 它正在尝试使用反射来访问该方法并调用它,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 declared final
.
我不能说为什么没有例外,但这是让你头疼的原因。至于为什么存在这条规则——正如我在评论中所说,Weld 需要创建代理,如果无法覆盖该方法,则不可能编织 @PostConstruct
调用并拦截它。
希望这能解答您的疑问。
这个让我很困惑。我在 Wildfly 10.1 上基于 JavaEE7 运行 创建了一个最小的 JAX-RS 应用程序。
@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 ""
可以 post 完整堆栈跟踪,但所有 Caused By 消息都是相同的。
我搜索了这个错误,当注入的 bean 不是 public 时,它就会发生,但我的是。我决定尝试删除 public 以查看它会抱怨什么并且......它有效。注入 bean,注入它可能进行的任何注入,调用 post 构造方法并打印所有打印件。
这与Does ejb stateless class has to be public?形成鲜明对比。这是怎么回事?
更新
Oliv37 提示我做了一些测试,结果如下:
- 如果
call
为package
,则仅当Manager
为package
时有效。 - 如果
call
是public
,则无论如何都有效。 - 如果
call
是final
,只有当Manager
是package
时才会调用@PostConstruct
方法。
现在问题变成了:为什么 CDI 检测的方法需要 public 以及为什么将其设置为 final 会导致 post 构造方法在 class 是 public?
更新2
完整堆栈跟踪:
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.access0(ServletInitialHandler.java:81)
at io.undertow.servlet.handlers.ServletInitialHandler.call(ServletInitialHandler.java:138)
at io.undertow.servlet.handlers.ServletInitialHandler.call(ServletInitialHandler.java:135)
at io.undertow.servlet.core.ServletRequestContextThreadSetupAction.call(ServletRequestContextThreadSetupAction.java:48)
at io.undertow.servlet.core.ContextClassLoaderSetupAction.call(ContextClassLoaderSetupAction.java:43)
at io.undertow.servlet.api.LegacyThreadSetupActionWrapper.call(LegacyThreadSetupActionWrapper.java:44)
at io.undertow.servlet.api.LegacyThreadSetupActionWrapper.call(LegacyThreadSetupActionWrapper.java:44)
at io.undertow.servlet.api.LegacyThreadSetupActionWrapper.call(LegacyThreadSetupActionWrapper.java:44)
at io.undertow.servlet.api.LegacyThreadSetupActionWrapper.call(LegacyThreadSetupActionWrapper.java:44)
at io.undertow.servlet.api.LegacyThreadSetupActionWrapper.call(LegacyThreadSetupActionWrapper.java:44)
at io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:272)
at io.undertow.servlet.handlers.ServletInitialHandler.access[=14=]0(ServletInitialHandler.java:81)
at io.undertow.servlet.handlers.ServletInitialHandler.handleRequest(ServletInitialHandler.java:104)
at io.undertow.server.Connectors.executeRootHandler(Connectors.java:202)
at io.undertow.server.HttpServerExchange.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 规范,章节 Session 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
.
通过使用非 public 修饰符,您违反了规范,并且 in 不会抛出 EJBException
的事实可能在 WFLY 中的 EJB impl 或功能中被忽略。无论哪种方式,您都处于灰色未指定区域。
现在对于 Weld - 它正在尝试使用反射来访问该方法并调用它,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
调用并拦截它。
希望这能解答您的疑问。