前言
Spring Boot 天生的適合 web 應用開發,它可以快速的嵌入 Tomcat, Jetty 或 Netty 用於包含一個 HTTP 服務器。且開發十分簡單,只需要引入 web 開發所需的包,然后編寫業務代碼即可。
自動配置原理?
在進行 web 開發之前讓我再來回顧一下自動配置,可以參考系列文章第三篇。Spring Boot 為 Spring MVC 提供了自動配置,添加了如下的功能:
- 視圖解析的支持。
- 靜態資源映射,WebJars 的支持。
- 轉換器 Converter 的支持。
- 自定義 Favicon 的支持。
- 等等
在引入每個包時候我們需要思考是如何實現自動配置的,以及我們能自己來配置哪些東西,這樣開發起來才會得心應手。
關於 Spring Boot Web 開發的更詳細介紹可以參考官方文檔。
1. JSON 格式轉換
Spring Boot 默認使用 Jackson 進行 JSON 化處理,如果想要切換成 FastJson 可以首先從官方文檔里查詢信息。從這里知道對於 ResponseBody 的渲染主要是通過 HttpMessageConverters, 而首先引入FastJson Pom依賴並排除 Spring Boot 自帶的 Jackson。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<artifactId>spring-boot-starter-json</artifactId>
<groupId>org.springframework.boot</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.47</version>
</dependency>
編寫轉換器處理 json 的日期格式同時處理中文亂碼問題。
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
/**
* 自定義JSON轉換器
*
* @param converters
*/
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
FastJsonConfig fastJsonConfig = new FastJsonConfig();
fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat);
//日期格式化
fastJsonConfig.setDateFormat("yyyy-MM-dd HH:mm:ss");
//處理中文亂碼問題
List<MediaType> fastMediaTypes = new ArrayList<>();
fastMediaTypes.add(MediaType.APPLICATION_JSON_UTF8);
converter.setSupportedMediaTypes(fastMediaTypes);
converter.setFastJsonConfig(fastJsonConfig);
converters.add(converter);
}
}
2. 靜態資源映射
By default, Spring Boot serves static content from a directory called
/static
(or/public
or/resources
or/META-INF/resources
) in the classpath or from the root of theServletContext
.
2.1 默認映射
官方文檔告訴我們 Spring Boot 對於靜態資源的映射目錄是 /static , /public , /resources 以及 /META-INF/resource。除此之外其實還映射了 /webjars/**
到 classpath:/META-INF/resources/webjars
。
很明顯此處是自動配置實現的,通過查看源碼分析這段配置。
而對於網站圖標,Spring Boot 也已經配置了默認位置,可以在看到。
// path: org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration
@Bean
public SimpleUrlHandlerMapping faviconHandlerMapping() {
SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
mapping.setOrder(Ordered.HIGHEST_PRECEDENCE + 1);
mapping.setUrlMap(Collections.singletonMap("**/favicon.ico", // 圖表
faviconRequestHandler()));
return mapping;
}
@Bean
public ResourceHttpRequestHandler faviconRequestHandler() {
ResourceHttpRequestHandler requestHandler = new ResourceHttpRequestHandler();
requestHandler.setLocations(resolveFaviconLocations());
return requestHandler;
}
private List<Resource> resolveFaviconLocations() {
String[] staticLocations = getResourceLocations(
this.resourceProperties.getStaticLocations());
List<Resource> locations = new ArrayList<>(staticLocations.length + 1);
Arrays.stream(staticLocations).map(this.resourceLoader::getResource)
.forEach(locations::add);
locations.add(new ClassPathResource("/"));
return Collections.unmodifiableList(locations);
}
根據 Spring Boot 默認的靜態資源映射規則,可以直接把需要的靜態資源放在響應的文件夾下然后直接引用即可。
而放在 Public 文件夾下的 HTML 頁面也可以直接訪問。
2.2 webjars
webjars 的思想是把靜態資源打包到 Jar 包中,然后使用 JVM 構建工具進行管理,如 maven , Gradle 等。
使用 webjars 第一步需要進入依賴,如要使用 bootstrap。
<!-- Web Jars 靜態資源文件 -->
<dependency>
<groupId>org.webjars</groupId>
<artifactId>bootstrap</artifactId>
<version>4.1.3</version>
</dependency>
引入之后查看 bootstrap 資源。
由於 Springboot 映射了 /webjars/**
到 classpath:/META-INF/resources/webjars
. 因此可以直接在文件中引用 webjars 的靜態資源。
<!-- Bootstrap core CSS -->
<link href="/webjars/bootstrap/4.1.3/css/bootstrap.min.css" rel="stylesheet">
<script src="/webjars/bootstrap/4.1.3/js/bootstrap.min.js"></script>
3. 模版引擎
Spring MVC 支持各種模版技術,如 Thymeleaf , FreeMarker , JSP 等。而Thyemeleaf 原型即頁面的特性或許更符合 Spring Boot 快速開發的思想而被官方推薦。
Thymeleaf 是適用於 Web 開發的服務端 Java 模版引擎,Thymeleaf 為開發工作流程帶來優雅自然的模版,由於其非侵入的特性,可以讓頁面不管是在靜態原型下還是用作模版引擎時都有良好的頁面展現。
<table>
<thead>
<tr>
<th th:text="#{msgs.headers.name}">Name</th>
<th th:text="#{msgs.headers.price}">Price</th>
</tr>
</thead>
<tbody>
<tr th:each="prod: ${allProducts}">
<td th:text="${prod.name}">Oranges</td>
<td th:text="${#numbers.formatDecimal(prod.price, 1, 2)}">0.99</td>
</tr>
</tbody>
</table>
3.1 引入 Thymeleaf
<!-- thymeleaf 模版-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
3.2 使用 Thymeleaf
根據 Spring Boot 自動配置原理,先看一下 Thymeleaf 的配置類,從中可以看出 Thymeleaf 的相關配置。我們可以知道 默認存放目錄是 templates 文件夾,文件后綴為 .html
且開啟了緩存。
@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";
/**
* Whether to enable template caching.
*/
private boolean cache = true;
為了在開發中編寫模版文件時不用重啟,可以在配置中關閉緩存。
# 關閉模版緩存
spring.thymeleaf.cache=false
# 如果需要進行其他的配置,可以參考配置類:ThymeleafProperties
# org.springframework.boot.autoconfigure.thymeleaf.ThymeleafProperties
編寫 Controller 響應信息。
/**
* 獲取ID為1的用戶信息
*
* @return
*/
@GetMapping(value = "/user/1")
public String getUserById(Model model) {
User user1 = new User("Darcy", "password", 24, new Date(), Arrays.asList("Java", "GoLang"));
User user2 = new User("Chris", "password", 22, new Date(), Arrays.asList("Java", "Web"));
ArrayList<User> userList = new ArrayList<>();
userList.add(user1);
userList.add(user2);
model.addAttribute("userList", userList);
model.addAttribute("user", user1);
return "user";
}
因為 Thymelaf 默認模版位置在 templates 文件夾下,因此在這個文件夾下編寫頁面信息。
<!doctype html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<title>Thymeleaf 的基本使用</title>
<!-- 引入JS文件 -->
<!--<script th:src="@{/static/js/alert.js}"></script>-->
</head>
<body>
<div>
<p><b>Hello Thymeleaf Index</b></p>
用戶名稱:<input th:id="${user.username}" th:name="${user.username}" th:value="${user.username}">
<br/>
用戶技能:<input th:value="${user.skills}">
<br/>
用戶年齡:<input th:value="${user.age}">
<br/>
用戶生日:<input th:value="${#dates.format(user.birthday,'yyyy-MM-dd hh:mm:ss ')}">
</div>
<div th:object="${user}">
<p><b>Hello Thymeleaf Index</b></p>
用戶名稱:<input th:id="*{username}" th:name="*{username}" th:value="*{username}">
<br/>
用戶技能:<input th:value="*{skills}">
<br/>
用戶年齡:<input th:value="*{age}">
<br/>
用戶生日:<input th:value="*{#dates.format(birthday,'yyyy-MM-dd hh:mm:ss')}">
</div>
<div>
<p><b>Text 與 utext</b></p>
<!-- th:text 顯示HTML源碼,作為字符串 -->
<span th:text="${user.username}">abc</span>
<br>
<span th:utext="${user.username}">abc</span>
</div>
<div>
<p><b>URL 的引用</b></p>
<a th:href="@{https://www.baidu.com}">網站網址</a>
</div>
<div>
<p><b>表單的使用</b></p>
<form th:action="@{/th/postform}" th:object="${user}" method="post">
用戶名稱:<input type="text" th:field="*{username}">
<br/>
用戶技能:<input type="text" th:field="*{skills}">
<br/>
用戶年齡:<input type="text" th:field="*{age}">
<input type="submit">
</form>
</div>
<div>
<p><b>判斷的使用</b></p>
<div th:if="${user.age} == 18">18歲了</div>
<div th:if="${user.age} gt 18">大於18歲</div>
<div th:if="${user.age} lt 18">小於18歲</div>
<div th:if="${user.age} ge 18">大於等於</div>
<div th:if="${user.age} le 18">小於等於</div>
</div>
<div>
<p><b>選擇框</b></p>
<select>
<option>請選擇一本書</option>
<option th:selected="${user.username eq 'admin'}">管理員</option>
<option th:selected="${user.username eq 'Darcy'}">Darcy</option>
<option th:selected="${user.username eq 'Chris'}">Chris</option>
</select>
</div>
<div>
<p><b>遍歷功能</b></p>
<table>
<tr>
<th>用戶名稱</th>
<th>年齡</th>
<th>技能</th>
</tr>
<tr th:each="u:${userList}">
<td th:text="${u.username}"></td>
<td th:text="${u.age}"></td>
<td th:text="${u.skills}"></td>
</tr>
</table>
</div>
<div>
<p><b>Switch功能</b></p>
<div th:switch="${user.username}">
<p th:case="'admin'">歡迎管理員</p>
</div>
</div>
</body>
</html>
訪問頁面可以看到數據正常顯示。
文章代碼已經上傳到 GitHub Spring Boot Web開發 - 靜態資源。
文章代碼已經上傳到 GitHub Spring Boot Web開發 - 模版引擎。
<完>
本文原發於個人博客:https://www.codingme.net 轉載請注明出處