SpringBootMVC+thymeleaf模板初探

  我们已经学习了SpringBoo的基础概念和连接数据库。那么我们怎么利用SpringBoot实现我们的MVC架构,完成web站点的编码?今天就要介绍SpringMVC登场。

SpringMVC不是SpringBoot的一部分,但是SpringBoot让SpringMVC的编码更加快捷和方便。这中间的关系同SpringBoot与千千万万其他的Spring程序一样。
我们先来看SpringBoot的概念
Spring Web MVC是基于Servlet API构建的原始Web框架,从一开始就已包含在Spring框架中。正式名称“ Spring Web MVC”来自其源模块的名称,但更通常称为“ Spring MVC”。
概念很绕,也没有说个所以然,所以我们通过上手实战来进一步了解:
  1. 首先我们要有一个SpingBoot的项目.引入web的Start配置。
  2. 接下来我们就要创建一个Controller文件了,同asp.netMvc一样:从代码整洁角度上来说,应该按照约定将Contoller文件创建到专门的Contoller文件夹中,并以Conttoller结尾命名。只要我们给一个类打上@Controller的标志,Spring就会认为这是一个控制器并初始化它。
@Controller
public class IndexController {
}

     3.  紧接着我们创建具体的控制器方法,并打上@RequestMapping的标志路由地址。

 

@Controller
public class IndexController {
    @RequestMapping("/")
    public  String index(Model model){
        System.out.println("控制器方法被调用");
        return "index";
    }
}

  4. 创建对应的index页面

  5. 编译运行。

一个简单的mvc页面已经被创建并运行了,虽然目前什么也没有。但是我们已经得到一个极其简单的、空的mvc程序了。
为了把我们的例子显得更加正式一点,更加接近真实的程序,我们进一步引入一个帮手:Thymeleaf模板。
我们先来看看Thymeleaf的概念。
Thymeleaf是面向 Web 和独立环境的现代服务器端 Java 模板 引 擎,能够处理 HTML 、XML 、JavaScript 、 css 甚至纯文本,可 以作为 MVC Web 应用层的 View 层显示数据。Spring Boot 建议使用 HTtml 来完成动态页面。 Spring Boot 提供了大量的模板 引 擎, 包括 Thymeleaf、 FreeMarker 、 Velocity 等。Spring Boot 官方推荐使用 Thymeleaf 模板引 擎来完成动态页面,并且为 Thymeleaf 提供 了完美的 SpringMVC 的支持, Thymeleaf模板 引擎可以支持纯 HTML 浏览器展现 (模板表达式在脱离运行环境下不污染 HTML 结构)。
Thymeleaf它是一个模板引擎,和vue等前端框架不是一回事,也不冲突。完全可以将Thymeleaf和Vue并存,甚至抛开Thymeleaf,完全采用html+前端框架的Rest前后端分离。
在正式把Thymeleaf加入到SpringBoot之前,我们先大致了解一下Thymeleaf的语法。
我们看一段html
<input type="text" name="username" value="fkjava" th:value="${ user.username }" />

  这一段的意思是:Thymeleaf模板会把存储在它上下文中的user对象的username作为value赋值给name为username的text控件(如果user对象的username有数据,否则显示默认的fkjava)。

再看一个字符串拼接操作
<span th:text="'Welcome to fkit, '+ ${user.name} +'!'">

  再看一个静态资源引入的例子:

 

<link rel="stylesheet" th:href="@{bootstrap/dist/css/bootstrap.css}"/>
<link rel="stylesheet" th:href="@{bootstrap/dist/css/bootstrap-theme.css}"/>
<script type="text/javascript" th:src="@{jquery/dist/jquery.js}"></script>
<script type="text/javascript" th:src="@{bootstrap/dist/js/bootstrap.js}"></script>

  th:href还可以带参数

代码分析
1.最终解析的href为:    
  /seconddemo/    
  /seconddemo/usethymeleaf?name=Dear 相对路径,带一个参数   
  /seconddemo/usethymeleaf?name=Dear&alis=Dear 相对路径,带多个参数
  /seconddemo/usethymeleaf?name=Dear&alis=Dear 相对路径,带多个参数
  /seconddemo/usethymeleaf/Dear 相对路径,替换URL一个变量
  /seconddemo/usethymeleaf/Dear/Dear 相对路径,替换URL多个变量
2.URL最后的(name=${name})表示将括号内的内容作为URL参数处理,该语法避免使用字符串拼接,大大提高了可读性
3.@{/usethymeleaf}是Context相关的相对路径,在渲染时会自动添加上当前Web应用的Context名字,假设context名字为seconddemo,那么结果应该是/seconddemo/usethymeleaf,即URL中以”/“开头的路径(比如/usethymeleaf将会加上服务器地址和域名和应用cotextpath,形成完整的URL。
4.th:href属性修饰符:它将计算并替换使用href链接URL 值,并放入的href属性中。
5.th:href中可以直接使用静态地址

  Thymeleaf模板的语法以th开头。${} 表示要处理的数据。

具体可以看下表
th:with 赋值的一种,将prodStat.cout的值%2之后判断的布尔值赋值给isEvents,并将isEvents加入到Thymeleaf的上下文。
th:each 循环users的值,并将循环结果赋值到user变量中。
th:if 满足则显示,th:unless 不满足则显示
$表达式只能写在 th 标签内部,否则不会生效。
Thymeleaf还有很多功能,如内置对象:#dates 日期格式化内置对象等等。具体可以看看:https://fanlychie.github.io/post/thymeleaf.html
 
语法大致了解之后,我们来看看如何将Thymeleaf引入SpringBoot项目中。
  1. 修改pom.xml
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

    2. Html页面上增加Thymeleaf开关。

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">

  

  我们重新回到SpringMVC,并尝试将它和Thymeleaf模板引擎结合起来。我们看之前的入门代码

@Controller
public class IndexController {
    @RequestMapping("/")
    public  String index(Model model){
        System.out.println("控制器方法被调用");
        return "index2";
    }
}

  有几个关键点值得注意:

@Controller标签 表示这是一个控制器,并且最终控制器会将信息转发到return的页面,如本例中,index控制器运行完毕之后,最后会转跳到index2.html
@RequestMapping和路由相关,SpringMVC的路由通过@RequestMapping标签来控制。/代表根目录默认地址,如localhost:8080,如果我们把标签改成@RequestMapping(/test),那么就必须访问localhost:8080/test才可以访问到该控制器。
 
那么SpringMVC如何实现前后台值的传递呢?
Model是每次请求中都存在的默认参数,利用其addAttribute()方法即可将服务器的值传递到页面中。这是一种前后台传值的办法。类似的还有很多如:
@RequestMapping("/iftest")
public String iftest(WebRequest webRequest){
    webRequest.setAttribute("username","fkit",webRequest.SCOPE_REQUEST);
    webRequest.setAttribute("age",21,webRequest.SCOPE_REQUEST);
    webRequest.setAttribute("role","admin",webRequest.SCOPE_REQUEST);
    return  "success2";
}

@RequestMapping("/eachtest")
public  String eachtest(WebRequest webRequest){
    List<Book> books = new ArrayList<>();
    books.add(new Book(1,"Book1","1.jpg","wenpeng",108.00));
    books.add(new Book(2,"Book2","2.jpg","wenpeng",18.00));
    books.add(new Book(3,"Book3","3.jpg","wenpeng",128.00));
    books.add(new Book(4,"Book4","4.jpg","wenpeng",118.00));
    books.add(new Book(5,"Book5","5.jpg","wenpeng",8.00));
    webRequest.setAttribute("books",books,webRequest.SCOPE_REQUEST);
    return "success3";
}

  WebRequst通过setAttribute封装信息到username,age,甚至是books这种类的结合变量中。前台就可以接受并处理。

  sucess2.html
<div class="container">
    <div class="row">
        <div class="col-md-4">
            <p>th:if中条件成立时才显示结果</p><br/>
            <span th:if="${username != null}">username 不为空</span><br/>
            <span th:if="${age != null}">age不为空</span><br/>
            <p>th:unless与th:if相反,只有条件不成立,才会显示</p><br/>
            <span th:unless="${address != null}">address为空</span><br/>
            <p>th:swich,默认选中为*</p><br/>
            <div th:switch="${role}">
                <p th:case="'admin'">管理员</p>
                <p th:case="'guest'">来宾</p>
                <p th:case="'*'">其他</p>
            </div>
        </div>
    </div>
</div>

  success3.html

<div class="table table-responsive">
    <table class="table table-bordered table-hover">
        <thead>
            <th class="text-center">封面</th>
            <th class="text-center">书名</th>
            <th class="text-center">作者</th>
            <th class="text-center">价格</th>
        </thead>
        <tbody class="text-center">
            <tr th:each="book:${books}">
                <td><img src="img/1.jpg" th:src="@{'img/'+${book.image}}" height="60"></td>
                <td th:id="${book.id}" th:text="${book.title}" >书名</td>
                <td th:id="${book.id+'name'}" th:text="${book.author}">作者</td>
                <td th:text="${book.price}">价格</td>
            </tr>
        </tbody>
    </table>
</div>

  再譬如

@RequestMapping("/regexptest")
public  String regexptest(HttpServletRequest request,
                          HttpSession session){
    request.setAttribute("book","Spring boot 教材");
    session.setAttribute("school","中地数码");
    request.getServletContext().setAttribute("name","Thymeleaf 模板引擎");
    return "success1";
}

  通过HttpServletRequest ,HttpSession 等封装消息,在前台使用。

  succes1
<div class="container">
    <div class="row">
        <div class="col-md-4">
            <p>${param.x}将返回一个名为x的请求参数</p>
            访问页面传递的参数:<span th:text="${param.loginName[0]}">登录名</span>
            &nbsp;<span th:text="${param.password[0]}">密码</span>
            <p>${x}将返回存储在Thymeleaf上下文中的变量x或作为请求Request作用范围域中的属性</p>
            访问request作用域中的变量:<span th:text="${book}">图书信息</span>
            <p>$ {session.x }将返回一个名为 x 的会话 HttpSession作用范围域中的属性。</p>
            访问 sessioni 作用范围域中的变量:<span th:text="${ session .school}">培训中心</span>
            <p>${app lication.xi 将返回一个名为 x 的全局 ServletContext 上下文作用范围域中的属性。</p>
            访问 application 作用范围城中的变量:<span th:text="${application.name}">动态页面模板</span><br/>
        </div>
    </div>
</div>

  再比如

@PostMapping("login")
public ModelAndView login(@RequestParam("loginName") String loginName,
                          @RequestParam("password") String password,
                          ModelAndView mv)
{
    System.out.println("登录验证后台方法被调用");
    System.out.println("登录名:"+loginName+"密码:"+password);
    mv.setViewName("redirect:/main");
    return mv;
}

  这个地方肯能有人要问了,怎么一会postMapping一会requestmapping呢?

其实不止有@PostMapping,还有@GetMapping,@PutMapping、@DeleteMapping、@PatchMapping等等。但是他们都可以通过@RequestMapping(method=RequestMethod)用@RequestMapping代替。如@GetMapping等于@RequestMapping(method = RequestMethod.GET)
 
  前台页面
<div class="panel panel-primary">
    <div class="panel-heading">
        <h3 class="panel-title">Spring Boot thymeleaf 示例</h3>
    </div>
</div>
<div class="container">
    <div class="row">
        <div class="page-header">
            <h2>用户登录</h2>
            <form class="form-horizontal" action="login" method="post" id="loginform">
                <div class="form-group">
                    <div class="input-group col-md-4">
                        <span class="input-group-addon">
                            <i class="glyphicon glyphicon-user"></i>
                        </span>
                        <input class="form-control" placeholder="用户名/邮箱" type="text" name="loginName" id="loginName"/>
                    </div>
                </div>
                <div class="form-group">
                    <div class="input-group col-md-4">
                        <span class="input-group-addon">
                            <i class="glyphicon glyphicon-lock"></i>
                        </span>
                        <input class="form-control" placeholder="密码" type="password" name="password" id="password"/>
                    </div>
                </div>
                <div class="form-group">
                    <div class="col-md-4">
                        <div class="btn-group btn-group-justified">
                            <div class="btn-group">
                                <button type="button" class="btn btn-success" id="loginbtn">
                                    <span class="glyphicon glyphicon-log-in"></span>
                                    登录
                                </button>
                            </div>
                            <div class="btn-group">
                                <button type="button" class="btn btn-danger" id="regiserbtn">
                                    <span class="glyphicon glyphicon-edit"></span>
                                    注册
                                </button>
                            </div>
                        </div>
                    </div>
                </div>
            </form>
        </div>
    </div>
</div>

  但是上面的方法都属于前后端严重耦合的,不适用于前后端分离的大趋势。我们想要做出标准的前后段分离的Rest请求怎么办?

@RestController
public class JsonController {
    @RequestMapping("/findBook")
    public Book findBook(@RequestBody Book book){
        System.out.println(book);
        book.setAuthor("文菜鸟");
        book.setImage("1.jpg");
        book.setPrice(58.0);
        return book;
    }
}

  需要注意到@Controller,@RestController标注的区别。@Controller是传统控制器,turn返回的是具体的页面地址,通过路由转跳;@RestController是Rest风格的WebAPI,返回是json格式化的对象。

  前台页面
<script type="text/javascript">
    $(document).ready(function () {
        $("#2").click(function () {
            findBook();
        });
    })

    function findBook() {
        $.ajax({
            url:"/findBook",
            dataType:"json",
            type:"post",
            contentType:"application/json",
            data:JSON.stringify({id:2,title:"Book2"}),
            async:true,
            success:function (data) {
                console.log(data);
                $("#2name").html(data.author)
            },
            error:function (er,a,b) {
                alert("数据发送失败");
            }
        })
    }
</script>

  总结一下,今天我们学习了如何使用SpringBoot搭建一个简单的MVC程序,以及如何配合thymeleaf模板工作,并且学习了SpringMVC前后端数据交互的方法。以及前后端分离情况下,如果使用WebAPI进行开发的模式。但是使用thymeleaf和SpringMVC真的是唯一的方式么?欢迎大家讨论。

 

 

 

posted @ 2020-01-10 16:37  文鹏  阅读(771)  评论(0编辑  收藏  举报