1.项目结构搭建
2.相关依赖
<!--freemaker模块支持,它和普通的freemarker依赖不同-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<!--加上引擎依赖,会访问templates目录-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!--热部署支持(修改后即时刷新,不需要重启)-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
3.application.properties配置
#templates(thymeleaf...)相关,定义访问规则,覆盖thymeleaf依赖默认访问设置
spring.thymeleaf.suffix=.html
spring.thymeleaf.prefix=classpath:/
#spring.thymeleaf.prefix=classpath:/static/
#freemarker相关
#spring.freemarker.template-loader-path=classpath:/templates/demo/
#spring.freemarker.suffix=.ftl
spring.freemarker.template-loader-path=classpath:/templates/
spring.freemarker.suffix=.ftl
4.静态资源文件相关
static/ index.html
<html>
<head>
<title></title>
</head>
<body>
<h1>hello[static/index.html]</h1>
</body>
</html>
templates/demo index.html
<html>
<head>
<title></title>
</head>
<body>
<h1>hello[templates/demo/]</h1>
</body>
</html>
list.ftl
<html>
<head>
<meta charset="utf-8">
<title>freemarker+bootstrap学习</title>
<#--本地引入-->
<#--<link href="bootstrap/css/bootstrap.min.css" rel="stylesheet">-->
<!-- 直接引入 -->
<link rel="stylesheet" href="https://cdn.staticfile.org/twitter-bootstrap/4.1.0/css/bootstrap.min.css">
</head>
<body>
<div class="container-fluid">
<div class="table-responsive">
<table id="dataGrid" class="table table-striped table-bordered">
<thead>
<tr>
<th width="50"><input type="checkbox" class="checkall"></th>
<th>标题</th>
<th>姓名</th>
<th>微信</th>
<th width="50">操作</th>
</tr>
</thead>
<tbody>
<#list demoList as demo>
<tr>
<td>
<input type="checkbox" name="num" value="${demo.num}">
</td>
<td>${demo.title}</td>
<td>${demo.name}</td>
<td>${demo.weixin}</td>
<td>
<button class="btn btn-danger"
onclick="deleteRow(${demo.num})">删除
</button>
</td>
</tr>
</#list>
</tbody>
</table>
</div>
</div>
<script type="text/javascript">
function deleteRow(num) {
console.log("点击删除了标题", num);
}
</script>
</body>
</html>
list2.ftl
<html>
<head>
<meta charset="utf-8">
<title>freemarker+bootstrap学习</title>
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet" href="https://cdn.staticfile.org/twitter-bootstrap/4.1.0/css/bootstrap.min.css">
</head>
<body>
<div class="container-fluid">
<div class="row clearfix">
<div class="col-md-12 column">
<table class="table table-bordered table-condensed table-striped">
<thead>
<tr>
<th>id</th>
<th>姓名</th>
<th>微信</th>
<th colspan="2">操作</th>
</tr>
</thead>
<tbody>
<#list pageData as rowData>
<tr>
<td>${rowData.num}</td>
<td>${rowData.name}</td>
<td>${rowData.weixin}</td>
<td>
<#if rowData.num%2 == 0>
<a href="#">下架</a>
<#else>
<a href="">上架</a>
</#if>
</td>
</tr>
</#list>
</tbody>
</table>
</div>
<#--分页-->
<div class="col-md-12 column">
<ul class="pagination ">
<#if currentPage lte 1>
<li class="disabled"><a class="page-link" href="#">上一页</a></li>
<#else>
<li>
<a class="page-link" href="/pageList?page=${currentPage -1}&size=${size}">上一页</a>
</li>
</#if>
<#--1..n:遍历n次-->
<#list 1..totalPage as index>
<#if currentPage == index>
<li class="page-item active"><a class="page-link" href="#">${index}</a></li>
<#else>
<li><a class="page-link" href="/pageList?page=${index}&size=${size}">${index}</a></li>
</#if>
</#list>
<#if currentPage gte totalPage>
<li class="disabled"><a class="page-link" href="#">下一页</a></li>
<#else>
<li>
<a class="page-link" href="/pageList?page=${currentPage + 1}&size=${size}">下一页</a>
</li>
</#if>
</ul>
</div>
</div>
</div>
</body>
</html>
templates/ index.html
<html>
<head>
<title></title>
</head>
<body>
<h1>hello[templates/index.html]</h1>
</body>
</html>
resources/ index.html
<html>
<head>
<title></title>
</head>
<body>
<h1>hello[resources/index.html]</h1>
</body>
</html>
5.创建实体类
package com.example.demo;
public class Demo {
private int num;
private String title;
private String name;
private String weixin;
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getWeixin() {
return weixin;
}
public void setWeixin(String weixin) {
this.weixin = weixin;
}
@Override
public String toString() {
return "Demo{" +
"num=" + num +
", title='" + title + '\'' +
", name='" + name + '\'' +
", weixin='" + weixin + '\'' +
'}';
}
public Demo() {
}
public Demo(int num, String title, String name, String weixin) {
this.num = num;
this.title = title;
this.name = name;
this.weixin = weixin;
}
}
6.测试
package com.example.demo.controller;
import com.example.demo.Demo;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
//@RestController//配合@ModelAndView才能返回到页面
@Controller
public class DemoController {
@GetMapping("/demoList")
public ModelAndView list(Map<String, Object> map) {
List<Demo> demoList = new ArrayList<>(4);
demoList.add(new Demo(1, "标题1", "编程小石头1", "2501902696"));
demoList.add(new Demo(2, "标题2", "编程小石头2", "2501902696"));
demoList.add(new Demo(3, "标题3", "编程小石头3", "2501902696"));
demoList.add(new Demo(4, "标题4", "编程小石头4", "2501902696"));
map.put("demoList", demoList);
System.out.println("1");
// return new ModelAndView("demo/index", map);//ok
return new ModelAndView("demo/list", map);
}
//springboot默认情况下可以访问static目录,却不能访问templates目录
@RequestMapping(value = "/index",method = {RequestMethod.GET})
public String goIndex(){
return "index";//有thymeleaf依赖,后缀可有可无都能返回到指定页面
// return "index.html";//没有thymeleaf依赖,必须有后缀才能返回到指定页面(也可以直接url访问static下的静态资源)
}
@RequestMapping(value = "/testResources",method = {RequestMethod.GET})
public String testResources(){
System.out.println("[testResources]");
return "index.html";
}
}
package com.example.demo.controller;
import com.example.demo.Demo;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@RestController
public class DemoController2 {
/**
* 分页
* @param page 当前页
* @param size 单页容量
* @param map
* @return
*/
@GetMapping("/pageList2")
public ModelAndView list(@RequestParam(value = "page", defaultValue = "1") Integer page,
@RequestParam(value = "size", defaultValue = "2") Integer size,
Map<String, Object> map) {
System.out.println("[pageList]");
List<Demo> demoList = new ArrayList<>();
demoList.add(new Demo(1, "标题1", "编程小石头1", "2501902696"));
demoList.add(new Demo(2, "标题2", "编程小石头2", "2501902696"));
demoList.add(new Demo(3, "标题3", "编程小石头3", "2501902696"));
demoList.add(new Demo(4, "标题4", "编程小石头4", "2501902696"));
demoList.add(new Demo(5, "标题5", "编程小石头5", "2501902696"));
demoList.add(new Demo(6, "标题6", "编程小石头6", "2501902696"));
demoList.add(new Demo(7, "标题7", "编程小石头7", "2501902696"));
int start = (page - 1) * 2;//偏移量
int end = start + size;
List<Demo> subList = demoList.subList(start, end);
//Math.ceil(11.46)=Math.ceil(11.68)=Math.ceil(11.5)=12
//Math.ceil(-11.46)=Math.ceil(-11.68)=Math.ceil(-11.5)=-11
int totalPage = (int) Math.ceil(demoList.size()/size);//总页数
map.put("pageData", subList);//单页数据
map.put("totalPage", totalPage);//总页数
map.put("currentPage", page);
map.put("size", size);
return new ModelAndView("demo/list2", map);
}
}