SpringBoot08——Web03视图解析与模板引擎

什么是视图解析

 简单来说视图解析就是处理视图的一种手段

处理手段就是处理视图的方式

所以为了我们的数据能渲染到页面是上,我们需要视图解析

 怎么样进行视图解析

我们需要模板引擎来进行视图解析,但是springBoot不支持jsp模板引擎,所以在这边我们使用thymelea

 thymelea是一个现代化的服务端的java模板引擎,对于后端开发人员,语法比较简单

如何使用thymelea

方法:非常的简单,以下面这个例子

th:text就是把我们后台获取到的msg.headers.name,渲染到这个标签上,如果成功获取了,就渲染,没获取成功就用原来的

 基础的使用语法

 

 使用thymelea进行测试(在springboot01项目进行测试)

1.引入依赖

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>

2.底层是如何进行 thymelea自动配置的

 放入模板引擎解析器

 什么有前缀有后缀的

但是这些也都是从配置文件中进行绑定的

 还放入了模板引擎

还放入了视图解析器

 我们看到上面很多都使用了preperties,这个东西其实就是配置文件,是已经被写死的

里面也规定了页面放到哪里,也规定了使用的后缀为.html,所以的页面都会跳转到xxx.html

 

 3.开始测试

按照配置文件的要求,建立文件夹

按照官方模板,引入thymeleaf的名称空间

现在我们写th就有提示了

 

 前端从路径发起/LALALA请求,后端使用这个方法进行处理,并且传入一个model对象,model对象在请求域中放入值,最后跳转到success页面

注意:为什么我们不写全success.html,因为在源码里会自动给我们添加.html后缀

然后就会去templates目录下找success.html页面

@Controller
public class ViewTestController {

    @GetMapping("/LALALA")
    public String LALALA(Model model){
        model.addAttribute("msg","你好:LALALA");
        return "success";
    }
}

现在我们只要在标签中获取请求域中名称为msg的值就会自动把hhhhhhh渲染覆盖,但是如果单独拿出来这个前端页面,显示的只是hhhhhh,只有在服务器上运行才可以

<body>
<h1 th:text="${msg}">hhhhhhhh</h1>
</body><body>

现在放文本文件时th:text,我们也可以放入超链接

不同的是之前我们是修改文本的值,现在要修改a标签中href属性的值

 但是之前不是说超连接要放在@{}里面吗?

我们添加一个试试

我们发现第一个可以,第二个不行了 

查看网页发现

 我们第一个真正的解析了link的值,第二个只是把link当成了一个字符串地址去访问

也就是说第二个使用超链接的意思是想访问我们的项目路径

而且它还会根据需求自动拼装,如果我们给项目加了统一的请求路径

 

 它也自动的给我们加了/world

 使用thymeleaf快速构建后台管理系统

新建一个项目

 把前端需要的静态资源放入static文件夹中

 把登录页面放入templates文件夹中

 我们项目一旦进来(访问根路径)肯定需要展示的就是登陆页面,要写controller喽

注意包的路径

@Controller
//登录的controller
public class IndexController {
    
    //来登录页
    //处理请求,路径为/或者/login都可以跳转到login.html
    @GetMapping(value = {"/","/login"})
    public String loginPage(){

        //跳转到login.html
        return "login";
    }
}

现在可以成功的进入登录页面了

 现在我们的地址是这个/login,只要我们在这个/login用表单提交了账号和密码,我们就可以到首页(index.html)

所以我们带着表单提交应该是post请求,而且表单的acction应该是/login

引入名称空间

<form class="form-signin" action="http://view.jqueryfuns.com/2014/4/10/7_df25ceea231ba5f44f0fc060c943cdae/index.html" method="post" th:action="@{/login}">
        <div class="form-signin-heading text-center">
            <h1 class="sign-title">登录</h1>
            <img src="images/login-logo.png" alt=""/>
        </div>

 

 此时就会以post的方式拼接路径/login来发送请求,因为我们前面的进入登录页面也有/login,但是那个方式是get,现在我们使用post进行区分和提交表单

//从登录页面提交账号密码到主页
    @PostMapping("/login")
    public String main(String username,String password){
        return "index";
    }

此时就可以进入主界面了

但是现在有一个问题,我们在主界面的每一次刷新现在相当于都在对表单进行重复提交,有没有解决方法?

有的,我们现在只是经过了一次跳转,我们可以把登录成功之后设置为重定向,自己再定义一个get请求用于接收,这样子就不会造成表单的重复提交 

中间通过main.html这个中转重定向把post请求转换为了get请求

//从登录页面提交账号密码到主页
    @PostMapping("/login")
    public String main(String username,String password){
        //登录成功重定向到main页面
        return "redirect:/main.html";
    }

    //解决表单重复提交,现在是真正的要跳转到main页面
    @GetMapping("/main.html")
    public String mainPage(){
        return "main";
    }

 现在我们的表单只会提交一次了

现在我们如果一直刷新,刷新的只是这个请求,解决了重复提交

但是现在又出现一个问题,现在所有的浏览器只要访问我们的地址就可以访问到我们的主界面,但是我们想只有登录之后才可以访问到我们的主界面,有没有办法解决?

我们先模拟好用户对象,里面有账号名称和密码

@Data
public class user {
    private String userName;
    private String password;
}

在外面前端的表单里也填写好对应的名称

<input type="text" name="userName" class="form-control" placeholder="用户名" autofocus>
<input type="password" name="password" class="form-control" placeholder="密码">

后端接收post请求时,直接传入user对象,在方法中进行判断(现在简单的判断方法就是账号和密码(密码要为123546)有内容就可以登录了)

传入session,我们可以把可以登录的用户存储到session里面

我们也可以传入一个model,用于如果账号和密码都没有填写,我们再让它转发到登录页面(使用get请求的login)并且提示(放入model),账号密码错误

 

 现在只有输入正确的账号密码才能进入主界面了

但是怎么防止别人输入路径就能访问主界面呢?

我们之前把可以登录的用户放到session里面了

我们在真正的去main页面的方法里对于session里面有没有用户进行判断就可以了

//解决表单重复提交,现在是真正的要跳转到main页面
    @GetMapping("/main.html")
    public String mainPage(HttpSession session){
         Object loginUser = session.getAttribute("loginUser");
         if (loginUser!=null){
             //如果可登录的用户不为空,说明此时可以登录
             return "main";
         }else {
             //否则还是回到初始的登录页面
             //跳转到login.html
             return "login";
         }


    }

并且传入一个model信息给用户提示当前没有登录

//解决表单重复提交,现在是真正的要跳转到main页面
    @GetMapping("/main.html")
    public String mainPage(HttpSession session,Model model){
         Object loginUser = session.getAttribute("loginUser");
         if (loginUser!=null){
             //如果可登录的用户不为空,说明此时可以登录
             return "main";
         }else {
             //给用户提示
             model.addAttribute("msg","请重新登录");
             
             //否则还是回到初始的登录页面
             //跳转到login.html
             return "login";
         }


    }

实现给用户提示(我们的提示都放到model的msg里面了)

在前端表单上面添加内容

<label style="color: red" th:text="${msg}"></label>

<input type="text" name="userName" class="form-control" placeholder="用户名" autofocus>
<input type="password" name="password" class="form-control" placeholder="密码">

测试:

 

 直接访问路径(需要更换浏览器或者先清理缓存)

现在登录的问题大部分解决了,但是我们想登录之后在右上角实时显示登录的账号,可以吗?

可以,先找到前端所在的位置

 找到对应的代码

 但是我们之前的thymeleaf都是在标签里写的,这玩意没有标签

可以使用thymeleaf的行内写法

 我们之前把登录成功的人的登录信息都存放到loginuser里面了,所以现在直接拿出来用就可以了

我们已经完成了登录的基本流程,但是我们如果想点击其他的页面,还是会404,想办法把其他页面搞进来(以DataTables为例子)

把我们的静态页面放进来(我们的后台管理系统很大,所以我们可以新建一个文件夹,专门放table的文件)

并且给这几个新加的页面都添上thymeleaf名称空间

书写table的controller

@Controller
public class TableController {

    @GetMapping("/basic_table")
    public String basic_table(){
        return "tables/basic_table";
    }

    @GetMapping("/dynamic_table")
    public String dynamic_table(){
        return "tables/dynamic_table";
    }

    @GetMapping("/responsive_table")
    public String responsive_table(){
        return "tables/responsive_table";
    }

    @GetMapping("/editable_table")
    public String editable_table(){
        return "tables/editable_table";
    }


}

注意:为什么现在我们的return跳转需要加/tables呢?

因为我们之前源码里写了,只会找以templates目录下的,以.html结尾的文件,现在我们多了tables目录,所以需要自己加

我们现在的前端文件,超链接标签里的href一定也要改成和我们请求相互对应的(不能加.html)

例如

 现在都可以进来了

但是现在有一个问题,我们如果进入了一个table,我们在这里面切换其他的table会404

这是因为我们table里面的html里面的a标签的herf还是给我们加了.html,我们把这四个table里面的.html去掉就可以了

但是我们在去掉的时候发现一个问题,我们现在的四个table,他们的左边和上面部分其实都是一样的,我们可不可以把公共的这一部分提取出来?

首先观察重合的部分

1.引入的js文件

2.引入的部分css文件

 3..左侧导航栏部分

 4.头部内容信息

我们写一个公共的页面进行抽取

抽取公共的js

 抽取公共的css

 抽取公共的左侧导航栏和头部内容

现在我们已经对公共内容进行了抽取,如何应用?

首先加入thymeleaf名称空间

把我们的链接都使用thymeleaf包装起来

如何使用正确的语法进行抽取

下面这个例子我们的footer就是我们的公共内容

把公共的部分使用th:fragement=自己起的名字进行标注 

使用的时候使用th:insert=~{公共内容页面的名字::自己起的名字}进行引用

 或者直接th:insert=公共内容页面的名字::自己起的名字也可以

或者我们可以使用选择器

自己设置一个id

 使用的时候对应好自己的id,但是多一个#

现在对于我们的公共内容进行声明

 

 

 我们的使用方式也有好几种

 

insert会把我们的内容放在我们的div中 

 

 replace会把我们写的div给替换掉

 

 include会把我们公共内容外面的标签给去掉,直接把内容放在div中

 现在就可以真正的进行替换了

对于main

CSS

 JS

注意现在是选择器

左菜单

头部分内容

 现在的main.html是成功的

 那么我们对于其他四个页面全部都修改了

经过所有的替换,都是正常的

几个小bug

我们希望点击右上角的DashBoard,就跳转回主页

找到这个对于的超链接标签修改就可以了

 

我们现在的页面右上角又是不会动的了

 说明我们的公共页面没有成功引入

 

 现在可以了

我们还想点击退出就退到登录页

 可以了

现在我们完成了一些想要的功能,但是如果我们想遍历的把我们想加的数据加入先进的表如何做?

先制作一些假数据

 把现在的假数据放入请求域中

@GetMapping("/dynamic_table")
    public String dynamic_table(Model model){
        //表格内容的遍历
        List<User> users = Arrays.asList(new User("adasdad", "458613"),
                new User("adS", "458613"),
                new User("kuj", "458613"),
                new User("adastrsyhdad", "458613"),
                new User("vbcxcvb", "458613"));
        model.addAttribute("users",users);

        return "tables/dynamic_table";
    }

找到前端的表单,把请求域中的东西进行遍历

修改表头

 指明在请求域中遍历的对象,并且每次拿出来一个就放在一个user对象中

 获取用户名和密码

 现在测试

 除了我们想要的自动id没显示,其他的正常

我们可以加一个遍历的状态(看成对象),这个状态有自动递增id的方法

成功了

 

视图解析的源码分析(以登录方法开始测试) 

首先所有的请求都是从dispatchservlet的dodispatch方法开始

 找哪个handler(目标方法)能够处理当前的请求

 找handler的适配器(一个大的反射工具)

 使用适配器真正的执行方法

 进入适配器

执行目标方法

 设置参数解析器和返回值处理器

 

 执行真正的目标方法

 成功登录,重定向到/main.html

 执行完目标方法,跳转接口

 得到了我们的返回值,字符串的重定向操作

使用返回值处理器进行处理

 跳转接口,使用返回值处理器,看看哪个处理器能够解析现在的结果

 使用for循环寻找返回值处理器

 最终给我们选择的handler是

 为什么可以选择它?

因为它的判断条件是现在返回为空或者返回字符串(符合条件)

 回到

使用我们选择的处理器进行处理

 进入

 如果返回值是字符串,就把字符串封装到viewName

 把返回值放入mavContanier

 目标方法处理的过程中,所有数据都会被放在 ModelAndViewContainer 里面。包括数据和视图地址

判断是不是需要重定向

 它如何判断?判断我们的视图名是不是以重定向开头,我们是的

 这边的主要目的就是把我们的字符串重定向结果装起来

转换接口,向上返回(到了真正执行完目标方法的地方) 

 

现在我们的mavContranier

 为什么里面会有一个user?

因为我们传入的是一个user对象,也会被放到这个里面

方法的参数是一个自定义类型对象(从请求参数中确定的),把他重新放在 ModelAndViewContainer

继续向下执行

因为我们参数传入了一个model,所以会把我们model里面存放的值也放起来(如果登录失败提醒用户),但是现在我们登录成功,这里面没有值

 把我们的视图名和mavcon封装起来变成mav

 最后返回

方法执行结束后不用跳转接口,还是继续向下执行(会到真正的执行完方法并且封存好返回值mav之后)

任何目标方法执行完成以后都会返回 ModelAndView(数据和视图地址)。

当前方法执行结束后,再次跳转接口(回到真正执行完handle之后并且拿到封装好的视图和数据的mv)

 

 看看当前的mv是不是空,如果是空就跳转回原来的页面

 处理派发结果

 开始处理派发结果(不用跳转接口)

processDispatchResult 处理派发结果(页面改如何响应)

先判断处理期间有没有失败

 看看mv是不是空或者有没有被处理过(我们现在没有)

 使用rep和resp和mv进行渲染

render(mv, request, response); 进行页面渲染逻辑

不用跳转接口,还是在这个接口中

 从mv中拿到视图名

 

 使用视图解析器进行解析最后得到View对象

什么是view对象?

是一个接口

 规定了一个render方法,定义了页面的渲染逻辑

 根据方法的String返回值得到 View 对象【定义了页面的渲染逻辑】

不用跳转接口,现在想得到view对象

先判断视图解析器是不是空

 视图解析器的内容是

 for循环所有的视图解析器尝试是否能根据当前返回值得到View对象

 跳转接口,进入第一次循环,看看ContentNegotiating视图解析器可不可以

得到请求所有能接收的数据类型 

 

 获取候选的视图

如何获取候选的视图?不用跳转接口

 其实在这里面也是for循环所有的视图解析器,一个一个寻找(自己除外

 

 直到找到了thymeleaf视图解析器

 thymeleaf视图解析器如何判断?跳转接口

  thymeleaf视图解析器看看自己能不能创建一个View类型的对象

跳转接口进行创建尝试

 先看看视图名称是不是以重定向开头(是的

 对视图名称截取字符串,只保留/main.html 

截取之后进行new创建,真正的创建了View对象

ContentNegotiationViewResolver 里面包含了下面所有的视图解析器,内部还是利用下面所有视图解析器得到视图对象。 

最后向上返回

向上返回,返回到已经获取到了候选的视图

 现在的candidateView里面就是我们筛选出来候选的视图

 最终决定返回哪个页面,封装成View对象

 

 得到了 redirect:/main.html --> Thymeleaf new RedirectView()

最后向上返回我们需要的view对象

 视图对象调用自己的渲染方法

那么View是怎么渲染的呢?跳转接口

跳转接口

 获取到了想去页面的url地址

 最后使用url调用最原生的重定向方法

 最终完成了整个的重定向

 视图解析的源码分析(以向先进表中放数据方法开始测试)

 之前的流程一样,直到使用for循环到ContenNegotiating视图解析器看看能不能进行视图解析

 使用for循环看看哪个视图解析器能创建view对象

 来到thymeleaf的视图解析器

 看看是不是以重定向为开头(我们不是)

 看看是不是以转发为开头(我们不是)

 如果都不是就对页面进行加载

 不用跳转接口

先拿到IOC容器工厂

 在容器中找视图的名称,看看存不存在(现在不存在)

 如果不存在会准备一个视图对象

 创建view对象

 从容器中得到一个thymeleafView

 设置好模板的名称

 还要设置一堆东西,最后向上返回

 再次向上返回,使用thymeleafView进行渲染

 

 

使用thymeleafView进行渲染

 

 thymeleaf自己拿到模板引擎

 先拿到我们想给页面渲染的数据

 

 拿到我们想要响应的类型和编码

 利用模板引擎调用方法

 使用模板引擎,转换接口

 使用模板引擎直接把内容进行写出

 

 把页面内容进行刷出

 视图解析原理总结

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
【优质项目推荐】 1、项目代码均经过严格本地测试,运行OK,确保功能稳定后才上传平台。可放心下载并立即投入使用,若遇到任何使用问题,随时欢迎私信反馈与沟通,博主会第一时间回复。 2、项目适用于计算机相关专业(如计科、信息安全、数据科学、人工智能、通信、物联网、自动化、电子信息等)的在校学生、专业教师,或企业员工,小白入门等都适用。 3、该项目不仅具有很高的学习借鉴价值,对于初学者来说,也是入门进阶的绝佳选择;当然也可以直接用于 毕设、课设、期末大作业或项目初期立项演示等。 3、开放创新:如果您有一定基础,且热爱探索钻研,可以在此代码基础上二次开发,进行修改、扩展,创造出属于自己的独特应用。 欢迎下载使用优质资源!欢迎借鉴使用,并欢迎学习交流,共同探索编程的无穷魅力! 基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip 基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip 基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值