三、SpringBoot Web开发1. 静态资源访问(简述)
WebMvcAutoConfiguration.class:
public void addResourceHandlers(ResourceHandlerRegistry registry) {
if (!this.resourceProperties.isAddMappings()) {
logger.debug("Default resource handling disabled");
} else {
this.addResourceHandler(registry, "/webjars/**", "classpath:/META-INF/resources/webjars/");
this.addResourceHandler(registry, this.mvcProperties.getStaticPathPattern(), (registration) -> {
registration.addResourceLocations(this.resourceProperties.getStaticLocations());
if (this.servletContext != null) {
ServletContextResource resource = new ServletContextResource(this.servletContext, "/");
registration.addResourceLocations(new Resource[]{resource});
}
});
}
}
① 三种方式访问并处理静态资源:
自定义资源访问路径:
spring.resources.static-locations=classpath:/wzh/,classpath:/hello/
1.1 静态资源映射规则(分析)
首先,我们搭建一个普通的SpringBoot项目,回顾一下HelloWorld程序!【演示】
那我们要引入我们小实验的测试资源,我们项目中有许多的静态资源,比如,css,js等文件,这个SpringBoot怎么处理呢?
如果我们是一个web应用,我们的main下会有一个webapp,我们以前都是将所有的页面导在这里面的,对吧!
但是我们现在的pom呢,打包方式是为jar的方式,那么这种方式SpringBoot能不能来给我们写页面呢?当然是可以的,但是SpringBoot对于静态资源放置的位置,是有规定的!
我们先来聊聊这个静态资源映射规则;
SpringBoot中,SpringMVC的web配置都在 WebMvcAutoConfiguration 这个配置里面,我们可以去看看 WebMvcAutoConfigurationAdapter 中有很多配置方法;
比如: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(getResourceLocations(this.resourceProperties.getStaticLocations())).setCachePeriod(this.getSeconds(cachePeriod)).setCacheControl(cacheControl));
}
}
}
读一下源代码:比如所有的 /webjars/** , 都需要去 classpath:/META-INF/resources/webjars/ 找对应的资源,那什么是webjars呢?
webjars本质就是以jar包的方式引入我们的静态资源 , 我们以前要导入一个静态资源文件,直接导入即可。使用SpringBoot需要使用webjars,我们可以去搜索一下
网站:webjars.org/ 【网站带看,并引入jQuery测试】
要使用jQuery,我们只要要引入jQuery对应版本的pom依赖即可!【导入完毕,查看webjars目录结构,并访问Jquery.js文件】
导入依赖:
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>3.3.1</version>
</dependency>
查看目录结构:
访问:只要是静态资源,SpringBoot就会去对应的路径寻找资源,我们这里访问 ::8080/webjars/jquery/3.3.1/jquery.js
那我们项目中要是使用自己的静态资源该怎么导入呢?我们看下一行代码;
我们去找staticPathPattern发现第二种映射规则 : /** , 访问当前的项目任意资源,它会去找 resourceProperties 这个类,我们可以点进去看一下,
@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/"};
resourceProperties 可以设置和我们静态资源有关的参数,this.staticLocations = CLASSPATH_RESOURCE_LOCATIONS; 这里面指向了它会去寻找资源的文件夹;即上面数组的内容。再向下看一个方法;它还会在根路径下去寻找资源!
private String[] appendSlashIfNecessary(String[] staticLocations) {
String[] normalized = new String[staticLocations.length];
for(int i = 0; i < staticLocations.length; ++i) {
String location = staticLocations[i];
normalized[i] = location.endsWith("/") ? location : location + "/";
}
return normalized;
}
所以得出结论:
"classpath:/META-INF/resources/",
"classpath:/resources/",
"classpath:/static/",
"classpath:/public/",
"/" :当前项目的根目录
我们可以在resources根目录下新建对应的文件夹,都可以存放我们的静态文件;
比如我们访问 localhost:8080/1.js, 他就会去这些文件夹中寻找对应的静态资源文件;
静态资源文件夹说完后,我们继续看源码!可以看到一个欢迎页的映射,就是我们的首页!
@Bean
public WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext applicationContext) {
return new WelcomePageHandlerMapping(new TemplateAvailabilityProviders(applicationContext), applicationContext, this.getWelcomePage(), this.mvcProperties.getStaticPathPattern());
}
点进去继续看
private Optional<Resource> getWelcomePage() {
String[] locations = getResourceLocations(this.resourceProperties.getStaticLocations());
return Arrays.stream(locations).map(this::getIndexHtml).filter(this::isReadable).findFirst();
}
private Resource getIndexHtml(String location) {
return this.resourceLoader.getResource(location + "index.html");
}
欢迎页,静态资源文件夹下的所有index.html页面;被 /** 映射。
比如我访问 localhost:8080/ ,就会找静态资源文件夹下的 index.html 【测试一下】
我们继续向下阅读源码,可以看到一个好玩的功能,配置我们喜欢的图标!( SpringBoot 2.2.0以后版本已删除该部分 )
@Configuration
@ConditionalOnProperty(
value = {"spring.mvc.favicon.enabled"},
matchIfMissing = true
)
public static class FaviconConfiguration implements ResourceLoaderAware {
private final ResourceProperties resourceProperties;
private ResourceLoader resourceLoader;
public FaviconConfiguration(ResourceProperties resourceProperties) {
this.resourceProperties = resourceProperties;
}
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
@Bean
public SimpleUrlHandlerMapping faviconHandlerMapping() {
SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
mapping.setOrder(-2147483647);
mapping.setUrlMap(Collections.singletonMap("**/favicon.ico", this.faviconRequestHandler()));
return mapping;
}
@Bean
public ResourceHttpRequestHandler faviconRequestHandler() {
ResourceHttpRequestHandler requestHandler = new ResourceHttpRequestHandler();
requestHandler.setLocations(this.resolveFaviconLocations());
return requestHandler;
}
private List<Resource> resolveFaviconLocations() {
String[] staticLocations = WebMvcAutoConfiguration.WebMvcAutoConfigurationAdapter.getResourceLocations(this.resourceProperties.getStaticLocations());
List<Resource> locations = new ArrayList(staticLocations.length + 1);
Stream var10000 = Arrays.stream(staticLocations);
ResourceLoader var10001 = this.resourceLoader;
this.resourceLoader.getClass();
var10000.map(var10001::getResource).forEach(locations::add);
locations.add(new ClassPathResource("/"));
return Collections.unmodifiableList(locations);
}
}
“**/favicon.ico” , 就是我们的图标定义的格式!也是在我们的静态资源文件夹下定义,我们可以自定义来测试一下
【测试】自己放一个图标进去,然后在配置文件中关闭SpringBoot默认的图标!
#关闭默认图标
spring.mvc.favicon.enabled=false
清除浏览器缓存!刷新网页,发现图标已经变成自己的了!
我们也可以自己通过配置文件来指定一下,哪些文件夹是需要我们放静态资源文件的,在application.properties中配置;
spring.resources.static-locations=classpath:/hello/,classpath:/kuang/
一旦自己定义了静态文件夹的路径,原来的就都会失效了!
2. 首页如何定制① 首页可存放的目录(4个)② 在SpringBoot2.2.0以后版本中不能定制图标
但是我们可以写在html中引入
<html lang="en">
<head>
<meta charset="UTF-8">
<title>首页</title>
<link rel="shortcut icon" href="./favicon.ico" type="image/x-icon" />
</head>
<body>
<h1>首页</h1>
</body>
</html>
3. 模板引擎Thymeleaf(弃用)
注:SpringBoot2.x必须使用Thymeleaf3.x版本
① 步骤:
导入依赖
编写前端页面引入命名空间
编写后台代码
测试
(1)导入依赖
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
</dependency>
(2)编写前端页面引入命名空间 xmlns:th=””
注意:必须将页面放在template文件夹下
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>test</title>
</head>
<body>
<div th:text="${msg}"></div>
<div th:utext="${msg}"></div>
<hr>
<h3 th:each="user:${users}" th:text="${user}"></h3>
<h3 th:each="user:${users}">[[ ${user} ]]</h3>
</body>
</html>
(3)编写后台代码
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.Arrays;
@Controller
public class IndexController {
@RequestMapping("/test")
public String test(Model model){
model.addAttribute("msg","hello springboot
");
model.addAttribute("users", Arrays.asList("小王","男"));
return "test";
}
}
(4)测试
② 语法讲解
参考官方文档:#attribute-precedence
(1)th的属性:
(2)表达式:
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.
${session.foo}
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=${execId},execType='FAST')}
Fragment Expressions: ~{...}:片段引用表达式
<div th:insert="~{commons :: main}">...</div>
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: _
4. SpringMVC的自动配置(简述)
SpringBoot让我们可以扩展和定制MVC
① 定制
(1)定制视图解析器
步骤:
实现WebMvcConfigurer接口
自定义视图解析器,生成bean交给SpringBoot
添加注解 @Configuration
所以说,我们如果想要使用自己定制化的东西,我们只需要给容器中添加这个组件就好了!剩下的事情SpringBoot就会帮我们做了
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.View;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.Locale;
//自定义视图解析器
//如果需要自定义一些定制化的功能,只要写个组件交给springboot管理,spring boot会帮我们自动装配
@Configuration
public class MyMvcConfig1 implements WebMvcConfigurer {
//生成bean交给Springboot管理
@Bean
public ViewResolver myViewResolver(){
return new MyViewResolver();
}
//自定义一个自己的视图解析器MyViewResolver
public static class MyViewResolver implements ViewResolver{
@Override
public View resolveViewName(String s, Locale locale) throws Exception {
return null;
}
}
}
(2)定制日期格式
只需要在配置文件application.properties中添加需要配置的格式即可
# 自定义日期格式化
#spring.mvc.date-format
② 扩展
步骤:
实现WebMvcConfigurer接口
重写addViewControllers方法
添加注解@Configuration
访问:localhost:8080/xxx会自动帮我们跳转到test视图
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
//自定义视图跳转
//官方建议我们这么去扩展springmvc,自定义类扩展MVC的功能
@Configuration
public class MyMvcConfig2 implements WebMvcConfigurer {
//自定义url路径对应的跳转视图
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/xxx").setViewName("test");
}
}
③ 全面接管SpringMVC
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
//自定义视图解析器
//如果需要自定义一些定制化的功能,只要写个组件交给springboot管理,spring boot会帮我们自动装配
@Configuration
@EnableWebMvc //全面接管SpringMVC,之前Springboot自动配置好的静态资源映射全部失效
public class MyMvcConfig implements WebMvcConfigurer {
}
4.1 SpringMVC自动配置(分析)
在进行测试前,我们还需要知道一个东西,就是SpringBoot 对我们的SpringMVC还做了哪些配置,包括如何扩展,如何定制。只有把这些都搞清楚了,我们在之后使用才会更加得心应手。 途径一:源码分析,途径二:官方文档
地址 :
我们来阅读一段官方文档:
29.1.1 Spring MVC Auto-configuration
Spring Boot provides auto-configuration for Spring MVC that works well with most applications.
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 (covered later in this document)).//自动注册了Converter:【转换器,这就是我们网页提交数据到后台自动封装成为对象的东西,比如把18字符串自动转换为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).
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.
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。找到如下方法!
@Bean //我们在这里确实看到已经给容器中注册了一个bean
@ConditionalOnBean({ViewResolver.class})
@ConditionalOnMissingBean(
name = {"viewResolver"},
value = {ContentNegotiatingViewResolver.class}
)
public ContentNegotiatingViewResolver viewResolver(BeanFactory beanFactory) {
ContentNegotiatingViewResolver resolver = new ContentNegotiatingViewResolver();
resolver.setContentNegotiationManager((ContentNegotiationManager)beanFactory.getBean(ContentNegotiationManager.class));
resolver.setOrder(-2147483648);
return resolver;
}
我们可以点进这类看看!找到对应的解析视图的代码
注解说明:@Nullable 即参数可为null
@Nullable
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循环,挨个解析!
Iterator var5 = this.viewResolvers.iterator();
所以得出结论:ContentNegotiatingViewResolver 这个视图解析器就是用来组合所有的视图解析器的
我们再去研究下他的组合逻辑,看到有个属性viewResolvers,看看它是在哪里进行赋值的!
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());
既然它是在容器中去找视图解析器,我们是否可以猜想,我们就可以去实现定制了呢?
我们可以自己给容器中去添加一个视图解析器;这个类就会帮我们自动的将它组合进来;我们去实现一下
我们在我们的主程序中去写一个视图解析器来试试;
@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;
}
}
怎么看我们自己写的视图解析器有没有起作用呢?我们给dispatcherServlet中的 doDispatch( )方法加个断点进行调试一下,因为所有的请求都会走到这个方法中
我们启动我们的项目,然后随便访问一个页面,看一下Debug信息;
找到this;
找到视图解析器,我们看到我们自己定义的就在这里了;
所以说,我们如果想要使用自己定制化的东西,我们只需要给容器中添加这个组件就好了!剩下的事情SpringBoot就会帮我们做了
转换器和格式化器
找到格式化转换器
@Bean
public FormattingConversionService mvcConversionService() { //拿到配置文件中的格式化规则
WebConversionService conversionService = new WebConversionService(this.mvcProperties.getDateFormat());
this.addFormatters(conversionService);
return conversionService;
}
点击去
public String getDateFormat() {
return this.dateFormat;
}
可以看到在我们的Properties文件中,我们可以进行自动配置它!如果注册了自己的格式化方式,就会注册到Bean中,否则不会注册
我们可以在配置文件中配置日期格式化的规则( 参考前面的定制 ):
4.2 修改SpringBoot的默认配置(分析)方式一
这么多的自动配置,原理都是一样的,通过这个WebMVC的自动配置原理分析,我们要学会一种学习方式,通过源码探究,得出结论;这个结论一定是属于自己的,而且一通百通。
SpringBoot的底层静态内部类,大量用到了这些设计细节思想,所以,没事需要多阅读源码!得出结论;
SpringBoot在自动配置很多组件的时候,先看容器中有没有用户自己配置的(如果用户自己配置@bean),如果有就用用户配置的,如果没有就用自动配置的;如果有些组件可以存在多个静态内部类,比如我们的视图解析器,就将用户配置的和自己默认的组合起来!
① 扩展使用SpringMVC
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;
package com.kuang.myproject.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
//应为类型要求为WebMvcConfigurer,所以我们实现其接口
//可以使用自定义类扩展MVC的功能
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
//浏览器发送/kuang , 就会跳转到success页面;
registry.addViewController("/kuang").setViewName("success");
}
}
我们去浏览器访问一下:
确实也跳转过来了!所以说,我们要扩展SpringMVC,官方就推荐我们这么去使用,既保SpringBoot留所有的自动配置,也能用我们扩展的配置!
我们可以去分析一下原理:
WebMvcAutoConfiguration 是 SpringMVC的自动配置类,里面有一个类WebMvcAutoConfigurationAdapter
这个类上有一个注解,在做其他自动配置时会导入:@Import({WebMvcAutoConfiguration.EnableWebMvcConfiguration.class})
我们点进EnableWebMvcConfiguration这个类看一下,它继承了一个父类:DelegatingWebMvcConfiguration,这个父类中有这样一段代码
private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();
//从容器中获取所有的webmvcConfigurer
@Autowired(required = false)
public void setConfigurers(List<WebMvcConfigurer> configurers) {
if (!CollectionUtils.isEmpty(configurers)) {
this.configurers.addWebMvcConfigurers(configurers);
}
}
我们可以在这个类中去寻找一个我们刚才设置的viewController当做参考,发现它调用了一个
this.configurers.addViewControllers(registry);
我们点进去看一下
public void addViewControllers(ViewControllerRegistry registry) {
Iterator var2 = this.delegates.iterator();
while(var2.hasNext()) { //将所有的WebMvcConfigurer相关配置来一起调用!包括我们自己配置的和Spring给我们配置的
WebMvcConfigurer delegate = (WebMvcConfigurer)var2.next();
delegate.addViewControllers(registry);
}
}
所以得出结论:所有的WebMvcConfiguration都会被作用,不止Spring自己的配置类,我们自己的配置类当然也会被调用;
② 全面接管SpringMVC
If you want to take complete control of Spring MVC, you can add your own @Configuration annotated with @EnableWebMvc.
全面接管即:SpringBoot对SpringMVC的自动配置不需要了,所有都是我们自己去配置!只需在我们的配置类中要加一个@EnableWebMvc.
我们看下如果我们全面接管了SpringMVC了,我们之前SpringBoot给我们配置的静态资源映射一定会无效,我们可以去测试一下;