1.創建工程
使用idea可以快速創建SpringBoot的工程


這里選擇常用的類庫,SpringBoot將各種框架類庫都進行了封裝,可以減少pom文件中的引用配置:

比如Spring和Mybatis整合的時候,傳統Spring項目中需要引入:
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.1</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.1</version>
</dependency>
而在SpringBoot中引入的是:
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.1</version>
</dependency>
可以看到這個類庫中除了mybatis和mybatis-spring之外,還有spring-boot的東西

完整的pom.xml如下:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.10.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--使用jsp頁面-->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
</dependency>
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
</dependencies>
<build>
<finalName>boot</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.xml</include>
<include>**/*.properties</include>
</includes>
</resource>
</resources>
</build>
完整的工程路徑如下:

2. 實體類和DAO
public class Dept {
private Integer id;
private String name;
//getter/setter方法略
}
public interface DeptDAO {
//查詢列表,演示使用傳統的mapper映射文件
List<Dept> getDeltList();
//插入,演示使用注解編寫sql,省略xml配置
@Insert("insert into DEPT(NAME) values(#{name})")
@Options(useGeneratedKeys = true, keyProperty = "id", keyColumn = "ID")
void addDept(String name);
}
DeptMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.test.springboot.ssm.dao.DeptDAO">
<resultMap id="deptMap" type="Dept">
<id property="id" column="ID"/>
<result property="name" column="NAME"/>
</resultMap>
<select id="getDeltList" resultMap="deptMap">
select ID,NAME from DEPT
</select>
</mapper>
3.Service
public interface DeptService {
List<Dept> getDeltList();
void addDept(String name);
}
@Service
public class DeptServiceImpl implements DeptService {
@Autowired
private DeptDAO deptDAO;
@Override
public List<Dept> getDeltList() {
return deptDAO.getDeltList();
}
@Override
public void addDept(String name) {
deptDAO.addDept(name);
}
}
4. Controller和頁面
@Controller
public class DeptController {
@Autowired
private DeptService deptService;
@RequestMapping("list.html")
public ModelAndView list() {
List<Dept> deptList = deptService.getDeltList();
return new ModelAndView("list", "deptList", deptList);
}
@RequestMapping("add.html")
public String add(String name) {
deptService.addDept(name);
//添加成功后重定向到列表頁
return "redirect:list.html";
}
}
add.jsp
<form action="/add.html" method="post">
部門名:<input type="text" name="name"/><br/>
<input type="submit" value="add"/>
</form>
list.jsp
<c:forEach items="${deptList}" var="dept">
${dept.id}-${dept.name}<br/>
</c:forEach>
5.啟動類
到目前為止,項目與傳統的spring沒有任何區別。
傳統spring項目中需要增加下面兩個配置文件,而SpringBoot中沒有配置文件:
傳統Spring項目中有以下文件:
spring-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.2.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">
<!--掃描@Service注解-->
<context:component-scan base-package="com.test.springboot.ssm.service">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/>
</context:component-scan>
<!--讀取配置文件-->
<context:property-placeholder location="classpath:db.properties" ignore-unresolvable="true"/>
<!--從配置文件中獲取數據源-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!--spring管理session工廠-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="mapperLocations" value="classpath:com/test/springboot/ssm/dao/mapper/*.xml"/>
<!--配置實體類別名別名-->
<property name="typeAliasesPackage" value="com.test.springboot.ssm.pojo"/>
</bean>
<!--掃描所有mybatis的dao接口,生成代理實現類-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.test.springboot.ssm.dao"/>
</bean>
<!-- 配置事務管理器 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--事務增強-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!-- 傳播行為,匹配的是方法名 -->
<tx:method name="add*" rollback-for="Exception"/>
<tx:method name="delete*" rollback-for="Exception"/>
<tx:method name="update*" rollback-for="Exception"/>
<tx:method name="get*" propagation="SUPPORTS" read-only="true"/>
<tx:method name="do*" rollback-for="Exception"/>
</tx:attributes>
</tx:advice>
<!-- 通過AOP配置提供事務增強,讓service包下所有Bean的所有方法擁有事務 -->
<aop:config>
<aop:pointcut id="serviceMethod"
expression="execution(* com.test.springboot.ssm..*(..))"/>
<aop:advisor pointcut-ref="serviceMethod" advice-ref="txAdvice"/>
</aop:config>
</beans>
springMVC-servlet.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.2.xsd">
<mvc:annotation-driven/>
<!--掃描Controller所在的包-->
<context:component-scan base-package="com.ssm.blog.controller">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!-- 配置視圖解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/"></property><!--前綴-->
<property name="suffix" value=".jsp"></property><!--后綴-->
</bean>
</beans>
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-config.xml</param-value>
</context-param>
<!--配置listener,在啟動Web容器的時候加載Spring的配置-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!--配置DispatcherServlet -->
<servlet>
<servlet-name>springMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>springMVC</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>
</web-app>
而SpringBoot中不需要這三個配置文件,寫一個啟動類,運行main方法即可:
@SpringBootApplication
@EnableTransactionManagement//開啟事務管理
@ComponentScan("com.test.springboot.ssm")//掃描注解元素
@MapperScan("com.test.springboot.ssm.dao")//Mybatis的DAO所在包
public class SpringbootSsmApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootSsmApplication.class, args);
}
public static final String transactionExecution = "execution (* com.test.springboot.service..*(..))";
@Autowired
private DataSource dataSource;
//聲明式事務
@Bean
public DefaultPointcutAdvisor defaultPointcutAdvisor() {
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression(transactionExecution);
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor();
advisor.setPointcut(pointcut);
Properties attributes = new Properties();
attributes.setProperty("get*", "PROPAGATION_REQUIRED,-Exception");
attributes.setProperty("add*", "PROPAGATION_REQUIRED,-Exception");
attributes.setProperty("update*", "PROPAGATION_REQUIRED,-Exception");
attributes.setProperty("delete*", "PROPAGATION_REQUIRED,-Exception");
TransactionInterceptor txAdvice = new TransactionInterceptor(new DataSourceTransactionManager(dataSource), attributes);
advisor.setAdvice(txAdvice);
return advisor;
}
}
數據庫等配置信息放到application.properties中
#數據源的基本信息 spring.datasource.url = jdbc:mysql://localhost:3306/test?characterEncoding=utf-8 spring.datasource.username = root spring.datasource.password = spring.datasource.driverClassName = com.mysql.jdbc.Driver #mybatis中mapper文件的路徑 mybatis.mapper-locations=classpath*:com/test/springboot/ssm/dao/mappers/*.xml #起別名。可省略寫mybatis的xml中的resultType的全路徑 mybatis.type-aliases-package=com.test.springboot.ssm.pojo #springMVC中的視圖信息,響應前綴 spring.mvc.view.prefix=/ # 響應頁面默認后綴 spring.mvc.view.suffix=.jsp #DispatcherServlet中響應的url-pattern server.sevlet-path=*.html server.context-path=/boot #logging.level.root=debug logging.level.com.test.springboot.ssm.dao=trace
上面的程序只要啟動main方法就可以訪問了。
另外,如果需要打包發布到tomcat,需要再配置一個ServletInitializer,否則tomcat啟動后會出現404。
public class ServletInitializer extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(SpringbootSsmApplication.class);
}
}
5. 啟動原理解析
任何一個SpringBoot程序都有一個啟動類:
@SpringBootApplication
public class StartApplication {
public static void main(String[] args) {
SpringApplication.run(StartApplication.class, args);
}
}
啟動類中包含@SpringBootApplication注解和SpringApplication.run()方法
5.1@SpringBootApplication
@SpringBootApplication是一個組合注解,除了基本的原信息標注以外,重要的注解有三個:
@Configuration
@EnableAutoConfiguration
@ComponentScan
如下代碼等同於使用@SpringBootApplication注解
@Configuration
@EnableAutoConfiguration
@ComponentScan
public class StartApplication {
public static void main(String[] args) {
SpringApplication.run(StartApplication.class, args);
}
}
每次寫三個注解比較繁瑣,所以使用@SpringBootApplication更方便。
5.1.1 @Configuration
簡單的說,SpringBoot中使用一個@Configuration注解的類代替xml配置文件。
如spring-config.xml如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
">
<!--定義bean-->
</beans>
SpringBoot中寫成:
import org.springframework.context.annotation.Configuration;
@Configuration
public class SpringConfig {
}
如果定義一個bean,xml中寫成:
<bean id="dept" class="com.spring.test.springboot.pojo.Dept">
<property name="id" value="1"/>
</bean>
<bean id="employee" class="com.spring.test.springboot.pojo.Employee">
<property name="name" value="tom"/>
<property name="dept" ref="dept"/>
</bean>
SpringBoot中寫成:
@Bean
public Dept dept() {
Dept dept = new Dept();
dept.setId(1);
return dept;
}
@Bean
public Employee employee() {
Employee employee = new Employee();
employee.setName("tom");
employee.setDept(dept());//注入依賴對象直接調用@Bean注解的方法
return employee;
}
SpringBoot中使用@Bean標注一個方法,該方法的方法名將默認成bean的id。注意@Configuration的類要被@ComponentScan掃描到。
5.1.2 @ComponentScan
@ComponentScan 自動掃描並加載符合規則的組件。可以通過basePackages指定要掃描的包。如果不指定賽秒范圍,SpringBoot默認會從生命@ComponentScan所在類的包進行掃描。
@ComponentScan(basePackages = "com.spring.test.springboot.controller",includeFilters = {@ComponentScan.Filter(type= FilterType.ANNOTATION,value=Controller.class)})
等同於
<context:component-scan base-package="com.spring.test.springboot.controller">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
5.5.3 @EnableAutoConfiguration
這個注解的作用是將所有符合自動配置條件的bean自動加載到IoC容器。比如我們的項目引入了spring-boot-starter-web依賴,springboot 會自動幫我們配置 tomcat 和 springmvc。@EnableAutoConfigutation中@Import了EnableAutoConfigurationImportSelector,EnableAutoConfigurationImportSelector類使用了Spring Core包的SpringFactoriesLoader類的loadFactoryNamesof()方法。 SpringFactoriesLoader會查詢META-INF/spring.factories文件中包含的JAR文件。 當找到spring.factories文件后,SpringFactoriesLoader將查詢配置文件命名的屬性。spring.factories文件,內容如下:

5.2 SpringApplication
SpringApplication的run方法的實現是我們本次旅程的主要線路,該方法的主要流程大體可以歸納如下:
1) 如果我們使用的是SpringApplication的靜態run方法,那么,這個方法里面首先要創建一個SpringApplication對象實例,然后調用這個創建好的SpringApplication的實例方法。在SpringApplication實例初始化的時候,它會提前做幾件事情:
a) 根據classpath里面是否存在某個特征類(org.springframework.web.context.ConfigurableWebApplicationContext)來決定是否應該創建一個為Web應用使用的ApplicationContext類型。
b) 使用SpringFactoriesLoader在應用的classpath中查找並加載所有可用的ApplicationContextInitializer。
c) 使用SpringFactoriesLoader在應用的classpath中查找並加載所有可用的ApplicationListener。
d) 推斷並設置main方法的定義類。
2) SpringApplication實例初始化完成並且完成設置后,就開始執行run方法的邏輯了,方法執行伊始,首先遍歷執行所有通過SpringFactoriesLoader可以查找到並加載的SpringApplicationRunListener。調用它們的started()方法,告訴這些SpringApplicationRunListener,“嘿,SpringBoot應用要開始執行咯!”。
3) 創建並配置當前Spring Boot應用將要使用的Environment(包括配置要使用的PropertySource以及Profile)。
4) 遍歷調用所有SpringApplicationRunListener的environmentPrepared()的方法,告訴他們:“當前SpringBoot應用使用的Environment准備好了咯!”。
5) 如果SpringApplication的showBanner屬性被設置為true,則打印banner。
6) 根據用戶是否明確設置了applicationContextClass類型以及初始化階段的推斷結果,決定該為當前SpringBoot應用創建什么類型的ApplicationContext並創建完成,然后根據條件決定是否添加ShutdownHook,決定是否使用自定義的BeanNameGenerator,決定是否使用自定義的ResourceLoader,當然,最重要的,將之前准備好的Environment設置給創建好的ApplicationContext使用。
7) ApplicationContext創建好之后,SpringApplication會再次借助Spring-FactoriesLoader,查找並加載classpath中所有可用的ApplicationContext-Initializer,然后遍歷調用這些ApplicationContextInitializer的initialize(applicationContext)方法來對已經創建好的ApplicationContext進行進一步的處理。
8) 遍歷調用所有SpringApplicationRunListener的contextPrepared()方法。
9) 最核心的一步,將之前通過@EnableAutoConfiguration獲取的所有配置以及其他形式的IoC容器配置加載到已經准備完畢的ApplicationContext。
10) 遍歷調用所有SpringApplicationRunListener的contextLoaded()方法。
11) 調用ApplicationContext的refresh()方法,完成IoC容器可用的最后一道工序。
12) 查找當前ApplicationContext中是否注冊有CommandLineRunner,如果有,則遍歷執行它們。
13) 正常情況下,遍歷執行SpringApplicationRunListener的finished()方法、(如果整個過程出現異常,則依然調用所有SpringApplicationRunListener的finished()方法,只不過這種情況下會將異常信息一並傳入處理)
去除事件通知點后,整個流程如下:

6. Thymeleaf
SpringBoot官方不推薦使用JSP,官方推薦使用Thymeleaf。
Thymeleaf是一款用於渲染XML/XHTML/HTML5內容的模板引擎。類似JSP,Velocity,FreeMaker等,它也可以輕易的與Spring MVC等Web框架進行集成作為Web應用的模板引擎。與其它模板引擎相比,Thymeleaf最大的特點是能夠直接在瀏覽器中打開並正確顯示模板頁面,而不需要啟動整個Web應用。
6.1 搭建示例工程
引入thymeleaf的包:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
在application.properties文件中配置thymeleaf的視圖解析:
spring.thymeleaf.content-type=text/html spring.thymeleaf.mode =LEGACYHTML5 #開發時關閉緩存,不然沒法看到實時頁面 spring.thymeleaf.cache=false #配置靜態資源路徑 spring.mvc.static-path-pattern=/static/**
controller中的代碼和以前的項目一樣:
@RequestMapping("hello")
public String helloWorld(Model model) {
//向頁面傳值
model.addAttribute("welcome", "hello thymeleaf");
return "hello";
}
頁面寫在/resources/templates下

頁面hello.html,頁面的文件名與controller中方法的返回值一致。注意頁面的<html>標簽中有一個<html xmlns:th="http://www.thymeleaf.org">
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Title</title>
</head>
<body>
<p th:text="${welcome}"></p>
</body>
</html>
頁面中所有動態的內容都使用“th:”前綴。
並且在thymeleaf的頁面中,html語法要求很嚴格,比如標簽必須閉合。如果要在解析時自動進行標簽補全,需要引入jar包:
<dependency>
<groupId>net.sourceforge.nekohtml</groupId>
<artifactId>nekohtml</artifactId>
<version>1.9.22</version>
</dependency>
6.2 基礎語法
spring-boot很多配置都有默認配置,比如默認頁面映射路徑為
classpath:/templates/*.html
同樣靜態文件路徑為
classpath:/static/

首先頁面的<html>標簽要改寫:
<html xmlns:th="http://www.thymeleaf.org">
6.2.1 獲取變量值
thymeleaf通過${變量名.屬性名}來獲取屬性值,這個語法和EL表達式一樣。
頁面中所有動態的內容都使用“th:”前綴,並且要寫在標簽中。
<p th:text=${message}>this is tag p</p>
如果直接訪問靜態頁面,會顯示“this is tag p”
如果訪問動態內容,那么${message}的值會替換掉原來<p>標簽中的靜態內容。
常見頁面操作如下:
@RequestMapping("hello")
public String helloWorld(Model model) {
//向頁面傳值,普通文本
model.addAttribute("text", "hello thymeleaf");
//html轉義文本
model.addAttribute("htmlText", "<h1>html</h1>");
model.addAttribute("ahref", "test");
List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
model.addAttribute("list", list);
List<Dept> deptList = new ArrayList<>();
deptList.add(new Dept(1, "技術部"));
deptList.add(new Dept(2, "測試部"));
deptList.add(new Dept(3, "行政部"));
model.addAttribute("deptList", deptList);
return "hello";
}
<p th:text="${text}">我是文本</p>
<p th:utext="${htmlText}">我是轉義文本</p>
<p><a th:href="@{{ahref}?pa={text}(ahref=${ahref},text=${text})}">我是a標簽</a></p>
我是表格<br/>
<table border="1">
<tr th:each="dept:${deptList}">
<td th:text="${dept.id}">id</td>
<td th:text="${dept.name}">name</td>
</tr>
</table>
我是下拉框
<select >
<option th:each="dept:${deptList}" th:value="${dept.id}" th:text="${dept.name}" th:selected="${dept.id}==${param.id[0]}"></option>
</select><br/>
<input th:value="${text}">
<script th:src="@{static/test.js}" type="text/javascript"></script>
6.2.2 條件判斷
<div th:if="${ahref == 'test'}">xxxxxxx</div>
