Spring实战
第2章 开发Web应用
展现信息
- Taco Cloud是一个可以在线订购taco的地方。但除此之外,他还允许客户展现其创意,能够让他们通过丰富的配料设计自己的taco。
- 在Spring Web应用中,获取和处理数据是控制器的任务,而将数据渲染到HTML中并在浏览器中展现则是视图的任务。为了支撑taco的创建页面,我们需要构建如下组件。
- 用来定义taco配料属性的领域类
- 用来获取配料信息并将其传递至视图的Spring MVC控制器类。
- 用来在用户的浏览器中渲染配料列表的视图模板。
构建领域类
- 应用的领域指的是它所要解决的主题范围:也就是会影响到对应用理解的理念和概念。在Taco Cloud应用中,领域对象包括taco设计、组成这些设计的配料、顾客以及顾客所下的订单。作为开始,首先关注taco的配料。
- 在我们的领域中,taco配料是非常简单的对象。每种配料都有一个名称和类型,以便于对其进行可视化分类(蛋白质、奶酪、酱汁等)。每种配料还有一个ID,这样的话对它的引用就能非常容易和明确。
package tacos;
import lombok.Data;
import lombok.RequiredArgsConstructor;
@Data
@RequiredArgsConstructor
public class Ingredient {
private final String id;
private final String name;
private final Type type;
public static enum Type {
WRAP, PROTEIN, VEGGIES, CHEESE, SAUCE
}
}
- 这个普通的Java领域类缺少了常见的getter、setter方法。部分原因是节省空间,此外还因为使用了名为Lombok的库(可以在运行时动态生成这些方法)。实际上,类级别的@Data注解就是由Lombok提供的,会告诉Lombok生成所有缺失的方法,同时还会生成所有以final属性作为参数的构造器。通过使用Lombok,我们能够让Ingredient的代码简洁明了。
- 但是要注意的是如果要使用Spring库,首先要将其作为依赖添加到项目中。
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
创建控制器类
- 在Spring MVC框架中,控制器是重要的参与者。它们的主要职责是处理HTTP请求,要么将请求传递给视图以便于渲染HTML(浏览器展现),要么直接将数据写入响应体。
- 对于Taco Cloud应用来说,我们需要一个简单的控制器以完成如下功能。
- 处理路径为"/design"的HTTP GET请求
- 构建配料列表
- 处理请求,并将配料数据传递给要渲染的HTML的视图模板,发送给发起请求的Web浏览器。
package tacos.web;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.Errors;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import tacos.Ingredient;
import tacos.Ingredient.Type;
import tacos.Taco;
import javax.validation.Valid;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
@Slf4j
@Controller
@RequestMapping("/design")
public class DesignTacoController {
@ModelAttribute
public void addIngredients(Model model)
{
List<Ingredient> ingredients = Arrays.asList(
new Ingredient("FLTO", "Flour Tortilla", Type.WRAP),
new Ingredient("COTO", "Corn Tortilla", Type.WRAP),
new Ingredient("GRBF", "Ground Beef", Type.PROTEIN),
new Ingredient("CARN", "Carnitas", Type.PROTEIN),
new Ingredient("TMTO", "Diced Tomatoes", Type.VEGGIES),
new Ingredient("LETC", "Lettuce", Type.VEGGIES),
new Ingredient("CHED", "Cheddar", Type.CHEESE),
new Ingredient("JACK", "Monterrey Jack", Type.CHEESE),
new Ingredient("SLSA", "Salsa", Type.SAUCE),
new Ingredient("SRCR", "Sour Cream", Type.SAUCE)
);
Type[] types = Ingredient.Type.values();
for(Type type : types) {
model.addAttribute(type.toString().toLowerCase(),filterByType(ingredients, type));
}
}
@GetMapping
public String showDesignForm(Model model){
model.addAttribute("design", new Taco());
return "design";
}
private List<Ingredient> filterByType(List<Ingredient> ingredients, Type type){
return ingredients.stream().filter(x->x.getType().equals(type)).collect(Collectors.toList());
}
}
- 对于DisignTacoController,我们要先注意类级别的注解,首先是@Slf4j,这是Lombok所提供的注解,运行时会在这个类中自动生成一个SLF4J Logger。这个简单的注解和在类中通过以下代码显式声明效果相同。
private static final org.slf4j.Logger log=org.slf4j.LoggerFactory.getLogger(DesignTacoController.class);
- DesignTacoController用到的下一个注解是@Controller。这个注解将会使这个类被识别为控制器,并且将其作为组件扫描的候选者,所以Spring会发现它并自动创建一个DesignTacoController实例,并将该实例作为Spring应用上下文中的bean。
- 还带有@RequestMapping注解。当@RequestMapping注解用到类级别时,能指定该处理器所控制的请求类型。在本例中规定DesignTacoController将会处理以
"/design"
开头的请求。
处理GET请求
- 修饰showDesignForm()方法的@GetMapping注解对类级别的@RequestMapping进行了细化,指明了当接收到对
"/design"
的HTTP GET请求时,将会调用showDesignForm()来处理请求。
注解 | 描述 |
---|---|
@RequestMapping | 通用的请求处理 |
@GetMapping | 处理HTTP GET请求 |
@PostMapping | 处理HTTP POST请求 |
@PutMapping | 处理HTTP PUT请求 |
@DeleteMapping | 处理HTTP DELETE请求 |
@PatchMapping | 处理HTTP PATCH请求 |
设计视图
- 依然使用Thymeleaf来定义视图。像它这样的视图库在设计时是与特定的Web框架解耦的。这样的话,无法感知Spring的模型抽象,因此无法与控制器放在Model中的数据协同工作。但是,它们可以与Servlet的request属性协作。所以,在Spring将请求转移到视图之前,它会把模型数据复制到request属性中,Thymeleaf和其他的视图模板方案就可以访问到他们了。
- Thymeleaf末班就是增加一些额外元素属性的HTML,这些属性能够知道模板如何渲染request数据。举例来说,如果有个请求属性key为"message",我们想要使用Thymeleaf将其渲染到一个HTML<p>标签中,那么在Thymeleaf模板中我们可以这样写:
<p th:text="${message}">
placeholder message
</p>
- 当模板渲染为HTML的时候,<p>元素体将会被替换为Servlet Request中key为"message"的属性值。"th:text"是Thymeleaf命名空间中的属性,会执行这个替换过程,${}会告诉它要使用某个请求属性中的值。
- Thymeleaf还提供了一个属性"th:each",它会迭代一个元素集合,为集合中的每个条目都渲染HTML。在我们设计视图展现模型中的配料列表时,这就非常便利了。如果只想渲染"wrap"配料的列表,我们可以使用如下的HTML片段。
<h3>
Designate your wrap:
</h3>
<div th:each="ingredient : ${wrap}">
<input name="ingredients" type="checkbox" th:value="${ingredient.id}" />
<span th:text="${ingredient.name}">INGREDIENT</span>
</div>
- 在这里,我们在<div>标签里使用了th:each属性,这样的话就能针对wrap request属性所对应集合中的每个元素重复渲染<div>了。每次迭代的时候配料元素都会绑定到一个名为ingredient的Thymeleaf变量上。
- 在<div>元素中,有一个<input>复选框元素,还有一个为复选框提供标签的<span>元素。复选框使用Thymeleaf的th:value来为渲染出的<input>元素设置value属性,这里会将其设置为所找到的Ingredient的id属性。<span>元素使员工th:text将INGREDIENT占位符文本替换为Ingredient的name属性。
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Taco Cloud</title>
<link rel="stylesheet" th:href="@{/styles.css}"/>
</head>
<body>
<h1>Design your taco!</h1>
<img th:src="@{/images/TacoCloud.png}"/>
<form method="POST" th:object="${design}">
<span class="validationError"
th:if="${#fields.hasErrors('ingredients')}"
th:errors="*{ingredients}">Ingredient Error</span>
<div class="grid">
<div class="ingredient-group" id="wraps">
<h3>Designate your wrap:</h3>
<div th:each="ingredient : ${wrap}">
<input name="ingredients" type="checkbox" th:value="${ingredient.id}"/>
<span th:text="${ingredient.name}">INGREDIENT</span><br/>
</div>
</div>
<div class="ingredient-group" id="proteins">
<h3>Pick your protein:</h3>
<div th:each="ingredient : ${protein}">
<input name="ingredients" type="checkbox" th:value="${ingredient.id}"/>
<span th:text="${ingredient.name}">INGREDIENT</span><br/>
</div>
</div>
<div class="ingredient-group" id="cheeses">
<h3>Choose your cheese:</h3>
<div th:each="ingredient : ${cheese}">
<input name="ingredients" type="checkbox" th:value="${ingredient.id}"/>
<span th:text="${ingredient.name}">INGREDIENT</span><br/>
</div>
</div>
<div class="ingredient-group" id="veggies">
<h3>Determine your veggies:</h3>
<div th:each="ingredient : ${veggies}">
<input name="ingredients" type="checkbox" th:value="${ingredient.id}"/>
<span th:text="${ingredient.name}">INGREDIENT</span><br/>
</div>
</div>
<div class="ingredient-group" id="sauces">
<h3>Select your sauce:</h3>
<div th:each="ingredient : ${sauce}">
<input name="ingredients" type="checkbox" th:value="${ingredient.id}"/>
<span th:text="${ingredient.name}">INGREDIENT</span><br/>
</div>
</div>
</div>
<div>
<h3>Name your taco creation:</h3>
<input type="text" th:field="*{name}"/>
<span th:text="${#fields.hasErrors('name')}">XXX</span>
<span class="validationError"
th:if="${#fields.hasErrors('name')}"
th:errors="*{name}">Name Error</span>
<br/>
<button>Submit your taco</button>
</div>
</form>
</body>
</html>
处理表单提交
- 观察视图中的<form>标签,会发现method属性被设置成了post,除此之外也没有声明action属性。这意味着当表单提交时,浏览器会收集表单中的所有数据,并以HTTP POST请求的形式将其发送至服务器端,发送路径与渲染表单中的GET请求路径相同,也就是"/design"。
- 因此,在该POST请求的接收端,我们需要有一个控制器处理方法。此时应使用PostMapping来处理POST请求。
@PostMapping
public String processDesign(Taco disign){
...
log.info("Processing design:" + design);
return "redirect:/orders/current";
}
- 如该方法所示,@PostMapping与类级别的@RequestMapping协作,指定processDesign()方法时要处理针对"/design"的POST请求。
- 当表单提交时,表单中的输入域会绑定到Taco对象的属性中,该对象会以参数的形式传递给processDesign()。从这里开始,processDesign()就可以针对Taco对象采取任意操作了。
package tacos;
import lombok.Data;
import java.util.List;
@Data
public class Taco {
private String name;
private List<String> ingredients;
}
- Taco是一个非常简单的Java领域对象,其中包含了几项属性。与Ingredient类似,Taco类也添加了@Data注解,会在编译期自动生成必要的JavaBean方法,所以这些方法在运行期是可用的。在视图的表单内,name输入域只需要捕获一个简单的文本值。因此,Taco的name属性是String类型的。配料的复选框也有文本值,但是用户可能会选择一个或多个,所以他们所绑定的ingredients属性是一个List<String>,能够捕获选中的每种配料。
- 与showDesignForm()方法类似,processDesign()最后也返回了一个String类型的值,代表了一个要展现给用户的视图。但是区别在processDesign()返回的值带有"redirect:“前缀,表明这是一个重定向视图。更具体来讲,表明在processDesign()完成之后,用户的浏览器将会重定向到相对路径”/orders/current"。我们可以很容易的创建这样的控制器:
package tacos.web;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
@Slf4j
@Controller
@RequestMapping("/orders")
public class OrderController {
@GetMapping("/current")
public String orderForm(Model model)
{
model.addAttribute("order", new Order());
return "orderForm";
}
}
- 下面定义orderForm.html的视图:
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Taco Cloud</title>
<link rel="stylesheet" th:href="@{/styles.css}" />
</head>
<body>
<form method="post" th:action="@{/orders}" th:object="${order}">
<h1>Order your taco creations!</h1>
<img src="@{/images/TacoCloud.png"/>
<a th:href="@{/design}" id="another">Design another taco</a><br/>
<div th:if="${#fields.hasErrors()}">
<span class="validationError">
Please correct the problems below and resubmit.
</span>
</div>
<h3>Deliver my taco masterpieces to...</h3>
<label for="name">Name:</label>
<input type="text" th:field="*{name}"/><br/>
<label for="street">Street address:</label>
<input type="text" th:field="*{street}"/><br/>
<label for="city">City:</label>
<input type="text" th:field="*{city}"/><br/>
<label for="state">State:</label>
<input type="text" th:field="*{State}"/><br/>
<label for="zip">Zip code:</label>
<input type="text" th:field="*{zip}"/><br/>
<h3>Here's how I'll pay...</h3>
<label for="ccNumber">Credit Card #: </label>
<input type="text" th:field=*{ccNumber}"><br/>
<label for="ccExpiration">Expiration:</label>
<input type="text" th:field="*{ccExpiration}"/><br/>
<label for="ccCVV">CVV:</label>
<input type="text" th:field="*{ccCVV}"/><br/>
<input type="submit" value="Submit order"/>
</form>
</body>
</html>
- orderForm.html就是典型的HTML/Thymeleaf内容,不需要过多关注。但是,需要注意一点,这里的<form>标签和前面的design视图的<form>标签有所不同,它指定了一个表单的action。如果不指定action,那么表单将会以HTTP POST的形式提交到与展现该表单相同的URL上。在这里,我们明确指出了表单要POST提交到"/orders"。
- 因此,我们需要在OrderController中添加另一个方法,以便于处理针对"/orders"的POST请求。
@PostMapping
public String processOrder(Order order)
{
log.info("Order submitted: "+order);
return "redirect:/";
}
- 下面是Order类,和Taco很相似。
package tacos;
import lombok.Data;
@Data
public class Order {
private String name;
private String street;
private String city;
private String state;
private String zip;
private String ccNumber;
private String ccExpiration;
private String ccCVV;
}
- 现在,我们已经开发了OrderController的订单表单视图,接下来可以尝试运行一下,并最终点击"Submit Your Taco"按钮,我们看日志,会发现有两条日志如下:
2021-05-20 10:45:36.711 INFO 15160 --- [nio-8080-exec-4] tacos.DesignTacoController : Processing design:Taco(name=baby, ingredients=[FLTO, COTO, GRBF, CARN, TMTO, SLSA])
2021-05-20 10:52:15.220 INFO 15160 --- [io-8080-exec-10] tacos.web.OrderController : Order submitted: Order(name=baby, street=123, city=西安, state=陕西, zip=710000, ccNumber=124515, ccExpiration=12312, ccCVV=12)
校验表单输入
- 我们仔细查看上面的日志,会发现尽管processOrder()方法完成了工作并处理了表单提交,但是让一些错误的信息混了进来。Spring支持Java的Bean校验API(Bean Validation API),这样的话,我们能够更容易地声明检验规则,而不必在应用程序代码中显式编写声明逻辑。借助Spring Boot,要在项目中添加校验库,我们甚至不需要做任何特殊操作,这是因为Validation API以及其Hibernate实现将会作为Spring Boot web starter的传递性依赖自动添加到项目中。
- 要在Spring MVC中应用校验,我们需要:
- 在被校验的类上声明校验规则,也就是Taco类。
- 在控制器方法中声明要进行校验,也就是DesignTacoController的processDesign()方法和OrderController的processOrder()方法。
- 修改表单视图以展现校验错误。
- Validation API提供了一些可以添加到领域对象上的注解,以便于声明校验规则。Hibernate又添加了一些校验注解。
声明校验规则
- 对于Taco类来说,要确保name属性不能为空或null,同时希望选中的配料至少要包含一项。下面是更新后的Taco类。
package tacos;
import lombok.Data;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.util.List;
@Data
public class Taco {
@NotNull
@Size(min=5, message="Name must be ad least 5 characters long")
private String name;
@NotNull(message = "You must choose at least 1 ingredient")
private List<String> ingredients;
}
- 对于地址相关的属性,只想确保用户没有提交空白字段,为此应使用@NotBlank注解。
- 但是支付相关的字段就很复杂了,不仅要确保ccNumber属性不为空,还要保证它所包含的值是一个合法的信用卡号码。ccExpiration属性必须符合MM/YY格式(两位月份和年份)。ccCVV属性需要的是一个三位数字。为了实现这种校验,需要其他的一些Java Bean Validation API注解,并结合来自Hibernate的注解。下面是更新后的Order类
package tacos;
import lombok.Data;
import org.hibernate.validator.constraints.CreditCardNumber;
import javax.validation.constraints.Digits;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Pattern;
@Data
public class Order {
@NotBlank(message = "Name is required")
private String name;
@NotBlank(message = "Street is required")
private String street;
@NotBlank(message = "City is required")
private String city;
@NotBlank(message = "State is required")
private String state;
@NotBlank(message = "Zip code is required")
private String zip;
@CreditCardNumber(message = "Not a valid credit card number")
private String ccNumber;
@Pattern(regexp = "^(0[1-9]|1[0-2])([\\/])([1-9][0-9])$",
message = "Must be formatted MM/YY")
private String ccExpiration;
@Digits(integer = 3, fraction = 0, message = "Invalid CVV")
private String ccCVV;
}
- 这些注解都很容易理解,不再讨论
在表单绑定的时候执行校验
- 然后要修改每个控制器,让表单在POST提交至对应的控制器方法时执行对应的校验。
- 要校验提交的Taco,我们需要为DesignTacoController中processDesign()方法的Taco参数添加一个@Valid注解。
@PostMapping
public String processDesign(@Valid @ModelAttribute("design") Taco design, Errors errors){
if(errors.hasErrors())
return "design";
log.info("Processing design:" + design);
return "redirect:/orders/current";
}
- @Valid注解会告诉Spring MVC要对提交的Taco对象进行校验,而校验时机是在它绑定完表单数据之后,调用processDesign()之前。如果存在校验错误,那么这些错误的细节会捕捉到一个Errors对象中并传递给processDesign()。
- 为了对Order对象进行校验。OrderController的processOrder()方法也需要进行类似的变更。
@PostMapping
public String processOrder(@Valid Order order, Errors errors)
{
if(errors.hasErrors())
return "orderForm";
log.info("Order submitted: "+order);
return "redirect:/";
}
展现校验错误
- Thymeleaf提供了便捷访问Errors对象的方法,这就是借助fields及其th:errors属性。举例来说,为了展现信用卡字段的校验错误,可以添加一个<span>元素,该元素会将对错误的引用用到订单的表单模板上。
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Taco Cloud</title>
<link rel="stylesheet" th:href="@{/styles.css}"/>
</head>
<body>
<h1>Design your taco!</h1>
<img th:src="@{/images/TacoCloud.png}"/>
<form method="POST" th:object="${design}">
<span class="validationError"
th:if="${#fields.hasErrors('ingredients')}"
th:errors="*{ingredients}">Ingredient Error</span>
<div class="grid">
<div class="ingredient-group" id="wraps">
<h3>Designate your wrap:</h3>
<!-- tag::designateWrap[] -->
<!--/*@thymesVar id="wrap" type="tacos.Ingredient.Type"*/-->
<div th:each="ingredient : ${wrap}">
<input name="ingredients" type="checkbox" th:value="${ingredient.id}"/>
<span th:text="${ingredient.name}">INGREDIENT</span><br/>
</div>
</div>
<div class="ingredient-group" id="proteins">
<h3>Pick your protein:</h3>
<div th:each="ingredient : ${protein}">
<input name="ingredients" type="checkbox" th:value="${ingredient.id}"/>
<span th:text="${ingredient.name}">INGREDIENT</span><br/>
</div>
</div>
<div class="ingredient-group" id="cheeses">
<h3>Choose your cheese:</h3>
<div th:each="ingredient : ${cheese}">
<input name="ingredients" type="checkbox" th:value="${ingredient.id}"/>
<span th:text="${ingredient.name}">INGREDIENT</span><br/>
</div>
</div>
<div class="ingredient-group" id="veggies">
<h3>Determine your veggies:</h3>
<div th:each="ingredient : ${veggies}">
<input name="ingredients" type="checkbox" th:value="${ingredient.id}"/>
<span th:text="${ingredient.name}">INGREDIENT</span><br/>
</div>
</div>
<div class="ingredient-group" id="sauces">
<h3>Select your sauce:</h3>
<div th:each="ingredient : ${sauce}">
<input name="ingredients" type="checkbox" th:value="${ingredient.id}"/>
<span th:text="${ingredient.name}">INGREDIENT</span><br/>
</div>
</div>
</div>
<div>
<h3>Name your taco creation:</h3>
<input type="text" th:field="*{name}"/>
<span class="validationError"
th:if="${#fields.hasErrors('name')}"
th:errors="*{name}">Name Error</span>
<br/>
<button>Submit your taco</button>
</div>
</form>
</body>
</html>
使用视图控制器
- 到目前为止,我们已经为Taco Cloud应用编写了三个控制器。尽管这三个控制器服务于应用程序的不同功能,但是他们基本上都遵循相同的编程模型。
- 都使用了@Controller注解,表明它们是控制器类,并且应该被Spring的组件扫描功能自动发现并初始化为Spring应用上下文中的bean;
- 除了HomeController之外,其他的控制器都在类级别使用了@RequestMapping注解,据此定义该控制器所处理的基本请求模式。
- 都有一个或多个带有@GetMapping或@PostMapping注解的方法,指明了该由哪个方法来处理某种类型的请求。
- 我们所编写的大部分控制器都将遵循这个模式。但是如果一个控制器非常简单,不需要填充模型或处理输入(也就是HomeController),那么还有一种方式可以定义控制器。也就是只将请求转发到视图而不做任何其他事情的控制器:
package tacos.web;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("home");
}
}
- WebConfig实现了WebMvcConfigurer接口。WebMvcConfigurer定义了多个方法来配置Spring MVC。尽管只是一个接口,但是它提供了所有方法的默认实现,只需要覆盖所需的方法即可。
- addViewControllers()方法会接受一个ViewControllerRegistry对象,可以使用它来注册一个或多个视图控制器。在这里,我们调用了registry的addviewController()方法,将"/“传递进去,视图控制器将会针对该路径执行GET请求。会返回ViewControllerRegistration对象,我们马上就基于该对象调用setViewName()方法,来指明当请求”/"的时候,转发到"home"视图上。
- 这样的话,我们就可以删除HomeController了,应用的功能和之前完全一样。唯一要注意的是,我们要找到HomeControllerTest类,从@WebMvcTest注解中移除对HomeController的引用。
- 所有的配置类都可以实现WebMvcConfigurer接口并覆盖addViewController方法。举例来说我们可以将相同的视图控制器声明添加到TacoCloudApplication引导类中
package tacos;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@SpringBootApplication
public class TacoCloudApplication implements WebMvcConfigurer {
public static void main(String[] args) {
SpringApplication.run(TacoCloudApplication.class, args);
}
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("home");
}
}
选择视图模板库
- 大多数情况下,视图模板库的选择完全取决于个人喜好。Spring非常灵活,能够支持很多常见的模板方案。除了个别情况之外,所选择的模板库本身甚至不知道它在与Spring合作。
- 下面是Spring Boot自动配置功能所支持的模板方案。
模板 | Spring Boot starter依赖 |
---|---|
FreeMaker | spring-boot-starter-freemaker |
Groovy Templates | spring-boot-starter-groovy-templates |
JSP | 无(由Tomcat或Jetty提供) |
Mustache | spring-boot-starter-mustache |
Thymeleaf | spring-boot-starter-thymeleaf |
- 通常来讲,只需要选择想要的视图模板库,将其作为依赖项添加到构建文件中,然后就可以在"/templates"目录下编写模板了。Spring Boot会探测到你所选择的模板库,并自动配置为Spring MVC控制器生成视图所需的各种组件。