必須jar包:Spring相關jar包、attoparser-2.0.2.RELEASE.jar、thymeleaf-3.0.3.RELEASE.jar、thymeleaf-spring4-3.0.3.RELEASE.jar(該包爲thymeleaf3.03與spring4的整合包,若版本不同,可能會因版本差異出現異常,最後下載對應版本)、unbescape-1.1.4.RELEASE.jar,Thymeleaf所需jar包皆已打包到個人博客資源,有需要的可以去下載,其它的如hibernate-validator等請自行下載(因主題是Thymeleaf),也可到http://search.maven.org/查找自己對應版本的jar包。
該實例改自《Spring實戰 第四版》,改因是一些版本的差異問題,Thymeleaf3.0版本之後改動了不少地方
Thymeleaf模板視圖解析器配置步驟:模板解析器->模板引擎->視圖解析器,註釋掉的代碼爲個人JSP、Tiles視圖解析器的測試代碼,與本例無關。
Spitter.java(需加入hibernate-validator-x.x.x.Final.jar、jboss-logging-x.x.x.jar、slf4j-api-1.x.x.jar、commons-lang3-3.x.jar)
package spittr.vo;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.hibernate.validator.constraints.Length;
import org.hibernate.validator.constraints.NotEmpty;
public class Spitter {
private Long id;
@NotEmpty
@Length(min=5,max=15,message="{javax.validation.constraints.Size.message}")
private String username;
@NotEmpty
@Length(min=5,max=15,message="{javax.validation.constraints.Size.message}")
private String password;
@NotEmpty
@Length(min=2,max=30,message="{javax.validation.constraints.Size.message}")
private String firstName;
@NotEmpty
@Length(min=2,max=30,message="{javax.validation.constraints.Size.message}")
private String lastName;
public Spitter() {
}
public Spitter(String username, String password, String firstName, String lastName) {
this.username = username;
this.password = password;
this.firstName = firstName;
this.lastName = lastName;
}
public Spitter(Long id, String username, String password, String firstName, String lastName) {
this.id = id;
this.username = username;
this.password = password;
this.firstName = firstName;
this.lastName = lastName;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
@Override
public int hashCode() {
return HashCodeBuilder.reflectionHashCode(this, "firstName", "lastName", "username", "password");
}
@Override
public boolean equals(Object obj) {
return EqualsBuilder.reflectionEquals(this, obj, "firstName", "lastName", "username", "password");
}
}
SpittrWebAppInitializer.java
package spittr.config;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
/*
* 在Servlet3.0以上的環境中,容器會在類路徑中查找實現javax.servlet.ServletContainerInitializer接口的類,如果發現則用它來配置
* Servlet容器,Spring提供了這個接口的實現名爲SpringServletContainerInitializer,這個類反過來又會查找實現了
* WebApplicationInitializer的類並把配置任務交給它們來完成,AbstractAnnotationConfigDispatcherServletInitializer的祖先類已
* 對該接口進行了實現
*/
public class SpittrWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
/**
* 該方法用於配置ContextLoaderListener創建的應用上下文的bean,相當於web.xml配置中的
* <listener>org.springframework.web.ContextLoaderListener</listener> 差異:
* 註解配置需要添加的是配置類<br>
* 文件配置ContextLoaderListener在創建時自動查找WEB-INF下的applicationContext.xml文件,當文件不止1個時需通過設置
* 上下文參數(context-param)配置contextConfigLocation的值
*
* @return 帶有@Configuration註解的類(這些類將會用來配置ContextLoaderListener創建的應用上下文的bean)
*/
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[] { RootConfig.class };
}
/**
* 該方法用於配置DispatcherServlet所需bean,配置類一般用於生成控制層的bean(因Controller中一般包含對參數的設置及數據的返回)
* 相當於web.xml對Spring
* MVC的配置…<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>…<br>
* 配置類如WebConfig.class相當於DispatcherServlet中contetConfigLocation參數對應的配置文件
*
* @return 帶有@Configuration註解的類(這些類將會用來配置DispatcherServlet應用上下文中的bean)
*/
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[] { WebConfig.class };
}
/**
* DispatcherServlet默認映射路徑
*/
@Override
protected String[] getServletMappings() {
return new String[] { ("/") };
}
}
WebConfig.java
package spittr.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.context.ContextLoader;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.spring4.SpringTemplateEngine;
import org.thymeleaf.spring4.view.ThymeleafViewResolver;
import org.thymeleaf.templatemode.TemplateMode;
import org.thymeleaf.templateresolver.ITemplateResolver;
import org.thymeleaf.templateresolver.ServletContextTemplateResolver;
/**
*
* @author Wilson
* 該類用於掃描生成Web組件所需的bean,通過實現WebMvcConfigurerAdapter對SpringMVC的配置根據自己需求進行自定義
*/
@Configuration
@EnableWebMvc // 相當於<mvc:annotation-driver/>,啓用註解驅動的Spring MVC,使@RequestParam、@RequestMapping等註解可以被識別
@ComponentScan(basePackages = "spittr.web")
public class WebConfig extends WebMvcConfigurerAdapter {
// JSP視圖解析器
/*
* @Bean public ViewResolver internalViewResolver(){
* InternalResourceViewResolver viewResolver = new
* InternalResourceViewResolver("/WEB-INF/views/", ".jsp");
* viewResolver.setExposeContextBeansAsAttributes(true); return
* viewResolver; }
*/
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
/*@Bean
public MessageSource messageSource() {
ResourceBundleMessageSource message = new ResourceBundleMessageSource();
message.setBasename("welcome");
message.setUseCodeAsDefaultMessage(true);
return message;
}*/
// tiles文件解析器
/*
* @Bean public TilesConfigurer tilesConfigurer() { TilesConfigurer tiles =
* new TilesConfigurer(); tiles.setDefinitions(new String[] {
* "/WEB-INF/layout/tiles.xml" }); // 指定tiles文件位置
* tiles.setCheckRefresh(true); return tiles; }
*
* // Apache Tiles視圖解析器
*
* @Bean public ViewResolver tilesViewResolver() { return new
* TilesViewResolver(); }
*/
@Bean // 配置生成模板解析器
public ITemplateResolver templateResolver() {
WebApplicationContext webApplicationContext = ContextLoader.getCurrentWebApplicationContext();
// ServletContextTemplateResolver需要一個ServletContext作爲構造參數,可通過WebApplicationContext 的方法獲得
ServletContextTemplateResolver templateResolver = new ServletContextTemplateResolver(
webApplicationContext.getServletContext());
templateResolver.setPrefix("/WEB-INF/thymeleaf/");
templateResolver.setSuffix(".html");
// templateResolver.setCharacterEncoding("UTF-8");
// 設置模板模式,也可用字符串"HTML"代替,此處不建議使用HTML5,原因看下圖源碼
templateResolver.setTemplateMode(TemplateMode.HTML);
return templateResolver;
}
@Bean // 生成模板引擎併爲模板引擎注入模板解析器
public TemplateEngine templateEngine(ITemplateResolver templateResolver) {
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.setTemplateResolver(templateResolver);
return templateEngine;
}
@Bean // 生成視圖解析器並未解析器注入模板引擎
public ViewResolver viewResolver(TemplateEngine templateEngine) {
ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
viewResolver.setContentType("text/html; charset=utf-8");
viewResolver.setTemplateEngine(templateEngine);
return viewResolver;
}
}
圖一:
實現了該接口(WebMvcConfigurer)的@EnableWebMvc註解配置類將被回調和獲得定製默認配置的能力(個人覺得譯爲“計劃”不太合適),
繼承WebMvcConfigurerAdapter的類會被作爲提供所有接口方法的根實現的類,而WebMvcConfigurerAdapter中所有方法都爲空,所以可以根據
個人需求進行覆寫。若沒有配置視圖解析器,Spring會默認使用BeanNameViewResolver,這個視圖解析器會查找ID與視圖名匹配的bean,
並且查找的bean要實現View接口。但問題是雖然我進行了視圖解析器的配置,但我不知道它是如何作爲默認解析器的,因WebMvcConfigurerAdapter中
沒有任何關於默認解析器的配置操作,個人臆測是掃描到WebMvcConfigurer的實現類中包含ViewResolver的bean所以進行優先裝配,但還沒發現源碼。
圖二:
TemplateMode中的HTML5將在3.1版本中被移除,所以若有設置templateResolver.setTemplateMode(TemplateMode.HTML5)
及templateResolver.setTemplateMode("HTML5")習慣的都有必要改一下了
HomeController.java
package spittr.web;
import javax.validation.Valid;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import spittr.vo.Spitter;
@Controller
public class HomeController {
@RequestMapping("/")
public String home() {
return "home";
}
@RequestMapping("/toRegister")
public String toRegister(Model model) {
model.addAttribute(new Spitter()); //若要返回JSON數據可添加@ResponseBody註解,返回類型改爲類,如return new Spitter();
return "registerForm";
}
@RequestMapping(value = "/register", method = RequestMethod.POST)
public String register(@Valid Spitter spitter, BindingResult bindResult, Model model) {
if (bindResult.hasErrors()) {
System.out.println("錯誤數目:" + bindResult.getErrorCount());
model.addAttribute(spitter);
return "registerForm";
}
return "redirect:/cal/" + spitter.getId();
}
/*@RequestMapping(path = { "/cal/{spitterId}" }, method = RequestMethod.GET)
public String pathId(@PathVariable int spitterId, Model model) {
model.addAttribute("num", spitterId);
return "count";
}*/
}
home.html
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org"><!-- 聲明Thymeleaf的命名空間 -->
<head>
<title>Insert title here</title>
</head>
<body>
Welcome to Thymeleaf
<br/>
<a th:href="@{/toRegister}">register</a>
</body>
</html>
"@{}"用來計算相對於當前URL的路徑,相當於Spring<s:url>與JSTL<c:url>
registerForm.html
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8" http-equiv="charset" content="utf-8"></meta>
<title>Insert title here</title>
<style type="text/css">
label.error {
color: red;
}
input.error {
background-color: #ffcccc;
}
</style>
</head>
<body>
<form id="form-1" method="post" th:object="${spitter}" th:action="register">
<label th:class="${#fields.hasErrors('id')}?'error'">id:</label>
<input th:field="*{id}" th:class="${#fields.hasErrors('id')}?'error'" placeholder="id" type="text"></input>
<span th:if="${#fields.hasErrors('id')}">
<span th:text="${#fields.errors('id')}"></span>
</span>
<br>
<label th:class="${#fields.hasErrors('username')}?'error'">username:</label>
<input th:class="${#fields.hasErrors('username')}?'error'" placeholder="username" type="text"
th:field="*{username}"></input>
<span th:if="${#fields.hasErrors('username')}">
<span th:text="${#fields.errors('username')}"></span>
</span>
<br>
<label th:class="${#fields.hasErrors('password')}?'error'">password:</label>
<input th:class="${#fields.hasErrors('password')}?'error'" th:field="*{password}" placeholder="password"
type="text"></input>
<span th:if="${#fields.hasErrors('password')}">
<span th:text="${#fields.errors('password')}"></span>
</span>
<br>
<label th:class="${#fields.hasErrors('firstName')}?'error'">firstName:</label>
<input th:class="${#fields.hasErrors('firstName')}?'error'" th:field="*{firstName}" type="text"
placeholder="firstName"/></input>
<span th:if="${#fields.hasErrors('firstName')}">
<span th:text="${#fields.errors('firstName')}"></span>
</span>
<br>
<label th:class="${#fields.hasErrors('lastName')}?'error'">lastName:</label>
<input th:class="${#fields.hasErrors('lastName')}?'error'" placeholder="lastName" th:field="*{lastName}"
type="text"></input>
<span th:if="${#fields.hasErrors('lastName')}">
<span th:text="${#fields.errors('lastName')}"></span>
</span>
<br>
<input type="submit"></input>
</form>
</body>
</html>
${}爲變量表達式,一般是對象圖導航語言(OGNL),在使用Spring時則是SpEL表達式,在該例中th:object的設置會解析key爲spitter的model屬性,所以在跳轉到註冊頁前需把spitter添加到model中(如上紅字代碼),當提交時時spitter中屬性進行了重新填充所以即使錯誤回調錶單也不會爲空。
*{}爲選擇表達式,變量表達式是基於整個SpEL上下文計算的,而選擇表達式是基於某一個選中對象計算的,本例中選中對象是th:object屬性所設置的對象
th:class根據給定的表達式計算渲染爲哪個class屬性。
#fields爲表單中的所有域,可通過該屬性對錶單中的各種屬性進行相應的操作:
如:<span th:if="${#fields.hasErrors('lastName')}">
<span th:text="${#fields.errors('lastName')}"></span>
</span>
判斷lastName是否不符合格式(Spitter.java中進行了格式設置),若是則輸出lastName格式錯誤的原因,也可用通配符代替"lastName",如
<div class="errors" th:if="${#fields.hasErrors('*')}">
<ul>
<li th:each="err:${#fields.errors('*')}>
<span th:text="${err}"></span>
</li>
</ul>
</div>迭代輸出錯誤信息
更多thymeleaf標籤屬性:http://www.cnblogs.com/hjwublog/p/5051732.html
由於html文件是xhtml標註,所以不加</***>都會有提示但不礙事
包目錄(紅色框內爲本例所用文件)
把hibernate-validator中的ValidationMessages資源文件拷貝到根目錄下即可顯示錯誤信息。