SpringMVC自动配置
https://docs.spring.io/spring-boot/docs/2.2.5.BUILD-SNAPSHOT/reference/html/spring-boot-features.html#boot-features-spring-mvc-auto-configuration
springboot
自动配置好了Springmvc
.
以下是SpringBoot
对SpringMVC
的默认配置:
1.自动配置了ViewResolver
(视图解析器:根据方法的返回值得到视图对象view
,视图对象决定如何渲染,转发或者重定向)
ContentNegotiatingViewResolver->initServletContext()
:组合所有的视图解析器
如何定制:我们可以给容器中添加一个视图解析器,自动将其组合进来
测试:
启动类里
@Bean
public ViewResolver myViewResolver(){
return new MyViewResolver();
}
private static class MyViewResolver implements ViewResolver{
@Override
public View resolveViewName(String s, Locale locale) throws Exception {
return null;
}
}
DispatcherServlet
类里:所有请求一进来回来到doDispatch
方法
以调试的方式启动,看一下DispatcherServlet
用到的视图解析器是什么。
随便发起一个请求
2.HttpMessageConverters:
消息转换器, SpringMVC用来转换Http请求响应的,比如:方法返回了User
类型,我想以json
的形式写出去,那么我们就需要有一个能把User
以json
写出去的HttpMessageConverters
。
WebMvcAutoConfiguration
自动配置类里有一个配置类WebMvcAutoConfigurationAdapter
,这个配置有只有一个有参数的构造器,那么这个构造器的参数都从容器中获取,这些参数就包括,ObjectProvider<HttpMessageConverters> messageConvertersProvider
,所以只需要将自己的组件注册到容器中(@Bean,@Component
)
扩展springmvc
编写一个配置类(@Configuration
),是WebMvcConfigurerAdapter
类型;不能标注@EnableWebMvc
; springtboot2.x
版本WebMvcConfigurerAdapter
过时了。使用 implements WebMvcConfigurer
例如:添加视图解析器,启动访问http://localhost:8082/atguigu
//使用WebMvcConfigurer可以扩展springmvc的功能
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
/**
* 发一个请求来一个页面,只要不放数据,就不用写controller空方法了,直接做视图映射
* @param registry
*/
@Override
public void addViewControllers(ViewControllerRegistry registry) {
//要把什么请求映射到什么页面
//浏览器发送 /atguigu 请求来到 success页面
registry.addViewController("/atguigu").setViewName("success");
}
}
既保留了所有的自动配置,也能用我们扩展的配置;
原理:
1),WebMvcAutoConfiguration
是SpringMVC
的自动配置类
2),再做其他自动配置时会导入;@Import(EnableWebMvcConfiguration.class)
这个类的描述如下,重要的是父类DelegatingWebMvcConfiguration
@Configuration(proxyBeanMethods = false)
public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration implements ResourceLoaderAware {
父类DelegatingWebMvcConfiguration
:有这么一段代码
自动装配一旦标注在方法上,方法的参数就要从容器中获取
//从容器中获取所有的WebMvcConfigurer
@Autowired(required = false)
public void setConfigurers(List<WebMvcConfigurer> configurers) {
if (!CollectionUtils.isEmpty(configurers)) {
//将获取到的WebMvcConfigurer添加到configurers中
this.configurers.addWebMvcConfigurers(configurers);
}
}
configurers
将所有的WebMvcConfigurer
相关配置都来一起调用
例如:
//还是父类`DelegatingWebMvcConfiguration`:
protected void addViewControllers(ViewControllerRegistry registry) {
this.configurers.addViewControllers(registry);
}
//WebMvcConfigurerComposite:configurers就是WebMvcConfigurerComposite,delegates保存着所有的WebMvcConfigurer
public void addViewControllers(ViewControllerRegistry registry) {
Iterator var2 = this.delegates.iterator();
//遍历所有的WebMvcConfigurer
while(var2.hasNext()) {
WebMvcConfigurer delegate = (WebMvcConfigurer)var2.next();
//然后把它们添加视图解析器的方法都调一遍
delegate.addViewControllers(registry);
}
}
3),容器中所有的WebMvcConfigurer都会一起起作用
4),我们的配置类也会被调用
效果:springmvc的自动配置和我们的扩展配置都会起作用;
全面接管SpringMVC
springboot
对springmvc
的自动配置不需要了,所有都是我们自己配;所有的SpringMVC的自动配置都失效。
我们需要在配置类中添加@EnableWebMvc
比如对静态资源的访问:
在静态资源文件夹下放置index.jsp
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>首页</h1>
</body>
</html>
启动项目,http://localhost:8082/
,是可以正常访问的。
然后在配置类上加上注解@EnableWebMvc
,重启发现访问不了。
原理:
为什么加了@EnableWebMvc
自动配置就失效了?
1),@EnableWebMvc
的核心
@Import({DelegatingWebMvcConfiguration.class})
public @interface EnableWebMvc {
2),DelegatingWebMvcConfiguration
:
@Configuration(proxyBeanMethods = false)
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
3),WebMvcAutoConfiguration
:
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
//容器中没有这个组件的时候,这个自动配置类才生效
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,
ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {
4)、@EnableWebMvc
将WebMvcConfigurationSupport
组件导入进来;那么WebMvcAutoConfiguration
自然不能生效
5)、导入的WebMvcConfigurationSupport
只是SpringMVC
最基本的功能;
如何修改SpringBoot的默认配置
模式:
1)、SpringBoot
在自动配置很多组件的时候,先看容器中有没有用户自己配置的(@Bean、@Component
)如果有就用用户配置的,如果没有,才自动配置(在自动配置类里,可以看到很多@ConditionalOnMissingBean
,如果用户没有自己配置的组件,就是用自动配置的组件);如果有些组件可以有多个(ViewResolver
)将用户配置的和自己默认的组合起来;
2)、在SpringBoot
中会有非常多的xxxConfigurer
帮助我们进行扩展配置 ,例如前面的WebMvcConfigurer
3)、在SpringBoot
中会有很多的xxxCustomizer
帮助我们进行定制配置
添加视图解析器
访问模板引擎里的login.html,登陆页,但是这里不涉及到数据,可以不写controller空方法,直接做视图映射
//访问模板引擎里的login.html,登陆页
//因为不涉及数据,也可以写在视图解析器里
@RequestMapping({"/","/login.html"})
public String login(){
return "login";
}
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
//方式一
//发一个请求来一个页面,只要不放数据,就不用写controller空方法了,直接做视图映射
@Override
public void addViewControllers(ViewControllerRegistry registry) {
//要把什么请求映射到什么页面
//浏览器发送 /atguigu 请求来到 success页面
registry.addViewController("/atguigu").setViewName("success");
// registry.addViewController("/").setViewName("login");
// registry.addViewController("/login.html").setViewName("login");
}
//另一种方式 利用前面分析扩展springmvc的原理
//所有的WebMvcConfigurer都会一起起作用
@Bean
public WebMvcConfigurer webMvcConfigurer(){
WebMvcConfigurer webMvcConfigurer = new WebMvcConfigurer() {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("login");
registry.addViewController("/login.html").setViewName("login");
}
};
return webMvcConfigurer;
}
}
国际化
以前springmvc
:
1),编写国际化配置文件
2),使用ResourceBundleMessageSource
管理国际化资源文件
3)、如果使用jsp页面,在页面使用fmt:message
取出国际化内容
springboot
国际化:
1),编写国际化配置文件,抽取页面需要显示的国际化消息
例如:在登陆页面页面
新建国际化文件:
login.properties 没有指定语言信息情况下默认的国际化配置
login_zh_CN.properties 中文的
login_en_US.properties 英文的
login_zh_CN.properties
语言代码zh+国家代码CN,当新建了一个login_zh_CN.properties
,idea
自动切换到国际化视图
写配置:随便进一个国际化配置文件
2),Springboot自动配置好了管理国际化资源文件的组件
MessageSourceAutoConfiguration:
//启动指定类的ConfigurationProperties功能
@EnableConfigurationProperties
public class MessageSourceAutoConfiguration {
@Bean
//将配置文件中以spring.messages开头的配置与MessageSourceProperties绑定到一起,
//并通过@Bean注解将MessageSourceProperties 注入到容器中
//有些xxxProperties标注了@ConfigurationProperties(prefix = "。。。"),但是这个是没标注。而是在这里标注
@ConfigurationProperties(prefix = "spring.messages")
public MessageSourceProperties messageSourceProperties() {
return new MessageSourceProperties();
}
//参数properties从容器中获取,就是上面那个
@Bean
public MessageSource messageSource(MessageSourceProperties properties) {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
if (StringUtils.hasText(properties.getBasename())) {
//设置国际化资源文件的基础名(去掉语言国家代码的)
//比如:login_en_US.properties login就叫基础名
//进入MessageSourceProperties ,可以看到基础名默认叫messages
messageSource.setBasenames(StringUtils
.commaDelimitedListToStringArray(StringUtils.trimAllWhitespace(properties.getBasename())));
}
if (properties.getEncoding() != null) {
messageSource.setDefaultEncoding(properties.getEncoding().name());
}
messageSource.setFallbackToSystemLocale(properties.isFallbackToSystemLocale());
Duration cacheDuration = properties.getCacheDuration();
if (cacheDuration != null) {
messageSource.setCacheMillis(cacheDuration.toMillis());
}
messageSource.setAlwaysUseMessageFormat(properties.isAlwaysUseMessageFormat());
messageSource.setUseCodeAsDefaultMessage(properties.isUseCodeAsDefaultMessage());
return messageSource;
}
配置:spring.messages.basename=i18n.login
3),去页面获取国际化的值
如果使用jsp页面,在页面使用fmt:message
取出国际化内容
但是现在springboot
推荐使用的是thymeleaf
,如何获取呢
文本:th:text="#{login.tip}
<h1 class="h3 mb-3 font-weight-normal" th:text="#{login.tip}">Please sign in</h1>
placeholder:th:placeholder="#{login.username}"
<input type="text" class="form-control" placeholder="Username" required="" autofocus="" th:placeholder="#{login.username}">
行内写法:[[#{login.remenber}]]
<input type="checkbox" value="remember-me" /> [[#{login.remenber}]]
更多下载thymeleaf
文档查看:
https://www.thymeleaf.org/documentation.html 官网
上面的参考自第4,12章
测试国际化:
启动项目,打开谷歌浏览器,查看语言,设置-》高级里查看
访问:http://localhost:8082/crud/login.html
乱码解决:
然后到国际化文件中,把中文重写。重新启动,访问即可。把浏览器的语言换成美国英语,刷新页面又会变成英文
这里是通过改变浏览器的语言来实现国际化的切换,那如何实现通过点按钮来切换呢
原理:
国际化Locale
(区域信息对象);LocaleResolver
(获取区域信息对象);
WebMvcAutoConfiguration
:
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(prefix = "spring.mvc", name = "locale")
public LocaleResolver localeResolver() {
if (this.mvcProperties.getLocaleResolver() == WebMvcProperties.LocaleResolver.FIXED) {
return new FixedLocaleResolver(this.mvcProperties.getLocale());
}
AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver();
localeResolver.setDefaultLocale(this.mvcProperties.getLocale());
return localeResolver;
}
进入WebMvcProperties
发现,所以默认LocaleResolver
是ACCEPT_HEADER
private LocaleResolver localeResolver = LocaleResolver.ACCEPT_HEADER;
AcceptHeaderLocaleResolver
:
public Locale resolveLocale(HttpServletRequest request) {
Locale defaultLocale = this.getDefaultLocale();
if (defaultLocale != null && request.getHeader("Accept-Language") == null) {
return defaultLocale;
} else {
//从请求头中拿到区域信息
Locale requestLocale = request.getLocale();
默认的区域信息解析器就是根据请求头带来的区域信息获取Locale
进行国际化
可以看看:
自己写一个LocaleResolver
,不使用WebMvcAutoConfiguration
下自动配置的LocaleResolver
public class MyLocalResolver implements LocaleResolver {
@Override
public Locale resolveLocale(HttpServletRequest httpServletRequest) {
String l = httpServletRequest.getParameter("l");
//如果链接没带区域信息就使用系统默认的
//也可以使用httpServletRequest.getLocale()获取请求头的
Locale locale = Locale.getDefault();
if (!StringUtils.isEmpty(l)){
String[] s = l.split("_");
locale=new Locale(s[0],s[1] );
}
return locale;
}
@Override
public void setLocale(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Locale locale) {
}
}
//在配置类中注入
@Bean
public LocaleResolver localeResolver(){
return new MyLocalResolver();
}
可以在连接上携带区域信息:
<a class="btn btn-sm" th:href="@{/login.html(l='zh_CN')}">中文</a>
<a class="btn btn-sm" th:href="@{/login.html(l='en_US')}">English</a>
测试:点击中文,英文即可切换。连接不带区域信息的使用系统默认的