从 JSF @ManagedBean 迁移到 CDI @Named 后,多次调用构造函数并提交输入值始终为 null

After migration from JSF @ManagedBean to CDI @Named, constructor called multiple times and submitted input values always null

编辑:评论区解决了我的问题!问题是我对范围使用了不正确的导入。

我有一个简单的 JSF 应用程序(登录、从数据库中提取数据、允许用户编辑数据)。它运行良好,我想更新代码以使用 CDI(焊接),但我遇到了麻烦。 我正在关注/正在查看:http://docs.jboss.org/weld/reference/latest/en-US/html/example.html 没有焊接的原始东西:

login.xhtml

<h:form id="inputForm">
    <h:panelGrid columns="2" cellpadding="5" cellspacing="1">
        <h:outputText id="nameDesc" value="Name"></h:outputText>
        <h:inputText id="nameInput" value="#{login.loginName}" binding="#{name}"></h:inputText>

        <h:outputText id="passwordDesc" value="Password"></h:outputText>
        <h:inputSecret id="passwordInput" value="#{login.password}" binding="#{password}"></h:inputSecret>

    </h:panelGrid>
    <h:commandButton value="Login" action="#{login.login(name.value, password.value)}"/>
</h:form>

LoginBean.java:

@ManagedBean(name="login")
@SessionScoped
public class LoginBean implements Serializable {
    private static final long serialVersionUID = 1L;
    @ManagedProperty(value="#{db}")
    private DatabaseBean db;
    private String password;
    private String loginName;
    // other stuff and functions

    public String getLoginName () {
       return loginName;
     }

    public void setLoginName (String name) {
       this.loginName = name;
    }

    public String getPassword () {
       return password;
    }

    public void setPassword (final String password) {
       this.password = password;
    }

    public void setDb(DatabaseBean db) {
        this.db = db;
    }

DatabaseBean.java:

@ManagedBean(name="db", eager=true)
@ApplicationScoped
public class DatabaseBean implements Serializable {
 @PostConstruct
    public void init() {
      //... connect to database etc
    }    

}

---------我试图得到它运行焊接(仅从上面进行更改以使其更短):-------- LoginBean.java,从@ManagedBean 更改为@Named,为DatabaseBean 添加了@Inject

@Named("login")
@SessionScoped
public class LoginBean implements Serializable {
     // stuff 
         private @Inject DatabaseBean db;
}

DatabaseBean.java,从@ManagedBean 更改为@Named:

@Named("db")
@ApplicationScoped
public class DatabaseBean implements Serializable {
}

LoginBean 有一个函数:

public String login(String name, String password) {
    System.out.println("login called"+name);
    // other stuff

}

在我的第二个实现中(我尝试使用 Weld 的那个),打印被调用一次:"login called",用户名是空的(我用 name.IsEmpty() 检查了这个) .

我也试过通过构造函数注入它:

loginBean.java

@Inject
public LoginBean(DatabaseBean db) {
    System.out.println("constructor");
    this.db = db;
}

当我这样做时,我得到了很多 "constructor" 打印,所以它被调用了几次,但我不明白为什么 - 我想这就是问题所在,只有一个 LoginBean 实例被调用输入(用户名和密码)然后由于某种原因创建了许多新的。这是为什么?

我用Eclipse和Tomcat8来运行吧。 感谢阅读!

managed bean constructor called multiple times

CDI 可能比预期更频繁地调用构造函数,而 generating/creating 增强 subclasses/proxies。另见 Field.get(obj) returns all nulls on injected CDI managed beans, while manually invoking getters return correct values。只是不要记录构造函数调用,它只会让你自己感到困惑。 @PostConstruct 是唯一有趣的挂钩方法。


the print is called once: "login called", and the username is empty (I checked this with name.IsEmpty()).

关于调用操作方法时表单输入值 null 的具体问题,因此 @SessionScoped CDI 托管 bean 似乎在每次访问时都被重新创建,这与以下行为相匹配一个 @Dependent 作用域 bean。当找不到有效的 CDI 托管 bean 范围时,这是默认范围。另见 What is the default Managed Bean Scope in a JSF 2 application?

这反过来表明您从错误的包中导入了 @SessionScoped。确保它来自 javax.enterprise.context 包而不是来自例如javax.faces.bean。 JSF 托管 bean 范围无法识别为有效的 CDI 托管 bean 范围。

import javax.inject.Named;
import javax.enterprise.context.SessionScoped;

@Named("login")
@SessionScoped
public class LoginBean implements Serializable {
    // ...
}