@ManagedBean 与 @Controller
首先,你应该选择一个框架来管理你的bean。您应该选择 JSF 或 Spring(或 CDI)来管理您的 bean。虽然以下方法有效,但它从根本上是错误的:
@ManagedBean // JSF-managed.
@Controller // Spring-managed.
public class BadBean {}
您最终会得到两个完全独立的同一个托管 bean 类的实例,一个由 JSF 管理,另一个由 Spring 管理。当您将其引用为#{someBean} 时,并不清楚实际上会在 EL 中使用哪个。如果您在faces-config.xml 中注册了SpringBeanFacesELResolver,那么它将是Spring 管理的,而不是JSF 管理的。如果您没有,那么它将是 JSF 管理的。
此外,当您从 javax.faces.* 包中声明 JSF 托管 bean 特定范围时,例如 @RequestScoped、@ViewScoped、@SessionScoped 或 @ApplicationScoped,它只会被 @ManagedBean 识别和使用。 @Controller 不会理解它,因为它需要自己的 @Scope 注释。缺席时默认为单例(应用程序范围)。
@ManagedBean // JSF-managed.
@ViewScoped // JSF-managed scope.
@Controller // Spring-managed (without own scope, so actually becomes a singleton).
public class BadBean {}
当您通过 #{someBean} 引用上述 bean 时,它将返回 Spring 管理的应用程序范围 bean,而不是 JSF 管理的视图范围 bean。
@ManagedProperty 与 @Autowired
特定于 JSF 的 @ManagedProperty 仅适用于 JSF 管理的 bean,即当您使用 @ManagedBean 时。 Spring 特定的 @Autowired 仅适用于 Spring 管理的 bean,即当您使用 @Controller 时。以下方法或多或少等效,不能混合使用:
@ManagedBean // JSF-managed.
@RequestScoped // JSF-managed scope.
public class GoodBean {
@ManagedProperty("#{springBeanName}")
private SpringBeanClass springBeanName; // Setter required.
}
@Component // Spring-managed.
@Scope("request") // Spring-managed scope.
public class GoodBean {
@Autowired
private SpringBeanClass springBeanName; // No setter required.
}
请注意,当您按照javadoc 在faces-config.xml 中注册SpringBeanFacesELResolver 时,
<application>
...
<el-resolver>org.springframework.web.jsf.el.SpringBeanFacesELResolver</el-resolver>
</application>
因此您可以通过#{springBeanName} 在EL 中引用Spring 托管bean,然后您也可以在@ManagedProperty 中引用它们,因为它基本上设置了给定EL 表达式的评估结果。反过来,通过@Autowired 注入 JSF 托管 bean,则完全不受支持。但是,当您从 SpringBeanAutowiringSupport 扩展您的 bean 时,您可以在 JSF 托管 bean 中使用 @Autowired。这将在构造函数调用期间自动在 Spring 自动装配上下文中注册 JSF 托管 bean 实例,这意味着 @Autowired 的所有内容都将在 @PostConstruct 及更高版本中可用。
@ManagedBean // JSF-managed.
@ViewScoped // JSF-managed scope.
public class GoodBean extends SpringBeanAutowiringSupport implements Serializable {
@Autowired
private SpringBeanClass springBeanName; // No setter required.
@PostConstruct
private void init() {
// springBeanName is now available.
}
}
或者当您的架构不允许从不同的基类扩展 bean 时,您始终可以在 Spring 自动装配上下文中手动注册 JSF 托管 bean 实例,如下所示。有关技巧,另请参阅 How to integrate JSF 2 and Spring 3 (or Spring 4) nicely。
@ManagedBean // JSF-managed.
@ViewScoped // JSF-managed scope.
public class GoodBean implements Serializable {
@Autowired
private SpringBeanClass springBeanName; // No setter required.
@PostConstruct
private void init() {
FacesContextUtils
.getRequiredWebApplicationContext(FacesContext.getCurrentInstance())
.getAutowireCapableBeanFactory().autowireBean(this);
// springBeanName is now available.
}
}
@XxxScoped 与 @Scope
Spring 的 @Scope 对 JSF 作用域的支持有限。 JSF 的@ViewScoped 没有等价物。您基本上要么自行开发自己的范围,要么坚持在 Spring 自动装配上下文中手动注册 JSF 托管 bean 实例,如上所示。
而且,另一方面,Spring WebFlow 在 JSF 2.2 中通过新的 @FlowScoped 注释被接管。因此,如果您碰巧已经使用 JSF 2.2,那么如果您只想要流范围,则不一定需要使用 Spring WebFlow。
CDI - 试图统一一切
从 Java EE 6 开始,CDI 作为 Spring DI 的标准替代品提供。它分别具有 @Named 和 @Inject 注释以及它自己的一组范围。我不确定它如何与 Spring 交互,因为我不使用 Spring,但 @Inject 在 @ManagedBean 内工作,而 @ManagedProperty 在 @ManagedBean 内可以引用 @Named bean。另一方面,@ManagedProperty 在 @Named bean 中不起作用。
CDI 的目的是将所有不同的 bean 管理框架统一到一个规范/接口中。 Spring 可能是一个完整的 CDI 实现,但他们选择仅部分实现它(仅支持 JSR-330 javax.inject.*,但不支持 JSR-299 javax.enterprise.context.*)。另见Will Spring support CDI? 和this tutorial。
JSF 将迁移到 CDI 进行 bean 管理,并在未来的版本中弃用 @ManagedBean 和朋友。
@Named // CDI-managed.
@ViewScoped // CDI-managed scope.
public class BetterBean implements Serializable {
@Inject
private SpringBeanClass springBeanName; // No setter required.
@PostConstruct
private void init() {
// springBeanName is now available.
}
}
另见: