必须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配置中的
* org.springframework.web.ContextLoaderListener 差异:
* 注解配置需要添加的是配置类
* 文件配置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的配置…org.springframework.web.servlet.DispatcherServlet …
* 配置类如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 // 相当于 ,启用注解驱动的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;
}
}
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
Insert title here
Welcome to Thymeleaf
register
"@{}"用来计算相对于当前URL的路径,相当于Spring
registerForm.html
Insert title here
${}为变量表达式,一般是对象图导航语言(OGNL),在使用Spring时则是SpEL表达式,在该例中th:object的设置会解析key为spitter的model属性,所以在跳转到注册页前需
把spitter添加到model中(如上红字代码),当提交时时spitter中属性进行了重新填充所以即使错误回调表单也不会为空。
*{}为选择表达式,变量表达式是基于整个SpEL上下文计算的,而选择表达式是基于某一个选中对象计算的,本例中选中对象是th:object属性所设置的对象
th:class根据给定的表达式计算渲染为哪个class属性。
#fields为表单中的所有域,可通过该属性对表单中的各种属性进行相应的操作:
如:
判断lastName是否不符合格式(Spitter.java中进行了格式设置),若是则输出lastName格式错误的原因,也可用通配符代替"lastName",如
把hibernate-validator中的ValidationMessages资源文件拷贝到根目录下即可显示错误信息。