Gio*_*gio 1 jsf dependency-injection managed-bean
我有两个托管Java Bean:
import javax.annotation.PostConstruct;
import javax.faces.bean.ApplicationScoped;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ManagedProperty;
import javax.ws.rs.Path;
@Path("/sync")
@ManagedBean(name="syncService", eager=true)
@ApplicationScoped
public class SyncService {
@ManagedProperty(value="#{ldapDirectoryAccess}")
private DirectoryAccess directoryAccess;
public void setDirectoryAccess(DirectoryAccess directoryAccess) {
System.out.println("SyncService.setDirectoryAccess()");
this.directoryAccess = directoryAccess;
}
public SyncService() {
System.out.println("SyncService() - constructed: " + this);
}
@PostConstruct
public void init() {
System.out.println("DirectoryAccess injected: " + directoryAccess + " in: " + this);
}
...
}
@ManagedBean(name="ldapDirectoryAccess", eager=true)
@ApplicationScoped
public class LdapDirectoryAccess implements DirectoryAccess {
public LdapDirectoryAccess() {
System.out.println("LdapDirectoryAccess constructed: " + this);
}
...
}
Run Code Online (Sandbox Code Playgroud)
当我在Tomcat中部署应用程序时,会在以下输出catalina.out
:
SyncService() - constructed: ...SyncService@705ebb4d
...
LdapDirectoryAccess constructed: ...LdapDirectoryAccess@3c1fd5aa
SyncService.setDirectoryAccess()
DirectoryAccess injected: ...LdapDirectoryAccess@3c1fd5aa in:
...SyncService@705ebb4d
LdapDirectoryAccess constructed: ...LdapDirectoryAccess@59d6a4d1
Run Code Online (Sandbox Code Playgroud)
因此,首先按预期构造每个bean的实例,然后将第二个bean注入第一个bean。但是随后,创建了第二个bean类的另一个实例。这怎么可能?在本教程中,我发现了以下内容:
@ApplicationScoped
只要Web应用程序存在,Bean就存在。它是在涉及该应用程序中该bean的第一个HTTP请求时创建的(或者,当Web应用程序启动并且@ManagedBean中设置了eager = true属性时),并在Web应用程序关闭时被销毁。
因此,我希望在启动应用程序时为每个bean创建一个实例,并且在关闭应用程序时都销毁这两个实例。但是LdapDirectoryAccess
被构造了两次。
此外,当我打开该页面时,会SyncService
看到:
SyncService() - constructed: ... SyncService@1cb4a09c
Run Code Online (Sandbox Code Playgroud)
所以SyncService
也建立了第二个实例,我不明白为什么。同样,directoryAccess
这次没有任何属性被注入,并且服务抛出空指针异常。
这意味着的第一个实例SyncService
正确构建,但是随后
SyncService
创建了的第二个实例(为什么?)LdapDirectoryAccess
注入(为什么?)SyncService
用于提供对我的REST API的调用。为什么选择此实例而不是创建的第一个实例?我已经看了这个问题及其答案:
web.xml
不包含任何标签提及com.sun.faces.config.ConfigureListener
因此,经过几个小时的调查,我完全失去了主意。你有什么提示吗?
SyncService
创建了的第二个实例(为什么?)
因为两个完全不了解的框架被指示进行管理(实例化和使用)。
@Path
@ManagedBean
因此,SyncService
实际上,您有一个实例由JAX-RS管理,而您有另一个实例SyncService
由JSF管理,只有在这种情况下,才能@ManagedProperty
识别JSF特定的实例。JAX-RS不了解@ManagedProperty
,因此对此无能为力。
基本上,您是在同一类中将JAX-RS资源和JSF托管Bean紧密耦合在一起。紧密耦合是不好的编程习惯。您需要拆分SyncService
为一个独立的JAX-RS资源和一个独立的JSF托管bean。而且,您需要将转换LdapDirectoryAccess
为使用另一个JAX-RS和JSF都可以识别的bean管理框架,以便可以在两者中注入一个实例。在现代Java EE 8中,这将是由CDI的bean管理@javax.enterprise.context.ApplicationScoped
。在旧版Java EE 6/7中,您可能会@javax.ejb.Singleton
为此滥用EJB 。
考虑到您仍在使用不推荐使用的产品@ManagedBean
而不是其替代产品@Named
,我将假定您仍在使用旧版Java EE,因此仅展示EJB方法。
import javax.annotation.PostConstruct;
import javax.ejb.Singleton;
@Singleton
public class LdapDirectoryAccessService implements DirectoryAccess {
@PostConstruct
public void init() {
System.out.println("LdapDirectoryAccess constructed: " + this);
}
}
Run Code Online (Sandbox Code Playgroud)
import javax.annotation.PostConstruct;
import javax.ejb.EJB;
import javax.ws.rs.Path;
@Path("/sync")
public class SyncResource {
@EJB
private DirectoryAccess directoryAccess;
@PostConstruct
public void init() {
System.out.println("DirectoryAccess injected: " + directoryAccess + " in: " + this);
}
}
Run Code Online (Sandbox Code Playgroud)
import javax.annotation.PostConstruct;
import javax.ejb.EJB;
import javax.faces.bean.RequestScoped;
import javax.faces.bean.ManagedBean;
@ManagedBean
@RequestScoped
public class SyncBacking {
@EJB
private DirectoryAccess directoryAccess;
@PostConstruct
public void init() {
System.out.println("DirectoryAccess injected: " + directoryAccess + " in: " + this);
}
}
Run Code Online (Sandbox Code Playgroud)
请注意,要在JAX-RS资源中注入EJB可能需要在Java EE 6/7中进行其他配置,为此请参见下面列表的第一个链接。并且,如果要LdapDirectoryAccessService
在服务器启动期间急于初始化,请添加@javax.ejb.Startup
注释。
归档时间: |
|
查看次数: |
576 次 |
最近记录: |