java框架之SpringBoot(5)-SpringMVC的自动配置
本篇文章内容详细可参考官方文档第 29 节。
SpringMVC介绍
SpringBoot 非常适合 Web 应用程序开发。可以使用嵌入式 Tomcat,Jetty,Undertow 或 Netty 创建自包含的 HTTP 服务器。大多数 Web 应用程序可以通过使用 spring-boot-starter-web 模块快速启动和运行。你还可以选择使用该 spring-boot-starter-webflux 模块构建响应式 Web 应用程序 。
SpringMVC 框架是一个丰富的“模型视图控制器” Web框架。SpringMVC 可以通过使用 @Controller 或 @RestController 注解来标注控制器处理传入的 HTTP 请求。控制器中的方法通过使用 @RequestMapping 注解完成请求映射 。
SpringMVC 是 Spring Framework 的一部分,详细信息可参考官方文档,也可参考博客。
SpringMVC的自动配置
SpringMVC 的自动配置类为 org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration 。
视图解析器
SpringBoot 为 SpringMVC 自动配置了 ContentNegotiatingViewResolver 和 BeanNameViewResolver 即视图解析器。
- 视图解析器可以根据方法的返回值得到对应的视图对象,视图对象来决定如何渲染到页面。
BeanNameViewResolver
查看 BeanNameViewResolver 配置:
@Bean
@ConditionalOnBean(View.class)
@ConditionalOnMissingBean
public BeanNameViewResolver beanNameViewResolver() {
BeanNameViewResolver resolver = new BeanNameViewResolver();
resolver.setOrder(Ordered.LOWEST_PRECEDENCE - 10);
return resolver;
}
org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration.WebMvcAutoConfigurationAdapter#beanNameViewResolver
查看 BeanNameViewResolver 的 resolveViewName 方法:
@Override
public View resolveViewName(String viewName, Locale locale) throws BeansException {
ApplicationContext context = getApplicationContext();
if (!context.containsBean(viewName)) {
if (logger.isDebugEnabled()) {
logger.debug("No matching bean found for view name '" + viewName + "'");
}
// Allow for ViewResolver chaining...
return null;
}
if (!context.isTypeMatch(viewName, View.class)) {
if (logger.isDebugEnabled()) {
logger.debug("Found matching bean for view name '" + viewName +
"' - to be ignored since it does not implement View");
}
// Since we're looking into the general ApplicationContext here,
// let's accept this as a non-match and allow for chaining as well...
return null;
}
return context.getBean(viewName, View.class);
}
org.springframework.web.servlet.view.BeanNameViewResolver#resolveViewName
可以看到,该方法就是在容器中查看是否包含对应名称的 View 实例,如果包含,则直接返回。
ContentNegotiatingViewResolver
查看 ContentNegotiatingViewResolver 配置:
@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));
resolver.setOrder(Ordered.HIGHEST_PRECEDENCE);
return resolver;
}
org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration.WebMvcAutoConfigurationAdapter#viewResolver
查看 ContentNegotiatingViewResolver 的 resolveViewName 方法:
@Override
public View resolveViewName(String viewName, Locale locale) throws Exception {
RequestAttributes attrs = RequestContextHolder.getRequestAttributes();
Assert.isInstanceOf(ServletRequestAttributes.class, attrs);
List<MediaType> requestedMediaTypes = getMediaTypes(((ServletRequestAttributes) attrs).getRequest());
if (requestedMediaTypes != null) {
List<View> candidateViews = getCandidateViews(viewName, locale, requestedMediaTypes);
View bestView = getBestView(candidateViews, requestedMediaTypes, attrs);
if (bestView != null) {
return bestView;
}
}
if (this.useNotAcceptableStatusCode) {
if (logger.isDebugEnabled()) {
logger.debug("No acceptable view found; returning 406 (Not Acceptable) status code");
}
return NOT_ACCEPTABLE_VIEW;
}
else {
logger.debug("No acceptable view found; returning null");
return null;
}
}
org.springframework.web.servlet.view.ContentNegotiatingViewResolver#resolveViewName
看到第 7 行通过 getCandidateViews(viewName, locale, requestedMediaTypes) 方法获取候选 View 的列表,查看该方法:
private List<View> getCandidateViews(String viewName, Locale locale, List<MediaType> requestedMediaTypes)
throws Exception {
List<View> candidateViews = new ArrayList<View>();
for (ViewResolver viewResolver : this.viewResolvers) {
View view = viewResolver.resolveViewName(viewName, locale);
if (view != null) {
candidateViews.add(view);
}
for (MediaType requestedMediaType : requestedMediaTypes) {
List<String> extensions = this.contentNegotiationManager.resolveFileExtensions(requestedMediaType);
for (String extension : extensions) {
String viewNameWithExtension = viewName + "." + extension;
view = viewResolver.resolveViewName(viewNameWithExtension, locale);
if (view != null) {
candidateViews.add(view);
}
}
}
}
if (!CollectionUtils.isEmpty(this.defaultViews)) {
candidateViews.addAll(this.defaultViews);
}
return candidateViews;
}
org.springframework.web.servlet.view.ContentNegotiatingViewResolver#getCandidateViews
可以看到该方法其实是在遍历 this.viewResolvers 来解析获取所有候选的 View,最后返回。那这个 this.viewResolvers 是什么呢?查看该解析器的初始化方法:
@Override
protected void initServletContext(ServletContext servletContext) {
Collection<ViewResolver> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(getApplicationContext(), ViewResolver.class).values();
if (this.viewResolvers == null) {
this.viewResolvers = new ArrayList<ViewResolver>(matchingBeans.size());
for (ViewResolver viewResolver : matchingBeans) {
if (this != viewResolver) {
this.viewResolvers.add(viewResolver);
}
}
}
else {
for (int i = 0; i < viewResolvers.size(); i++) {
if (matchingBeans.contains(viewResolvers.get(i))) {
continue;
}
String name = viewResolvers.get(i).getClass().getName() + i;
getApplicationContext().getAutowireCapableBeanFactory().initializeBean(viewResolvers.get(i), name);
}
}
if (this.viewResolvers.isEmpty()) {
logger.warn("Did not find any ViewResolvers to delegate to; please configure them using the " +
"'viewResolvers' property on the ContentNegotiatingViewResolver");
}
AnnotationAwareOrderComparator.sort(this.viewResolvers);
this.cnmFactoryBean.setServletContext(servletContext);
}
org.springframework.web.servlet.view.ContentNegotiatingViewResolver#initServletContext
这个方法实际上是通过 3-4 行从容器中获取了所有 ViewResolver.class 类型 bean 的集合,即从容器中获取了所有的视图解析器。接着返回到 org.springframework.web.servlet.view.ContentNegotiatingViewResolver#resolveViewName 方法,在 8-10 行获取到最合适的 View 返回。
总的来说,这个视图解析器的作用就是从容器中拿到所有其它的视图解析器解析出 View ,最终从这些 View 中挑选一个最合适的返回。所以我们如果要自己定义视图解析器,只需要将自定义的视图解析器注册到容器即可。
静态资源映射
上一节中有提到,参考【静态资源映射】。
默认首页
上一节中有提到,参考【欢迎页】。
页面图标
上一节中有提到,参考【页面图标】。
类型转换器和格式化器
SpringBoot 为 SpringMVC 自动配置了转换器(Converter)和格式化器(Formater)。例如:客户端发来一个请求携带一些参数,这些参数要与请求方法上的入参进行绑定,进行绑定的过程中涉及到类型转换(如要将 "18" 转换成 Integer 类型),这时就会用到转换器。而如果这些参数中有些参数要转换成日期或其它特殊类型,我们要按约定将请求参数按指定方式格式化(如要将 "3.4.2018" 格式化成我们需要的日期格式),这时就需要用到格式化器。转换器与格式化器的注册在自动配置类中也可以看到:
格式化器
@Bean
@ConditionalOnProperty(prefix = "spring.mvc", name = "date-format")
public Formatter<Date> dateFormatter() {
return new DateFormatter(this.mvcProperties.getDateFormat());
}
org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration.WebMvcAutoConfigurationAdapter#dateFormatter
可以看到,SpringBoot 在配置类中注册了一个日期格式化器,而这个日期格式化的规则可以通过配置文件中的 spring.mvc.date-format 属性进行配置。
转换器
@Override
public void addFormatters(FormatterRegistry registry) {
for (Converter<?, ?> converter : getBeansOfType(Converter.class)) {
registry.addConverter(converter);
}
for (GenericConverter converter : getBeansOfType(GenericConverter.class)) {
registry.addConverter(converter);
}
for (Formatter<?> formatter : getBeansOfType(Formatter.class)) {
registry.addFormatter(formatter);
}
}
private <T> Collection<T> getBeansOfType(Class<T> type) {
return this.beanFactory.getBeansOfType(type).values();
}
org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration.WebMvcAutoConfigurationAdapter#addFormatters
在该方法中可以看到,从容器中获取了所有的转换器进行注册。所以我们如果要自己定义转换器,只需要将自定义的转换器注册到容器即可。
消息转换器
消息转换器(HttpMessageConvert)是 SpringMVC 用来转换 HTTP 请求和响应的,例如我们要将一个 JavaBean 实例以 Json 方式输出,此时就会用到消息转换器。消息转换器的注册定义在 SpringMVC 的自动配置类的一个内部类中,这里贴出部分源码:
@Configuration
@Import(EnableWebMvcConfiguration.class)
@EnableConfigurationProperties({ WebMvcProperties.class, ResourceProperties.class })
public static class WebMvcAutoConfigurationAdapter extends WebMvcConfigurerAdapter {
private static final Log logger = LogFactory
.getLog(WebMvcConfigurerAdapter.class);
private final ResourceProperties resourceProperties;
private final WebMvcProperties mvcProperties;
private final ListableBeanFactory beanFactory;
private final HttpMessageConverters messageConverters;
final ResourceHandlerRegistrationCustomizer resourceHandlerRegistrationCustomizer;
public WebMvcAutoConfigurationAdapter(ResourceProperties resourceProperties,
WebMvcProperties mvcProperties, ListableBeanFactory beanFactory,
@Lazy HttpMessageConverters messageConverters,
ObjectProvider<ResourceHandlerRegistrationCustomizer> resourceHandlerRegistrationCustomizerProvider) {
this.resourceProperties = resourceProperties;
this.mvcProperties = mvcProperties;
this.beanFactory = beanFactory;
this.messageConverters = messageConverters;
this.resourceHandlerRegistrationCustomizer = resourceHandlerRegistrationCustomizerProvider
.getIfAvailable();
}
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.addAll(this.messageConverters.getConverters());
}
org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration.WebMvcAutoConfigurationAdapter
可以看到 messageConverters 是通过构造方法以懒加载的形式注入,即 messageConverters 同样是注册在容器中。继续查看 messageConverters 对应类:
public class HttpMessageConverters implements Iterable<HttpMessageConverter<?>> {
private static final List<Class<?>> NON_REPLACING_CONVERTERS;
static {
List<Class<?>> nonReplacingConverters = new ArrayList<Class<?>>();
addClassIfExists(nonReplacingConverters, "org.springframework.hateoas.mvc."
+ "TypeConstrainedMappingJackson2HttpMessageConverter");
NON_REPLACING_CONVERTERS = Collections.unmodifiableList(nonReplacingConverters);
}
private final List<HttpMessageConverter<?>> converters;
/**
* Create a new {@link HttpMessageConverters} instance with the specified additional
* converters.
* @param additionalConverters additional converters to be added. New converters will
* be added to the front of the list, overrides will replace existing items without
* changing the order. The {@link #getConverters()} methods can be used for further
* converter manipulation.
*/
public HttpMessageConverters(HttpMessageConverter<?>... additionalConverters) {
this(Arrays.asList(additionalConverters));
}
/**
* Create a new {@link HttpMessageConverters} instance with the specified additional
* converters.
* @param additionalConverters additional converters to be added. Items are added just
* before any default converter of the same type (or at the front of the list if no
* default converter is found) The {@link #postProcessConverters(List)} method can be
* used for further converter manipulation.
*/
public HttpMessageConverters(
Collection<HttpMessageConverter<?>> additionalConverters) {
this(true, additionalConverters);
}
/**
* Create a new {@link HttpMessageConverters} instance with the specified converters.
* @param addDefaultConverters if default converters should be added
* @param converters converters to be added. Items are added just before any default
* converter of the same type (or at the front of the list if no default converter is
* found) The {@link #postProcessConverters(List)} method can be used for further
* converter manipulation.
*/
public HttpMessageConverters(boolean addDefaultConverters,
Collection<HttpMessageConverter<?>> converters) {
List<HttpMessageConverter<?>> combined = getCombinedConverters(converters,
addDefaultConverters ? getDefaultConverters()
: Collections.<HttpMessageConverter<?>>emptyList());
combined = postProcessConverters(combined);
this.converters = Collections.unmodifiableList(combined);
}
org.springframework.boot.autoconfigure.web.HttpMessageConverters
可以看到该类实现了迭代器接口,且构造函数使用了可变参数,即会将容器中所有的 HttpMessageConverter 注入。所以我们如果要使用自己定义的消息转换器,也只需要将消息转换器注册到容器即可。
消息代码处理器
消息代码处理器(MessageCodesResolver)是用于定义 SpringMVC 的消息代码的生成规则。
@Override
public MessageCodesResolver getMessageCodesResolver() {
if (this.mvcProperties.getMessageCodesResolverFormat() != null) {
DefaultMessageCodesResolver resolver = new DefaultMessageCodesResolver();
resolver.setMessageCodeFormatter(
this.mvcProperties.getMessageCodesResolverFormat());
return resolver;
}
return null;
}
org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration.WebMvcAutoConfigurationAdapter#getMessageCodesResolver
数据绑定初始化器
数据绑定初始化器(ConfigurableWebBindingInitializer)是初始化 Web 数据绑定器(WebDataBinder),而 Web 数据绑定器是用来绑定 web 数据的,比如将请求参数绑定到 JavaBean 中就会用到 Web 数据绑定器。
@Override
protected ConfigurableWebBindingInitializer getConfigurableWebBindingInitializer() {
try {
return this.beanFactory.getBean(ConfigurableWebBindingInitializer.class);
}
catch (NoSuchBeanDefinitionException ex) {
return super.getConfigurableWebBindingInitializer();
}
}
org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration.EnableWebMvcConfiguration#getConfigurableWebBindingInitializer
SpringBoot 在自动配置很多组件时,会先判断容器中有没有用户已注册的对应组件,如果没有,才自动配置它提供的默认组件。如果有些组件可以有多个(例如视图解析器),那么 SpringBoot 会将用户自己的配置和默认的配置组合起来。
扩展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
@Configurationclass of typeWebMvcConfigurerbut without@EnableWebMvc.If you want to take complete control of Spring MVC, you can add your own
@Configurationannotated with@EnableWebMvc.如果你想在保留 Spring Boot MVC 一些默认配置的基础上添加一些额外的 MVC 配置,例如:拦截器、格式化器、视图控制器等,你可以使用 @Configuration 注解自定义一个 WebMvcConfigurer 类但不能使用 @EnableWebMvc。
如果你想完全控制 Spring MVC,你可以在你的配置类上同时使用 @Configuration 和 @EnableWebMvc。
定义SpringMVC配置类
WebMvcConfigurerAdapter 是 WebMvcConfigurer 的适配器类,按照上面描述我们可以定义 SpringMVC 扩展配置类如下:
package com.springboot.webdev1.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
/**
* 要扩展什么功能直接重写对应方法即可
* 既保留了所有自动配置,也使用了我们扩展的配置
*/
@Configuration
public class MyMvcConfig extends WebMvcConfigurerAdapter {
/**
* 添加视图映射
* @param registry
*/
@Override
public void addViewControllers(ViewControllerRegistry registry) {
// 请求路径 /test 将会转发到名为 test 的视图
registry.addViewController("/test").setViewName("test");
}
}
com.springboot.webdev1.config.MyMvcConfig
再次查看 SpringMVC 的自动配置类会发现该类有一个内部类也是继承了 WebMvcConfigurerAdapter ,即 SpringBoot 也是通过该方式实现 SpringMVC 的自动配置:
@Configuration
@Import(EnableWebMvcConfiguration.class)
@EnableConfigurationProperties({ WebMvcProperties.class, ResourceProperties.class })
public static class WebMvcAutoConfigurationAdapter extends WebMvcConfigurerAdapter {
org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration.WebMvcAutoConfigurationAdapter
在该类上有一个注解 @Import(EnableWebMvcConfiguration.class) ,查看 EnableWebMvcConfiguration 类:
@Configuration
public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration {
org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration.EnableWebMvcConfiguration
接着查看 DelegatingWebMvcConfiguration 类:
package org.springframework.web.servlet.config.annotation;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.format.FormatterRegistry;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.validation.MessageCodesResolver;
import org.springframework.validation.Validator;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.servlet.HandlerExceptionResolver;
@Configuration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();
@Autowired(required = false)
public void setConfigurers(List<WebMvcConfigurer> configurers) {
if (configurers == null || configurers.isEmpty()) {
return;
}
this.configurers.addWebMvcConfigurers(configurers);
}
@Override
protected void addInterceptors(InterceptorRegistry registry) {
this.configurers.addInterceptors(registry);
}
@Override
protected void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
this.configurers.configureContentNegotiation(configurer);
}
@Override
public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
this.configurers.configureAsyncSupport(configurer);
}
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
this.configurers.configurePathMatch(configurer);
}
@Override
protected void addViewControllers(ViewControllerRegistry registry) {
this.configurers.addViewControllers(registry);
}
@Override
protected void configureViewResolvers(ViewResolverRegistry registry) {
this.configurers.configureViewResolvers(registry);
}
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
this.configurers.addResourceHandlers(registry);
}
@Override
protected void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
this.configurers.configureDefaultServletHandling(configurer);
}
@Override
protected void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
this.configurers.addArgumentResolvers(argumentResolvers);
}
@Override
protected void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) {
this.configurers.addReturnValueHandlers(returnValueHandlers);
}
@Override
protected void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
this.configurers.configureMessageConverters(converters);
}
@Override
protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
this.configurers.extendMessageConverters(converters);
}
@Override
protected void addFormatters(FormatterRegistry registry) {
this.configurers.addFormatters(registry);
}
@Override
protected Validator getValidator() {
return this.configurers.getValidator();
}
@Override
protected MessageCodesResolver getMessageCodesResolver() {
return this.configurers.getMessageCodesResolver();
}
@Override
protected void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
this.configurers.configureHandlerExceptionResolvers(exceptionResolvers);
}
@Override
protected void addCorsMappings(CorsRegistry registry) {
this.configurers.addCorsMappings(registry);
}
}
org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration
可以看到通过第 22 行的 setConfigurers(List<WebMvcConfigurer> configurers) 方法注入了所有的 WebMvcConfigurer 实例,即包含了我们自己编写的 WebMvcConfigurer bean。这就是我们自定义 WebMvcConfigurer 能生效的原因。
全面接管SpringMVC配置
全面接管 SpringMVC 配置在上面描述中也有说明,我们只需要在配置类的基础上再使用 @EnableWebMvc 注解即可:
package com.springboot.webdev1.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
/**
* 全面接管 SpringMVC 配置,Spring 的自动配置已失效
*/
@EnableWebMvc
@Configuration
public class MyMvcConfig extends WebMvcConfigurerAdapter {
/**
* 添加视图映射
* @param registry
*/
@Override
public void addViewControllers(ViewControllerRegistry registry) {
// 请求路径 /test 将会转发到名为 test 的视图
registry.addViewController("/test").setViewName("test");
}
}
com.springboot.webdev1.config.MyMvcConfig
为什么加上 @EnableWebMvc 注解 SpringBoot 对于 SpringMVC 的自动配置就失效了呢?查看 SpringMVC 的自动配置类:
@Configuration
@ConditionalOnWebApplication
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class,
WebMvcConfigurerAdapter.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class,
ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {
org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration
可以看到配置类上有一个 @ConditionalOnMissingBean(WebMvcConfigurationSupport.class) 注解,该注解的意思就是当容器中不存在 WebMvcConfigurationSupport 这个类型的 bean 时自动配置类才生效。再查看 @EnableWebMvc 注解:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(DelegatingWebMvcConfiguration.class)
public @interface EnableWebMvc {
}
org.springframework.web.servlet.config.annotation.EnableWebMvc
可以看到它使用 @Import(DelegatingWebMvcConfiguration.class) 给容器中导入了 DelegatingWebMvcConfiguration 类型的 bean,而 DelegatingWebMvcConfiguration 又继承自 WebMvcConfigurationSupport ,即使用了 @EnableWebMvc 注解后容器中会注入 WebMvcConfigurationSupport 类型的 bean,此时就与 SpringMVC 自动配置类上的 @ConditionalOnMissingBean(WebMvcConfigurationSupport.class) 注解条件相斥,所以自动配置类失效。
java框架之SpringBoot(5)-SpringMVC的自动配置的更多相关文章
- SpringBoot中SpringMVC的自动配置以及扩展
一.问题引入 我们在SSM中使用SpringMVC的时候,需要由我们自己写SpringMVC的配置文件,需要用到什么就要自己配什么,配置起来也特别的麻烦.我们使用SpringBoot的时候没有进行配置 ...
- SpringBoot中对SpringMVC的自动配置
https://docs.spring.io/spring-boot/docs/1.5.10.RELEASE/reference/htmlsingle/#boot-features-developin ...
- 7、springmvc的自动配置
1.springmvc的自动配置 文档:https://docs.spring.io/spring-boot/docs/2.1.1.RELEASE/reference/htmlsingle/#boot ...
- SpringMVC的自动配置解析
https://docs.spring.io/spring-boot/docs/1.5.10.RELEASE/reference/htmlsingle/#boot-features-developin ...
- java框架之SpringBoot(4)-资源映射&thymeleaf
资源映射 静态资源映射 查看 SpringMVC 的自动配置类,里面有一个配置静态资源映射的方法: @Override public void addResourceHandlers(Resource ...
- java框架之SpringBoot(6)-Restful风格的CRUD示例
准备 环境 IDE:Idea SpringBoot版本:1.5.19 UI:BootStrap 4 模板引擎:thymeleaf 3 效果:Restful 风格 CRUD 功能的 Demo 依赖 &l ...
- SpringBoot | 4.1 SpringMVC的自动配置
目录 前言 1. SpringMVC框架的设计与流程 1.1 SpringMVC框架的示意图 1.2 SpringMVC的组件流程 2. *自动配置的源码分析 2.1 导入Web场景启动器 2.2 找 ...
- SpringBoot:配置文件及自动配置原理
西部开源-秦疆老师:基于SpringBoot 2.1.6 的博客教程 秦老师交流Q群号: 664386224 未授权禁止转载!编辑不易 , 转发请注明出处!防君子不防小人,共勉! SpringBoot ...
- SpringBoot自定义starter及自动配置
SpringBoot的核心就是自动配置,而支持自动配置的是一个个starter项目.除了官方已有的starter,用户自己也可以根据规则自定义自己的starter项目. 自定义starter条件 自动 ...
随机推荐
- 关于python协程中aiorwlock 使用问题
最近工作中多个项目都开始用asyncio aiohttp aiomysql aioredis ,其实也是更好的用python的协程,但是使用的过程中也是遇到了很多问题,最近遇到的就是 关于aiorwl ...
- [rook] rook的控制流
以下是rook为一个pod准备可用块存储的过程: 1. rook operator运行,并且在k8s每台机器上运行一个rook agent的pod: 2. 用户创建一个pvc,并指定storagecl ...
- Linux基础知识之用户和用户组以及 Linux 权限管理
已经开始接触Linux用户管理,用户组管理,以及权限管理这几个逼格满满的关键字.这几个关键字对于前端程序猿的我来说真的是很高大上有木有,以前尝试学 Linux 的时候看到这些名词总是下意识的跳过不敢看 ...
- iis asp.net4.0注册
asp.net4.0下载地址:https://download.microsoft.com/download/9/5/A/95A9616B-7A37-4AF6-BC36-D6EA96C8DAAE/do ...
- 自定义命令杀死 java 进程 alias kjava
alias kjava='ps -ef|grep ProcessName |awk "{print $2}"|xargs kill -9' 上面脚本放在杀JAVA进程中,会出现一些 ...
- 怎么让self.view的Y从navigationBar下面开始计算
原文地址 http://blog.sina.com.cn/s/blog_1410870560102wu9a.html 在iOS 7中,苹果引入了一个新的属性,叫做[UIViewController s ...
- JavaScript 字符串与json对象互转的几种方法
第一种:浏览器支持的转换方式(Firefox,chrome,opera,safari,ie)等浏览器: JSON.parse(jsonstr); //可以将json字符串转换成json对象 JSON. ...
- 某公司面试java试题之【二】,看看吧,说不定就是你将要做的题
这次做的题是在是太多了,五页呢,吓死宝宝了!
- saltstack通过jinja模板,grains方式将配置的变量值写入到配置文件中?
需求描述: 在通过saltstack进行jinja模板获取值的时候,可以通过grains的方式,获取一些操作系统相关的信息,比如,OS,ip地址等,在这里演示下,做个记录. 演示: 1.修改sls文件 ...
- 用google map实现周边搜索功能
项目要实现根据经纬度获取附近的建筑,由于项目在海外运营,谷歌地图首当其冲. 首先说明的是,该功能需要在服务端实现,也就是安卓的SDK不适用. api文档地址: https://developers.g ...