用SpringBoot开发JSF应用程序(二)

在Spring Boot上创建JSF应用程序

在我们开发一个查询和新增“产品”的简单应用程序时,我们将首先创建Product实体。 对于初学者,请在com.auth0.samples.bootfaces包中创建Product.java文件。 该实体将具有以下代码:

package com.auth0.samples.bootfaces;
import lombok.Data;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import java.math.BigDecimal;

@Data
@Entity
public class Product {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @Column
    private String name;

    @Column
    private BigDecimal price;

    protected Product() {
    }

    public Product(String name, BigDecimal price) {
        this.name = name;
        this.price = price;
    }
}

这是一个非常简单的Product实体,只有三个属性:

  1. id, 实体的主键
  2. name,产品的名称
  3. price,产品的价格

您可能已经注意到IDE报错,无法识别@Data annotation。 这个注解来自lombok库,我们需要将它导入到我们的应用程序中。 Project Lombok旨在减少在Java应用程序的许多部分(如getter和setter)中重复的样板代码。 在上面的实体中,我们使用@Data来承担为实体属性定义许多访问器方法的负担。 Lombok还有许多其他功能,请查看其文档。

若要导入它,请将以下元素作为dependecies 的子元素添加到pom.xml文件中:

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
<version>1.16.16</version>
</dependency>

现在,我们将创建Spring Boot使用的application.properties文件,配置HSQLDB连接String,以及Spring Data以禁用Hibernate的自动创建功能。

请注意,Hibernate是Spring Data的传递依赖项,默认情况下它会读取使用Entity注解的类并尝试为它们创建表。 如前所述,在我们的应用程序中,我们将使用Flyway。 我们需要在src / main / webapp /文件夹中创建application.properties文件,其中包含以下内容:

spring.datasource.url=jdbc:hsqldb:file:data/products
spring.jpa.hibernate.ddl-auto=none

第一个属性将HSQLDB配置为将数据持久保存到应用程序根目录的数据文件夹中,第二个属性是禁用Hibernate自动创建功能的属性。 由于我们已禁用此功能,因此我们现在需要添加一个Flyway脚本来创建产品表。 我们通过在src / main / resources / db / migration /文件夹中创建一个名为V1__products.sql的文件来实现。 该文件将包含以下脚本:

create table product (
  id identity not null,
  name varchar (255) not null,
  price double not null
);

现在已经定义了Product实体类和一个在HSQLDB上持久化它的表,我们可以扩展JpaRepositorySpringBoot接口,以提供一个托管bean来与数据库通信。为此,我们在com.auth0.samples.bootFaces包中创建一个名为ProductRepository的接口,其内容如下:

package com.auth0.samples.bootfaces;
import org.springframework.data.jpa.repository.JpaRepository;
public interface ProductRepository extends JpaRepository<Product, Long> {
}

JpaRepository接口附带了一些预定义的方法,允许开发人员findAll (查询所有实体实例),getOne (根据id获取单个实体),delete (删除实体),save (保存新实体)等。

接下来,我们需要准备好前端处理代码了。 为了使用户能够通过我们的应用程序创建“产品”,我们需要:

1.一个JSF应用程序基本布局的模板。

2.一个JSF接口(xhtml文件),包含用于创建新产品的表单。

3.一个Spring控制器,作为表单接口的辅助bean。

构建JSF接口来创建“产品”

首先,让我们创建应用程序的模板。 这个模板非常简单。 首先,在src / main / webapp /文件夹中创建一个名为layout.xhtml的文件,然后将以下代码添加到其中:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://xmlns.jcp.org/jsf/html"
      xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
      xmlns:f="http://xmlns.jcp.org/jsf/core"
      xmlns:p="http://primefaces.org/ui"><f:view>
    <h:head>
        <meta charset="utf-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <title>Product</title>
    </h:head>
    <h:body>
        <div class="ui-g">
            <div class="ui-g-12">
                <p:toolbar>
                    <f:facet name="left">
                        <p:button href="/" value="List of Products" />
                        <p:button href="/product" value="New Product" />
                    </f:facet>
                </p:toolbar>
            </div>
            <div class="ui-g-12">
                <ui:insert name="content" />
            </div>
        </div>
</h:body>
</f:view>
</html>

在JSF上定义视图几乎就像定义常规HTML文件一样,但是有一些不同的元素,例如来自JSF和相关框架(如PrimeFaces)上定义的名称空间。 代码中关于布局的最重要的元素是p:toolbar和ui:insert。 第一个是PrimeFaces提供的组件,我们使用它在我们的模板中定义导航菜单。 此菜单将允许用户切换到新增“产品”的维护视图,或者显示“产品”列表的查询视图。

第二个元素ui:insert定义了允许子视图定义其内容的模板的确切位置。 模板可以有多个ui:insert元素,使用不同的名称进行区别即可。

注意:JSF使用一种名为Facelets的技术来定义模板。可以在Oracle网站的JavaEE 7教程中阅读有关它的所有内容(https://docs.oracle.com/javaee/7/tutorial/jsf-facelets001.htm)。

定义模板之后,我们创建对应的Spring控制器。在com.auth0.samples.bootFaces包中创建一个名为ProductController的类,并添加以下代码:

package com.auth0.samples.bootfaces;
import org.ocpsoft.rewrite.annotation.Join;
import org.ocpsoft.rewrite.el.ELBeanName;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

@Scope(value = "session")
@Component(value = "productController")
@ELBeanName(value = "productController")
@Join(path = "/product", to = "/product-form.jsf")
public class ProductController {
    @Autowired
    private ProductRepository productRepository;

    private Product product = new Product();

    public String save() {
        productRepository.save(product);
        product = new Product();
        return "/product-list.xhtml?faces-redirect=true";
    }

    public Product getProduct() {
        return product;
    }
}

这个类只有两个方法:

  • save方法,用于触发JSF提交按钮,以此保存一个新“产品”;
  • getProduct方法,接口使用它将表单上的输入绑定到Product的实例。 此实例与ProductController实例同时创建,并在用户保存新产品后立即创建新实例。

需要注意的是,save方法重定向到product-list.xhtml,该列表列出了我们数据库中已经存在的“产品”。

更重要的是,这个类有四个注解:

  • @ Scope是一个Spring注解,它定义每个用户将存在此类的单个实例。
  • @ Component将此类定义为Spring组件,并将其命名为将在表单接口中使用的productController-name。
  • @ ELBeanName是Rewrite提供的注解,用于在其作用域上配置bean的名称。
  • @ Join置-由Rewrite提供的另一个注解 - 配/ productURL以响应product-form.xhtml的内容。

最后,让我们创建使用上述控制器的表单。 我们将在src / main / webapp /文件夹中创建一个名为product-form.xhtml的文件,其中包含以下内容:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://xmlns.jcp.org/jsf/html"
      xmlns:ui="http://xmlns.jcp.org/jsf/facelets" xmlns:p="http://primefaces.org/ui"><ui:composition template="layout.xhtml">
    <ui:define name="content">
        <h:form id="productForm">
            <p:panel header="Product Details">
                <h:panelGrid columns="1">
                    <p:outputLabel for="name" value="Name: " />
                    <p:inputText id="name" value="#{productController.product.name}" />
                    <p:outputLabel for="price" value="Price: " />
                    <p:inputNumber id="price" value="#{productController.product.price}" />
                    <h:commandButton value="Save" action="#{productController.save}" />
                </h:panelGrid>
            </p:panel>
        </h:form>
</ui:define>
</ui:composition>
</html>

此文件使用ui:composition元素显式定义layout.xhtml作为此视图的模板,并使用ui:define来通知该视图必须在模板的内容区域中呈现。 然后定义表单来创建新“产品”。 此表单由一个p:inputText和一个p:inputNumber元素组成,用户可以定义产品的名称和产品的价格。 最后一个元素专门用于处理数字属性,不允许用户输入非数字型字符。

最后,定义了一个h:commandButton,即视图(页面)上的一个按钮,该按钮可以触发ProductController组件的save方法。 在这个视图中,可以看到我们将产品对象和定义在ProductController组件中的行为通过ProductController名称关联在一起,该名称通过该组件的@Component和@ELBeanName注解进行定义。

如果现在通过IDE或通过mvn spring-boot:run命令运行应用程序,我们将能够在浏览器中访问http:// localhost:8080 / product。通过该页面,我们可以创建新“产品”,但我们无法列出刚创建的“产品”——接下来,我们来解决这个问题。

为“产品列表”构建JSF接口

为了使我们的用户能够看到已创建产品的列表,我们将首先定义一个将处理接口背后逻辑的辅助bean。 这个支持bean将被称为ProductListController,我们将使用以下代码在com.auth0.samples.bootfaces包中创建它:

package com.auth0.samples.bootfaces;
import org.ocpsoft.rewrite.annotation.Join;
import org.ocpsoft.rewrite.annotation.RequestAction;
import org.ocpsoft.rewrite.el.ELBeanName;
import org.ocpsoft.rewrite.faces.annotation.Deferred;
import org.ocpsoft.rewrite.faces.annotation.IgnorePostback;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import java.util.List;

@Scope (value = "session")
@Component (value = "productList")
@ELBeanName(value = "productList")
@Join(path = "/", to = "/product-list.jsf")
public class ProductListController {
    @Autowired
    private ProductRepository productRepository;

    private List<Product> products;

    @Deferred
    @RequestAction
    @IgnorePostback
    public void loadData() {
        products = productRepository.findAll();
    }

    public List<Product> getProducts() {
        return products;
    }
}

与ProductController类似,此类有四个注解:

  • @ Scope(value =“session”)定义每个用户只有一个此类的实例。
  • @ Component将此类定义为Spring组件,并将其命名为productList。
  • @ ELBeanName在重写范围上配置Bean的名称。
  • @ Join配置/ URL将使用/product-list.jsf进行响应。

请注意,此控制器有一个名为loadData的方法,该方法使用@ Deferred,@ RequestAction和@IgnorePostback进行注解。 在渲染界面之前,需要使用这些注解来加载“产品”集合。 我们也可以在getProducts中加载这个集合,但这会使渲染过程变慢,因为这个方法在JSF 生命周期(lifecycle)中会被调用很多次。

最后,我们将创建对应的“产品”列表界面——文件名叫product-list.xhtml,在src / main / webapp /文件夹中,代码如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
      xmlns:h="http://xmlns.jcp.org/jsf/html"
      xmlns:f="http://xmlns.jcp.org/jsf/core"
      xmlns:p="http://primefaces.org/ui">
<ui:composition template="layout.xhtml">
    <ui:define name="content">
        <h:form id="form">
            <p:panel header="Products List">
                <p:dataTable id="table" var="product" value="#{productList.products}">
                    <p:column>
                        <f:facet name="header"># Id</f:facet>
                        <h:outputText value="#{product.id}" />
                    </p:column>

                    <p:column>
                        <f:facet name="header">Name</f:facet>
                        <h:outputText value="#{product.name}" />
                    </p:column>

                    <p:column>
                        <f:facet name="header">Price</f:facet>
                        <h:outputText value="#{product.price}">
                            <f:convertNumber type="currency" currencySymbol="$ " />
                        </h:outputText>
                    </p:column>
                </p:dataTable>
            </p:panel>
        </h:form>
</ui:define>
</ui:composition>
</html>

我们借助PrimeFaces提供的p:dataTable组件呈现“产品”集合。 该组件通过value属性从支持bean(本例中为ProductListController)接收对象集合,并迭代它创建HTML表的行。 该表的列由p:column元素定义,也由PrimeFaces提供。 请注意,在此界面中,我们使用了一个名为f:convertNumber的元素来正确格式化“产品”的价格。

运行应用程序,并在浏览器中输入http:// localhost:8080 ,将显示以下内容:

译者注:

由于是基于Maven构建,所以使用install构建target目录中的内容。

构建内容如下:

(未完待续)

 

用SpringBoot开发JSF应用程序系列文章:

用SpringBoot开发JSF应用程序(一)

用SpringBoot开发JSF应用程序(二)

用SpringBoot开发JSF应用程序(三)

 

 

 

猜你喜欢

转载自blog.csdn.net/cysunc/article/details/85326238
今日推荐