5.视图解析与模板引擎
视图解析:SpringBoot默认不支持 JSP
,需要引入第三方模板引擎技术实现页面渲染。
(1).视图解析
(1.1)、视图解析原理流程
1、目标方法处理的过程中,所有数据都会被放在 ModelAndViewContainer 里面。包括数据和视图地址
2、方法的参数是一个自定义类型对象(从请求参数中确定的),把他重新放在 ModelAndViewContainer
3、任何目标方法执行完成以后都会返回 ModelAndView(数据和视图地址)。
4、processDispatchResult
处理派发结果(页面该如何响应)
DisplatchServlet的 第1078行
- 1、
render(mv, request, response)
; 进行页面渲染逻辑
- 1、根据
方法的String返回值
得到 View 对象【定义了页面的渲染逻辑】
- 1、所有的视图解析器尝试是否能根据当前返回值得到View对象 (for遍历尝试)
- 2、得到了 redirect:/main.html --> Thymeleaf new RedirectView()
- 3、ContentNegotiationViewResolver 里面包含了下面所有的视图解析器,内部还是利用下面所有视图解析器得到视图对象。
- 4、view.render(mv.getModelInternal(), request, response); 视图对象调用自定义的render进行页面渲染工作
view.render(mv.getModelInternal(), request, response); 1393行
- RedirectView类 如何渲染【重定向到一个页面】
- 1、获取目标url地址
- 2、response.sendRedirect(encodedURL);
视图解析:
- 返回值以 forward: 开始: new InternalResourceView(forwardUrl); --> 转发request.getRequestDispatcher(path).forward(request, response);
- 返回值以 redirect: 开始: new RedirectView() --》 render就是重定向
(2).Thymeleaf基本语法
(2.1)、表达式
(2.2)、字面量
文本值: ‘one text’ , ‘Another one!’ ,…数字: 0 , 34 , 3.0 , 12.3 ,…布尔值: true , false
空值: null
变量: one,two,… 变量不能有空格
(2.3)、文本操作
字符串拼接: +
变量替换: |The name is ${name}|
(2.4)、数学运算
运算符: + , - , * , / , %
(2.5)、布尔运算
运算符: and , or
一元运算: ! , not
(2.6)、比较运算
比较: > , < , >= , <= ( gt , lt , ge , le )等式: == , != ( eq , ne )
(2.7)、条件运算
If-then: (if) ? (then)
If-then-else: (if) ? (then) : (else)
Default: (value) ?: (defaultvalue)
(2.8)、特殊操作
无操作: _
(2.9)、设置属性值-th:attr
设置单个值
<form action="subscribe.html" th:attr="action=@{/subscribe}"> <fieldset> <input type="text" name="email" /> <input type="submit" value="Subscribe!" th:attr="value=#{subscribe.submit}"/> </fieldset> </form>
设置多个值
<img src="../../images/gtvglogo.png" th:attr="src=@{/images/gtvglogo.png},title=#{logo},alt=#{logo}" />
以上两个的代替写法 th:xxxx
<input type="submit" value="Subscribe!" th:value="#{subscribe.submit}"/> <form action="subscribe.html" th:action="@{/subscribe}">
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html#setting-value-to-specific-attributes
行内写法
1. 假如要写的内容不在标签中而在行内,那么就用这个方式。(非session) <h1>[[${xxx}]]</h1> 2. 假如是取Session的值 <h1>[[$session.name.xxx}]]</h1>
(2.10)、迭代
<tr th:each="prod : ${prods}"> <td th:text="${prod.name}">Onions</td> <td th:text="${prod.price}">2.41</td> <td th:text="${prod.inStock}? #{true} : #{false}">yes</td> </tr>
<tr th:each="prod,iterStat : ${prods}" th:class="${iterStat.odd}? 'odd'"> <td th:text="${prod.name}">Onions</td> <td th:text="${prod.price}">2.41</td> <td th:text="${prod.inStock}? #{true} : #{false}">yes</td> </tr>
(2.11)、条件运算
<a href="comments.html" th:href="@{/product/comments(prodId=${prod.id})}" th:if="${not #lists.isEmpty(prod.comments)}">view</a>
<div th:switch="${user.role}"> <p th:case="'admin'">User is an administrator</p> <p th:case="#{roles.manager}">User is a manager</p> <p th:case="*">User is some other thing</p> </div>
(3).Thymeleaf的使用
(3.1)、引入依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency>
(3.2)、自动配置好了thymeleaf
ThymeleafAutoConfiguration 类
@Configuration(proxyBeanMethods = false) @EnableConfigurationProperties(ThymeleafProperties.class) @ConditionalOnClass({ TemplateMode.class, SpringTemplateEngine.class }) @AutoConfigureAfter({ WebMvcAutoConfiguration.class, WebFluxAutoConfiguration.class }) public class ThymeleafAutoConfiguration { }
自动配好的策略
- 1、所有thymeleaf的配置值都在
ThymeleafProperties 类
- 2、配置好了
SpringTemplateEngine
- 3、配好了
ThymeleafViewResolver
- 4、我们只需要直接开发页面
(3.2)、页面开发
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>Success</h1> <h1 th:text="${A}"></h1> <a th:href="${baidu}">点击我去金橘社区 ${baidu}</a> <br> <br> <a th:href="@{baidu}">点击我去金橘社区 @{baidu}</a> <br> <br> <a th:href="@{/baidu}">点击我去金橘社区 @{/baidu}</a> </body> </html>
package com.jsxs.controller; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; /** * @Author Jsxs * @Date 2023/7/7 17:30 * @PackageName:com.jsxs.controller * @ClassName: ViewTestController * @Description: TODO * @Version 1.0 */ @Controller public class ViewTestController { @GetMapping("/toTest") public String toTest(Model model){ // model 会自动放入到请求域中和HttpRequest是一样的,只能接受一次请求的操作 model.addAttribute("A","a"); model.addAttribute("baidu","https://www.jsxs1.cn"); return "success"; // 假如说没有模板解析器的话,这里的路径会报黄。 } }
# 给整个服务器添加前缀 server: servlet: context-path: /jsxs
(4).后台管理系统总结
1. 假如在template中再新建包的话,我们只需要在 controller 的返回值中添加上新建包路径即可 /新建包名/xxx。 2. controller 页面跳转的实质是转发;不是重定向。 3. return: 的值会默认拼接 templates/xxxx.html; return forward: return redirect 找的都是请求的路径不是页面。 4. 静态资源只要放在四大区域就行,前端调用的时候可以省略掉前面的四大区域路径只写相对路径即可。 5. 抽取公共模板(第一种) (1). 公共页(top.html): 在标签中设置 th:fragment="AAAA" eg: <div th:fragment="AAAA"></div> (2). 使用公共页方: th:insert="~{公共页的HTML名字 :: AAAA}" eg: 1. <div th:insert="~{top :: AAAA}"></div> 或 2.<div th:insert="top :: AAAA"></div> 或 3.<div th:replace="~{top :: utopbar}"></div> 或 4.<div th:include="~{top :: topbar}"></div> 假如说公共页面和被添加公共页面不再同一个包中那么就要加上路径指定在哪 eg:<div th:include="~{commons/top :: topbar}"></div> 6.抽取公共模板(第二种 ->选择器方式) (1). 公共页(top.html): 在标签中设置 id="BBB" (2). 使用方: <div th:replace="top :: #BBB"> (当然以上的几种方法都适用)
公共方:
<footer th:fragment="copy"> © 2011 The Good Thymes Virtual Grocery </footer> • 1 • 2 • 3
使用公共方
<body> ... 会带上div(全部都要) <div th:insert="~{footer :: copy}"></div> 引入的东西不会在div里面 <div th:replace="~{footer :: copy}"></div> 引入的中西会在div里面 <div th:include="~{footer :: copy}"></div> </body>
实际效果:
假如引入的是css、js。也是一样的只不过把div改为link或script <body> ... <div> <footer> © 2011 The Good Thymes Virtual Grocery </footer> </div> <footer> © 2011 The Good Thymes Virtual Grocery </footer> <div> © 2011 The Good Thymes Virtual Grocery </div> </body>
(4.1)、举列子(公共方)
1.公共方的页面
2.replace->引入的东西会在link标签里面内嵌link标签
<link th:replace="~{}">