前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >SpringBoot | SpringBoot Web开发

SpringBoot | SpringBoot Web开发

作者头像
啵啵鱼
发布2022-11-23 17:55:07
1K0
发布2022-11-23 17:55:07
举报
文章被收录于专栏:Java一条龙学习笔记系列

文章目录

SpringBoot Web开发

1.Web开发探究

使用SpringBoot的步骤

  1. 创建一个SpringBoot应用,选择我们需要的模块,SpringBoot就会默认将我们的需要的模块自动配置好;
  2. 手动在配置文件中配置部分配置项目就可以运行起来了。
  3. 专注编写业务代码,不需要考虑以前那样一大堆的配置了。

自动装配

  • spring boot到底帮我们配置了什么?我们能不能进行修改?能修改哪些东西?能不能扩展?
    • xxxxAutoConfiguraion… 向容器中自动配置组件;
    • xxxxProperties:自动配置类,装配配置文件中自定义的一些内容!

2.静态资源处理

(1)静态资源映射规则

  • 搭建一个普通的SpringBoot项目。
    • 写请求非常简单,那我们要引入我们前端资源,我们项目中有许多的静态资源,比如css,js等文件,这 个SpringBoot怎么处理呢?
    • 如果我们是一个web应用,我们的main下会有一个webapp,我们以前都是将所有的页面导在这里面 的,对吧!但是我们现在的pom呢,打包方式是为jar的方式,那么这种方式SpringBoot能不能来给我们 写页面呢?当然是可以的,但是SpringBoot对于静态资源放置的位置,是有规定的!
  • SpringBoot中,SpringMVC的web配置都在 WebMvcAutoConfiguration 这个配置类里面;有一个方法: addResourceHandlers 添加资源处理。
代码语言:javascript
复制
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();
        // webjars配置
        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呢?

  • Webjars本质就是以jar包的方式引入我们的静态资源 , 我们以前要导入一个静态资源文件,直接导入即可。
  • 使用SpringBoot需要使用Webjars,我们可以去搜索一下:
  • 网站:https://www.webjars.org 【网站带看,并引入jQuery测试】
  • 要使用jQuery,我们只要要引入jQuery对应版本的pom依赖即可!
代码语言:javascript
复制
<dependency>
    <groupId>org.webjars</groupId>
    <artifactId>jquery</artifactId>
    <version>3.4.1</version>
</dependency>
  • 访问:只要是静态资源,SpringBoot就会去对应的路径寻找资源,这里访问 : http://localhost:8080/webjars/jquery/3.4.1/jquery.js

(2)第二种静态资源映射规则

1)默认的静态资源路径
  • 当项目中要是使用自己的静态资源该怎么导入呢?看下一行代码;
  • 去找staticPathPattern发现第二种映射规则:/**, 访问当前的项目任意资源,它会去找 resourceProperties 这个类,我们可以点进去看一下分析:(2.5.6版本)
代码语言:javascript
复制
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();
    }

    // 进入方法
    public String[] getStaticLocations() {
        return this.staticLocations;
    }
    
    ......
}

2.6.11版本通过以下方式查看

代码语言:javascript
复制
  this.resourceProperties = webProperties.getResources();
  • 点击getResources()方法
  • 点击this.resources
  • 点击new Resources()
  • ResourceProperties(2.5.4版本)/ Resources(2.6.11版本) 可以设置和我们静态资源有关的参数;这里面指向了它会去寻找资源的文件夹,即上面数组的内容。 所以得出结论,以下四个目录存放的静态资源可以被我们识别:
代码语言:javascript
复制
"classpath:/META-INF/resources/"
"classpath:/resources/"
"classpath:/static/"
"classpath:/public/"
  • 在resources根目录下新建对应的文件夹,都可以存放我们的静态文件; 比如我们访问 http://localhost:8080/1.js , 他就会去这些文件夹中寻找对应的静态资源文件;
2)自定义静态资源路径
  • 也可以自己通过配置文件来指定一下,哪些文件夹是需要我们放静态资源文件的,在 application.properties中配置;
代码语言:javascript
复制
spring.web.resources.static-locations=classpath:/BoBooY/,classpath:/BoBooY2/
  • 一旦自己定义了静态文件夹的路径,原来的自动配置就都会失效了!

3.首页和图标定制

(1)首页

  • 继续向下看源码!可以看到一个欢迎页的映射,就是我们的首页!
代码语言:javascript
复制
@Bean
public WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext applicationContext, FormattingConversionService mvcConversionService, ResourceUrlProvider mvcResourceUrlProvider) {
    // getWelcomePage 获得欢迎页
    WelcomePageHandlerMapping welcomePageHandlerMapping = new WelcomePageHandlerMapping(new TemplateAvailabilityProviders(applicationContext), applicationContext, this.getWelcomePage(), this.mvcProperties.getStaticPathPattern());
    welcomePageHandlerMapping.setInterceptors(this.getInterceptors(mvcConversionService, mvcResourceUrlProvider));
    welcomePageHandlerMapping.setCorsConfigurations(this.getCorsConfigurations());
    return welcomePageHandlerMapping;
}
  • 点击去继续
代码语言:javascript
复制
private Optional<Resource> getWelcomePage() {
    String[] locations = WebMvcAutoConfiguration.getResourceLocations(this.resourceProperties.getStaticLocations());
    // ::是java8 中新引入的运算符
	// Class::function的时候function是属于Class的,应该是静态方法。
	// this::function的funtion是属于这个对象的。
	// 简而言之,就是一种语法糖而已,是一种简写
    return Arrays.stream(locations).map(this::getIndexHtml).filter(this::isReadable).findFirst();
}

// 欢迎页就是一个location下的的 index.html 而已
private Resource getIndexHtml(String location) {
    return this.resourceLoader.getResource(location + "index.html");
}
  • 欢迎页,静态资源文件夹下的所有 index.html 页面;被 /** 映射。
  • 比如我访问 http://localhost:8080/ ,就会找静态资源文件夹下的 index.html 【可以测试一下】
  • 新建一个 index.html ,在我们上面的3个目录中任意一个;然后访问测试 http://localhost:8080/ 看结果!
  • 注意:此功能需要取消自定义静态资源路径。
代码语言:javascript
复制
"classpath:/resources/"
"classpath:/static/"
"classpath:/public/"

(2)图标

  • 2.2版本之后已删除!!!
  • 与其他静态资源一样,Spring Boot在配置的静态内容位置中查找 favicon.ico。如果存在这样的文件,它将自动用作应用程序的favicon。
  1. 关闭SpringBoot默认图标!
代码语言:javascript
复制
# 关闭默认图标
spring.mvc.favicon.enabled=false
  1. 自己放一个图标在静态资源目录下,我放在 resources目录下,图标的命名必须是favicon.ico!
  2. 清除浏览器缓存!刷新网页,发现图标已经变成自己的了!

4.Thymeleaf模板引擎及语法

(1)介绍

  • 前端交给我们的页面,是html页面。如果是我们以前开发,我们需要把他们转成jsp页面,jsp好处就是当我们查出一些数据转发到JSP页面以后,我们可以用jsp轻松实现数据的显示,及交互等。
  • jsp支持非常强大的功能,包括能写Java代码,但是呢,我们现在的这种情况,SpringBoot这个项目首先是以jar的方式,不是war,像第二,我们用的还是嵌入式的Tomcat,所以呢,他现在默认是不支持jsp 的。
  • 那不支持jsp,如果我们直接用纯静态页面的方式,那给我们开发会带来非常大的麻烦,那怎么办呢?
  • SpringBoot推荐你可以来使用模板引擎:
  • 模板引擎,我们其实大家听到很多,其实jsp就是一个模板引擎,还有以用的比较多的freemarker,包括 SpringBoot给我们推荐的Thymeleaf,模板引擎有非常多,但再多的模板引擎,他们的思想都是一样 的,什么样一个思想呢我们来看一下这张图:
  • 模板引擎的作用就是我们来写一个页面模板,比如有些值呢,是动态的,我们写一些表达式。而这些 值,从哪来呢,就是我们在后台封装一些数据。然后把这个模板和这个数据交给我们模板引擎,模板引擎按照我们这个数据帮你把这表达式解析、填充到我们指定的位置,然后把这个数据最终生成一个我们 想要的内容给我们写出去,这就是我们这个模板引擎,不管是jsp还是其他模板引擎,都是这个思想。只 不过呢,就是说不同模板引擎之间,他们可能这个语法有点不一样。其他的我就不介绍了,我主要来介 绍一下SpringBoot给我们推荐的Thymeleaf模板引擎,这模板引擎呢,是一个高级语言的模板引擎,他 的这个语法更简单。而且呢,功能更强大。
  • 首先,我们来看SpringBoot里边怎么用。

(2)引入Thymeleaf

  • 怎么引入呢,对于springboot来说,什么事情不都是一个start的事情嘛,我们去在项目中引入一下。给如下三个网址:
  • Thymeleaf 官网:https://www.thymeleaf.org/
  • Thymeleaf 在Github 的主页:https://github.com/thymeleaf/thymeleaf
  • Spring官方文档: 找到我们对应的版本,Spring Boot Reference Documentation
  • 找到对应的pom依赖:可以适当点进源码看下本来的包!
代码语言:javascript
复制
<!--thymeleaf-->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
  • Maven会自动下载jar包,我们可以去看下下载的东西;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3C8F9icU-1662430755757)(https://cdn.jsdelivr.net/gh/BoBooY-GitHub/SpringBoot_NoteImgs@main/imgs/202209041108838.png)]

(3)分析Thymeleaf

  • 前面已经引入了Thymeleaf,那这个要怎么使用呢?
  • 我们首先得按照SpringBoot的自动配置原理看一下我们这个Thymeleaf的自动配置规则,在按照那个规则,我们进行使用。
  • 先去找一下Thymeleaf的自动配置类:ThymeleafProperties
代码语言:javascript
复制
@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";
    private Charset encoding;
    private boolean cache;
    private Integer templateResolverOrder;
    private String[] viewNames;
    private String[] excludedViewNames;
    private boolean enableSpringElCompiler;
    private boolean renderHiddenMarkersBeforeCheckboxes;
    private boolean enabled;
    private final ThymeleafProperties.Servlet servlet;
    private final ThymeleafProperties.Reactive reactive;
}
  • 可以在其中看到默认的前缀和后缀!
  • 我们只需要把我们的html页面放在类路径下的templates下,thymeleaf就可以帮我们自动渲染了。
  • 使用thymeleaf什么都不需要配置,只需要将他放在指定的文件夹下即可!

测试

  1. 编写一个TestController
代码语言:javascript
复制
@Controller
public class HelloController {
    @RequestMapping("/hello")
    public String hello(Model model) {
        return "hello";
    }
}

​ 2.编写一个测试页面 test.html 放在 templates 目录下

代码语言:javascript
复制
<!DOCTYPE html>
<html lang="en" >
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <p>hello world!</p>
</body>
</html>

​ 3.启动项目请求测试

(4)Thymeleaf语法学习

1)测试
  • 语法学习,参考官网:Thymeleaf
  • 做个最简单的练习:我们需要查出一些数据,在页面中展示。
  1. 修改测试请求,增加数据传输;
代码语言:javascript
复制
@Controller
public class HelloController {
    @RequestMapping("/hello")
    public String hello(Model model) {
        model.addAttribute("msg","这是一个测试");
        return "hello";
    }
}

​ 2.要使用thymeleaf,需要在html文件中导入命名空间的约束,方便提示。可以去官方文档的#3中看一下命名空间拿来过来:

代码语言:javascript
复制
<html xmlns:th="http://www.thymeleaf.org">

​ 3.编写前端页面

代码语言:javascript
复制
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>测试</title>
</head>
<body>

  <h1>测试页</h1>
  <!-- 所有的html元素都可以被thymeleaf替换接管: th:元素名
  th:text就是将div中的内容设置为它指定的值 -->
  <div th:text="${msg}"></div>

</body>
</html>

​ 4.查看结果

2)语法汇总

​ 1.可以使用任意的 th:attr 来替换Html中原生属性的值!参考官网文档#10 th语法

​ 2.具体能写那些表达式呢?可以参考官方文档 #4

代码语言:javascript
复制
Simple expressions:(表达式语法)
Variable Expressions: ${...}:获取变量值;OGNL;
  1)、获取对象的属性、调用方法
  2)、使用内置的基本对象: #18
      #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.
  3)、内置的一些工具对象:
      #execInfo : information about the template being processed.
      #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.
  ============================================================================
  ======
          
      Selection Variable Expressions: *{...}:选择表达式:和${}在功能上是一样;
      Message Expressions: #{...}:获取国际化内容
      Link URL Expressions: @{...}:定义URL;
      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: _

练习测试

  1. 编写一个Controller,放一些数据。
代码语言:javascript
复制
@Controller
public class TestController {
    @RequestMapping("/test")
    public String test(Model model, Map<String,Object> map) {
        model.addAttribute("msg", "<h1>BoBooY</h1>");
        map.put("users", Arrays.asList("BoBooY","test"));
        return "test";
    }
}

​ 2. 编写前端页面

代码语言:javascript
复制
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <!-- 所有的html元素都可以被thymeleaf替换接管: th:元素名
 th:text就是将div中的内容设置为它指定的值 -->
    <div th:text="${msg}"></div>
    <!--  不转义  -->
    <div th:utext="${msg}"></div>
    <!--遍历数据-->
    <!--th:each每次遍历都会生成当前这个标签:官网#9-->
    <ul>
        <li th:each="user:${users}" th:text="${user}"></li>
    </ul>
</body>
</html>

​ 3. 启动测试!

5.Spring MVC 配置原理

(1)视图解析器

阅读官网

在进行项目编写前,还需要知道一个东西,就是SpringBoot对我们的SpringMVC还做了哪些配置,包括如何扩展,如何定制。

只有把这些都搞清楚了,在之后的使用才会更加得心应手。

代码语言:javascript
复制
Spring MVC Auto-configuration
// Spring Boot为Spring MVC提供了自动配置,它可以很好地与大多数应用程序一起工作。
Spring Boot provides auto-configuration for Spring MVC that works well with most applications.
// 自动配置在Spring默认设置的基础上添加了以下功能:
The auto-configuration adds the following features on top of Spring’s defaults:
// 包含视图解析器
Inclusion of ContentNegotiatingViewResolver and BeanNameViewResolver beans.
// 支持静态资源文件夹的路径,以及webjars
Support for serving static resources, including support for WebJars 
// 自动注册了Converter:
// 转换器,这就是我们网页提交数据到后台自动封装成为对象的东西,比如把"1"字符串自动转换为int类型
// Formatter:【格式化器,比如页面给我们了一个2019-8-10,它会给我们自动格式化为Date对象】
Automatic registration of Converter, GenericConverter, and Formatter beans.
// HttpMessageConverters
// SpringMVC用来转换Http请求和响应的的,比如我们要把一个User对象转换为JSON字符串,可以去看官网文档解释;
Support for HttpMessageConverters (covered later in this document).
// 定义错误代码生成规则的
Automatic registration of MessageCodesResolver (covered later in this document).
// 首页定制
Static index.html support.
// 图标定制
Custom Favicon support (covered later in this document).
// 初始化数据绑定器:帮我们把请求数据绑定到JavaBean中!
Automatic use of a ConfigurableWebBindingInitializer bean (covered later in this document).

/*
如果您希望保留Spring Boot MVC功能,并且希望添加其他MVC配置(拦截器、格式化程序、视图控制器和其他功能),则可以添加自己
的@configuration类,类型为webmvcconfiguer,但不添加@EnableWebMvc。如果希望提供
RequestMappingHandlerMapping、RequestMappingHandlerAdapter或ExceptionHandlerExceptionResolver的自定义
实例,则可以声明WebMVCregistrationAdapter实例来提供此类组件。
*/
If you want to keep Spring Boot MVC features and you want to add additional MVC configuration 
(interceptors, formatters, view controllers, and other features), you can add your own 
@Configuration class of type WebMvcConfigurer but without @EnableWebMvc. If you wish to provide 
custom instances of RequestMappingHandlerMapping, RequestMappingHandlerAdapter, or 
ExceptionHandlerExceptionResolver, you can declare a WebMvcRegistrationsAdapter instance to provide such components.

// 如果您想完全控制Spring MVC,可以添加自己的@Configuration,并用@EnableWebMvc进行注释。
If you want to take complete control of Spring MVC, you can add your own @Configuration annotated with @EnableWebMvc.
  • 仔细对照,看一下它怎么实现的,它告诉我们SpringBoot已经帮我们自动配置好了SpringMVC,然后自动配置了哪些东西呢?

ContentNegotiatingViewResolver 内容协商视图解析器

自动配置了ViewResolver,就是之前学习的SpringMVC的视图解析器;

即根据方法的返回值取得视图对象(View),然后由视图对象决定如何渲染(转发,重定向)。

  • 去看看这里的源码:我们找到 WebMvcAutoConfiguration , 然后搜索ContentNegotiatingViewResolver。找到如下方法!
代码语言:javascript
复制
@Bean
@ConditionalOnBean(ViewResolver.class)
@ConditionalOnMissingBean(name = "viewResolver", value = ContentNegotiatingViewResolver.class)
public ContentNegotiatingViewResolver viewResolver(BeanFactory beanFactory) {
    ContentNegotiatingViewResolver resolver = new ContentNegotiatingViewResolver();
    resolver.setContentNegotiationManager(beanFactory.getBean(ContentNegotiationManager.class));
    // ContentNegotiatingViewResolver使用所有其他视图解析器来定位视图,因此它应该具有较高的优先级
    resolver.setOrder(Ordered.HIGHEST_PRECEDENCE);
    return resolver;
}
  • 点进这类看看!找到对应的解析视图的代码;
代码语言:javascript
复制
@Nullable // 注解说明:@Nullable 即参数可为null
public View resolveViewName(String viewName, Locale locale) throws Exception {
    RequestAttributes attrs = RequestContextHolder.getRequestAttributes();
    Assert.state(attrs instanceof ServletRequestAttributes, "No current ServletRequestAttributes");
    List<MediaType> requestedMediaTypes = this.getMediaTypes(((ServletRequestAttributes)attrs).getRequest());
    if (requestedMediaTypes != null) {
        // 获取候选的视图对象
        List<View> candidateViews = this.getCandidateViews(viewName, locale, requestedMediaTypes);
        // 选择一个最适合的视图对象,然后把这个对象返回
        View bestView = this.getBestView(candidateViews, requestedMediaTypes, attrs);
        if (bestView != null) {
            return bestView;
        }
    }

    String mediaTypeInfo = this.logger.isDebugEnabled() && requestedMediaTypes != null ? " given " + requestedMediaTypes.toString() : "";
    if (this.useNotAcceptableStatusCode) {
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Using 406 NOT_ACCEPTABLE" + mediaTypeInfo);
        }

        return NOT_ACCEPTABLE_VIEW;
    } else {
        this.logger.debug("View remains unresolved" + mediaTypeInfo);
        return null;
    }
}
  • 继续点进去看,他是怎么获得候选的视图的呢?
  • getCandidateViews中看到他是把所有的视图解析器拿来,进行while循环,挨个解析!
代码语言:javascript
复制
Iterator var5 = this.viewResolvers.iterator();
  • 所以得出结论:ContentNegotiatingViewResolver 这个视图解析器就是用来组合所有的视图解析器的
  • 再去研究下他的组合逻辑,看到有个属性viewResolvers,看看它是在哪里进行赋值的!
代码语言:javascript
复制
protected void initServletContext(ServletContext servletContext) {
    // 这里它是从beanFactory工具中获取容器中的所有视图解析器
    // ViewRescolver.class 把所有的视图解析器来组合的
    Collection<ViewResolver> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(this.obtainApplicationContext(), ViewResolver.class).values();
    ViewResolver viewResolver;
    if (this.viewResolvers == null) {
        this.viewResolvers = new ArrayList(matchingBeans.size());
    }
    // ...............
}
  • 既然它是在容器中去找视图解析器,是否可以猜想,可以去实现一个视图解析器了呢?
  • 可以自己给容器中去添加一个视图解析器;这个类就会帮我们自动的将它组合进来;
  1. 在主程序中去写一个视图解析器来试试;
代码语言:javascript
复制
@Bean // 放到bean中
public ViewResolver myViewResolver(){
    return new MyViewResolver();
}

// 写一个静态内部类,视图解析器就需要实现ViewResolver接口
private static class MyViewResolver implements ViewResolver{
    @Override
    public View resolveViewName(String s, Locale locale) throws Exception {
        return null;
    }
}

​ 2. 看我们自己写的视图解析器有没有起作用呢? 我们给 DispatcherServlet 中的doDispatch方法加个断点进行调试一下,因为所有的请求都会走到这个方法中。

​ 3. 启动我们的项目,然后在浏览器随便访问一个页面,看一下Debug信息;

  • 找到视图解析器,看到我们自己定义的就在这里了;
  • 所以说,如果想要使用自己定制化的东西,只需要给容器中添加这个组件就好了!剩下的事情SpringBoot就会帮我们做了!

(2)转换器和格式化器

  • 找到格式化转换器:(版本2.6.11)
代码语言:javascript
复制
@Bean
@Override
public FormattingConversionService mvcConversionService() {
    Format format = this.mvcProperties.getFormat();
    WebConversionService conversionService = new WebConversionService(new DateTimeFormatters().dateFormat(format.getDate()).timeFormat(format.getTime()).dateTimeFormat(format.getDateTime()));
    addFormatters(conversionService);
    return conversionService;
}
  • 点进去:
代码语言:javascript
复制
public String getDateFormat() {
    return this.dateFormat;
}

public void setDateFormat(String dateFormat) {
    this.dateFormat = dateFormat;
}

// Date format to use. For instance, `dd/MM/yyyy`. 默认的
private String dateFormat;
  • 可以看到在Properties文件中,我们可以进行自动配置它!

(3)修改SpringBoot的默认配置(扩展SpringMVC)

这么多的自动配置,原理都是一样的,通过这个WebMVC的自动配置原理分析,我们要学会一种学习方式,通过源码探究,得出结论;这个结论一定是属于自己的,而且一通百通。

SpringBoot的底层,大量用到了这些设计细节思想,所以,没事需要多阅读源码!得出结论;

SpringBoot在自动配置很多组件的时候,先看容器中有没有用户自己配置的(如果用户自己配置@bean),如果有就用用户配置的,如果没有就用自动配置的;

如果有些组件可以存在多个,比如我们的视图解析器,就将用户配置的和自己默认的组合起来!

扩展使用SpringMVC 官方文档如下:

代码语言:javascript
复制
If you want to keep Spring Boot MVC features and you want to add additional MVC configuration (interceptors, formatters, view controllers, and other features), you can add your own @Configuration class of type WebMvcConfigurer but without @EnableWebMvc. If you wish to provide custom instances of RequestMappingHandlerMapping, RequestMappingHandlerAdapter, or ExceptionHandlerExceptionResolver, you can declare a WebMvcRegistrationsAdapter instance to provide such components.

  • 要做的就是编写一个@Configuration注解类,并且类型要为WebMvcConfigurer,还不能标注@EnableWebMvc注解;我们去自己写一个;
  • 我们新建一个包叫config,写一个类MyMvcConfig;
代码语言:javascript
复制
// 扩展springmvc Dispacherservle
//可以使用自定义类扩展MVC的功能
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        // 浏览器发送/test , 就会跳转到test页面;
        registry.addViewController("/test").setViewName("test");
    }
}
  • 编写test.html页面
代码语言:javascript
复制
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org/">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
这是一个测试MyMvcConfig的页面
</body>
</html>
  • 测试结果

确实也跳转过来了!所以说,要扩展SpringMVC,官方推荐我们这么去使用,既保留SpringBoot 所有的自动配置,也能用我们扩展的配置!

  • 具体可以去分析一下原理:
    1. WebMvcAutoConfiguration 是 SpringMVC的自动配置类,里面有一个类 WebMvcAutoConfigurationAdapter
    2. 这个类上有一个注解,在做其他自动配置时会导入: @Import(EnableWebMvcConfiguration.class)
    3. 当点进EnableWebMvcConfiguration这个类看一下,它继承了一个父类: DelegatingWebMvcConfiguration
代码语言:javascript
复制
@Configuration
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);
        }

    }
}
  1. 可以在这个类中去寻找一个上面刚才设置的viewController当做参考,发现它调用了一个。
代码语言:javascript
复制
public void addWebMvcConfigurers(List<WebMvcConfigurer> configurers) {
    if (!CollectionUtils.isEmpty(configurers)) {
        this.delegates.addAll(configurers);
    }

}

​ 5. 点进去:

代码语言:javascript
复制
public void addFormatters(FormatterRegistry registry) {
    Iterator var2 = this.delegates.iterator();

    while(var2.hasNext()) {
        // 将所有的WebMvcConfigurer相关配置来一起调用!包括我们自己配置的和Spring给我们配置的
        WebMvcConfigurer delegate = (WebMvcConfigurer)var2.next();
        delegate.addFormatters(registry);
    }

}
  • 所以得出结论:所有的WebMvcConfiguration都会被作用,不止Spring自己的配置类,我们自己的配置类当然也会被调用;

(4)全面接管SpringMVC

  • 官方文档:
代码语言:javascript
复制
If you want to take complete control of Spring MVC
you can add your own @Configuration annotated with @EnableWebMvc.
  • 全面接管即:SpringBoot对SpringMVC的自动配置不需要了,所有都是我们自己去配置!
  • 只需在我们的配置类中要加一个@EnableWebMvc。
  • 看下如果我们全面接管了SpringMVC了,我们之前SpringBoot给我们配置的静态资源映射一定会无效,我们可以去测试一下;
  • 不加注解之前,访问首页:
  • 给配置类加上注解:@EnableWebMvc
  • 发现所有的SpringMVC自动配置都失效了!回归到了最初的样子;
  • 所以,开发中不推荐使用全面接管SpringMVC。

为什么加了一个注解,自动配置就失效了!看下源码:

  1. 可以发现它是导入了一个类,点进注解@EnableWebMvc看:
代码语言:javascript
复制
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import({DelegatingWebMvcConfiguration.class})
public @interface EnableWebMvc {
}
  1. 它继承了一个父类WebMvcConfigurationSupport
代码语言:javascript
复制
@Configuration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
    private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();
.......
}
  1. 来回顾一下Webmvc自动配置类:
代码语言:javascript
复制
@Configuration
@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 {
    public static final String DEFAULT_PREFIX = "";
    public static final String DEFAULT_SUFFIX = "";
    private static final String[] SERVLET_LOCATIONS = new String[]{"/"};

    public WebMvcAutoConfiguration() {
    }
    ......
}
  • 总结一句话:@EnableWebMvc将WebMvcConfigurationSupport组件导入进来了;而导入的WebMvcConfigurationSupport只是SpringMVC最基本的功能!
  • 这就是在SpringMVC上加了一层封装!
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-10-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 文章目录
  • SpringBoot Web开发
    • 1.Web开发探究
      • 2.静态资源处理
        • (1)静态资源映射规则
        • (2)第二种静态资源映射规则
      • 3.首页和图标定制
        • (1)首页
        • (2)图标
      • 4.Thymeleaf模板引擎及语法
        • (1)介绍
        • (2)引入Thymeleaf
        • (3)分析Thymeleaf
        • (4)Thymeleaf语法学习
      • 5.Spring MVC 配置原理
        • (1)视图解析器
        • (2)转换器和格式化器
        • (3)修改SpringBoot的默认配置(扩展SpringMVC)
        • (4)全面接管SpringMVC
    相关产品与服务
    容器服务
    腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档