05 SpringBoot Web 开发、Thymeleaf 模版 Spring MVC

SpringBoot Web 开发、Thymeleaf 模版 Spring MVC

专栏列表

  1. Spring Boot 入门及环境构建
  2. Spring Boot 配置
  3. Spring Boot 自动配置及原理分析
  4. Spring Boot 日志
  5. SpringBoot Web 开发、Thymeleaf 模版 Spring MVC
  6. SpringBoot RESTful风格的增删改查 CRUD

Web开发

  1. 创建SpringBoot应用,选中我们需要的模块
  2. SpringBoot已经默认将这些场景配置好了,只需要在配置文件中指定少量配置就可以运行起来
  3. 自己编写业务代码

自动配置原理

这个场景 Spring Boot 帮我们配置了什么?能不能修改?能修改哪些配置?能不能扩展?xxx

xxxxAutoAonfiguration:帮我们给容器中自动配置组件xxxxProperties:配置类来封装配置文件的内容

web自动配置规则

  1. WebMvcAutoConfiguration
  2. WebMvcProperties
  3. ViewResolver自动配置
  4. 静态资源自动映射
  5. Formatter与Converter自动配置
  6. HttpMessageConverter自动配置
  7. 静态首页
  8. favicon
  9. 错误处理

SpringBoot对静态资源的映射规则

WebMvcAutoConfiguration类的addResourceHandlers方法:(添加资源映射)

public void addResourceHandlers(ResourceHandlerRegistry registry) {
  if (!this.resourceProperties.isAddMappings()) {
    logger.debug("Default resource handling disabled");
  } else {
    Duration cachePeriod = this.resourceProperties.getCache().getPeriod();
    CacheControl cacheControl = this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl();
    if (!registry.hasMappingForPattern("/webjars/**")) {
      this.customizeResourceHandlerRegistration(registry.addResourceHandler(new String[]{"/webjars/**"}).addResourceLocations(new String[]{"classpath:/META-INF/resources/webjars/"}).setCachePeriod(this.getSeconds(cachePeriod)).setCacheControl(cacheControl));
    }
    
    String staticPathPattern = this.mvcProperties.getStaticPathPattern();
    if (!registry.hasMappingForPattern(staticPathPattern)) {
      this.customizeResourceHandlerRegistration(registry.addResourceHandler(new String[]{staticPathPattern}).addResourceLocations(WebMvcAutoConfiguration.getResourceLocations(this.resourceProperties.getStaticLocations())).setCachePeriod(this.getSeconds(cachePeriod)).setCacheControl(cacheControl));
    }
  }
}

所有 /webjars/** ,都去 classpath:/META-INF/resources/webjars/ 找资源

webjars:以jar包的方式引入静态资源;

webjars官网

1573815091111

例如:添加jquery的webjars

<--引入 jquery-webjar 在访的时候只需要写 webjars 下面资源的名称即可 --></--引入>
<dependency>
  <groupId>org.webjars</groupId>
  <artifactId>jquery</artifactId>
  <version>3.4.1</version>
</dependency>

1573815506777

访问地址对应就是:http://localhost:8080/webjars/jquery/3.4.1/jquery.js

非webjars,自己的静态资源怎么访问

资源配置类:

//说明可以在配置文件中配置相关参数
@ConfigurationProperties(
  prefix = "spring.resources",
  ignoreUnknownFields = false
)
public class ResourceProperties {
  private static final String[] CLASSPATH_RESOURCE_LOCATIONS = new String[]{"classpath:/META-INF/resources/", "classpath:/resources/", "classpath:/static/", "classpath:/public/"};
  private String[] staticLocations;
  private boolean addMappings;
  private final ResourceProperties.Chain chain;
  private final ResourceProperties.Cache cache;
  
  public ResourceProperties() {
    this.staticLocations = CLASSPATH_RESOURCE_LOCATIONS;
    this.addMappings = true;
    this.chain = new ResourceProperties.Chain();
    this.cache = new ResourceProperties.Cache();
  }
}

1573817274649

上图中添加的映射访问路径staticPathPattern值是/**,对应的资源文件夹就是上面配置类ResourceProperties中的CLASSPATH_RESOURCE_LOCATIONS数组中的文件夹:

数组中的值在项目中的位置
classpath:/META-INF/resources/src/main/resources/META-INF/resources/
classpath:/resources/src/main/resources/resources/
classpath:/static/src/main/resources/static/
classpath:/public/src/main/resources/public/
“/” 当前项目的根路径

localhost:8080/abc —> 去静态资源文件夹里面找abc

欢迎页映射

1573819949494

location就是静态资源路径,所以欢迎页的页面就是上面静态资源下的index.html,被/**映射,因此直接访问项目就是访问欢迎页

网站图标映射(favicon.ico)

所有的 favicon.ico 都是在静态资源文件下找;


模板引擎

常见的模板引擎有JSPVelocityFreemarkerThymeleaf

SpringBoot推荐使用Thymeleaf

Thymeleaf 语法更简单,功能更强大

image-20200619010433951

引入thymeleaf

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

如需切换thymeleaf版本:

<properties>
  <thymeleaf.version>X.X.X.RELEASE</thymeleaf.version>
  <!-- 布局功能的支持程序  thymeleaf3主程序  layout2以上版本 -->
  <!-- thymeleaf2   layout1-->
  <thymeleaf-layout-dialect.version>2.2.2</thymeleaf-layout-dialect.version>
</properties>

Thymeleaf使用

package org.springframework.boot.autoconfigure.thymeleaf;

......

@ConfigurationProperties(
    prefix = "spring.thymeleaf"
)
public class ThymeleafProperties {
    private static final Charset DEFAULT_ENCODING;
    public static final String DEFAULT_PREFIX = "classpath:/templates/";
    public static final String DEFAULT_SUFFIX = ".html";
    private boolean checkTemplate = true;
    private boolean checkTemplateLocation = true;
    private String prefix = "classpath:/templates/";
    private String suffix = ".html";
    private String mode = "HTML";
}

默认只要我们把HTML页面放在classpath:/templates/,thymeleaf就能自动渲染;

  1. 创建模板文件t1.html,并导入thymeleaf的名称空间

    <html lang="en" xmlns:th="http://www.thymeleaf.org">
    
    <!DOCTYPE html>
    <html lang="en" xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    
    </body>
    </html>
    
  2. 使用模板

    <!DOCTYPE html>
    <html lang="en" xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <title>[[${title}]]</title>
    </head>
    <body>
    <h1 th:text="${title}"></h1>
    <div th:text="${info}">这里的文本之后将会被覆盖</div>
    </body>
    </html>
    
  3. 在controller中准备数据

    @Controller
    public class HelloT {
      @RequestMapping("/ht")
      public String ht(Model model) {
        model.addAttribute("title","hello Thymeleaf")
                 .addAttribute("info","this is first thymeleaf test");
        return "t1";
      }
    }
    

语法规则

th:text --> 改变当前元素里面的文本内容;

th:任意html属性 --> 来替换原生属性的值

thymeleaf

官方配置参考文档

中文参考书册

Simple expressions: (表达式语法)
  Variable Expressions: ${...}: 获取变量值;OGNL
  	1)获取对象的属性、调用方法使用内置的基本对象:
  	2)使用内置的基本对象:
      #ctx: the context object.
      #vars: the context variables.
      #locale: the context locale.
      #request: (only in Web Contexts) the HttpServletRequest object.
      #response: (only in Web Contexts) the HttpServletResponse object.
      #session: (only in Web Contexts) the HttpSession object.
      #servletContext: (only in Web Contexts) the ServletContext object.
	      e.g.: Established locale country: <span th:text="${#locale.country}">US</span>.
  	3)内置的一些工具对象:
      #execInfo: information about the template being processed.
      #messages: methods for obtaining externalized messages inside variables expressions, in the same way as they would be obtained using #{…} syntax.
      #uris: methods for escaping parts of URLs/URIs
      #conversions: methods for executing the configured conversion service (if any).
      #dates: methods for java.util.Date objects: formatting, component extraction, etc.
      #calendars: analogous to #dates, but for java.util.Calendar objects.
      #numbers: methods for formatting numeric objects.
      #strings: methods for String objects: contains, startsWith, prepending/appending, etc.
      #objects: methods for objects in general.
      #bools: methods for boolean evaluation.
      #arrays: methods for arrays.
      #lists: methods for lists.
      #sets: methods for sets.
      #maps: methods for maps.
      #aggregates: methods for creating aggregates on arrays or collections.
      #ids: methods for dealing with id attributes that might be repeated (for example, as a result of an iteration).
      
  Selection Variable Expressions: *{...}: 选择表达式: 和${}在功能上是一样
  	补充: 配合 th:object="${session.user}"
  	<div th:object="${session.user}">
  		<p>Name: <span th:text="*{firstName}">Sebastian</span>.</p>
    	<p>Surname: <span th:text="*{lastName}">Pepper</span>.</p>
    	<p>Nationality: <span th:text="*{nationality}">Saturn</span>.</p>
  	</div>
  Message Expressions: #{...}: 获取国际化内容
  Link URL Expressions: @{...}: 定义URL
  	@{/order/process(execId=${exeId},execType='FAST')}
  Fragment Expressions: ~{...}: 片段引用
  
Literals(字面量)
  Text literals: 'one text', 'Another one!',…
  Number literals: 0, 34, 3.0, 12.3,…
  Boolean literals: true, false
  Null literal: null
  Literal tokens: one, sometext, main,…
  
Text operations(文本操作)
  String concatenation: +
  Literal substitutions: |The name is ${name}|
  
Arithmetic operations(数学运算)
  Binary operators: +, -, *, /, %
  Minus sign (unary operator): -
  
Boolean operations(布尔运算)
  Binary operators: and, or
  Boolean negation (unary operator): !, not
  
Comparisons and equality(比较运算)
  Comparators: >, <, >=, <= (gt, lt, ge, le)
  Equality operators: ==, != (eq, ne)
  
Conditional operators(条件运算(三元运算符))
  If-then: (if) ? (then)
  If-then-else: (if) ? (then) : (else)
  Default: (value) ?: (defaultvalue)
  
Special tokens
	No-Operation: _

All these features can be combined and nested:

'User is of type ' + (${user.isAdmin()} ? 'Administrator' : (${user.type} ?: 'Unknown'))

SpringMVC自动配置

Spring Boot为Spring MVC提供了自动配置,可与大多数应用程序完美配合。

官方参考文档-1.5.10

以下是SpringBoot对SpringMVC的默认配置

org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration

自动配置在Spring的默认值之上添加了以下功能:

  • 包含 ContentNegotiatingViewResolverBeanNameViewResolver --> 视图解析器

    • 自动配置了 ViewResolver (视图解析:根据方法的返回值得到视图对象(View),视图对象决定如何渲染(转发、重定向))
    • ContentNegotiatingViewResolver:组合所有的视图解析的
    • 如何定制:我们可以自己给容器中添加一个视图解析器;自动的将其组合进来
  • 支持服务静态资源,包括对WebJars的支持(官方文档中有介绍-2.2.1) --> 静态资源文件夹路径, webjars

  • 自动注册 of ConverterGenericConverterFormatter beans --> 转换器,格式化器

    • Converter:转换器 public String hello(User user):类型转换使用 Converter
    • Formatter 格式化器
      • 2017-12-17===Date
      • 2017/12/17===Date
      • 2017.12.17===Date
  • 支持 HttpMessageConverters官方文档中有介绍) --> SpringMVC用来转换Http请求和响应的;User—Json;

    • HttpMessageConverter:SpringMVC 用来转换 Http 请求和响应的;User --> Json

    • HttpMessageConverters 是从容器中确定,HttpMessageConverter

      自己给容器中添加 HttpMessageConverters,只需要将自己的组件注册在容器中(@Bean, @Component

  • 自动注册 MessageCodesResolver官方文档中有介绍) --> 定义错误代码生成规则

  • 静态 index.html 支持 --> 静态首页访问

  • 定制 Favicon 支持(官方文档中有介绍) --> 网站图标

  • 自动使用 ConfigurableWebBindingInitializer bean(官方文档中有介绍

    • 我们可以配置一个 ConfigurableWebBindingInitializer 来替换默认的(添加到容器)

    初始化 WebDataBinder

    请求数据 ==== JavaBean

    org.springframework.boot.autoconfigure.web: web的所有自动场景

如果您想保留 Spring Boot MVC 的功能,并且需要添加其他 MVC 配置(拦截器,格式化程序和视图控制器等),可以添加自己的 WebMvcConfigurer 类型的 @Configuration 类,但不能@EnableWebMvc 注解。如果您想自定义 RequestMappingHandlerMappingRequestMappingHandlerAdapter 或者 ExceptionHandlerExceptionResolver 实例,可以声明一个 WebMvcRegistrationsAdapter 实例来提供这些组件。

如果您想完全掌控 Spring MVC,可以添加自定义注解了 @EnableWebMvc 的 @Configuration 配置类。

视图解析器

视图解析器:根据方法的返回值得到视图对象(View),视图对象决定如何渲染(转发?重定向?)

  • 自动配置了ViewResolver
  • ContentNegotiatingViewResolver:组合所有的视图解析器的;

1573873741438

视图解析器从哪里来的?

1573874365778

所以我们可以自己给容器中添加一个视图解析器;自动的将其组合进来

@Component
public class MyViewResolver implements ViewResolver {
  @Override
  public View resolveViewName(String s, Locale locale) throws Exception {
    return null;
  }
}
1573875409759

转换器、格式化器

  • Converter:转换器; public String hello(User user):类型转换使用Converter(表单数据转为user)
  • Formatter 格式化器; 2017.12.17===Date;
@Bean
//在配置文件中配置日期格式化的规则
@ConditionalOnProperty(prefix = "spring.mvc", name = "date-format")
public Formatter<Date> dateFormatter() {
  //日期格式化组件
  return new DateFormatter(this.mvcProperties.getDateFormat());
}

自己添加的格式化器转换器,我们只需要放在容器中即可

HttpMessageConverters

  • HttpMessageConverter:SpringMVC用来转换Http请求和响应的;User—Json;
  • HttpMessageConverters 是从容器中确定;获取所有的HttpMessageConverter;

自己给容器中添加HttpMessageConverter,只需要将自己的组件注册容器中(@Bean,@Component)

MessageCodesResolver

我们可以配置一个ConfigurableWebBindingInitializer来替换默认的;(添加到容器)

扩展SpringMVC

以前的配置文件中的配置

<mvc:view-controller path="/hello" view-name="success"/>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
  <mvc:view-controller path="/hello" view-name="success"/>
  <mvc:interceptors>
    <mvc:interceptor>
      <mvc:mapping path="/hello"/>
      <bean></bean>
    </mvc:interceptor>
    <mvc:default-servlet-handler/>
  </mvc:interceptors>
</beans>

现在,编写一个配置类(@Configuration),是 WebMvcConfigurer 类型;不能标注 @EnableWebMvc

package com.kevinkda.test.spring.boot.springbootdemo01;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

/**
 * @author Kevin KDA on 2020/6/27 00:32
 * @version 1.0.0
 * @package com.kevinkda.test.spring.boot.springbootdemo01
 * @classname MyMvcConfig
 * @apiNote <p></p>
 * @since 1.0.0
 */
// 使用 WebMvcConfigurerAdapter 可以来扩展 SpringMVC 的功能

@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
  @Override
  public void addViewControllers(ViewControllerRegistry registry) {
    //        super.addViewControllers(registry);
    //        浏览器发送 /kevinkda 请求 到success
    registry.addViewController("/hi").setViewName("success");
  }
}

访问:http://localhost:8080/hi

原理:

我们知道WebMvcAutoConfiguration是SpringMVC的自动配置类

下面这个类是WebMvcAutoConfiguration中的一个内部类

1573891167026

看一下@Import({WebMvcAutoConfiguration.EnableWebMvcConfiguration.class})中的这个类,

这个类依旧是WebMvcAutoConfiguration中的一个内部类

1573891478014

重点看一下这个类继承的父类DelegatingWebMvcConfiguration

public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
  private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();
  
  public DelegatingWebMvcConfiguration() {
  }
  
  //自动注入,从容器中获取所有的WebMvcConfigurer
  @Autowired(
    required = false
  )
  public void setConfigurers(List<WebMvcConfigurer> configurers) {
    if (!CollectionUtils.isEmpty(configurers)) {
      this.configurers.addWebMvcConfigurers(configurers);
    }
  }
  ......
/**
 * 查看其中一个方法
 * this.configurers:也是WebMvcConfigurer接口的一个实现类
 * 看一下调用的configureViewResolvers方法 ↓
 */
protected void configureViewResolvers(ViewResolverRegistry registry) {
  this.configurers.configureViewResolvers(registry);
}
public void configureViewResolvers(ViewResolverRegistry registry) {
  Iterator var2 = this.delegates.iterator();
  
  while(var2.hasNext()) {
    WebMvcConfigurer delegate = (WebMvcConfigurer)var2.next();
    //将所有的WebMvcConfigurer相关配置都来一起调用
    delegate.configureViewResolvers(registry);
  }
}
@Configuration
public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration {
  private final WebmvcConfigurerComposite configurers = new WebmvcConfigurerComposite();
  
  // 从容器中获取所有的 Jwebmvc Configurer
  @Autowired(required=false)
  public void setConfigurers(List <WebMvcConfigurer> configurers) {
    if(!CollectionUtils.isEmpty(configurers)) {
      this.configurers.addWebMvcConfigurers(configurers);
      // 一个参考实现;将所有的 WebMvcConfigurer 相关配置都来一起调用
      // @Override
      // public void addViewControllers(ViewControllerRegistry registry) {
      //   for  (WebMvcConfigurer delegate: this.delegates) {
      //     delegate.addViewControllers(registry);
      //   }
      // }
    }
  }
}

容器中所有的WebMvcConfigurer都会一起起作用;

我们的配置类也会被调用;

​ 效果:SpringMVC的自动配置和我们的扩展配置都会起作用;

1573892805539

全面接管SpringMVC

SpringBoot对SpringMVC的自动配置不需要了,所有都是由我们自己来配置;所有的SpringMVC的自动配置都失效了

我们只需要在配置类中添加 @EnableWebMvc 即可;

// 使用 WebMvcConfigurerAdapter 可以来扩展 SpringMVC 的功能

@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
  @Override
  public void addViewControllers(ViewControllerRegistry registry) {
    //        super.addViewControllers(registry);
    //        浏览器发送 /kevinkda 请求 到success
    registry.addViewController("/hi").setViewName("success");
  }
}
1573892899452

原理:

为什么 @EnableWebMvc 自动配置就失效了;

我们看一下 EnableWebMvc 注解类

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import({DelegatingWebMvcConfiguration.class})
public @interface EnableWebMvc {}

重点在于@Import({DelegatingWebMvcConfiguration.class})

DelegatingWebMvcConfigurationWebMvcConfigurationSupport的子类

@Configuration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {}

我们再来看一下springmvc的自动配置类WebMvcAutoConfiguration

@Configuration(
  proxyBeanMethods = false
)
@ConditionalOnWebApplication(
  type = Type.SERVLET
)
@ConditionalOnClass({Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class})

//重点是这个注解,只有当容器中没有这个类型组件的时候该配置类才会生效
@ConditionalOnMissingBean({WebMvcConfigurationSupport.class})

@AutoConfigureOrder(-2147483638)
@AutoConfigureAfter({DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class, ValidationAutoConfiguration.class})
public class WebMvcAutoConfiguration
  1. @EnableWebMvc将WebMvcConfigurationSupport组件导入进来;
  2. 导入的WebMvcConfigurationSupport只是SpringMVC最基本的功能;

如何修改SpringBoot的默认配置

模式
  1. SpringBoot在自动配置很多组件的时候,先看容器中有没有用户自己配置的(@Bean@Component)如果有就用用户配置的,如果没有,才自动配置;如果有些组件可以有多个(ViewResolver)将用户配置的和自己默认的组合起来;
  2. 扩展配置
    1. 在SpringBoot中会有非常多的xxxConfigurer帮助我们进行扩展配置
    2. 在SpringBoot中会有很多的xxxCustomizer帮助我们进行定制配置
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Kevin KDA

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值