我正在JBoss中阅读此示例,其中使用@RequestScoped
备份JSF page
的bean来传递用户凭据信息,然后将其保存在@sessionScoped bean
中。
以下是JBoss docs.
@Named @RequestScoped
public class Credentials {
private String username;
private String password;
@NotNull @Length(min=3, max=25)
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
@NotNull @Length(min=6, max=20)
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
}
JSF表格:
<h:form>
<h:panelGrid columns="2" rendered="#{!login.loggedIn}">
<f:validateBean>
<h:outputLabel for="username">Username:</h:outputLabel>
<h:inputText id="username" value="#{credentials.username}"/>
<h:outputLabel for="password">Password:</h:outputLabel>
<h:inputSecret id="password" value="#{credentials.password}"/>
</f:validateBean>
</h:panelGrid>
<h:commandButton value="Login" action="#{login.login}" rendered="#{!login.loggedIn}"/>
<h:commandButton value="Logout" action="#{login.logout}" rendered="#{login.loggedIn}"/>
</h:form>
用户实体:
@Entity
public class User {
private @NotNull @Length(min=3, max=25) @Id String username;
private @NotNull @Length(min=6, max=20) String password;
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String setPassword(String password) { this.password = password; }
}
SessionScoped bean
@SessionScoped @Named
public class Login implements Serializable {
@Inject Credentials credentials;
@Inject @UserDatabase EntityManager userDatabase;
private User user;
public void login() {
List<User> results = userDatabase.createQuery(
"select u from User u where u.username = :username and u.password = :password")
.setParameter("username", credentials.getUsername())
.setParameter("password", credentials.getPassword())
.getResultList();
if (!results.isEmpty()) {
user = results.get(0);
}
else {
// perhaps add code here to report a failed login
}
}
public void logout() {
user = null;
}
public boolean isLoggedIn() {
return user != null;
}
@Produces @LoggedIn User getCurrentUser() {
return user;
}
}
我的问题是
1)@RequestScoped
bean被注入@SessionScoped
bean。在RequestScoped
的一个实例上设置的凭证信息与注入@SessionScoped
bean的相同的保证是什么。为什么不注入池中不同的@RequestScoped
甚至是新实例?
2)为什么bean被赋予@SessionScoped
而不是@Stateful
。我想@Stateful
可以在这里工作。
3)如何管理@sessionScoped
bean的生命周期?那是什么时候被摧毁了?如果我导航到另一个JSF
页面,如果我提取currentUser.userName
等信息,我将检索我在用于登录的第一个JSF
页面上设置的相同信息。(上面的第1步)
4)如果我没有指定@RequestScoped
,那么Credentials bean将获得作为defualt范围的@Dependent
范围。在docs中提到,设置@Dependent
的任何实例变量会立即丢失。但我不明白为什么?事实上,这提示我@Dependent
范围的用途是什么?
由于
修改 感谢kolossus提供详细而优秀的答案。为了更好地理解,我需要对你的一些要点做一些澄清
@RequestScoped
bean支持的JSF,则每个客户端都可以从池中处理一个@RequestScoped
bean实例。实际上,两个客户端实际上并不在直接实例上工作,而是间接引用作为代理的单个实例。客户端使用此代理执行所有方法调用或事务。所以代理持有这个间接引用多长时间?也就是说,在上面的示例中,@RequestScoped
bean(Credentials
)的实例变量在JSF中设置。但事实是,实例变量的这种设置通过代理间接发生在@RequestScoped bean的一个实例上。但是当这个实例被注入SessionScoped
bean时,它是被注入的代理吗?由于SessionScoped
的生命周期适用于在客户端和应用程序之间建立的会话,因此代理也会在此生命周期内生效。这是否意味着此single instance of @RequestScoped bean
绑定到SessionScoped
并且@RequestScoped
bean实例或其代理的生命周期由SessionScoped
bean的生命周期确定?答案 0 :(得分:8)
@RequestScoped
bean被注入@SessionScoped
bean。在RequestScoped的一个实例上设置的凭据信息与注入@SessionScopedbean
的凭证信息相同的保证是什么。为什么不注入池中不同的@RequestScoped
甚至是新实例?
这是合法的,这要归功于CDI实际获取对所请求bean的引用的方式:client proxies。来自the CDI spec
通过编程查找获得的注入引用或引用通常是上下文引用。对具有正常范围的bean的上下文引用[...]不是对a的直接引用bean的上下文实例[...]而是上下文引用是客户端代理对象 客户端代理实现/扩展bean的部分或全部bean类型,并将所有方法调用委托给bean的当前实例 ...
这种间接的原因有很多:
- 容器必须保证在调用对正常范围的bean的任何有效注入引用时,调用始终由注入bean的当前实例处理。在某些情况下,例如,如果请求范围bean被注入到会话范围的bean或servlet中,则此规则需要间接引用
CDI通过使用代理来处理具有不匹配范围的bean的注入。因此,您可以将请求范围的bean注入到会话范围的bean中,并且引用在每个请求上仍然有效,因为对于每个请求,代理重新连接到请求范围bean的实时实例
这意味着,代理在每个注入点代替真实的东西。代理通过扩展/实现它应该模仿的类型的祖先树来模仿注入点处声明的类型。在您现在实际需要使用该对象时,代理会对当前对话中请求的bean的现有实例执行基于上下文的查找。这是一个请求范围的对象,您可以确保在当前对话/上下文中只有一个实例。
为什么bean被赋予@SessionScoped而不是@Stateful。我想@Stateful会在这里工作。
@Stateful
在这里不起作用,就像我说here一样,它们并不便宜;除非你真的需要,坚持使用香草HttpSession
。更不用说一旦SFSB的客户端释放了它被销毁的bean,即SFSB与当前会话无关,@SessionScoped
就是。
@sessionScoped bean的生命周期是如何管理的?那是什么时候被摧毁了?如果我导航到另一个JSF页面,如果我提取诸如currentUser.userName之类的信息,我将检索我在用于登录的第一个JSF页面上设置的相同信息。(上面的步骤1)
取决于您@SessionScoped
引用的javax.faces.bean.SessionScoped
:HttpSession
与当前的@Dependent
/浏览器会话直接相关联,因此只要死亡,它就会终止;然而JBoss implies that javax.enterprise.context.*
scoped beans don't actually go anywhere until the "context" dies
在整个上下文被销毁之前,实际上无法从上下文中删除bean
将@Inject @New Login aDependentLoginBean; //implicit @Dependent scope applied
@Inject Login aSessionScopedLoginBean; //standard Login bean's scope applied
视为任何方法局部变量:它只有在它的父结构存在的情况下才有用。话虽这么说,它的最佳用途不是支持JSF视图。它最有用的应用程序正在覆盖bean在ad-hoc上指定的范围。使用您当前的示例,我可以在我的应用程序中的其他位置使用以下内容:
@Dependent
与@New一起,您可以将任何其他bean重新用于{{1}}
相关:强>