微服务专题02-Spring Web MVC 视图技术

前言

前面的章节我们讲了Spring Application。本节,继续微服务专题的内容分享,共计16小节,分别是:

本节内容重点为:

  • Thymeleaf 视图技术:介绍新一代视图技术 Thymeleaf ,包括其使用场景、实际应用以及技术优势
  • 视图解析:介绍 Spring Web MVC 视图解析的过程和原理、以及内容协调视图处理器的使用场景
  • 国际化:利用Locale技术,实现视图内容的国际化

上节回顾

在上一节,主要针对于SpringBoot的main class -> SpringApplication进行了详细说明,在Spring 注解编程模型我们提到了一个元注解 @Component ,所以这里我想补充一下Spring关于这个注解的实际应用是怎么样的一个流程?

@ComponentScan -> @Confiugration Class -> basePackages -> @Component Beans ->

@ComponentScan扫描带有 @Confiugration 的类

BeanDefinition -> BeanDefinitionRegistry ->

DI

Beans -> BeanFactory -> getBean or @Autowired

IOC

实际上就是我们所谓的IoC/DI -> 注入Bean后再获取Bean,现在很多人过分强调IOC/DI,其实没必要,在IOC/DI背后更核心的内容重要的我们要理解Bean的生命周期

Bean 生命周期

实例化 :Bean Class -> Bean Object

初始化前 -> Bean before/pre init()

  • org.springframework.beans.factory.config.BeanPostProcessor#postProcessBeforeInitialization

初始化 -> init()

  • org.springframework.beans.factory.InitializingBean#afterPropertiesSet()

初始化后

  • org.springframework.beans.factory.config.BeanPostProcessor#postProcessAfterInitialization()

销毁

  • org.springframework.beans.factory.DisposableBean#destroy()

主要内容

Thymeleaf 视图技术

Thymeleaf 是新一代 Java 模板引擎,它类似于 Velocity、FreeMarker 等传统 Java 模板引擎,但是与传统 Java 模板引擎不同的是,Thymeleaf 支持 HTML 原型。

推荐一个开源项目,Spring Boot Starter 支持模板引擎 velocity 以及 velocity tools,该Starter是官方Starter的补充https://github.com/alibaba/velocity-spring-boot-project

渲染上下文(模型) Model

  1. 接口类型
  • Model
  • ModelMap
  • ModelAndView : 包含 Model 和 View 两个模块。
  1. 注解类型

@ModelAttrtibute: 常用于功能处理方法的入参上,返回值以及控制器的一般方法(非功能处理方法)上。

EL 表达式

  1. 字符值
  2. 多种数据类型
  3. 逻辑表达式(if else)
  4. 迭代表达式(for each / while)
  5. 反射(Java Reflection / CGLib)

视图解析

模板寻址

Q:如何通过配置文件设置模板的完整路径?

A:公式为:prefix(文件前缀) + view-name(视图名称) + suffix(文件后缀)。
在这里插入图片描述
模板文件存放的路径为:

classpath:/templates/thymeleaf/index.dota2

在 Spring 源码里,我们也可以看到 view 的实际存放地址,印证了我们前面的猜想:

view.setUrl(getPrefix() + viewName + getSuffix())

模板缓存

在配置文件中设置模板缓存, Cache = false/true,如下所示:

在这里插入图片描述

# Thymeleaf 配置
# 默认 Cache = true 
spring.thymeleaf.cache = false
spring.thymeleaf.prefix = classpath:/templates/thymeleaf/
spring.thymeleaf.suffix = .dota2

Spring MVC 模板渲染逻辑

接下里,分别以 M (Model)、V (View)、C (Controller)三个方面解析 Spring MVC 模板渲染的逻辑。

C (Controller)

C 指的控制层,核心控制类是:DispatcherServlet

M(Model)

在前面,有关 Spring MVC 的部分我们已经提到过,Model 可以分为接口类型和注解类型。现在我们用模板引擎来定义 Model:通用的方式就是以模板上下文形式,内建/内嵌自己工具变量。

关于模板引擎,我们这里提两种不同的实现 JSP 内置(built-in)九大变量,与 Thymeleaf 内置变量。

我们知道,JSP 其实就等同于 Servlet。所谓内置,指的是通过 JSP 模板引擎控制的。

首先看一下,Jsp 九大内置对象都有哪些?

内置对象名 类型 关注点
request HttpServletRequest 关注当前请求
response HttpServletResponse 关注相应结果
config ServletConfig 关注配置
Application ServletContext 关注当前应用
session HttpSession 关注当前会话
Exception Throwable 关注异常
page Object(this) 关注 Jsp 对象
out JspWriter 关注输出
pageContext PageContext 关注当前页面

Q:那么 Thymeleaf 是否也像 Jsp 一样有内置变量呢?

A: 有的,上下文(模型),比如 strings、 numbers 等工具类。业务逻辑可以参考源码 StandardExpressionObjectFactory 使其构建 IExpressionContext

同样的我们也可以自定义内置对象:

   public static class StringUtil {
    
    

        public StringUtil() {
    
    
        }

        public boolean isNotBlank(String value) {
    
    
            return StringUtils.hasText(value);
        }

    }

在这里插入图片描述
在页面上引用:
在这里插入图片描述
同样的我们也可以自定义内置对象:

   public static class StringUtil {
    
    

        public StringUtil() {
    
    
        }

        public boolean isNotBlank(String value) {
    
    
            return StringUtils.hasText(value);
        }

    }

在页面上引用:
在这里插入图片描述
页面输出:
在这里插入图片描述

V(View)

我们先来说说视图对象,通常有三种,分别是 Servlet、Spring MVC 、Struts。

Servlet:

Servlet通过以下三个类处理视图逻辑,分别是:

  • RequestDispatcher#forward
  • RequestDispatcher#include
  • HttpServletResponse#sendRedirect

Spring MVC:

等同于 forward 功能的类: InternalResourceView,等同于 redirect 功能的类: RedirectView

Struts:

核心处理类是: Action,详细划分为:ForwardActionRedirectAction

接下来我们说说视图处理对象,通常有 Spring MVC 与 Struts 两种框架处理:

  • Spring MVC

Spring MVC处理流程:首先会接受到前端的请求,通过我们前面提到的正则表达式过滤拦截以 *.do 为结尾的所有请求,交给 DispatcherServlet 处理, 进而交给 Controller ,处理完毕后将 View 视图交给 ViewResolver 解析,最后通过 View#render 方法返回给 HTML ,通过 HttpServletResponse 最后完成整个调用逻辑。

这里主要讲一下 Thymeleaf 与 JSP 处理视图过程的异同。

1、Thymeleaf

Thymeleaf 两大核心处理组件。一个是 ViewResolver 进而调用 ThymeleafViewResolver。另一个是 View 进而调用 ThymeleafView

整个过程可以简单地概括为三步:

  • 通过模板名称解析模板资源(ClassPathResource),处理逻辑可以参考类 TemplateResolution
  • 读取资源,并且渲染内容 HTML。处理逻辑可以参考 IEngineContextProcessorTemplateHandler
  • HTML 内容输出到 Response

Thymeleaf 处理过程的源码路径可以参考:方法 org.thymeleaf.TemplateEngine#process(org.thymeleaf.TemplateSpec, org.thymeleaf.context.IContext, java.io.Writer) 和 方法 org.thymeleaf.engine.TemplateManager#parseAndProcess

2、JSP

通常我们可以在配置文件里对于JSP做如下配置:

  <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
      <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
      <property name="prefix" value="/WEB-INF/jsp/"/>
      <property name="suffix" value=".jsp"/>
  </bean>

区别于 Thymeleaf,JSP在处理 ViewResolver 时调用的是 InternalResourceViewResolver。处理 View 调用 JstlView。而 Thymeleaf 的重定向则依靠于 RequestDispatcher

  • Struts

Struts 处理流程:首先会接受到前端的请求,通过正则表达式过滤拦截以 *.do 为结尾的所有请求,交给 ActionServlet 处理 进而调用 Action,而 Action 会通过 ForwardAction 将请求重定向到 RequestDispatcher,进而将处理结果交给 JSP(Servlet),这样就会渲染在 HTML 页面上,通过 HttpServletResponse 最后完成整个调用逻辑。

重点内容

学习方法

学会配置代码

假设需要了解的技术是 thymeleaf ,那么可以猜想是否有 thymeleaf 的 Properties,所以果断在源码里搜索 ThymeleafProperties

第一步:找到 @ConfigurationProperties,确认前缀

@ConfigurationProperties(prefix = "spring.thymeleaf")
public class ThymeleafProperties {
    
    
    //此类说明:thymeleaf 可以通过以 spring.thymeleaf 为前缀的配置项进行个性化配置。
}

第二步:进一步确认 ThymeleafProperties 的属性字段,是否字段和属性名一一对应。

@ConfigurationProperties(prefix = "spring.thymeleaf")
public class ThymeleafProperties {
    
    
    ...
	private boolean cache = true;
    ...
    private String mode = "HTML";
    ...
}

根据此类的属性,我们可以这样配置 thymeleaf:

spring.thymeleaf.cache
spring.thymeleaf.mode=HTML

Tips:同样的,我们要学习 MessageSource ,那么也可以根据 MessageSource + Properties = MessageSourceProperties 这样的推论,依次找到配置项前缀 spring.messages 等信息。

有了这里理论依据,我们就根据此仿造写一个demo。

thymeleaf 的案例演示

父子pom配置详见本节末github地址。这里给出核心代码结构:
在这里插入图片描述
页面渲染,index.html

<!DOCTYPE html>

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

<head>
    <title>Good Thymes Virtual Grocery</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <link rel="stylesheet" type="text/css" media="all"
          href="../../css/gtvg.css" th:href="@{/css/gtvg.css}" />
</head>

<body>

<p th:text="#{home.welcome}">Welcome to our grocery store!</p>

<p th:text="${!#strings.isEmpty(message)}">Message!</p>

</body>

</html>

IndexController

@Controller
public class IndexController {
    
    

    @GetMapping({
    
    "/", ""})
    public String index(Model model) {
    
    
        model.addAttribute("string",new StringUtil());
        return "index";
    }

    public static class StringUtil {
    
    

        public StringUtil() {
    
    
        }

        public boolean isNotBlank(String value) {
    
    
            return StringUtils.hasText(value);
        }

    }

    @ModelAttribute(name = "message")
    public String message() {
    
    
        return "Hello,World";
    }
}

index.dota2
在这里插入图片描述
走你!
在这里插入图片描述
成功渲染页面!

学会打断点

DispatcherServlet#doDispatch -> 拦截请求
在这里插入图片描述
Controller -> 执行业务,并且控制视图
在这里插入图片描述
DispatcherServlet#resolveViewName -> 视图解析

在这里插入图片描述
DispatcherServlet#render -> 视图渲染

在这里插入图片描述

接着调用DispatcherServlet#renderFragment ,处理请求地址:
在这里插入图片描述
进入实现类:

org.thymeleaf.TemplateEngine#process(org.thymeleaf.TemplateSpec, org.thymeleaf.context.IContext, java.io.Writer)

在这里插入图片描述
真正解析地址处理方法:

回到org.thymeleaf.engine.TemplateManager#parseAndProcess主流程:
在这里插入图片描述
View渲染,看看OutputBuffer(ob)里的内容不就是视图层H5页面么!
在这里插入图片描述
两相对比,不能发现,这就是整个SpringMVC的处理流程:
在这里插入图片描述

国际化

什么是 Locale (国际化)?

作为 SpringMVC 的组件- ViewResolver,其作用是根据视图名和 Locale 解析出视图,而用户的区域也称为 Locale,该信息是可以由前端直接获取的。通过这种方式,可以实现一种国际化的目的,比如针对美国用户可以提供一个视图,而针对中国用户则可以提供另一个视图。
在这里插入图片描述

国际化之 Spring MVC

看看 Locale 在 Spring MVC 中的处理:

在源码里,通过类 Servlet 调用 ServletRequest#getLocale():

在这里插入图片描述
打开浏览器开发者模式,在 Request Headers 的字段里通常有设置中文编码的字段:Accept-Language: en,zh;q=0.9,zh-TW;q=0.8。locale()应用于浏览器中的常见字段 LocaleContextHolder来自于 DispatcherServlet 进而调用 FrameworkServlet#initContextHolders 方法,信息存储通过 ThreadLocal 实现。

以之前演示的demo为例:
在这里插入图片描述
在页面配置引入:
在这里插入图片描述
最终的页面展示:
在这里插入图片描述

国际化之 Spring Boot

看看 Locale 在 Spring Boot 中的处理,这里给出处理 Locale 的层级关系。首先可以查看源码: MessageSource ,此类专门读取配置信息,其衍生类 MessageSourceAutoConfiguration 与配置项 MessageSourceProperties。在前面我们提到的 DEMO 中,就提到了需要默认配置 message.properties,这样 spring 框架才会获取到对应的配置。

同样,我们也可以自定义配置文件名命名规则。

  • message.properties(spring 默认配置)
  • message_en.properties
  • message_zh.properties
  • message_zh_CN.properties
  • message_zh_TW.properties

Thymeleaf 在这一过程中是如何处理的?
可以参考源码:org.springframework.context.support.AbstractMessageSource#getMessage(java.lang.String, java.lang.Object[], java.lang.String, java.util.Locale)。

	@Override
	public final String getMessage(String code, @Nullable Object[] args, @Nullable String defaultMessage, Locale locale) {
    
    
		String msg = getMessageInternal(code, args, locale);
		if (msg != null) {
    
    
			return msg;
		}
		if (defaultMessage == null) {
    
    
			return getDefaultMessage(code);
		}
		return renderDefaultMessage(defaultMessage, args, locale);
	}

思考人生

JSP为什么被Spring抛弃?

  • Java EE 和 Spring 竞争关系的

  • Spring 想独立门户
    所以Spring搞了一个WebFlux~

    • WebFlux支持的功能
      • Servlet 3.1
      • Reactor +Netty
    • @Controller@RequestParam
      • Spring Web MVC
      • Spring WebFlux
      • 不再看到 Servlet API
        • ServletRequest
        • ServletResponse

JSP为什么性能要好?

JSP -> JSP 模板 -> 翻译 Servlet Java 源文件 -> 编译 Servlet Class -> Servlet 加载 -> Servlet 执行(字节码执行)

不同于JSP,Thymeleaf 的作用机制:Thymeleaf -> Thymeleaf 模板 -> 解释执行模板表达式(动态运行时)

解释型语言要比编译型语言慢!

后记

下节预告:REST理论(英文)

本节示例代码:https://github.com/harrypottry/microservices-project/spring-mvc-view

更多架构知识,欢迎关注本套Java系列文章Java架构师成长之路

猜你喜欢

转载自blog.csdn.net/qq_34361283/article/details/106169681
今日推荐