关于尚硅谷2022JavaWeb中Thymeleaf出现的ViewBaseServlet的粗浅理解

起因

在学习JavaWeb的时候看了尚硅谷的2022版的课程里面有关于ViewBaseServlet不是很能理解。但这块又是Thymeleaf能智能识别静态资源的基础,对这块的不清晰的逻辑严重耽误我去理解其课程。所以我决定花费一些时间进行测试以及阅读源码,整理出一个逻辑链条,以帮助和我一样的萌新。

不过本片也是管中窥豹,只见一斑,所以会有很多错误,还希望各位大神能对此篇文章进行指正。

源码如下

package com.atguigu.myssm.myspringmvc;


import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.WebContext;
import org.thymeleaf.templatemode.TemplateMode;
import org.thymeleaf.templateresolver.ServletContextTemplateResolver;

import java.io.IOException;
public class ViewBaseServlet extends HttpServlet {

    private TemplateEngine templateEngine;

    @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);

    }

    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());
    }
}

分段进行分析:

第一部分

 public class ViewBaseServlet extends HttpServlet {            
                                                              
    private TemplateEngine templateEngine;   

templateEngine翻译过来就是模板引擎,里面封装了很多关于如何进行配置(从拼接字符串到如何找到这个文件),跳转页面,实现日志系统,等等。

我们这里所需要做的事是最后一行,处理数据,并跳转到相应的页面

如何跳转这需要慢慢道来。

第二部分

   @Override                                     
    public void init() throws ServletException { 

首先这里的init方法,是为了让tomcat调用而写的,其实重写的是GenericServlet的旗下的无参init方法

应该这么说:在你对HttpServlet进行继承后的类。是在工作的时候是被tomcat调用的类。因为查看源码无法查看到其依赖所以我推测是利用反射调用的。
而因为层层继承的原因,在调用你对象的时候默认的将前面所继承的东西也有一定程度上的调用。所以这里暴露出来的init只是为了方便开发者从而重写的方法。

        // 1.获取ServletContext对象                                 
        ServletContext servletContext = this.getServletContext();  

在这里获取全局的作用域。这里我的推测是:主要让thymeleaf知道需要从那个服务器进行解析

关于这点在看完所有的代码后就能一目了然。

        // 2.创建Thymeleaf解析器对象                                                                                                                                          
        ServletContextTemplateResolver templateResolver = 
        							new ServletContextTemplateResolver(servletContext);

这里开始正式使用Thymeleaf里的代码,templateResolver这个东西就是Thymeleaf解析器。
其类为:ServletContexTemplateResolver。

并需要将上述的作用域传入其中,这里我推测目的是进行解析服务器端,所以就需要全局的作用域作为解析的目标

        // 3.给解析器对象设置参数                              
        // ①HTML是默认模式,明确设置是为了代码更容易理解         
        templateResolver.setTemplateMode(TemplateMode.HTML); 

这里注释所说的非常完美,其目的就是需要让解析器知道默认的模板是什么。

        // ②设置前缀                                                         
        String viewPrefix = servletContext.getInitParameter("view-prefix");  
                                                                            
        templateResolver.setPrefix(viewPrefix);                             
                                                                            
        // ③设置后缀                                                         
        String viewSuffix = servletContext.getInitParameter("view-suffix"); 
                                                                            
        templateResolver.setSuffix(viewSuffix);                 

这里就是我所说那个页面从这里就可以通过web.xml文件进行配置后得到view_prefix以及view-shffix从而找到对应的文件,一般而言配置的文件时以html结尾的文件,所以前面那段设置的模板是html:代码入下:
templateResolver.setTemplateMode(TemplateMode.HTML);

如何进行找到?
拼接字符串后进行查找:

算法:view-prefix   后续需要传入的模板名称  view-shffix.
配置:  /         inedex       .html

       // ④设置缓存过期时间(毫秒)                      
        templateResolver.setCacheTTLMs(60000L);        
                                                       
        // ⑤设置是否缓存                                
        templateResolver.setCacheable(true);           
                                                       
        // ⑥设置服务器端编码方式                         
        templateResolver.setCharacterEncoding("utf-8");    

这段就是在进行解析器的一些配置,

        // 4.创建模板引擎对象                            
        templateEngine = new TemplateEngine();     

这里真正的创建模板引擎对象,以后所需要使用的都是这个模板引擎对象。

		 // 5.给模板引擎对象设置模板解析器                       
        templateEngine.setTemplateResolver(templateResolver);
                                                             
     } 

使用模板引擎对象,并之前的解析器里的数据封装到模板引擎中

第三部分

    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());        

这个函数主要是负责进行页面的跳转或者说是进行内部转发的。
.
个人推测这是需要req以及resp方法。

而将reqresp全局作用域进行封装到一个WebContext这个类里面。后期想要进行调用就比较简单,不需要分别将这个三个值进行处理。

而这个WebContext作用就是用来存储全局作用域以及其请求以及响应,以便于模板引擎所需要获取。

        // 3.处理模板数据                                                   
        templateEngine.process(templateName, webContext, resp.getWriter());
    }                                                                      
}

使用模板引擎进行处理传入的数据,其中有封装了响应的webContext,以及需要传入的模板的名称。

响应的打印:resp.getWriter().这个代码的作用就是用于跳转页面并进行显示。

总结

逻辑链条:
1.我们所有的操作都是使用Thymeleaf的templateEngine进行完成。
 (主要是用templateEngine的process方法)

2.但这个templateEngine需要有很多东西进行配置

3.比如要想要创建这个templateEngine需要知道解析器是什么

4.这些都是关于配置的问题,所以我们需要使用init方法进行初始化时进行配置。

5.所以需要我们首先创建解析器

6.而这个解析器的创建又是依赖性很强的东西,他需要知道你当前服务器的application级别的作用域

8.所以第一步要获取到你服务器现在application作用域
 ServletContext servletContext = this.getServletContext();

9.得到application作用域后就可以创建解析器了 :
 ServletContextTemplateResolver templateResolver =
          new ServletContextTemplateResolver(servletContext);

10.创建后的解析器不能立马使用,而是需要经过一些配置
配置默认模板为html配置前缀信息配置后缀信息配置是否需要缓存配置缓存的过期时间.

11.到这里开始解析器配置完毕所以需要创建模板引擎
 templateEngine = new TemplateEngine();

12.然后根据已经配置好的templateResolver进行传入到templateEngine
  templateEngine.setTemplateResolver(templateResolver);

13.现在一切都已经配置好了,但还没有开始,不要忘记了我们为什么要使用ViewBaseServlet,是因为它能将后台的活动的数据与静态的页面进行渲染。

14.所以它应该是个工具类,所以还需要一个工具方法。

15.这里自然创造了一个可以获取文件名,组装成文件的一种方法(函数)process函数

16.这个process函数不仅仅是将页面进行渲染,还需要进行跳转到对应的页面。

17.所以其三个参数不仅需要跳转页面的文件名,还需要servlet的请求响应的对应的对象,

18.所以process的函数的参数:
  (String templateName, HttpServletRequest req, HttpServletResponse resp)

19.支线:进入到这个process函数里需要设定响应的字符集以及媒体类型
  (说实话这个可以用过滤器做了,这句应该就可以不用写了,比较方便)

20.回到主线:当然我们发现req与resp两个参数作为请求响应很不好使用,原因就是因为分为两个。不利于很深的数据传参,所以我们封装到一个名为WebContext中。

21.所以拥有了这句代码:WebContext webContext = new WebContext(req, resp, getServletContext());

22.然后我们需要之前我们配置好的templateEngine使用其处理方法,并将:资源名称、WebContext、以及最重要的resp.getWriter()进行传入进去。

23.资源名称是为了将前面所配置的前缀与后缀连在一起找到资源

24.WebContext为了封装请求与响应作为参数很好往下传递,以外还有这其他的Thymeleaf内部的作用。(具体是什么我翻了源码看来半天不知道干什么,只知道了很多地方需要这个参数)

25.最后一个我为什么说很重要,因为resp.getWriter()这句代码就是为了能够跳转其资源页面和进行渲染的关键,只有拿到了这个才能进行跳转以及渲染。可以这么说这句话就是跳转和渲染必不可少的工具。

这是整体的逻辑链。

总结逻辑链条以及全篇:

通过继承HttpServlet从而进行配置,然后在到渲染的大致流程就如上面所说。
其中还有一些没有弄清楚的问题:
关于解析器用到上下文作用域,这个具体用处我看了源码还是不太明白。
主要不明白的点在于后期处理的时候封装成webContext里又封装了一遍这样的作用域,如果要使用为什么不从解析器里进行获取。

个人碎碎念:

总结来说尚硅谷的2022版本的JavaWeb真的对小白很不友好。但也不得不承认,在我管中窥豹般手斯完这个代码后。我自己看源码的能力,逻辑思维能力,总结能力也得到了很大的提升。对Servlet的理解又更上了一层。甚至有一瞬间感觉自己脑子好像长出来了(笑。

关于逻辑链的第25点也是我的一些推测,我JSP学的也不是很精,所以这点也是我的推论,这个推论主要源于桃桃老师所说的为什么对于这个方法(函数,我还是喜欢叫函数)具有转发的功能。

我希望我的这篇文章能给一部分正在求学路上的小白们一些帮助,但由于我也是新人,这篇文章也只是一窥Thymeleaf的冰山一小角。

所以肯定会有很多错误。希望大神看到这篇文章也一定要指正,于我,于其他求学的小白来说都非常重要。

你可能感兴趣的:(JavaWeb,servlet,java,apache)