我们有一个web应用程序,目前正在使用Java EE 7、JSF 2.2和Glassfish 4.0进行开发。有两个特定的托管bean具有循环依赖关系。
常规控制器
@Named
@SessionScoped
public class UsuarioController implements Serializable {
/** snipet **/
@Inject
private EnderecoController enderecoController;
/** snipet **/
}
EnderecoController
@Named
@ViewScoped
public class EnderecoController {
/** snipet **/
@Inject
private UsuarioController esuarioController;
/** snipet **/
}
当web应用程序被打包并部署到正常的glassfish 4.0安装中时,它运行良好。
然而,在开发过程中,我们使用maven嵌入式glassfish在IDE内部进行本地测试。应用程序部署失败,出现以下异常。
SEVERE: Exception while loading the app : CDI deployment failure:WELD-001408 Unsatisfied dependencies for type [EnderecoController] with qualifiers [@Default] at injection point [[BackedAnnotatedField] @Inject private net.jhm.exemplo.view.UsuarioController.enderecoController]
org.jboss.weld.exceptions.DeploymentException: WELD-001408 Unsatisfied dependencies for type [EnderecoController] with qualifiers [@Default] at injection point [[BackedAnnotatedField] @Inject private net.jhm.exemplo.view.UsuarioController.enderecoController]
at org.jboss.weld.bootstrap.Validator.validateInjectionPointForDeploymentProblems(Validator.java:403)
at org.jboss.weld.bootstrap.Validator.validateInjectionPoint(Validator.java:325)
at org.jboss.weld.bootstrap.Validator.validateGeneralBean(Validator.java:177)
at org.jboss.weld.bootstrap.Validator.validateRIBean(Validator.java:208)
at org.jboss.weld.bootstrap.Validator.validateBean(Validator.java:519)
at org.jboss.weld.bootstrap.Validator.validateBeans(Validator.java:505)
at org.jboss.weld.bootstrap.Validator.validateDeployment(Validator.java:480)
at org.jboss.weld.bootstrap.WeldBootstrap.validateBeans(WeldBootstrap.java:536)
at org.glassfish.weld.WeldDeployer.event(WeldDeployer.java:216)
at org.glassfish.kernel.event.EventsImpl.send(EventsImpl.java:131)
at org.glassfish.internal.data.ApplicationInfo.load(ApplicationInfo.java:328)
at com.sun.enterprise.v3.server.ApplicationLifecycle.deploy(ApplicationLifecycle.java:493)
at com.sun.enterprise.v3.server.ApplicationLifecycle.deploy(ApplicationLifecycle.java:219)
at org.glassfish.deployment.admin.DeployCommand.execute(DeployCommand.java:491)
at com.sun.enterprise.v3.admin.CommandRunnerImpl$2$1.run(CommandRunnerImpl.java:527)
at com.sun.enterprise.v3.admin.CommandRunnerImpl$2$1.run(CommandRunnerImpl.java:523)
at java.security.AccessController.doPrivileged(Native Method)
at javax.security.auth.Subject.doAs(Subject.java:356)
at com.sun.enterprise.v3.admin.CommandRunnerImpl$2.execute(CommandRunnerImpl.java:522)
at com.sun.enterprise.v3.admin.CommandRunnerImpl.doCommand(CommandRunnerImpl.java:546)
at com.sun.enterprise.v3.admin.CommandRunnerImpl.doCommand(CommandRunnerImpl.java:1423)
at com.sun.enterprise.v3.admin.CommandRunnerImpl.access$1500(CommandRunnerImpl.java:108)
at com.sun.enterprise.v3.admin.CommandRunnerImpl$ExecutionContext.execute(CommandRunnerImpl.java:1762)
at com.sun.enterprise.v3.admin.CommandRunnerImpl$ExecutionContext.execute(CommandRunnerImpl.java:1674)
at com.sun.enterprise.admin.cli.embeddable.DeployerImpl.deploy(DeployerImpl.java:133)
at com.sun.enterprise.admin.cli.embeddable.DeployerImpl.deploy(DeployerImpl.java:109)
at org.glassfish.maven.PluginUtil.doDeploy(PluginUtil.java:108)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.glassfish.maven.AbstractDeployMojo.doDeploy(AbstractDeployMojo.java:259)
at org.glassfish.maven.DeployMojo.execute(DeployMojo.java:69)
at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo(DefaultBuildPluginManager.java:101)
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:209)
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:153)
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:145)
at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:84)
at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:59)
at org.apache.maven.lifecycle.internal.LifecycleStarter.singleThreadedBuild(LifecycleStarter.java:183)
at org.apache.maven.lifecycle.internal.LifecycleStarter.execute(LifecycleStarter.java:161)
at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:320)
at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:156)
at org.apache.maven.cli.MavenCli.execute(MavenCli.java:537)
at org.apache.maven.cli.MavenCli.doMain(MavenCli.java:196)
at org.apache.maven.cli.MavenCli.main(MavenCli.java:141)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced(Launcher.java:290)
at org.codehaus.plexus.classworlds.launcher.Launcher.launch(Launcher.java:230)
at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode(Launcher.java:409)
at org.codehaus.plexus.classworlds.launcher.Launcher.main(Launcher.java:352)
Aug 07, 2013 12:22:31 PM PluginUtil doDeploy
INFO: Deployed null
有人能为我们的开发环境帮助解决这个问题吗?有些人更喜欢嵌入式glassfish插件,而不是用于本地开发的完整服务器。
这似乎是一个依赖性/类路径问题,特别是与maven嵌入的glassfish有关,但我们不知道从哪里开始。在谷歌周围很难找到CDI WELD-NNNNN异常的解释。
经过多次搜索和阅读,我们终于解决了这个问题。事实证明,这个Web应用程序最初是为Java EE 6开发的,并决定在开发过程中使用Java EE 7。好Java EE 7中有些东西是不同的。它以不同的方式处理托管bean作用域。首先,在JavaEE7文档中甚至没有提到@ViewScoped
注释(有一个新的@FlowScoped
,但我们仍在阅读)。我们将日志级别提升到了FINEST,并在无尽的细节中搜寻,以了解发生了什么。
为了让它像今天这样与代码一起工作,我们必须了解CDI实现的一个关键区别。在JavaEE6之前,CDI将扫描所有包,容器将考虑所有bean。这种行为显然在JavaEE7中发生了变化,在某种程度上,只有用特定作用域注释的类才被考虑成为托管bean。换句话说,@Named
注释需要伴随范围注释之一(@RequestScoped
、@SessionScoped
、@DependentScoped
、@FlowScoped
等)。由于@ViewScoped
不再是官方作用域列表的一部分,所以当CDI启动时,类EnderecoController
不会成为托管bean。尝试将实例注入UsuarioController
会导致一个通用的WELD依赖异常,因为从未创建该bean的实例。
为了在后可移植性方面发挥作用,我们必须更改WEB-IBNF/beans.xml
文件,将属性bean-discovery-mode="annotated"
更改为bean-discovery-mode="all"
。
<?xml version="1.0" encoding="UTF-8"?>
<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"
bean-discovery-mode="all">
</beans>
使用all
发现模式会导致CDI在扫描托管bean时包括所有要考虑的类,包括那些没有CDI作用域注释的类。我们现在正在更好地理解新的范围管理,以使代码适应JavaEE7标准。
我仍然觉得奇怪的是,原始代码可以在一个完整的glassfish安装中工作,但不能在maven嵌入式glassefish插件中工作。
我个人对Java EE/CDI的咆哮
此外,我想明确评论一下WELD-001408不满足的依赖性堆栈种族给出的荒谬而宽泛的描述。该消息只是表示CDI无法提供依赖项注入。没有详细说明是什么类型的错误导致最初没有创建注入的bean。甚至没有"对不起,找不到要实例化的bean"。
不满足的依赖关系可能由于各种原因而发生。在尝试实例化依赖项时发生的任何异常都会隐藏在日志文件之外。您可能要花一个小时,直到您意识到您的bean的构造函数正在抛出一个NullPointerException
。这种异常包装的无稽之谈就是为什么在谷歌上搜索这条错误消息会导致大量的人由于不同的原因而出现相同的错误的原因。
我希望他们能改进错误处理,引发异常消息,这样我们就能更好地理解为什么不能满足某些特定的依赖关系。