1. 概述

在这篇文章中,我们将探讨如何在JSF(JavaServer Faces)的管理bean和页面中访问Spring定义的bean,以便将业务逻辑的执行委托给Spring bean。本文假设读者已经对JSF和Spring有了一定的理解,文章基于Mojarra实现的JSF。

2. 在Spring中

首先,我们定义一个Spring中的bean,名为UserManagementDAO,它将在内存存储中添加用户名。该bean由以下接口定义:

public interface UserManagementDAO {
    boolean createUser(String newUserData);
}

bean的实现通过以下Java配置进行配置:

public class SpringCoreConfig {
    @Bean
    public UserManagementDAO userManagementDAO() {
        return new UserManagementDAOImpl();
    }
}

或者使用以下XML配置:

<bean class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor" />
<bean class="com.baeldung.dao.UserManagementDAOImpl" id="userManagementDAO"/>

我们在XML中定义bean,并注册CommonAnnotationBeanPostProcessor以确保能够识别@PostConstruct注解。

3. 配置

接下来的部分解释了使Spring和JSF上下文集成的配置项。

3.1. 不使用web.xml的Java配置

通过实现WebApplicationInitializer,我们可以程序化地配置ServletContextMainWebAppInitializer类中的onStartup()实现如下:

public void onStartup(ServletContext sc) throws ServletException {
    AnnotationConfigWebApplicationContext root = new AnnotationConfigWebApplicationContext();
    root.register(SpringCoreConfig.class);
    sc.addListener(new ContextLoaderListener(root));
}

AnnotationConfigWebApplicationContext启动Spring上下文,并通过注册SpringCoreConfig类来添加bean。

类似地,在Mojarra实现中,有一个FacesInitializer类来配置FacesServlet。要使用这种配置,只需扩展FacesInitializer。完整的MainWebAppInitializer实现如下:

public class MainWebAppInitializer extends FacesInitializer implements WebApplicationInitializer {
    public void onStartup(ServletContext sc) throws ServletException {
        AnnotationConfigWebApplicationContext root = new AnnotationConfigWebApplicationContext();
        root.register(SpringCoreConfig.class);
        sc.addListener(new ContextLoaderListener(root));
    }
}

3.2. 使用web.xml

首先,我们需要在应用程序的web.xml文件中配置ContextLoaderListener

<listener>
    <listener-class>
        org.springframework.web.context.ContextLoaderListener
    </listener-class>
</listener>

这个监听器负责在Web应用启动时启动Spring应用程序上下文。默认情况下,它会查找名为applicationContext.xml的Spring配置文件。

3.3. faces-config.xml

现在,我们在faces-config.xml文件中配置SpringBeanFacesELResolver

<el-resolver>org.springframework.web.jsf.el.SpringBeanFacesELResolver</el-resolver>

EL解析器是JSF框架支持的可插件组件,允许我们自定义JSF运行时在评估表达式语言(EL)表达式时的行为。这个EL解析器将允许JSF运行时通过JSF中定义的EL表达式访问Spring组件。

4. 在JSF中访问Spring Bean

至此,我们的JSF Web应用已准备好从JSF后端bean或JSF页面访问Spring bean。

4.1. 从JSF 2.0的后端bean

现在可以从JSF后端bean访问Spring bean。根据你运行的JSF版本,有两种可能的方法。在JSF 2.0中,您需要在JSF管理bean上使用@ManagedProperty注解。

@ManagedBean(name = "registration")
@RequestScoped
public class RegistrationBean implements Serializable {
    @ManagedProperty(value = "#{userManagementDAO}")
    transient private IUserManagementDAO theUserDao;

    private String userName;
    // getters and setters
}

请注意,当使用@ManagedProperty时,getter和setter方法是必需的。现在,为了验证从管理bean访问Spring bean的能力,我们将添加createNewUser()方法:

public void createNewUser() {
    FacesContext context = FacesContext.getCurrentInstance();
    boolean operationStatus = userDao.createUser(userName);
    context.isValidationFailed();
    if (operationStatus) {
        operationMessage = "User " + userName + " created";
    }
}

方法的核心是使用userDao Spring bean,并调用其功能。

4.2. 从JSF 2.2的后端bean

另一种仅适用于JSF 2.2及更高版本的方法是使用CDI的@Inject注解。这适用于带有@ManagedBean注解的JSF管理bean以及带有@Named注解的CDI管理bean。

实际上,使用CDI注解,这是注入bean的唯一有效方法:

@Named( "registration")
@RequestScoped
public class RegistrationBean implements Serializable {
    @Inject
    UserManagementDAO theUserDao;
}

使用这种方法,getter和setter不再是必需的。另外,请注意EL表达式是不存在的。

4.3. 从JSF视图

createNewUser()方法将由以下JSF页面触发:

<h:form>
    <h:panelGrid id="theGrid" columns="3">
        <h:outputText value="Username"/>
        <h:inputText id="firstName" binding="#{userName}" required="true"
          requiredMessage="#{msg['message.valueRequired']}" value="#{registration.userName}"/>
        <h:message for="firstName" style="color:red;"/>
        <h:commandButton value="#{msg['label.saveButton']}" action="#{registration.createNewUser}"
          process="@this"/>
        <h:outputText value="#{registration.operationMessage}" style="color:green;"/>
    </h:panelGrid>
</h:form>

要显示页面,请启动服务器并导航到:

http://localhost:8080/jsf/index.jsf

我们也可以在JSF视图中使用EL来访问Spring bean。为了测试,只需将之前引入的JSF页面的第7行更改为:

<h:commandButton value="Save"
  action="#{registration.userDao.createUser(userName.value)}"/>

在这里,我们在JSF页面内直接调用Spring DAO的createUser方法,将userName绑定值传递给方法,完全绕过了管理bean。

5. 总结

我们探讨了Spring和JSF上下文之间的基本集成,使我们能够在JSF的bean和页面中访问Spring bean。

值得注意的是,尽管JSF运行时提供了可插拔架构,使得Spring框架能够提供集成组件,但来自Spring框架的注解(如@Autowired@Component等)不能在JSF上下文中使用,反之亦然。

这意味着你不能在JSF管理bean中使用@Autowired@Component等注解,也不能在Spring管理bean上使用@ManagedBean注解。然而,你可以在JSF 2.2+的管理bean和Spring bean(因为Spring支持JSR-330)中使用@Inject注解。

本文所附的源代码可在GitHub获取。