跳转至

Java - JSF

Java - JSF

更新日期:2020-07-24


1. 整合JSF与Spring框架

JSF框架是JavaEE官方提供的用来编写Web应用程序的框架。而对于Spring框架(不是Spring MVC),我们最经常使用的是它的依赖注入特性。
JSF也有一套依赖注入的玩意儿,就是使用ManageredBean之类的注解来标记托管类。所谓整合就是希望能同时使用JSF和Spring的依赖注入,并能够互通。比如如下用法:

1
2
3
4
public class TestAction {

   @Autowired
    TestForm testForm;

1.1 配置Spring

首先我们使用通常的做法来配置Spring。

Spring配置类:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
package org.hakurei.appconfig.spring;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

/**
 * Spring配置类
 */
@Configuration
// 自动扫描此package下面的注解类
@ComponentScan("org.hakurei")
public class SpringConfig {

        /** 此处添加其它配置 */
}

这个配置类扫描org.hakurei包下面的注解类,加入到Spring的应用上下文中。

Web应用程序启动类:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/**
 * Web应用程序初始化
 */
public class SpringWebInit implements WebApplicationInitializer {

        // 启动函数
        public void onStartup(ServletContext servletContext) throws ServletException {

                // log4j对象
                Logger logger = LogManager.getLogger(SpringWebInit.class);

                logger.error("Web应用程序初始化...");

                logger.info("配置Spring应用上下文");
                AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();

                logger.info("注册自己的Spring配置类:" + SpringConfig.class.getName());
                ctx.register(SpringConfig.class);

                logger.info("关联Servlet上下文");
                ctx.setServletContext(servletContext);

                logger.info("配置Listener: ContextLoaderListener, RequestContextListener");
                servletContext.addListener(new ContextLoaderListener(ctx));
                servletContext.addListener(new RequestContextListener());

                // 配置dispatcher servlet
                Dynamic dispatcher = servletContext.addServlet("dispatcher", new DispatcherServlet(ctx));
                int loadOrder = 0;
                dispatcher.addMapping("/");
                dispatcher.setLoadOnStartup(loadOrder);

                logger.info("配置dispatcher servlet");
                logger.info("mapping: " + dispatcher.getMappings());
                logger.info("Servlet加载顺序: " + loadOrder);
   }  
}

1.2 整合

使用JSF配置文件faces-config.xml:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<?xml version="1.0" encoding="UTF-8"?>
<faces-config xmlns="http://java.sun.com/xml/ns/javaee"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_1.xsd"

        version="2.1">
        <application>
                <el-resolver>
                 org.springframework.web.jsf.el.SpringBeanFacesELResolver
                </el-resolver>
        </application>
 </faces-config>

2. JSF的相关配置

2.1 配置Faces Servlet

Faces Servlet是JSF实现的一套Servlet。这个是JSF里面的概念,本身和PrimeFaces的Faces这个词没有关系。 Faces Servlet位于jsf-api-x.x.xx.jar中的javax.faces.webapp.FacesServlet里面。

此类是一个final类,我们无法继承。它在web.xml中的配置如下

1
2
3
4
5
6
7
8
9
<servlet>
    <servlet-name>Faces Servlet</servlet-name>
    <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>Faces Servlet</servlet-name>
    <url-pattern>*.xhtml</url-pattern>
</servlet-mapping>

此处默认我们使用xhtml文件。

有的项目中还能看到如下配置,暂时没有发现这个配置处理的.jsf文件是为了处理什么内容。

1
2
3
4
<servlet-mapping>
    <servlet-name>Faces Servlet</servlet-name>
    <url-pattern>*.jsf</url-pattern>
</servlet-mapping>

2.2 配置ConfigureListener

在使用JSF的项目中,经常能在web.xml中见到如下配置:

1
2
3
<listener>
    <listener-class>com.sun.faces.config.ConfigureListener</listener-class>
</listener>
但是我发现不加这个也没什么问题。 查看此类的代码,发现它实现了一大堆的Listener接口:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
/**             
 * <p>Parse all relevant JavaServer Faces configuration resources, and          
 * configure the Reference Implementation runtime environment.</p>              
 * <p/>         
 */             
public class ConfigureListener implements ServletRequestListener,               
        HttpSessionListener,            
        ServletRequestAttributeListener,                
        HttpSessionAttributeListener,           
        ServletContextAttributeListener,                
        ServletContextListener {                

原来这个Listener用于初始化JSF的一些东西。一般会自动加载,并不需要我们在web里面配置。甚至有时候加上了会导致某些内容初始化两次。

而最初是由于GlassFish v3的一个bug导致这个Listener无法正常自动加载,所以需要手动配置,一般在使用tomcat时并不需要加。

建议如果不加的时候不报错就不要加。

如果查看jsf-impl-x.xx.xxx.jar里面的META-INF/jsf_core.tld文件会发现,这里面已经配置了这个Listener。

JSF01

3. 搭建JSF+Primefaces项目

3.1 搭建JSF

3.1.1 下载Jsf库的Jar包

在Maven库中搜索如下三个Jar包并安装:

  • servlet-api (javax.servlet/javax.servlet-api)
  • jsf-api (com.sun.faces/jsf-api)
  • jsf-impl (com.sun.faces/jsf-impl)
3.1.2 在web.xml中配置JSF

(1) 配置Servlet

下添加如下配置:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
<servlet>   
    <servlet-name>Faces Servlet</servlet-name>
    <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>  
<servlet-mapping>   
    <servlet-name>Faces Servlet</servlet-name>
    <url-pattern>*.jsf</url-pattern>
</servlet-mapping>  
<servlet-mapping>   
    <servlet-name>Faces Servlet</servlet-name>
    <url-pattern>*.xhtml</url-pattern>
</servlet-mapping>  

(2) 可选配置:Listener

1
2
<listener>  
    <listener-class>com.sun.faces.config.ConfigureListener</listener-class>

※参考章节2.2 配置ConfigureListener.

3.1.3 在xhtml页面中使用jsf标签

在html头部加上如下声明:

1
2
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"

3.2 整合Primefaces

实际上使用Primefaces和使用Jstl标签库一样容易,只需要三步。

3.2.1 下载Primefaces的Jar包

在Maven库中搜索如下Jar包并安装:

  • primefaces (org.primefaces/primefaces)
3.2.2 在xhtml页面中使用primefaces标签

在html头部加上如下声明:

1
xmlns:p="http://primefaces.org/ui"
JSF标签和primefaces标签可以同时使用。

4. JSF项目MVC分层结构

MVC构架是指?

1
Model-View-Controller

4.1 JSF中的MVC

在JSF中,使用Form类来存储业务数据,使用Action类来处理前台事件,使用xhtml来表现前台页面。对应的文档结构如下:

对于web工程来说

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
learnJsf
├─src
|  ├─main
|  |  ├─webapp
|  |  |   ├─WEB-INF
|  |  |   |    ├─web.xml
|  |  |   |    ├─view
|  |  |   |    |  ├─index.xhtml
|  |  |   |    |  └login.xhtml
|  |  |   |    ├─lib
|  |  |   ├─META-INF
|  |  ├─resources
|  |  ├─java
|  |  |  ├─learn.model
|  |  |  |      ├─LoginForm.java
|  |  |  |      └UserInforEntity.java
|  |  |  ├─learn.controller
|  |  |  |        └LoginAction.Java

4.2 各层直接的交互方式

Form类与前台页面xhtml显示的数据实时绑定。后台处理时可以直接从Form类中获取页面上用户输入的各种数据。

button可以指定为执行完Action后去update指定的组件,框架会默认通过ajax使用最新的数据来刷新此组件。如图中的search按钮执行完检索后用新存放在infors的数据来刷新searchRst这个dataTable。

button必须放在form元素中。否则它所绑定的action事件将不会生效。

在Action类中可以获取LoginForm对象,然后直接操作Form中的数据。

Form类只用来保存页面数据,并不应包含逻辑处理。

LoginForm.java

1
2
3
4
5
6
7
@ManagedBean
@SessionScoped

private String userName; // get set
private String password; // get set
private String List<UserInforEntity> infors; // get set
private Boolean isShow; // get set

LoginAction.java

1
2
3
4
5
@ManagedBean
@RequestScoped

public void login() {}
public void search() {}

login.xhtml

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
<h:form>    
    <input text="用户名" value="#{loginForm.userName}" />
    <password text="密码" value="#{loginForm.password}" />
    <button action="#{loginAction.login()}" />

    <button update="searchRst" action="#{loginAction.search()}" />
</h:form>   

<p:dataTable id="searchRst" 
    value="#{loginForm.infors}"
    var="infor">

    <p:column value="#{infor.name}" />
    <p:column value="#{infor.sex}" />
</p:dataTable>  

5. 复合组件

5.1 什么是组件以及复合组件?

在HTML中页面中用<XX></XX>来表示的元素称之为组件。叫做component,常被叫做element。如<div /><button />等都可以称之为组件。这样的组件我称之为HTML基本组件。

复合组件又可以叫自定义组件,是指由我们拼装HTML基本组件而产生的新的组件。

比如一个对话框组件

对话框

定义好了这样一个对话框组件后,我们就可以在需要弹警告框的地方插入这个组件,就像我们插入一个button一样简单。

1
2
<p:commandButton value="执行" action="#{xxxAction.func1}" />
<z:warnningDlg msgStr="这里填警告框的内容" okAction="#{响应确定按钮的action}" isShow="#{xxxxForm.isShowWarnning}"/>

5.2 什么时候使用复合组件?

如果有这么一小组元素,你有很多页面都使用这组元素。就比如自己用DIV实现的对话框,你希望在别的页面也可以方便的使用他,那么就可以将这个对话框定义为一个复合组件。这样就不需要我们在每个页面都写一套对话框。

这里的使用场景要区别于模板,复合组件使用模板技术来定义,但应用范畴有所不同。我们通常在页面整体布局的时候会使用模板,可以方便的使所有页面的结构保持统一。复合组件不是从页面结构上,而是从页面上具体的一组一组元素上,来使各个页面保持统一。

5.3 定义一个复合组件

首先我们要先明确一下定义和引用复合组件时的文档结构关系。

以标准的Maven项目目录结构为例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
learnJsf
├─src
|  ├─main
|  |  ├─resources
|  |  |     ├─META-INF
|  |  |     |    ├─resources
|  |  |     |    |     ├─view
|  |  |     |    |     |  ├─index.xhtml   -- 在这个文件中使用这个组件
|  |  |     |    |     |  ├─template
|  |  |     |    |     |  ├─components
|  |  |     |    |     |  |     └warnningDialog.xhtml   -- 组件定义在这里
|  |  |     |    |     ├─js
|  |  |     |    |     ├─css
|  |  ├─java

(1) 组件定义语法

warnningDialog.xhtml

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
xmlns:composite="http://java.sun.com/jsf/composite"

<!-- Interface -->
<composite:interface>
    <composite:attribute name="msgStr" />
    <composite:attribute name="okAction" method-signature="void action()" />
    <composite:attribute name="isShow" />
</composite:interface>

<!-- Implementation -->
<composite:implementation>
    <h:form>
        <div id="warnningDialog" style="position:absolute;left:0;top:0;display:#{cc.attrs.isShow?block:none};">
            <h:outputScript library="js" name="common.js" />    
            <div style="z-index:100;opacity:0.7;position:absolute;width:100vw;height:100vh;left:0;top:0;background-color:black;"></div> 
            <div style="z-index:200;position:absolute;width:50vw;height:50vh;left:25vw;top:25vh;background-color:white;">   
                This is a warnning  dialog : #{cc.attrs.msgStr}
                <p:commandButton value="確定" onclick="closeWarnningDialog()" action="#{cc.attrs.okAction}"/>
            </div>  
        </div>      
    </h:form>
</composite:implementation>

cc.attrs是jsf组件的保留关键字。

(2) 在index.xhtml中使用这个复合组件

index.xhtml

1
2
3
xmlns:z="http://java.sun.com/jsf/composite/view/components"

<z:warnningDialog msgStr="这里填警告框的内容" okAction="#{响应确定按钮的action}" isShow="#{xxxxForm.isShowWarnning}"/>