Thymeleaf
一、简介
-
Thymeleaf是一个流行的模板引擎
该模板引擎采用Java语言开发,模板引擎是一个技术名词,是跨领域跨平台的概念。也可以叫做视图模板技术
在Java语言体系下有模板引擎,在C#、PHP语言体系下也有模板引擎。
① 新建一个继承HttpServlet类的Servlet类,该类中包含初始化init方法和模板处理processTemplate方法
② 再新建的Servlet组件需继承该Servlet类,这样可调用上述两个方法并配合xml配置文件对html文件组装
③ 组装进来的html文件内就可以使用thymeleaf语言进行实时渲染了(还需要保存作用域进行传参)
-
主要目标
在于提供一种可被浏览器正确显示的、格式良好的模板创建方式,因此也可以用作静态建模。你可以使用它创建经过验证的XML与HTML模板。使用thymeleaf创建的html模板可以在浏览器里面直接打开(展示静态数据),这有利于前后端分离。
需要注意的是Thymeleaf不是spring旗下的。这里我们使用Thymeleaf 3版本。
-
主要作用
是把数据库中的数据(或者叫Java内存中的数据)加载到html页面上,这个过程叫做渲染,因此其语法主要是如何解析model中的数据,因此thymeleaf就是用来帮我们做视图渲染的一个技术。
补充说明:在之前用Html,Css,Javascript写的页面中的数据都是假的,不是数据库中真正的数据。
二、将数据库的内容实时渲染到html页面上
-
添加thymeleaf的jar包
-
在web.xml文件中添加上下文参数配置
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <!-- 配置上下文参数,该配置在ViewBaseServlet类中init方法中被调用 view-prefix:配置前缀 view-suffix:配置后缀 /:代表web根目录 --> <context-param> <param-name>view-prefix</param-name> <param-value>/</param-value> </context-param> <context-param> <param-name>view-suffix</param-name> <param-value>.html</param-value> </context-param> </web-app>
注:在Servlet3.0后,和的配置可通过注解@WebServlet("/index")代替
-
新建一个Servlet类(ViewBaseServlet),有初始init方法和模板处理方法
/** * @author e_n * @version 1.0.0 * @ClassName ViewBaseServlet.java * @Description 属于固定写法,继承了HttpServlet类, * @CreateTime 2022/03/03 22:13 */ public class ViewBaseServlet extends HttpServlet { private TemplateEngine templateEngine; /** * @title init * @Description 初始化函数 * @author e_n * @CreateTime 2022/3/9 21:00 */ @Override public void init() throws ServletException { // 1.获取ServletContext对象 ServletContext servletContext = this.getServletContext(); // 2.创建Thymeleaf解析器对象 ServletContextTemplateResolver templateResolver = new ServletContextTemplateResolver(servletContext); // 3.给解析器对象设置参数 // ①HTML是默认模式,明确设置是为了代码更容易理解 templateResolver.setTemplateMode(TemplateMode.HTML); // ②设置前缀 String viewPrefix = servletContext.getInitParameter("view-prefix"); templateResolver.setPrefix(viewPrefix); // ③设置后缀 String viewSuffix = servletContext.getInitParameter("view-suffix"); templateResolver.setSuffix(viewSuffix); // ④设置缓存过期时间(毫秒) templateResolver.setCacheTTLMs(60000L); // ⑤设置是否缓存 templateResolver.setCacheable(true); // ⑥设置服务器端编码方式 templateResolver.setCharacterEncoding("utf-8"); // 4.创建模板引擎对象 templateEngine = new TemplateEngine(); // 5.给模板引擎对象设置模板解析器 templateEngine.setTemplateResolver(templateResolver); } /** * @title processTemplate * @Description 可以完成资源的转发和数据的渲染 * @author e_n * @CreateTime 2022/3/7 16:18 */ protected void processTemplate(String templateName, HttpServletRequest req, HttpServletResponse resp) throws IOException { // 1.设置响应体内容类型和字符集 resp.setContentType("text/html;charset=UTF-8"); // 2.创建WebContext对象 WebContext webContext = new WebContext(req, resp, getServletContext()); // 3.处理模板数据 templateEngine.process(templateName, webContext, resp.getWriter()); } }
-
根据请求创建对应的Servlet组件(继承ViewBaseServlet)
// 在Servlet3.0后,注解@WebServlet("/index")代替<servlet>和<servlet-mapping>的配置 @WebServlet("/index") public class IndexServlet extends ViewBaseServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) { Connection conn = null; try { // 1.连接数据库,获取数据库中的数据,返回一个集合 conn = JdbcUtils.getConnection(); ListIpm ipm = new ListIpm(); List<Customers> custList = ipm.getList(conn); /* 2.将数据库中返回的集合(custList)以键值对的形式保存到session作用域中, 该集合对应的key值是自己起的变量名,叫什么都可以。 在指定的html文件中,该集合就可以被thymeleaf以session.key的形式调用 */ HttpSession s = req.getSession(); s.setAttribute("cl",custList); /* 3.调用父类的模板处理方法 第一个参数:逻辑视图名称(模板名称) thymeleaf会将这个 逻辑视图名称 对应到 物理视图名称 上去 物理视图名称:view-prefix + 逻辑视图名称 + view-suffix 所以真实的视图名称:/ index .html 这样就将web文件夹下的index.html文件上组装上了(跳转到该/index.html页面上了) 或者说该模板处理方法的第一个参数的作用就是配合ViewBaseServlet类中的init方法链接到指定的html文件(模板)上 接下来,就是在该html文件上使用thymeleaf的标签,将数据库中的数据动态的整合进来,达到渲染的目的 */ super.processTemplate("index",req,resp); } catch (Exception e) { e.printStackTrace(); } finally { JdbcUtils.closeResource(conn,null); } } }
-
在指定的html文件(即模板)中使用thymeleaf标签进行渲染,将数据库的数据动态加载到html中
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <html> <head> <meta charset="UTF-8"> <link th:rel="stylesheet" th:href="@{CSS/index.css}"> </head> <body> <div id="div_contain"> <div id="div_fruit_list"> <p class="center f32">欢迎使用客户后台管理系统</p> <table id="tbl_fruit"> <tr> <th>cust_id</th> <th>cust_name</th> <th>cust_email</th> <th>cust_birth</th> </tr> <!-- session.cl 就是在Servlet组件中保存在session作用域内的集合 --> <tr th:if="${#lists.isEmpty(session.cl)}"> <td colspan="4">无客户名单!</td> </tr> <tr th:unless="${#lists.isEmpty(session.cl)}" th:each="cust : ${session.cl}"> <td th:text="${cust.id}">1</td> <td th:text="${cust.name}">太白金星</td> <td th:text="${cust.email}">tbstart@163.com</td> <td th:text="${cust.birth}">1000-1-1</td> </tr> </table> </div> </div> </body> </html>
三、编辑和修改特定库存信息,并实时渲染到html页面上
-
在index.html文件内的某单元格上设置超链接,在html内向服务器发送请求
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <html> <head> <meta charset="UTF-8"> <base href="http://localhost:8080/thymeleaf/" > <link rel="stylesheet" href="CSS/index.css" > <!-- <link th:rel="stylesheet" th:href="@{CSS/index.css}"> --> </head> <body> <div id="div_contain"> <div id="div_fruit_list"> <p class="center f32">欢迎使用客户后台管理系统</p> <table id="tbl_fruit"> <tr> <th>cust_id</th> <th>cust_name</th> <th>cust_email</th> <th>cust_birth</th> </tr> <tr th:if="${#lists.isEmpty(session.cl)}"> <td colspan="4">无客户名单!</td> </tr> <tr th:unless="${#lists.isEmpty(session.cl)}" th:each="cust : ${session.cl}"> <!-- 1.href="edit.do":在html页面上点击该连接对应的内容,就会转接到对应 的servlet组件上 2.在edit.do的括号内的,是显示在url栏的参数,该参数是要被DAO操作调用的,故需 按照SQL语法的需求进行配置。 此处进行的是修改操作,MySQL中的修改数据的语法中一般是where id = ?,故此次 的参数也应该是html表格中的id 3.拼接字符串的写法:<td><a th:text="${cust.id}" th:href="@{'/edit.do?id='+${cust.id}}">1</a></td> 4.下面是使用thymeleaf语法替换普通的拼接字符串写法 --> <td ><a th:text="${cust.id}" th:href="@{/edit.do(cid=${cust.id})}">1</a></td> <td th:text="${cust.name}">太白金星</td> <td th:text="${cust.email}" th:onclick="|delCust(${cust.id})|">tbstart@163.com</td> <td th:text="${cust.birth}">1000-1-1</td> </tr> </table> </div> </div> </body> </html>
-
创建“/edit.do”对应的servlet组件
@WebServlet("/edit.do") public class EditServlet extends ViewBaseServlet { private ListIpm listIpm = new ListIpm(); @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) { Connection conn = null; try { conn = JdbcUtils.getConnection(); // 1.去数据库中查询某一个库存记录,根据thymeleaf在html中的超链接标签中提供的id String idStr = req.getParameter("cid"); // 2.将判断idStr不是空字符串且不为null的语句写成一个工具类内的方法,方便后续频繁调用 if (StringUtil.isNotEmpty(idStr)) { int id = Integer.parseInt(idStr); // 3.通过DAO调取数据库中对应的数据 Customers customer = listIpm.getCustomerById(conn, id); // 4.将获取到的数据(引用类型),以键值对的形式保存到作用域中,就可在html中调用了 req.setAttribute("cu" ,customer); // 5.调用模板处理方法,连接到edit.html文件中(如果该页面不存在,就创建一个) super.processTemplate("edit",req,resp); } } catch (Exception e) { e.printStackTrace(); } finally { JdbcUtils.closeResource(conn,null); } } }
补充:工具类
public class StringUtil { public static boolean isEmpty(String str) { return str==null || "".equals(str); } public static boolean isNotEmpty(String str) { return !isEmpty(str); } }
-
根据模板处理方法,创建一个edit.html文件
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <html> <head> <meta charset="UTF-8"> <base href="http://localhost:8080/thymeleaf/" > <link rel="stylesheet" href="CSS/edit.css" > <!-- <link th:rel="stylesheet" th:href="@{CSS/index.css}">--> </head> <body> <div id="div_contain"> <div id="div_fruit_list"> <p class="center f32">修改/编辑库存信息</p> <form th:action="@{update.do}" method="post"> <table id="tbl_fruit" th:object="${cu}"> <tr> <th>cust_id</th> <td><input type="text" name="id" th:value="*{id}"/></td> </tr> <tr> <th>cust_name</th> <td><input type="text" name="name" th:value="*{name}"/></td> </tr> <tr> <th>cust_email</th> <td><input ttype="text" name="email" th:value="*{email}"/></td> </tr> <tr> <th>cust_birth</th> <td><input type="text" name="birth" th:value="*{birth}"/></td> </tr> <tr> <th colspan="2"> <input th:type="submit" th:value="提交" /> </th> </tr> </table> </form> </div> </div> </body> </html>
-
创建th:action="@{update.do}"对应的Servlet组件
请求类型:表单是post请求,其它的都是get请求
@WebServlet("/update.do") public class UpdateServlet extends ViewBaseServlet { private ListIpm listIpm = new ListIpm(); @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) { Connection conn = null; try { // 1.设置post请求的编码格式 req.setCharacterEncoding("UTF-8"); // 2.读取html文件内post表单的各个数据 String idStr = req.getParameter("id"); int id = Integer.parseInt(idStr); String name = req.getParameter("name"); String email = req.getParameter("email"); String birth = req.getParameter("birth"); SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd"); Date date = format.parse(birth); // 3.调用DAO内的方法,将表单内的数据写入到数据库中 conn = JdbcUtils.getConnection(); listIpm.updateById(conn,name,email,date,id); /* 4.重定位 这样就会使客户端重新发一次,得到注解为“index”的servlet组件的响应, 重新读一遍数据库中的数据(或者说重新执行了一次第二部分的流程),这样就显示数据库内 更新后的数据了 */ resp.sendRedirect("index"); } catch (Exception e) { e.printStackTrace(); } finally { JdbcUtils.closeResource(conn,null); } } }
四、删除数据库中的数据,并实时渲染到html页面上
-
在index.html文件内的某单元格上使用thymeleaf的事件方法,转接到js文件上
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <html> <head> <meta charset="UTF-8"> <base href="http://localhost:8080/thymeleaf/" > <link rel="stylesheet" href="CSS/index.css" > <!-- <link th:rel="stylesheet" th:href="@{CSS/index.css}">--> <script language="JavaScript" th:src="@{js/index.js}"></script> </head> <body> <div id="div_contain"> <div id="div_fruit_list"> <p class="center f32">欢迎使用客户后台管理系统</p> <div style="border: 0px solid red; width:60% ; margin-left: 20%; text-align: right"> <a th:href="@{/add.html}" style="border: 0px solid blue; margin-bottom: 10px"> 新添加客户 </a> </div> <table id="tbl_fruit"> <tr> <th>cust_id</th> <th>cust_name</th> <th>cust_email</th> <th>cust_birth</th> </tr> <tr th:if="${#lists.isEmpty(session.cl)}"> <td colspan="4">无客户名单!</td> </tr> <tr th:unless="${#lists.isEmpty(session.cl)}" th:each="cust : ${session.cl}"> <td ><a th:text="${cust.id}" th:href="@{/edit.do(cid=${cust.id})}">1</a></td> <td th:text="${cust.name}">太白金星</td> <!-- 1. 转接到删除操作 2. 拼接字符串的方式:<td th:text="${cust.email}" th:οnclick="'delCust('+${cust.id}+')'">tbstart@163.com</td> 3. |delCust(${cust.id})|被thymeleaf解析为: delCust()是对应js文件中的方法名,即需要在关联的js文件中创建delCust()方法 ${cust.id}:是传入到delCust方法内的参数 --> <td th:text="${cust.email}" th:onclick="|delCust(${cust.id})|">tbstart@163.com</td> <td th:text="${cust.birth}">1000-1-1</td> </tr> </table> </div> </div> </body> </html>
-
对应事件的js文件
/* html文件中thymeleaf语句th:οnclick="|delCust(${cust.id})|" 1. js中的方法名与delCust对应 2. 方法中的s参数与${cust.id}对应 */ function delCust(s) { if (confirm('是否确认删除')) { // sid是传递在url框上的参数,同时也是被del.do所对应的组件所获取的参数来源 window.location.href = 'del.do?sid=' + s; } }
-
#####创建对应的Servlet组件
@WebServlet("/del.do") public class DelCustServlet extends ViewBaseServlet { private ListIpm listIpm = new ListIpm(); @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) { String idStr = req.getParameter("sid"); if (StringUtil.isNotEmpty(idStr)) { Connection conn = null; try { int id = Integer.parseInt(idStr); conn = JdbcUtils.getConnection(); listIpm.delById(conn,id); /* 直接重定位到index请求,即使tomcat再转到index请求对应的组件上(此模块中是 IndexServlet),这样就会重新执行IndexServlet组件,重新对html页面进行渲染, 那么删除了数据的新数据库内容就会实时显示在新的浏览器页面上了 */ resp.sendRedirect("index"); } catch (Exception e) { e.printStackTrace(); } finally { JdbcUtils.closeResource(conn,null); } } } }
五、向数据库中增加数据,并实时渲染到html页面上
-
在index.html文件上添加div层,转接到新的html的表单上
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <html> <head> <meta charset="UTF-8"> <base href="http://localhost:8080/thymeleaf/" > <link rel="stylesheet" href="CSS/index.css" > <script language="JavaScript" th:src="@{js/index.js}"></script> </head> <body> <div id="div_contain"> <div id="div_fruit_list"> <p class="center f32">欢迎使用客户后台管理系统</p> <!-- 为了向数据库中插入新的数据,故新增一个div层,并在该层内增加一个跳转链接标签,点击后可 跳到新的html页面上 --> <div style="border: 0px solid red; width:60% ; margin-left: 20%; text-align: right"> <a th:href="@{/add.html}" style="border: 0px solid blue; margin-bottom: 10px"> 新添加客户 </a> </div> <table id="tbl_fruit"> <tr> <th>cust_id</th> <th>cust_name</th> <th>cust_email</th> <th>cust_birth</th> </tr> <tr th:if="${#lists.isEmpty(session.cl)}"> <td colspan="4">无客户名单!</td> </tr> <tr th:unless="${#lists.isEmpty(session.cl)}" th:each="cust : ${session.cl}"> <td ><a th:text="${cust.id}" th:href="@{/edit.do(cid=${cust.id})}">1</a></td> <td th:text="${cust.name}">太白金星</td> <td th:text="${cust.email}" th:onclick="|delCust(${cust.id})|">tbstart@163.com</td> <td th:text="${cust.birth}">1000-1-1</td> </tr> </table> </div> </div> </body> </html>
-
创建新的html文件,用于发送表单
<!-- 注意:该html文件没有通过组件的模板处理方法及xml配置文件,与组件进行组装,故该html文件内不能使用 任何thymeleaf语言标签 --> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <base href="http://localhost:8080/thymeleaf/" > <link rel="stylesheet" href="CSS/edit.css" > </head> <body> <div id="div_contain"> <div id="div_fruit_list"> <p class="center f32">新增库存信息</p> <form action="add.do" method="post"> <table id="tbl_fruit"> <tr> <th>cust_name</th> <td><input type="text" name="name" /></td> </tr> <tr> <th>cust_email</th> <td><input type="text" name="email" /></td> </tr> <tr> <th>cust_birth</th> <td><input type="text" name="birth" /></td> </tr> <tr> <th colspan="2"> <input type="submit" value="添加" /> </th> </tr> </table> </form> </div> </div> </body> </html>
-
创建新的html文件对应的Servlet组件
@WebServlet("/add.do") public class AddServlet extends ViewBaseServlet { private ListIpm listIpm = new ListIpm(); @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding("UTF-8"); Connection conn = null; try { String name = req.getParameter("name"); String email = req.getParameter("email"); String birth = req.getParameter("birth"); SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd"); Date date = format.parse(birth); conn = JdbcUtils.getConnection(); listIpm.addCustomer(conn,name,email,date); resp.sendRedirect("index"); } catch (Exception e) { e.printStackTrace(); } finally { JdbcUtils.closeResource(conn,null); } } }
六、对应上述二/三/四/五的增删改查操作的DAO内的接口和实现类
-
接口
public interface ListDAO { // 获取所有的客户信息 List<Customers> getList(Connection conn); // 根据id获取指定客户的信息 Customers getCustomerById(Connection conn,int id); // 修改指定的客户信息 void updateById(Connection conn, String name, String email, Date date, int id); // 删除指定的客户信息 void delById(Connection conn, int id); // 增加客户信息 void addCustomer(Connection conn,String name, String email, Date date); }
-
实现类
public class ListIpm extends BaseDAO<Customers> implements ListDAO{ @Override public List<Customers> getList(Connection conn) { String sql = "select cust_name name,cust_email email,cust_birth birth,cust_id id from customers"; return super.CommonRetrieve(conn, sql); } @Override public Customers getCustomerById(Connection conn,int id) { String sql = "select cust_name name,cust_email email,cust_birth birth,cust_id id from customers where cust_id" + " = ?"; return super.singleCommonRetrieve(conn,sql,id); } @Override public void updateById(Connection conn, String name, String email, Date date, int id) { String sql = "update customers set cust_name = ? , cust_email= ? , cust_birth = ? where cust_id = ?"; super.update(conn,sql,name,email,date,id); } @Override public void delById(Connection conn, int id) { String sql = "delete from customers where cust_id = ?"; super.update(conn,sql,id); } @Override public void addCustomer(Connection conn, String name, String email, Date date) { String sql = "insert into customers values(0,? , ? , ?)"; super.update(conn,sql,name,email,date); } }