详解注解配置整合Spring MVC+Thymeleaf整合实例

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/z28126308/article/details/54429853

必须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资源文件拷贝到根目录下即可显示错误信息。















發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章