SpringBoot系列文章


文章目录

  • 1. 简介
  • 2. SpringBoot对静态资源的映射规则
  • 2.1 访问webjars资源
  • 2.2 访问自己的静态资源
  • 2.3 欢迎页映射
  • 2.4 网站图标映射
  • 3. 模板引擎
  • 3.1 引入模板引擎thymeleaf
  • 3.2 thymeleaf 的使用
  • 3.3 thymeleaf 语法规则
  • 4. SpringMVC配置
  • 4.1 视图解析器
  • 4.2 转换器、格式化器
  • 4.3 HttpMessageConverters
  • 4.4 MessageCodesResolver
  • 4.5 ConfigurableWebBindingInitializer
  • 5. 扩展SpringMVC
  • 5.1 原理
  • 6. 全面接管SpringMVC
  • 6.1 原理
  • 7. 如何修改SpringBoot的默认配置


1. 简介

使用SpringBoot进行Web开发的大致流程

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

2. 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();
                //如果请求是/webjars/**,将请求与classpath:/META-INF/resources/webjars/进行映射,setCachePeriod方法,
                //设置缓存时间
                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));
                }
				//将/**请求与getStaticLocations()获取的资源进行映射
                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));
                }

            }
        }

2.1 访问webjars资源

所有/webjars/**的请求,SpringBoot都去classpath:/META-INF/resources/webjars/下找资源(映射)

webjars:以jar包的形式引入的静态资源,前端框架可以去webars官网去找对应的maven依赖。

java如何在非springboot项目引入redis_spring boot

例如:添加jquery的webjars

java如何在非springboot项目引入redis_spring boot_02


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

2.2 访问自己的静态资源

/**,访问当前项目的任何资源,如果没有controller处理,springBoot默认去classpath:/META-INF/resources/", "classpath:/resources/", "classpath:/static/", "classpath:/public/寻找相应资源。这一点,可以根据addResourceHandlers中的getStaticLocations()源码中看出。

getStaticLocations()

@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();
    }
	
    public String[] getStaticLocations() {
        return this.staticLocations;
    }

java如何在非springboot项目引入redis_MVC_03


上图中添加的映射访问路径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/

classpath 等价于 main/java + main/resources + 第三方jar包的根目录,另外,当SpringBoot检索资源时,“/”就代表了从根路径搜索,而搜索的范围包括:main/java、main/resources、第三方jar包的根目录。

如果访问localhost:8080/abc,SpringBoot就会去这些静态资源文件夹中找abc的文件,也就是说,当请求地址是/**类型的,如果没有处理,SpringBoot默认会去上面的那些文件夹中寻找资源。

2.3 欢迎页映射

java如何在非springboot项目引入redis_静态资源_04


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

2.4 网站图标映射

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

可以在配置文件中使用spring.resources.staticLocations,来改变静态资源文件夹的路径。

3. 模板引擎

常见的模板引擎有JSP、Velocity、Freemarker、Thymeleaf

SpringBoot推荐使用Thymeleaf;

java如何在非springboot项目引入redis_MVC_05

3.1 引入模板引擎thymeleaf

<!--引入模板引擎thymeleaf -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

3.2 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";
    private Charset encoding;
    private boolean cache;
    ...

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

使用:

  1. 在src/main/resources/temeplates下创建html文件,导入thymeleaf的名称空间(可以自动提示)
<html lang="en" xmlns:th="http://www.thymeleaf.org">
  1. 在Controller层设置数据
@Controller
public class HelloController {

    @RequestMapping("/success")
    public String success(Model model){
        model.addAttribute("title","success");
        model.addAttribute("info","this is thymeleaf");
        return "t";
    }
}
  1. 使用thymeleaf语法(t.html)
<!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>
    <span th:text="${info}">这里的文本将会被覆盖</span>
</body>
</html>

3.3 thymeleaf 语法规则

  • th:text :改变当前元素(标签)里面的文本内容,如果有特殊字符,不会转译,直接输出,用在标签里面。
  • th:utext: 也是改变当前标签里面的文本内容,如果有特殊字符,会转译,不会直接输出,用在标签里面。
  • th:任意html属性,替换原生属性的值

    具体用法可以参考官方文档attribute-precedence

表达式:

  • ${…} 获取变量值,OGNL
  • 获取对象的属性、调用方法
  • 使用内置的基本对象和工具对象
  • {…} 选择表达式,和${}功能一样,不一样的是可以用代替对象
  • #{…}:获取国际化内容
  • @{…}:定义URL

具体用法可以 参考官方文档Standard Expression

4. SpringMVC配置

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

想要看SpringBoot中对SpringMvc的默认配置,可以查看官方文档,也可以看SpringMVC的自动配置类org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration

以下是springboot对SpringMVC的默认配置(官方文档中的介绍)

  • 包含ContentNegotiatingViewResolver和BeanNameViewResolver。–> 视图解析器
  • 支持服务静态资源,包括对WebJars的支持。–> 静态资源文件夹路径
  • 自动注册Converter,GenericConverter和Formatterbeans。–> 转换器,格式化器
  • 支持HttpMessageConverters)。–> SpringMVC用来转换Http请求和响应的;User—Json;
  • 自动注册MessageCodesResolver)。–> 定义错误代码生成规则
  • 静态index.html支持。–> 静态首页访问
  • 定制Favicon支持。–> 网站图标
  • 自动使用ConfigurableWebBindingInitializerbean。

4.1 视图解析器

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

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

java如何在非springboot项目引入redis_学习笔记_06

视图解析器从哪里来的?

java如何在非springboot项目引入redis_spring boot_07


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

java如何在非springboot项目引入redis_静态资源_08


使用debug检验

java如何在非springboot项目引入redis_jar_09

4.2 转换器、格式化器

  • Converter:转换器,比如页面中的发过来的消息都是String类型的,转换器可以转换成java类型。
  • Formatter 格式化器; 比如将日期转换成Date类型,2017.12.17===Date;
@Bean
//在配置文件中配置日期格式化的规则,就会注册这个日期格式化组件
@ConditionalOnProperty(prefix = "spring.mvc", name = "date-format")
public Formatter<Date> dateFormatter() {
    return new DateFormatter(this.mvcProperties.getDateFormat());//日期格式化组件
}

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

public void addFormatters(FormatterRegistry registry) {
  ApplicationConversionService.addBeans(registry, this.beanFactory);
}

4.3 HttpMessageConverters

  • HttpMessageConverter:SpringMVC用来转换Http请求和响应的,比如将数据转换成json格式。
  • HttpMessageConverters 是从容器中确定,获取所有的HttpMessageConverter;

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

4.4 MessageCodesResolver

定义错误代码生成规则

4.5 ConfigurableWebBindingInitializer

初始化WebDataBinder(Web数据绑定器:将请求数据与javaBean绑定),我们可以配置一个ConfigurableWebBindingInitializer来替换默认的(添加到容器)

上面是SpringBoot中对SpringMVC的自动配置,还有其他的web自动配置,可以在org.springframework.boot.autoconfigure.web下看SpringBoot对web的其他自动配置。

java如何在非springboot项目引入redis_学习笔记_10

5. 扩展SpringMVC

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

以前在SpringMVC中,可以配置

<mvc:view-controller path="/hello" view-name="success"/>

当收到hello请求时,返回success页面。

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

//使用WebMvcConfigurerAdapter可以来扩展SpringMVC的功能 ,实现WebMvcConfigurer接口
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
    
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        //收到hi请求,返回t页面,也就是将t.html交给模板引擎渲染
        registry.addViewController("/hi").setViewName("t");
    }
}

5.1 原理

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

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

java如何在非springboot项目引入redis_jar_11


看一下@Import({WebMvcAutoConfiguration.EnableWebMvcConfiguration.class})中的这个类,这个类依旧是WebMvcAutoConfiguration中的一个内部类

java如何在非springboot项目引入redis_MVC_12


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

@Configuration(
    proxyBeanMethods = false
)
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
    private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();

    public DelegatingWebMvcConfiguration() {
    }

    @Autowired(required = false) //使用@Autowired,默认从容器中获取configurers
    //将容器中所有的WebMvcConfigurer存到this.configurers中
    public void setConfigurers(List<WebMvcConfigurer> configurers) {
        if (!CollectionUtils.isEmpty(configurers)) {
            this.configurers.addWebMvcConfigurers(configurers);
        }
    }
	...
	
	/* 拿这个类中的addViewControllers方法举例
	this.configurers:也是WebMvcConfigurer接口的一个实现类
	*/
	protected void addViewControllers(ViewControllerRegistry registry) {
        this.configurers.addViewControllers(registry);
    }

this.configurers.addViewControllers(registry),遍历所有的WebMvcConfigurer,并且都执行addViewControllers(registry)

public void addViewControllers(ViewControllerRegistry registry) {
        Iterator var2 = this.delegates.iterator();

        while(var2.hasNext()) {
            WebMvcConfigurer delegate = (WebMvcConfigurer)var2.next();
            delegate.addViewControllers(registry);
        }
    }

delegate.addViewControllers(registry),调用WebMvcConfigurer接口中的 addViewControllers方法,也就是调用WebMvcConfigurer实现类的方法,实现扩展功能。

default void addViewControllers(ViewControllerRegistry registry) {
    }

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

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

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

java如何在非springboot项目引入redis_MVC_13

6. 全面接管SpringMVC

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

@Configuration
@EnableWebMvc
public class MyMvcConfig implements WebMvcConfigurer

java如何在非springboot项目引入redis_jar_14

6.1 原理

为什么使用了@EnableWebMvc注解之后,springMVC的自动配置就失效了

我们看一下EnableWebMvc注解类

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
//主要是注册了DelegatingWebMvcConfiguration类
@Import({DelegatingWebMvcConfiguration.class})
public @interface EnableWebMvc {
}

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

DelegatingWebMvcConfigurationWebMvcConfigurationSupport的子类

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

@Configuration(
    proxyBeanMethods = false
)
@ConditionalOnWebApplication(
    type = Type.SERVLET
)
@ConditionalOnClass({Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class})
//重点是这个注解,只有容器中没有WebMvcConfigurationSupport类型的bean时,
//才会将WebMvcAutoConfiguration 注册到容器中,自动配置才会生效
@ConditionalOnMissingBean({WebMvcConfigurationSupport.class})

@AutoConfigureOrder(-2147483638)
@AutoConfigureAfter({DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class, ValidationAutoConfiguration.class})
public class WebMvcAutoConfiguration {
	...
}

@EnableWebMvcWebMvcConfigurationSupport组件导入进来,导入的WebMvcConfigurationSupport只提供SpringMVC最基本的功能。

7. 如何修改SpringBoot的默认配置

SpringBoot在自动配置很多组件的时候,先看容器中有没有用户自己配置的(@Bean、@Component),如果有,就使用用户配置的,如果没有,才自动配置;如果有些组件可以有多个(如ViewResolver),SpringBoot会将用户配置的和自己默认的组合起来使用;

  • 在SpringBoot中会有非常多的xxxConfigurer帮助我们进行扩展配置
  • 在SpringBoot中会有很多的xxxCustomizer帮助我们进行定制配置