詳解註解配置整合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資源文件拷貝到根目錄下即可顯示錯誤信息。















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