Spring Boot实战(七)Spring Boot 的 Web 开发 7.2 Thymeleaf模板引擎

7.2 Thymeleaf基础知识
Thymeleaf是一个Java类库,它是一个 xml/xhtml/html5的模板引擎,可以作为MVC的Web应用的View层。
Thymeleaf还提供了额外的模块与Spring MVC集成,所以我们可以使用Thymeleaf完全替代JSP。
下面我们演示日常工作中常用的Thymeleaf用法,我们将把本节的内容在7.2.4节运行演示。
1.引入 Thymeleaf
下面的代码是一个基本的Thymeleaf模板页面,在这里我们引入了Bootstrap(作为样式控制)和jQuery(DOM)操作,当然它们不是必须的:

<!DOCTYPE html>
<html xmlns:th="http:www.thymeleaf.org">  <!-- 通过此命名空间,将镜头页面转换为动态的视图。需要进行动态处理的元素将使用“th:”为前缀 -->
<head>
<meta content="text/html;charset=UTF-8">
<link th:src="@{bootstrap/css/bootstrap.min.css}" rel="stylesheet" /><!-- 通过@{}引用Web静态资源,这在JSP下是极易出错的 -->
<title>Insert title here</title>
</head>
<body>

<script th:src="@{jquery-1.10.2.min.js}" type="text/javascript"></script>
<script th:src="@{bootstrap/js/bootstrap.min.js}"></script>
</body>
</html>

2.访问model中的数据
通过“${}”访问model中的属性,这和JSP极为相似。

<div class="panel panel-primary">
	<div class="panel-heading">
		<h3 class="panel-title">访问model</h3>
	</div>
	<div class="panel-body">
		<span th:text="${singlePerson.name}"></span>
	</div>
</div>

3.model中的数据迭代
Thymeleaf的迭代和JSP的写法也很相似,代码如下:

<div class="panel panel-primary">
	<div class="panel-heading">
		<h3 class="panel-title">列表</h3>
	</div>
	<div class="panel-body">
		<ul class="list-group">
			<li class="list-group-item" th:each="person:${people}">
				<span th:text="${person.name}"></span>
				<span th:text="${person.age}"></span>
			</li>
		</ul>
	</div>
</div>

4.数据判断
代码如下:

<div th:if="${not #list.isEmpty(people)}">
	<div class="panel panel-primary">
		<div class="panel-heading">
			<h3 class="panel-title">列表</h3>
		</div>
		<div class="panel-body">
			<ul class="list-group">
				<li class="list-group-item" th:each="person:${people}">
					<span th:text="${person.name}"></span>
					<span th:text="${person.age}"></span>
				</li>
			</ul>
		</div>
	</div>
</div>

5.在JavaScript中访问model
在项目中,我们经常需要在JavaScript访问model中的值,在Thymeleaf实现代码如下:

<script th:inline="javascript">
	var single = [[${singlePerson}]];
	console.log(single.name+"/"+single.age);
</script>

还有一种是需要在html的代码里访问model中的属性,例如,我们需要在列表后单击每一行后面的按钮获得model中的值,可做如下处理:

<li class="list-group-item" th:each="person:${people}">
	<span th:text="${person.name}"></span>
	<span th:text="${person.age}"></span>
	<button class="btn" th:onclick="'getName(\'' + ${person.name} + '\');'">获得名字</button>
</li>

6.其它知识
更多完整的Thymeleaf的知识,请查看http://www.thymeleaf.org的官网。
7.2.2 与Spring MVC集成
在Spring MVC中,若我们需要集成一个模板引擎的话,需要定义ViewResolver,而ViewResolver需要定义一个View,如4.2.2节中我们为JSP定义的ViewResolver的代码:

@Bean
public InternalResourceViewResolver viewResolver(){
		InternalResourceViewResolver  viewResolver  = new 
InternalResourceViewResolver();
		viewResolver.setPrefix("/WEB-INF/classes/views/");  	
		viewResolver.setSuffix(".jsp");
		viewResolver.setViewClass(JstlView.class);
		return viewResolver;
		}

通过上面的代码可以看出,使用JsltView定义一个InternalResourceViewResolver,因而使用Thymeleaf作为我们的模板引擎也应该做类似的定义。庆幸的是,Thymeleaf为我们定义好了org.thymeleaf.spring4.view.ThymeleafView 和 org.thymeleaf.spring4.view.ThymeleafViewResolver(默认使用ThymeleafView作为View)。Thymeleaf给我们提供了一个SpringTemplateEngine类,用来驱动在Spring MVC下使用Thymeleaf模板引擎,另外还提供了一个TemplateResolver用来设置通用的模板引擎(包含前缀、后缀等),这使我们在Spring MVC中集成Thymeleaf引擎变得十分简单。
7.2.3 Spring Boot的Thymeleaf支持 在上一节我们讲述了Thymeleaf与Spring MVC集成的配置,讲述的目的是为了方便大家理解Spring MVC 和Thymeleaf集成的原理。但在Spring Boot中这一切都是不需要的,Spring Boot通过 org.springframework.boot.autoconfigure.thymeleaf包对Thymeleaf进行了自动配置。如图
在这里插入图片描述
通过ThymeleafAutoConfiguration类对集成所需要的Bean进行自动配置,包括templateResolver、templateEngine和thymeleafViewResolver的配置。
通过ThymeleafProperties来配置Thymeleaf,在application.properties中,以spring.thymeleaf开头来配置,通过查看ThymeleafProperties的主要源码,我们可以看出如何设置属性以及默认配置:

/*
 * Copyright 2012-2018 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.boot.autoconfigure.thymeleaf;

import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.List;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.http.MediaType;
import org.springframework.util.MimeType;
import org.springframework.util.unit.DataSize;

/**
 * Properties for Thymeleaf.
 *
 * @author Stephane Nicoll
 * @author Brian Clozel
 * @author Daniel Fern谩ndez
 * @author Kazuki Shimizu
 * @since 1.2.0
 */
@ConfigurationProperties(prefix = "spring.thymeleaf")
public class ThymeleafProperties {

	private static final Charset DEFAULT_ENCODING = StandardCharsets.UTF_8;

	public static final String DEFAULT_PREFIX = "classpath:/templates/";

	public static final String DEFAULT_SUFFIX = ".html";


	/**
	 * 前缀设置,Spring Boot默认模板,放置在classpath:/template/目录下
	 */
	private String prefix = DEFAULT_PREFIX;

	/**
	 * 后缀设置,默认为html
	 */
	private String suffix = DEFAULT_SUFFIX;

	/**
	 * 模板模式设置,默认为 HTML
	 */
	private String mode = "HTML";

	/**
	 * 模板编码设置,默认为UTF-8
	 */
	private Charset encoding = DEFAULT_ENCODING;

	/**
	 * 是否开启模板缓存,默认开启,开发时请关闭
	 */
	private boolean cache = true;

	/**
	 * Order of the template resolver in the chain. By default, the template resolver is
	 * first in the chain. Order start at 1 and should only be set if you have defined
	 * additional "TemplateResolver" beans.
	 */
	private Integer templateResolverOrder;

	/**
	 * Comma-separated list of view names (patterns allowed) that can be resolved.
	 */
	private String[] viewNames;

	/**
	 * Comma-separated list of view names (patterns allowed) that should be excluded from
	 * resolution.
	 */
	private String[] excludedViewNames;

	/**
	 * Enable the SpringEL compiler in SpringEL expressions.
	 */
	private boolean enableSpringElCompiler;

	/**
	 * Whether hidden form inputs acting as markers for checkboxes should be rendered
	 * before the checkbox element itself.
	 */
	private boolean renderHiddenMarkersBeforeCheckboxes = false;

	/**
	 * Whether to enable Thymeleaf view resolution for Web frameworks.
	 */
	private boolean enabled = true;

	private final Servlet servlet = new Servlet();

	private final Reactive reactive = new Reactive();

	public boolean isEnabled() {
		return this.enabled;
	}

	public void setEnabled(boolean enabled) {
		this.enabled = enabled;
	}

	public boolean isCheckTemplate() {
		return this.checkTemplate;
	}

	public void setCheckTemplate(boolean checkTemplate) {
		this.checkTemplate = checkTemplate;
	}

	public boolean isCheckTemplateLocation() {
		return this.checkTemplateLocation;
	}

	public void setCheckTemplateLocation(boolean checkTemplateLocation) {
		this.checkTemplateLocation = checkTemplateLocation;
	}

	public String getPrefix() {
		return this.prefix;
	}

	public void setPrefix(String prefix) {
		this.prefix = prefix;
	}

	public String getSuffix() {
		return this.suffix;
	}

	public void setSuffix(String suffix) {
		this.suffix = suffix;
	}

	public String getMode() {
		return this.mode;
	}

	public void setMode(String mode) {
		this.mode = mode;
	}

	public Charset getEncoding() {
		return this.encoding;
	}

	public void setEncoding(Charset encoding) {
		this.encoding = encoding;
	}

	public boolean isCache() {
		return this.cache;
	}

	public void setCache(boolean cache) {
		this.cache = cache;
	}

	public Integer getTemplateResolverOrder() {
		return this.templateResolverOrder;
	}

	public void setTemplateResolverOrder(Integer templateResolverOrder) {
		this.templateResolverOrder = templateResolverOrder;
	}

	public String[] getExcludedViewNames() {
		return this.excludedViewNames;
	}

	public void setExcludedViewNames(String[] excludedViewNames) {
		this.excludedViewNames = excludedViewNames;
	}

	public String[] getViewNames() {
		return this.viewNames;
	}

	public void setViewNames(String[] viewNames) {
		this.viewNames = viewNames;
	}

	public boolean isEnableSpringElCompiler() {
		return this.enableSpringElCompiler;
	}

	public void setEnableSpringElCompiler(boolean enableSpringElCompiler) {
		this.enableSpringElCompiler = enableSpringElCompiler;
	}

	public boolean isRenderHiddenMarkersBeforeCheckboxes() {
		return this.renderHiddenMarkersBeforeCheckboxes;
	}

	public void setRenderHiddenMarkersBeforeCheckboxes(
			boolean renderHiddenMarkersBeforeCheckboxes) {
		this.renderHiddenMarkersBeforeCheckboxes = renderHiddenMarkersBeforeCheckboxes;
	}

	public Reactive getReactive() {
		return this.reactive;
	}

	public Servlet getServlet() {
		return this.servlet;
	}

	public static class Servlet {

		/**
		 * Content-Type value written to HTTP responses.
		 */
		private MimeType contentType = MimeType.valueOf("text/html");

		/**
		 * Whether Thymeleaf should start writing partial output as soon as possible or
		 * buffer until template processing is finished.
		 */
		private boolean producePartialOutputWhileProcessing = true;

		public MimeType getContentType() {
			return this.contentType;
		}

		public void setContentType(MimeType contentType) {
			this.contentType = contentType;
		}

		public boolean isProducePartialOutputWhileProcessing() {
			return this.producePartialOutputWhileProcessing;
		}

		public void setProducePartialOutputWhileProcessing(
				boolean producePartialOutputWhileProcessing) {
			this.producePartialOutputWhileProcessing = producePartialOutputWhileProcessing;
		}

	}

	public static class Reactive {

		/**
		 * Maximum size of data buffers used for writing to the response. Templates will
		 * execute in CHUNKED mode by default if this is set.
		 */
		private DataSize maxChunkSize = DataSize.ofBytes(0);

		/**
		 * Media types supported by the view technology.
		 */
		private List<MediaType> mediaTypes;

		/**
		 * Comma-separated list of view names (patterns allowed) that should be executed
		 * in FULL mode even if a max chunk size is set.
		 */
		private String[] fullModeViewNames;

		/**
		 * Comma-separated list of view names (patterns allowed) that should be the only
		 * ones executed in CHUNKED mode when a max chunk size is set.
		 */
		private String[] chunkedModeViewNames;

		public List<MediaType> getMediaTypes() {
			return this.mediaTypes;
		}

		public void setMediaTypes(List<MediaType> mediaTypes) {
			this.mediaTypes = mediaTypes;
		}

		public DataSize getMaxChunkSize() {
			return this.maxChunkSize;
		}

		public void setMaxChunkSize(DataSize maxChunkSize) {
			this.maxChunkSize = maxChunkSize;
		}

		public String[] getFullModeViewNames() {
			return this.fullModeViewNames;
		}

		public void setFullModeViewNames(String[] fullModeViewNames) {
			this.fullModeViewNames = fullModeViewNames;
		}

		public String[] getChunkedModeViewNames() {
			return this.chunkedModeViewNames;
		}

		public void setChunkedModeViewNames(String[] chunkedModeViewNames) {
			this.chunkedModeViewNames = chunkedModeViewNames;
		}

	}

}

7.2.4 实战
1.新建Spring Boot项目
选择Thymeleaf依赖,spring-boot-starter-thymeleaf 会自动包含spring-boot-starter-web,如图
在这里插入图片描述
在这里插入图片描述
2.示例JavaBean
此类用来在模板页面展示数据用,包含name属性和age属性:

package com.wisely;

public class Person {
	private String name;
	private Integer age;
	
	public Person() {
		super();
	}
	public Person(String name,Integer age) {
		this.name=name;
		this.age=age;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public Integer getAge() {
		return age;
	}
	public void setAge(Integer age) {
		this.age = age;
	}
	

}

3.脚本样式静态文件
根据默认原则,脚本样式、图片等静态文件应放置在 src/main/resources/static 下,这里引入了Bootstrap和jQuery,结构如图
在这里插入图片描述
4.演示页面
根据默认原则,页面应放置在 src/main/resources/templates下。在 src/main/resources/templates下新建 index.html,如图。

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta content="text/html;charset=UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<link th:href="@{bootstrap/css/bootstrap.min.css}" rel="stylesheet" />
<link th:href="@{bootstrap/css/bootstrap-theme.min.css}" rel="stylesheet" />
<title>Insert title here</title>
</head>
<body>
	
	<div class="panel panel-primary">
		<div class="panel-heading">
			<h3 class="panel-title">访问model</h3>
		</div>
		<div class="panel-body">
			<span th:text="${singlePerson.name}"></span>
		</div>
	</div>
	
	<div th:if="${not #lists.isEmpty(people)}">
		<div class="panel panel-primary">
			<div class="panel-heading">
				<h3 class="panel-title">列表</h3>
			</div>
			<div class="panel-body">
				<ul class="list-group">
					<li class="list-group-item" th:each="person:${people}">
						<span th:text="${person.name}"></span>
						<span th:text="${person.age}"></span>
						<button class="btn" th:onclick="getName([[${person.name}]]);">获得名字</button>
					</li>
				</ul>
			</div>
		</div>
	</div>
	<script th:src="@{jquery-1.6.2.min.js}" type="text/javascript"></script>
	<script th:src="@{bootstrap/js/bootstrap.min.js}"></script>
	<script th:inline="javascript">
		var single = [[${singlePerson}]];
		console.log(single.name+"/"+single.age);
		
		function getName(name){
			console.log(name);
		}
	</script>
</body>
</html>

5.数据准备
代码如下:

package com.wisely;

import java.util.ArrayList;
import java.util.List;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@SpringBootApplication
public class Ch72Application {
	
	@RequestMapping("/")
	public String index(Model model) {
		Person single = new Person("aa", 11);
		
		List<Person> people = new ArrayList<Person>();
		Person p1 = new Person("xx", 11);
		Person p2 = new Person("yy", 22);
		Person p3 = new Person("zz", 33);
		people.add(p1);
		people.add(p2);
		people.add(p3);
		
		model.addAttribute("singlePerson",single);
		model.addAttribute("people",people);
		
		return "index";
	}

	public static void main(String[] args) {
		SpringApplication.run(Ch72Application.class, args);
	}

}


6.运行
访问http://localhost:8080,效果如图
在这里插入图片描述
单击“获得名字” 选项,效果如图
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_40929047/article/details/86596289