65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 JSTL 模板的 Spring Boot Web 应用程序开发。

starIconemptyStarIconemptyStarIconemptyStarIconemptyStarIcon

1.00/5 (1投票)

2019年10月22日

MIT

11分钟阅读

viewsIcon

17347

downloadIcon

194

本教程讨论了如何设置一个可以利用 JSTL 模板和 taglib 的 Spring Boot 应用程序。

引言

当今的技术都是可互换的。也就是说,一个技术集中的想法,也可以在另一个技术集中实现。对于 ASP.NET MVC,页面的视图可以创建一个通用的布局,以及一个或多个模板和部分视图。在 Java 端,你可以做完全相同的事情。事实上,有多种方法可以做到这一点。一种是 Apache Tiles 框架;一种是 Thymeleaf 框架;另一种是使用 JSTL TagLib 框架。在使用方式上,它们都差不多。最大的区别在于语法不同。

在本教程中,我将讨论如何设置一个基于 Spring Boot 的 MVC Web 应用程序,并使用 JSTL 模板和 TagLib 来创建可重用的 JSP 视图模板,然后使用模板和可重用的视图片段将三个不同的页面组合在一起。为什么选择 JSTL 和 TagLib?没有特别好的理由。我在上一个项目中用过它。我也能够轻松地将其集成到本教程中。所以,我决定写关于它的文章。

POM 文件

此示例应用程序基于我之前的教程《使用 Spring Boot 和 JSP、WAR 存档创建 MVC Web 应用程序》创建。在之前的教程中,我讨论了如何将 Spring Boot 应用程序创建为 WAR 存档。该教程没有超出将静态内容文件保存在 resources 文件夹下的 static 子文件夹的默认设置。本教程将进一步(或者说退一步,JSTL 和 TagLib 是老技术)介绍。我将把静态内容移到 webapp 文件夹,而不是使用 static 子文件夹来存放静态 Web 资源,并使用 JstlView 类来渲染最终的 HTML 视图。在代码方面,这只需要做这些。

POM 文件

我使用了我在之前的教程中使用的同一个 POM 文件,没有做任何更改。它用于构建 Spring Boot MVC 应用程序。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" 
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
	http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<artifactId>boot-jstl-war</artifactId>
	<packaging>war</packaging>
	<name>Hanbo Boot JSTL War Sample App</name>
	<description>An example of Spring Boot, JSP and WAR</description>
	<version>1.0.0</version>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.0.5.RELEASE</version>
	</parent>

	<properties>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-tomcat</artifactId>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>jstl</artifactId>
		</dependency>
		<dependency>
			<groupId>org.apache.tomcat.embed</groupId>
			<artifactId>tomcat-embed-jasper</artifactId>
			<scope>provided</scope>
		</dependency>
	</dependencies>
	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>
</project>

我在 POM 项目中包含了 JSTL 作为依赖。不确定是否需要,但为了以防万一就包含了。无论如何,有了这个 POM 文件,我就可以构建项目,创建一个 WAR 存档。然后我就可以在命令行上运行 WAR 存档。

Web 配置类

Spring Boot 的一个很酷之处在于,你可以用最少的配置来构建应用程序。这意味着开发者将使用默认设置。在我之前的教程中,我必须将静态内容放在 resources 文件夹的 static 子文件夹中。为了使用 JSTL 模板和 TagLib,我需要覆盖默认配置。这是我添加到项目的新配置类。

package org.hanbo.boot.app.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView;

@EnableWebMvc
@Configuration
public class WebConfig implements WebMvcConfigurer
{
   @Value("${web.assets.pattern}")
   private String assetsPattern;

   @Value("${web.assets.directory}")
   private String assetsDirectory;
   
   @Value("${web.jsp.filePrefix}")
   private String jspFilePrefix;
   
   @Value("${web.jsp.fileSuffix}")
   private String jspFileSuffix;

   @Override
   public void addResourceHandlers(ResourceHandlerRegistry registry)
   {
       registry
         .addResourceHandler(assetsPattern)
         .addResourceLocations(assetsDirectory);
   }
   
   @Bean
   public InternalResourceViewResolver viewResolver()
   {
      InternalResourceViewResolver viewResolver 
                         = new InternalResourceViewResolver();
      viewResolver.setViewClass(JstlView.class);
      viewResolver.setPrefix(jspFilePrefix);
      viewResolver.setSuffix(jspFileSuffix);
      
      return viewResolver;
   }
}

关于这个类,有几点需要注意。

  • 该类实现了 WebMvcConfigurer 接口。它以前实现的是一个不同的接口。那个接口已经过时了。这是需要实现的新接口。
  • 通过实现 WebMvcConfigurer 接口,它给了我两个方法。
    • 一个是 addResourceHandlers,它允许我指定静态资源的位置。
    • 另一个是 viewResolver。这指定了视图解析器是什么,以及作为页面模板的文件的前缀和后缀是什么。
  • 我从“application.properties”注入了单个属性值。
  • 我用两个注解注解了这个类,表明该类用于配置。

您可以在文件 resource/application.properties 中找到所有注入的属性。

基于以上信息,你可以找到静态资源,例如 *.css*.js 文件。以及 jsp/tag/jspf 文件。

  • CSS 文件可以在 webapp/assets/ 的子文件夹中找到。
  • JSP 文件可以在 webapp/WEB-INF/jsp/ 文件夹中找到。
  • 标签文件可以在 webapp/WEB-INF/tags/ 文件夹中找到。

这是最简单的部分。下一个概念,如何创建 JSTL 模板和可重用的 HTML 组件,会稍微难一些。在下一节,我将讨论如何做到这一点。

JSTL 模板和组件

在我深入研究精彩内容之前,这里是 JSTL、taglib 和 JSPF 工作方式的总体概述。首先,我需要创建一个页面模板。页面模板是一个带有许多占位符的 JSP 页面。占位符是可重用片段可以嵌入其中的地方。模板文件命名为“<tempateName>.tag”。

可重用的 JSP 组件只是 JSP 或 HTML 代码片段。每个组件都定义在自己的文件中。在 JSTL 模板文件中,可以使用特定标签包含这些组件。并且可以在目标 JSP 文件中定义额外的片段。JSTL 视图的工作方式是 JstlView 使用目标 JSP 来查找模板文件。在模板文件中,首先从所有 JSPF 文件中获取所有标记的可重用组件;然后将目标 JSP 文件中定义的额外组件添加到最终的 JSP 文件中。然后,最终 JSP 文件中的标签将动态替换为真实值。最后,它将被发送到客户端。页面上的 JavaScript 将在客户端激活。

目标 JSP 文件不像一般的 JSP 文件。它更像一个 XML 文件,指定要使用的 JSTL 模板;JSP 主体的内容是什么;以及模板中缺失组件(尚未填充)的 HTML 内容。所有这些都将创建一个可以填充实际值的 JSP 页面。当 MVC 控制器调用并返回 ModelAndView 对象时,其中的数据将用于动态创建最终页面。

JSTL 模板页面

在我的示例项目中,我在 webapp/WEB-INF/tags 子文件夹中有一个名为“pageTemplate.tag”的文件。此文件的内容如下:

<%@tag description="Overall Page template" pageEncoding="UTF-8"%>
<%@attribute name="additionalCss" fragment="true" %>
<%@attribute name="additionalJs" fragment="true" %>

<!DOCTYPE HTML>

<html>
   <head>
      <%@ include file="htmlHead.jspf" %>
   </head>
   
   <body class="subpage">
      <%@ include file="header.jspf" %>

      <section class="wrapper">
         <div class="inner">
            <jsp:doBody/>
         </div>
      </section>
         
      <%@ include file="footer.jspf" %>
      
      <%@ include file="jsFooter.jspf" %>
   </body>
</html>

如果您查看此文件,您会发现它就像一个 HTML 页面的草稿。它不完整。许多地方只是占位符。最上面,您会看到这个:

<%@tag description="Overall Page template" pageEncoding="UTF-8"%>
<%@attribute name="additionalCss" fragment="true" %>
<%@attribute name="additionalJs" fragment="true" %>

第二行和第三行定义了两个可以稍后由 JstlView 对象添加的区域。然后,有四个地方可以看到这个:

...
      <%@ include file="htmlHead.jspf" %>
...
      <%@ include file="header.jspf" %>

...
      <%@ include file="footer.jspf" %>
      
      <%@ include file="jsFooter.jspf" %>
...

这四个地方是将 JSPF 文件中定义的 HTML 代码片段导入到此模板中。这就是如何将可重用的 HTML/JSP 组件用于多个目标 JSP 文件。很方便,不是吗?

而在模板的中间,我定义了 this:

      <section class="wrapper">
         <div class="inner">
            <jsp:doBody/>
         </div>
      </section>

这是我将在目标 JSP 文件中渲染页面特定 html/jsp 组件的地方。在我们看目标 JSP 文件之前,让我们来看看 JSPF 文件中定义的 HTML/JSP 可重用组件。

JSPF 组件

这四个地方定义了获取可重用组件的位置。这四个地方从与 tag 文件(JSTL 模板文件)相同的文件夹中获取 jspf 文件,并用于填充这些位置。这是其中一个 jspf 文件,“header.jspf”。

      <header id="header">
         <div class="inner">
            <a href="${pageContext.request.contextPath}/" class="logo">
                <strong>Spring Boot</strong> JSTL Template</a>
            <nav id="nav">
               <a href="${pageContext.request.contextPath}/">Home</a>
               <a href="${pageContext.request.contextPath}/posts">Posts</a>
               <a href="${pageContext.request.contextPath}/updates">Updates</a>
            </nav>
            <a href="#navPanel" class="navPanelToggle"><span class="fa fa-bars"></span></a>
         </div>
      </header>

上面的代码片段将替换引用该文件的标签。JSPF 中的 JSP 代码段将在 JSP 到 HTML 渲染的后期阶段被动态替换为真实值。

模板文件和可重用组件还不够。要使整个设置正常工作,最后一块就是有一个目标 JSP 页面来指定如何将这些东西组合在一起。接下来将讨论这个目标 JSP 文件如何工作。

最终的 JSP 页面

为了演示模板和使用 JSPF 文件定义的 jsp/html 组件的可重用性,我在我的测试 MVC 控制器中创建了三个不同的操作。每个操作都将显示一个看起来不同的页面,但使用相同的模板和组件。

这是一个 JSP 文件,它将演示最终 JSP 页面如何与 JstlView 对象一起工作,这里是这个 JSP 页面的内容。

<%@page contentType="text/html" trimDirectiveWhitespaces="true" pageEncoding="UTF-8"%>
<%@taglib prefix="t" tagdir="/WEB-INF/tags" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@taglib uri="http://www.springframework.org/tags/form" prefix="form"%>

<t:pageTemplate>
   <jsp:attribute name="additionalCss">
   </jsp:attribute>
   <jsp:attribute name="additionalJs">
   </jsp:attribute>
   <jsp:body>
      <article class="12u$(large)">
         <header class="align-left">
            <h3>Senor Philip</h3>
            <p>Last updated on 10/10/2019 - By HanBo</p>
         </header>
         <div class="row">
            <section class="12u$(large)">
               <p>Reference from <a href="https://www.wikipedia.org">Wikipedia</a>.
               <p>Philip relied on skillful civil servants, 
               such as Guillaume de Nogaret and Enguerrand de Marigny, 
               to govern the kingdom rather than on his nobles. 
               Philip and his advisors were instrumental in the transformation 
               of France from a feudal country to a centralized state. 
               The king, who sought an uncontested monarchy, 
               compelled his vassals by wars and restricted feudal usages. 
               His ambitions made him highly influential in European affairs. 
               His goal was to place his relatives on foreign thrones. 
               Princes from his house ruled in Naples and Hungary. 
               He tried and failed to make another relative the Holy Roman Emperor. 
               He began the long advance of France eastward by 
               taking control of scattered fiefs.</p>
            </section>
         </div>
      </article> 
      <article class="12u$(large)">
         <header class="align-left">
            <h3>Senor Philip Takes the World</h3>
            <p>Last updated on 10/08/2019 - By HanBo</p>
         </header>
         <div class="row">
            <section class="12u$(large)">
               <p>Reference from <a href="https://www.wikipedia.org">Wikipedia</a>.
               <p>The constant deficits led Philip to order the arrest of the Lombard merchants, 
               who had earlier made him extensive loans on the 
               pledge of repayment from future taxation.
               The Lombards' assets were seized by government agents and the crown 
               extracted 250,000 LT by forcing the Lombards to purchase French nationality. 
               Despite this draconian measure, the deficits continued to stack up in 1293. 
               By 1295, Philip had replaced the Templars with the Florentine Franzesi bankers 
               as his main source of finance. The Italians could raise huge loans far beyond 
               the capacities of the Templars, and Philip came to rely on them more and more. 
               The royal treasure was transferred from the Paris Temple 
               to the Louvre around this time.</p>
            </section>
         </div>
      </article> 
   </jsp:body>
</t:pageTemplate>

这个 JSP 页面的顶部,都是我可以在后续 JSP 内容渲染中使用的 jstl 标签。最重要的是这个:

...
<%@taglib prefix="t" tagdir="/WEB-INF/tags" %>
...

这会将 taglig 命名空间导入到这个目标 JSP 页面,并使我能够指定可使用的页面模板。页面模板的名称将是 tag 文件的文件名,在本例中是“pageTemplate.tag”。tagdir 属性定义了 JstlView 可以找到模板文件的文件夹。这是指定要使用的模板的代码:

...
<t:pageTemplate>
...
</t:pageTemplate>

还记得 tag 文件中的这个吗?

      <section class="wrapper">
         <div class="inner">
            <jsp:doBody/>
         </div>
      </section>

我们如何为这个 <jsp:doBody/> 提供主体内容?这里,它在目标 JSP 文件中定义:

   <jsp:body>
      <article class="12u$(large)">
         <header class="align-left">
            <h3>Senor Philip</h3>
            <p>Last updated on 10/10/2019 - By HanBo</p>
         </header>
         <div class="row">
            <section class="12u$(large)">
               <p>Reference from <a href="https://www.wikipedia.org">Wikipedia</a>.
               <p>Philip relied on skillful civil servants, 
               such as Guillaume de Nogaret and Enguerrand de Marigny, 
               to govern the kingdom rather than on his nobles. 
               Philip and his advisors were instrumental in the 
               transformation of France from a feudal country to a centralized state. 
               The king, who sought an uncontested monarchy, compelled his vassals 
               by wars and restricted feudal usages. His ambitions made him 
               highly influential in European affairs. His goal was to place 
               his relatives on foreign thrones. Princes from his house ruled 
               in Naples and Hungary. He tried and failed to make another relative 
               the Holy Roman Emperor. He began the long advance of France 
               eastward by taking control of scattered fiefs.</p>
            </section>
         </div>
      </article> 
      <article class="12u$(large)">
         <header class="align-left">
            <h3>Senor Philip Takes the World</h3>
            <p>Last updated on 10/08/2019 - By HanBo</p>
         </header>
         <div class="row">
            <section class="12u$(large)">
               <p>Reference from <a href="https://www.wikipedia.org">Wikipedia</a>.
               <p>The constant deficits led Philip to order the arrest 
               of the Lombard merchants, who had earlier made him extensive 
               loans on the pledge of repayment from future taxation. 
               The Lombards' assets were seized by government agents 
               and the crown extracted 250,000 LT by forcing the Lombards 
               to purchase French nationality. Despite this draconian measure, 
               the deficits continued to stack up in 1293. By 1295, 
               Philip had replaced the Templars with the Florentine Franzesi bankers 
               as his main source of finance. The Italians could raise huge loans 
               far beyond the capacities of the Templars, and Philip came to rely 
               on them more and more. The royal treasure was transferred from the 
               Paris Temple to the Louvre around this time.</p>
            </section>
         </div>
      </article> 
   </jsp:body>

上面的 <jsp:body>...<jsp:body> 将替换 <jsp:doBody> 部分。

同样,在这个目标 JSP 文件中,我也有这些:

   <jsp:attribute name="additionalCss">
   </jsp:attribute>
   <jsp:attribute name="additionalJs">
   </jsp:attribute>

以上部分意味着在渲染此目标 JSP 文件的此占位符区域的最终 JSP 页面时,不添加任何新内容。在另一个名为“WEB-INF/jsp/updates.jsp”的目标 JSP 文件中,我在这里添加了要包含的额外 CSS 文件和 JS 文件。

   <jsp:attribute name="additionalCss">
      <link rel="stylesheet" href="${pageContext.request.contextPath}/assets/css/mystyle.css" />
   </jsp:attribute>
   <jsp:attribute name="additionalJs">
      <script src="${pageContext.request.contextPath}/assets/js/myscript.sj"></script>
   </jsp:attribute>

接下来,我们只需要控制器和操作方法来使这些页面生动起来,这将在下一节中讨论。

MVC Controller

此示例应用程序的 MVC 控制器与我在之前的教程中的一样。也就是说,不需要特殊的编码。只是对 MVC 如何与 Spring MVC 框架一起工作做一个非常简短的解释。首先,我们定义一个类,并用 @Controller 注解该类。我们可以将 @RequestMapping 添加到类或方法。@RequestMapping 注解用于指定 HTTP 路由,以便请求(由 URL 和 HTTP 方法指定)可以被正确的类和类中的特定方法处理。

在处理请求的方法中,我通常使用 ModelAndView 作为返回对象。这个独特的类将指定一个视图页面和一组可用于渲染页面的数据模型。在后台,由于我们使用 JstlView 对象进行渲染,它将获取 JSP 页面,找到主模板,渲染所有占位符和可重用组件,最后使用数据模型创建最终页面。这是整个控制器类:

package org.hanbo.boot.app.controllers;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class IndexController
{
   @RequestMapping(value = "/", method = RequestMethod.GET)
   public ModelAndView index()
   {
      ModelAndView retVal = new ModelAndView();
      retVal.setViewName("index");
      return retVal;
   }

   @RequestMapping(value = "/posts", method = RequestMethod.GET)
   public ModelAndView posts()
   {
      ModelAndView retVal = new ModelAndView();
      retVal.setViewName("posts");
      return retVal;
   }
   
   @RequestMapping(value = "/updates", method = RequestMethod.GET)
   public ModelAndView updates()
   {
      ModelAndView retVal = new ModelAndView();
      retVal.setViewName("updates");
      return retVal;
   }
}

如上所示,在这个类中有三个方法。每个方法都将渲染一个页面。这三个方法的 URL 是:

  • https://:8080/ - 索引页
  • https://:8080/posts - 列出三个帖子的页面
  • https://:8080/updates - 列出两个更新的页面

所有页面都填充了虚拟数据,但它们确实使用了相同的主模板和所有相同的组件。

如何运行示例应用程序

要构建示例应用程序,请在命令行上执行以下命令:

mvn clean install

该命令将下载所有依赖项,编译源代码,并将应用程序打包为 WAR 存档。

要将项目导入 Eclipse,请在命令行上运行以下命令:

mvn eclipse:eclipse

最后,要运行应用程序,请在命令行上执行以下命令:

java -jar target\boot-jstl-war-1.0.0.war

然后尝试这三个 URL,每个 URL 都应该显示一个不同的页面。索引页应显示如下:

在索引页上,有指向其他两个页面的链接,在页面左上角查找它们。第二个页面是“posts”页面,这是屏幕截图。

最后,“updates”页面的外观如下:

摘要

本教程讨论了如何创建利用 JSTL 模板和 taglib 组件的基于 Spring Boot 的 MVC Web 应用程序。在本教程中,我讨论了以下内容:

  • 应用程序所需的 POM 文件配置,用于构建和打包。
  • 应用程序配置,用于设置 JstlView、页面模板文件夹以及静态资源文件夹。
  • JSP 页面模板、模板中的占位符、可重用的 HTML/JSP 组件以及如何将它们导入模板、目标 JSP 页面的定义、MVC 控制器以及用于渲染页面的操作方法。
  • 如何构建和运行示例应用程序。

这是我为 2019 年写的最后一篇教程。不是我最好的作品,但不得不记录下来,因为它很有用。在处理 MVC 框架时,可重用模板始终很有用。将页面分解成不同的组件,并为不同的页面显示进行组合,这始终是一个好习惯。缺点是,当滥用时,目标 JSP 页面可能非常难以理解。尽管如此,我还是选择了这种方法,因为它有助于避免在多个页面中重复 HTML 代码。因此,使这些 HTML 代码片段可重用。

功劳归于应得者。本教程中,我使用了来自网站 templated.co 的页面模板。我使用的模板名为“Projection”。

历史

  • 2019/10/20 - 初稿
© . All rights reserved.