介绍 SpringBoot并不推荐使用jsp Thymeleaf 是一个跟 Velocity、FreeMarker 相似的模板引擎,它能够彻底替代 JSP 特色 动静结合 Thymeleaf 在有网络和无网络的环境下皆可运行 它可让美工在浏览器查看页面的静态效果,也可让程序员在服务器查看带数据的动态页面效果 这是因为它支持 html 原型,而后在 html 标签里增长额外的属性来达到模板+数据的展现方式 浏览器解释 html 时会忽略未定义的标签属性,因此 thymeleaf 的模板能够静态地运行 当有数据返回到页面时,Thymeleaf 标签会动态地替换掉静态内容,使页面动态显示 开箱即用 它提供标准和spring标准两种方言,能够直接套用模板实现JSTL、 OGNL表达式效果,避免天天套模板、该jstl、改标签的困扰。 同时开发人员也能够扩展和建立自定义的方言。 多方言支持 Thymeleaf 提供spring标准方言和一个与 SpringMVC 完美集成的可选模块,能够快速的实现表单绑定、属性编辑器、国际化等功能。 与SpringBoot完美整合 与SpringBoot完美整合,SpringBoot提供了Thymeleaf的默认配置 而且为Thymeleaf设置了视图解析器,咱们能够像之前操做jsp同样来操做Thymeleaf。
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency>
SpringBoot会自动为Thymeleaf注册一个视图解析器ThymeleafViewResolvercss
还配置了模板文件(html)的位置,与jsp相似的前缀+ 视图名 + 后缀风格:html
与解析JSP的InternalViewResolver相似,Thymeleaf也会根据前缀和后缀来肯定模板文件的位置:java
@ConfigurationProperties(prefix="spring.thymeleaf") public class ThymeleafProperties{ private static final Charset DEFAULT_ENCODING=StandardCharsets.UTF_8; public static final String DEFAULT_PREFIX="classpath:/templates/"; public static final String DERAULT_SUEEIX=". html"; ...... }
在配置文件中配置缓存,编码程序员
spring: thymeleaf: cache: false mode: HTML5 encoding: UTF-8
引入名称空间 <html lang="en" xmlns:th="http://www.thymeleaf.org"> 表达式 ${}:变量表达式 *{} :选择变量表达式,取出hero对象中的属性 <div th:object="${hero}"> <p th:text="*{username}"></p> <p th:text="*{phone}"></p> <p th:text="*{id}"></p> <p th:text="${hero.username}"></p> </div> #{...} : Message 表达式 URL 绝对网址 绝对URL用于建立到其余服务器的连接,它们须要指定一个协议名称(http://或https://)开头 <a th:href="@{https://www.itlike.com/}"> 上下文相关URL 与Web应用程序根相关联URL <a th:href="@{/hello}">跳转</a> 与服务器相关URL 服务器相关的URL与上下文相关的URL很是类似 <a th:href="@{~/hello}">跳转</a> 携带参数 <a th:href="@{/hero/detail(id=3,action='show_all')}">aa</a> 字面值 有的时候,咱们须要在指令中填写基本类型如:字符串、数值、布尔等,并不但愿被Thymeleaf解析为变量,这个时候称为字面值。(也就是原样输出) 字符串字面值 <p> 内容:<span th:text=”'thymeleaf'+1">template</span> </p> 数字字面值 <p> 内容:<span th:text=”2+1">template</span> </p> 布尔字面值 布尔类型的字面值是true或false 拼接 普通字符串与表达式拼接的状况 <span th:text=”'欢迎您:'+${hero.username}+'!'"></span> 字符串字面值须要用'',拼接起来很是麻烦,Thymeleaf对此进行了简化,使用一对|便可 <span th:text="|欢迎您:${hero.username}|"></span> 运算符 算术操做符 + - * / % 比较运算 > , <, >= and <= 可是>和<不能直接使用,由于xml会解析为标签 > gt < lt >= ge <= le <!-- 返回值为true --> <h1 th:text=" 1 lt 2"></h1> <!-- 返回值为假 --> <h1 th:text="1 gt 2 ? '真' : '假'"></h1> 三元运算 condition ? then : else <span th:text="${false}?'男':'女'"></span〉 简写 <!-- 默认表达式 变量值为空 就显示默认值 不然就显示变量自己 --> <h1 th:text="${hero.username}?:'我是空值'"></h1> <h1 th:text="${hero.username} != null ? ${hero.username} : '我是空值'"></h1> 内联写法 <!--原样输出--> <p>pppp-------—-[(${hero.username})]</p> <!--解析内部标签--> <p>pppp-------—-[[${hero.username}]]</p> 局部变量 <div th:with="heroN = ${allHero[0]}"> <h1 th:text="${heroN.username}"></h1> <h1 th:text="${heroN.phone}"></h1> </div> 判断 th:if <!--当条件知足时, 才会显示标签--> <h1 th:if="${1 lt 2}">内容1</h1> th:unless <!-- unless是与if相反 不知足条件时,才会显示标签 --> <h1 th:unless="${1 gt 2}">内容2</h1> th:switch <div th:switch="${hero.username}"> <p th:case="'itlike'">itlike</p> <p th:case="'gxq'">gxq</p> <p th:case="*">*为默认的值,至关于default</p> </div> 迭代 <div> <p th:each="heroItem:${allHero}"> <span th:text="${heroItem.username}"></span> <span th:text="${heroItem.phone}"></span> <span th:text="${#dates.format(heroItem.onlinetime,'yyyy-MM-dd')}"></span> </p> </div> <hr> <div> <p th:each="heroItem,stat:${allHero}"> <span th:text="${heroItem.username}"></span> <span th:text="${heroItem.phone}"></span> <span th:text="${stat.index}"></span> </p> </div> stat对象包含如下属性 index,从0开始的角标 count,当前遍历到第几个元素,从1开始 size,总元素个数 current,当前遍历到的元素信息 odd/even,返回是否为奇偶,boolean值 first/last,返回是否为第一或最后,boolean值 内置对象 环境相关对象 ${#ctx} 上下文对象,可用于获取其它内置对象。 ${#vars}: 上下文变量。 ${#locale}:上下文区域设置。 ${#request}: HttpServletRequest对象。 ${#response}: HttpServletResponse对象。 ${#session}: HttpSession对象。 ${#servletContext}: ServletContext对象。 全局对象功能 #strings:字符串工具类 #lists:List 工具类 #arrays:数组工具类 #sets:Set 工具类 #maps:经常使用Map方法。 #objects:通常对象类,一般用来判断非空 #bools:经常使用的布尔方法。 #execInfo:获取页面模板的处理信息。 #messages:在变量表达式中获取外部消息的方法,与使用#{...}语法获取的方法相同。 #uris:转义部分URL / URI的方法。 #conversions:用于执行已配置的转换服务的方法。 #dates:时间操做和时间格式化等。 #calendars:用于更复杂时间的格式化。 #numbers:格式化数字对象的方法。 #aggregates:在数组或集合上建立聚合的方法。 #ids:处理可能重复的id属性的方法。 示例 ${#strings.abbreviate(str,10)} str截取0-10位,后面的所有用…这个点代替,注意,最小是3位 ${#strings.toUpperCase(name)} 判断是否是为空:null: <span th:if="${name} != null">不为空</span> <span th:if="${name1} == null">为空</span> 判断是否是为空字符串: “” <span th:if="${#strings.isEmpty(name1)}">空的</span> 判断是否相同: <span th:if="${name} eq 'jack'">相同于jack,</span> <span th:if="${name} eq 'ywj'">相同于ywj,</span> <span th:if="${name} ne 'jack'">不相同于jack,</span> 不存在设置默认值: <span th:text="${name2} ?: '默认值'"></span> 是否包含(分大小写): <span th:if="${#strings.contains(name,'ez')}">包ez</span> <span th:if="${#strings.contains(name,'y')}">包j</span> 是否包含(不分大小写) <spanth:if="${#strings.containsIgnoreCase(name,'y')}">包</span> ${#strings.startsWith(name,'o')} ${#strings.endsWith(name, 'o')} ${#strings.indexOf(name,frag)}// 下标 ${#strings.substring(name,3,5)}// 截取 ${#strings.substringAfter(name,prefix)}// 从 prefix以后的一位开始截取到最后,好比 (ywj,y) = wj, 若是是(abccdefg,c) = cdefg//里面有2个c,取的是第一个c ${#strings.substringBefore(name,suffix)}// 同上,不过是往前截取 ${#strings.replace(name,'las','ler')}// 替换 ${#strings.prepend(str,prefix)}// 拼字字符串在str前面 ${#strings.append(str,suffix)}// 和上面相反,接在后面 ${#strings.toUpperCase(name)} ${#strings.toLowerCase(name)} ${#strings.trim(str)} ${#strings.length(str)} ${#strings.abbreviate(str,10)}// str截取0-10位,后面的所有用…这个点代替,注意,最小是3位 布局 方式1 hello.html <nav th:fragment="header"> <h1>头部</h1> </nav> base.html <div th:include="{common/base::header}"></div> 方式2 hello.html <footer id="footer"> <h1 th:class="${active == 'footer' ? 'red' : ''}" >尾部---</h1> <p>aaaaaa</p> </footer> base.html <div th:insert="{common/base::#footer}"></div> 引入方式 th:insert 将公共的标签及内容插入到指定标签当中 th:replace 将公共的标签替换指定的标签 th:include 将公共标签的内容包含到指定标签当中 传值 hello.html <div th:include="~{base::#footer(active='footers')}"></div> base.html <footer id="footer"> <h1 th:class="${active == 'footer' ? 'red' : ''}" >尾部---</h1> <p>aaaaaa</p> </footer> js模板 模板引擎不只能够渲染html,也能够对JS中的进行预处理。并且为了在纯静态环境下能够运行 在script标签中经过th:inline="javascript"来声明这是要特殊处理的js脚本 取值要采用注释的形式 <script th:inline="javascript"> var username = /*[[${name}]]*/ "myxq"; console.log(username); </script>
引入bootstrap <dependency> <groupId>org.webjars</groupId> <artifactId>bootstrap</artifactId> <version>4.0.0</version> </dependency> 在页面中引入资源文件 <link rel="stylesheet" href="js/bootstrap/dist/css/bootstrap.css" th:href="@{/webjars/bootstrap/4.0.0/css/bootstrap.css}"> <link rel="stylesheet" href="js/font-awesome/css/font-awesome.css" th:href="@{/css/font-awesome/css/font-awesome.css}"> <link rel="stylesheet" href="css/index.css" th:href="@{/css/index.css}"> <script th:href="@{/webjars/bootstrap/4.0.0/js/bootstrap.js}"></script>
@Configuration public class WebMvcConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addVievController("/")).setViewName("login"); registry.addVievController("/index.html")).setViewName("login"); } }
1.编写国际化配置文件 在资源文件夹resources下,建立一个国际化文件夹i18n 在i18n文件夹中建立一个login.properties文件 建立中文/英文国际化文件 在i18n文件夹中建立一个login_en_US.properties文件 在i18n文件夹中建立一个login_zh_CN.properties文件 点击Resource Bundle,新建property key name为login.username 在login.properties窗口下输入用户名 login_en_US.properties窗口中输入username login_zh_CN.properties窗口中输入用户 注意 要更改文件编码 idea中,设置->编译器->文本编码属性文件(*.properties) 设置属性文件的默认编码为UTF-8 2.在springboot中有一个messageSourceAutoConfiguration 会自动管理国际化资源文件 在全局配置文件中设置基础名 spring: messages: basename: i18n.login 3.在页面中获取国际化的值 <!-- 登陆 --> <div class="login"> <div class="login-wrap"> <div class="avatar"> <img src="./images/logo.jpg" class="img-circle" alt=""> </div> <form action="" class="col-md-offset-1 col-md-10"> <div class="input-group input-group-lg"> <span class="input-group-addon"> <i class="fa fa-id-card-o"></i> </span> <input type="text" class="form-control" name="username" th:placeholder="#{login.username}"> </div> <div class="input-group input-group-lg"> <span class="input-group-addon"> <i class="fa fa-key"></i> </span> <input type="password" class="form-control" name="password" th:placeholder="#{login.password}"> </div> <button type="submit" class="btn btn-lg btn-danger btn-block" th:text="#{login.btn}">登 录</button> <a class="language" th:href="@{/login(lan='zh_CN')}">中文</a>|<a class="language" th:href="@{/login(lan='en_US')}">English</a> </form> </div> </div> 4.切换中英文 默认 自定义 public class MyLocaleResolver implements LocaleResolver { @Override public Locale resolveLocale(HttpServletRequest request){ String lan=request.getParameter("lan"); Locale locale=Locale.getDefault(); if(!StringUtils.isEmpty(lan)){ String[] split=lan.split("_"); locale=new Locale(split[0],split[1]); } return locale; } @Override public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale){ } } @Configuration public class WebMvcConfig implements WebMvcConfigurer { @Bean public LocaleResolver localeResolver() { return new MyLocaleResolver(); } }
MessageSourceAutoConfiguration(自动管理国际化资源文件)
web
默认切换中英文spring
<!-- 登陆 --> <div class="login"> <div class="login-wrap"> <div class="avatar"> <img src="./images/logo.jpg" class="img-circle" alt=""> </div> <form action="" class="col-md-offset-1 col-md-10"> <div class="input-group input-group-lg"> <span class="input-group-addon"> <i class="fa fa-id-card-o"></i> </span> <input type="text" class="form-control" name="username" th:placeholder="#{login.username}"> </div> <div class="input-group input-group-lg"> <span class="input-group-addon"> <i class="fa fa-key"></i> </span> <input type="password" class="form-control" name="password" th:placeholder="#{login.password}"> </div> <button type="submit" class="btn btn-lg btn-danger btn-block" th:text="#{login.btn}">登 录</button> <a class="language" th:href="@{/login(lan='zh_CN')}">中文</a>|<a class="language" th:href="@{/login(lan='en_US')}">English</a> </form> </div> </div>
@PostMapping("/login") public String login(@RequestParam("username") String username,@RequestParam("password")String password,Model model,HttpSession session){ if(!StringUtils.isEmpty(username)&& "123456".equals(password)){ //登录成功,防止表单重复提交,能够重定向到主页 session.setAttribute("loginUser",username); return "redirect:/main.html"; }else{ //登录失败 model.addAttribute("msg","用户名密码错误"); return "login"; } }
<!--判断--> <p style="color:red;margin-left:60px;font-size:18px" th:text="${msg}" th:if="${not #strings.isEmpty(msg)}"></p>
public class LoginHandlerInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws ServletException, IOException { Object user=request.getSession().getAttribute("loginUser"); if(user==null){ //未登录,返回登录页面 request.setAttribute("msg","没有权限请先登录"); request.getRequestDispatcher("/login").forward(request,response); return false; } else{ //已登录,放行请求 return true; } } }
@Configuration public class WebMvcConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new LoginHandlerInterceptor()).addPathPatterns("/**") .excludePathPatterns("/","/1ogin","/main.html","login","/static/**"); } @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/**"); WebMvcConfigurer.super.addResourceHandlers(registry); } }
public interface HeroMapper extends Mapper<Hero> { }
@Autowired private MainService mainService; @RequestMapping("/main") public String main(Model mode){ List<Hero> herolist=mainService.getHeroList(); model.addAttribute("heroList",heroList); return"main"; }
<tr th:each="hero:${heroList}"> <td th:text="${hero.id}"></td> <td th:text="${hero.profession}"></td> <td th:text="${hero.username}"></td> <td th:text="${hero.email}"></td> <td th:text="${#dates.format(hero.onlinetime,'yyyy-MM-dd HH:mm')}"></td> <td> <a th:href="@{/edit}" class="btn btn-danger btn-sm">编辑</a> <a href="javascript:;" class="btn btn-warning btn-sm">删除</a> </td> </tr>
跳转到添加页面 <div class="panel-body"> <a href="@{/addPage}" class="btn btn-primary">添加英雄</a> </div> 添加按钮点击 <form th:action="@{/addHero}" class="form-horizontal" method="post"> <input type="hidden" name="_method" value="put" th:if="${hero!=null}"/> <input type="hidden" name="id" th:if="${hero!=null}" th:value="${hero.id}"> <div class="form-group"> <label class="col-md-2 control-label">名称</label> <div class="col-md-6"> <input type="text" name="username" th:value="${hero!=null}?${hero.username}" class="form-control"> </div> </div> <div class="form-group"> <label class="col-md-2 control-label">职业</label> <div class="col-md-6"> <input type="text" name="profession" th:value="${hero!=null}?${hero.profession}" class="form-control"> </div> </div> <div class="form-group"> <label class="col-md-2 control-label">电话</label> <div class="col-md-6"> <input type="text" name="phone" th:value="${hero!=null}?${hero.phone}" class="form-control"> </div> </div> <div class="form-group"> <label class="col-md-2 control-label">邮箱</label> <div class="col-md-6"> <input type="text" name="email" th:value="${hero!=null}?${hero.email}" class="form-control"> </div> </div> <div class="form-group"> <label class="col-md-2 control-label">上线日期</label> <div class="col-md-6"> <input type="text" name="onlinetime" th:value="${hero!=null}?${#dates.format(hero.onlinetime,'yyyy-MM-dd')}" class="form-control"> </div> </div> <div class="modal-footer"> <input type="submit" class="btn btn-danger" th:value="${hero!=null}?'修改':'添加'"> </div> </form> 日期处理(写在控制器中) @InitBinder public void InitBinder(WebDataBinder dataBinder){ dataBinder.registerCustomEditor(Date.class,new PropertyEditorSupport(){ public void setAsText(String value){ try{ setValue(new SimpleDateFormat("yyyy-MM-dd").parse(value)); } catch(ParseException e){ setValue(null); } } public String getAsText(){ return new SimpleDateFormat("yyyy-MM-dd").format((Date)getValue()); } }); }