@ApplicationScoped Bean被构造多次

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正确构建,但是随后

  1. SyncService创建了的第二个实例(为什么?)
  2. 没有LdapDirectoryAccess注入(为什么?)
  3. 的第二个实例SyncService用于提供对我的REST API的调用。为什么选择此实例而不是创建的第一个实例?

我已经看了这个问题及其答案:

  • 我正在使用Mojarra 2.2.18
  • 我的应用程序web.xml不包含任何标签提及com.sun.faces.config.ConfigureListener

因此,经过几个小时的调查,我完全失去了主意。你有什么提示吗?

Bal*_*usC 5

SyncService创建了的第二个实例(为什么?)

因为两个完全不了解的框架被指示进行管理(实例化和使用)。

  1. JAX-RS,通过 @Path
  2. JSF,通过 @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 次

最近记录:

6 年,9 月 前