SpringBoot——MVC原理

优质博文:IT-BLOG-CN

一、SpringMVC自动配置

SpringMVC auto-configurationSpringBoot自动配置好了SpringMVC。以下是SpringBootSpringMVC的默认配置:[WebMvcAutoConfiguration]

【1】包括ContentNegotiatingViewResolverBeanNameViewResolver如下:

java 复制代码
@Bean
@ConditionalOnBean({ViewResolver.class})
@ConditionalOnMissingBean(
	name = {"viewResolver"},
	value = {ContentNegotiatingViewResolver.class}
)
//存在于 WebMvcAutoConfiguration.java
public ContentNegotiatingViewResolver viewResolver(BeanFactory beanFactory) {
	ContentNegotiatingViewResolver resolver = new ContentNegotiatingViewResolver();
	resolver.setContentNegotiationManager((ContentNegotiationManager)beanFactory.getBean(ContentNegotiationManager.class));
	resolver.setOrder(-2147483648);
	return resolver;
}

//进入ContentNegotiatingViewResolver对象,查找解析视图的方法resolveViewName()
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;
		}
}

//进入上面的getCandidateViews()方法,查看获取的视图解析器,发现SpringBoot是将所有的视图解析器获取到viewResolvers,挨个遍历获取。
private List<View> getCandidateViews(String viewName, Locale locale, List<MediaType> requestedMediaTypes) throws Exception {
	List<View> candidateViews = new ArrayList();
	Iterator var5 = this.viewResolvers.iterator();

while(var5.hasNext()) {

【2】自动配置了ViewResolver(视图解析器:根据方法的返回值得到视图对象(View),视图对象决定如何渲染(转发?重定向?))

【3】ContentNegotiatingViewResolver:组合所有的视图解析器的;

JAVA 复制代码
//进入ContentNegotiatingViewResolver发现初始化视图解析器的时候,是从容器中BeanFactoryUtils获取所有的视图解析器。
protected void initServletContext(ServletContext servletContext) {
	Collection<ViewResolver> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(
		this.getApplicationContext(), ViewResolver.class).values();
	if(this.viewResolvers == null) {

【4】如何定制:我们可以自己给容器中添加一个视图解析器;自动的将其组合进来;

java 复制代码
 @Bean
 @ConditionalOnProperty(prefix = "spring.mvc", name = "date‐format")//在文件中配置日期格式化的规则
 public Formatter<Date> dateFormatter() {        
	   return new DateFormatter(this.mvcProperties.getDateFormat());//日期格式化组件            
 } 

//举个栗子如下:
//可以自定义一个视图解析器,放入容器,springboot就会自动识别,继承viewreserve
@Bean
public MyView myView(){
	return new MyView();
}
//需要实现ViewResolver接口
private static class MyView implements ViewResolver{

	@Override
	public View resolveViewName(String s, Locale locale) throws Exception {
		return null;
	}
}

【5】服务对静态资源的支持,静态资源文件夹路径,webjars等。静态首页访问,自定义favicon.ico 图标文件的支持。

【6】自动注册了of Converter,GenericConverter,Formatter beans

Converter:转换器; public String hello(User user):类型转换使用ConverterStringint等等。

Formatter 格式化器; 2017.12.17===Date,源码如下:可以看到格式可以通过spring.mvc.date-format调整。

java 复制代码
@Bean
@ConditionalOnProperty(
	prefix = "spring.mvc",
	name = {"date-format"}
)
public Formatter<Date> dateFormatter() {
	return new DateFormatter(this.mvcProperties.getDateFormat());
}

○ 自己添加的格式化器转换器,我们只需要放在容器中即可,上面代码块有演示。

【7】支持HttpMessageConverters

HttpMessageConverterSpringMVC用来转换Http请求和响应的;UserJson方式写出去;

HttpMessageConverters是从容器中确定;获取所有的HttpMessageConverter

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

【8】自动注册MessageCodesResolver,定义错误代码生成规则。自动使用ConfigurableWebBindingInitializer类;

java 复制代码
protected ConfigurableWebBindingInitializer getConfigurableWebBindingInitializer() {
		return (ConfigurableWebBindingInitializer)this.beanFactory.getBean(ConfigurableWebBindingInitializer.class);
}

它是从容器中获取ConfigurableWebBindingInitializer的,从而可知,我们可以配置一个ConfigurableWebBindingInitializer来替换默认的(添加到容器),如果没有配置会初始化一个Web数据绑定器:

java 复制代码
//初始化Web数据绑定器,作用就是将请求数据绑定到JavaBean中,参数等,涉及数据转换等等
protected ConfigurableWebBindingInitializer getConfigurableWebBindingInitializer() {
	ConfigurableWebBindingInitializer initializer = new ConfigurableWebBindingInitializer();
	initializer.setConversionService(this.mvcConversionService());
	initializer.setValidator(this.mvcValidator());
	initializer.setMessageCodesResolver(this.getMessageCodesResolver());
	return initializer;
}

【9】org.springframework.boot.autoconfigure.webweb的所有自动场景;上面能够得到的主要思想就是:如何修改Springboot的默认配置:

1)、在自动配置很多组件的时候,先看容器中有木有用户自己配置的(@Bean,@Component)如果有就是用用户配置的,如果没有就是用自动配置的,因为底层使用了@ConditionalOnMiss注解来判断,容器中是否已经存在此类配置。

2)、如果有些组件可以配置多个,比如视图解析器(ViewResolver)将用户配置的和自己默认的组合起来。

扩展 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.

【1】根据我们之前的配置xml来进行扩展:

xml 复制代码
<mvc:view‐controller path="/hello" view‐name="success"/>
<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/hello"/>
    <bean></bean>
    </mvc:interceptor>
</mvc:interceptors>

【2】SpringBoot编写一个配置类@Configuration,继承WebMvcConfigurerAdapter类型,不能标注@EnableWebMvc。 继承抽象类既保留了所有的自动配置,也能用我们扩展的配置;

java 复制代码
//使用WebMvcConfigurerAdapter可以来扩展SpringMVC的功能
@Configuration
public class MyMvcConfig extends WebMvcConfigurerAdapter {
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
       // super.addViewControllers(registry);
        //浏览器发送 /yintong 请求来到 success ,视图映射,当没有业务逻辑的时候就比较方便
        registry.addViewController("/yintong").setViewName("success");
    }
}

原理:

【1】WebMvcAutoConfigurationSpringMVC的自动配置类;

【2】在做其他自动配置时会导入;@Import(EnableWebMvcConfiguration.class)

java 复制代码
@Configuration
public static class EnableWebMvcConfiguration extends 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);
             //一个参考实现;将所有的WebMvcConfigurer相关配置都来一起调用;      
             @Override    
             public void addViewControllers(ViewControllerRegistry registry) {
                 for (WebMvcConfigurer delegate : this.delegates) {
                     delegate.addViewControllers(registry);
                 }
              }
          }
}

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

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

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

二、全面接管SpringMVC

让所有SpringMVC的自动配置都失效。使用我们需要的配置,需要在配置类中添加 @EnableWebMvc即可。非常不推荐,不然使用SpringBoot开发干嘛,哈哈。

java 复制代码
//使用WebMvcConfigurerAdapter可以来扩展SpringMVC的功能 
@EnableWebMvc 
@Configuration 
public class MyMvcConfig extends WebMvcConfigurerAdapter {     
    @Override     
    public void addViewControllers(ViewControllerRegistry registry) {
        // super.addViewControllers(registry);         
        //浏览器发送 /atguigu 请求来到 success                 
        registry.addViewController("/atguigu").setViewName("success");     
    } 
}

原理: 为什么@EnableWebMvc自动配置就失效了?

【1】@EnableWebMvc的核心组合注解:

java 复制代码
@Import(DelegatingWebMvcConfiguration.class)
public @interface EnableWebMvc {

【2】我们打开上面导入的DelegatingWebMvcConfiguration类,会发现其继承了WebMvcConfigurationSupport

java 复制代码
@Configuration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {

【3】我们看下SpringBoot自动配置的文件,发现如下:@ConditionalOnMissingBean(WebMvcConfigurationSupport.class),可知当容器中存在WebMvcConfigurationSupport类时,就不会导入自动配置的类了,第二步导入的就是这个类。

java 复制代码
@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 {

【4】@EnableWebMvcWebMvcConfigurationSupport组件导入进来;

【5】导入的WebMvcConfigurationSupport只是SpringMVC最基本的功能;

结论:SpringBoot中会有非常多的xxxConfigurer帮助我们进行扩展配置。同时,在SpringBoot中也会有很多的xxxCustomizer帮助我们进行定制配置。

相关推荐
一只叫煤球的猫7 小时前
写代码很6,面试秒变菜鸟?不卖课,面试官视角走心探讨
前端·后端·面试
bobz9657 小时前
tcp/ip 中的多路复用
后端
bobz9657 小时前
tls ingress 简单记录
后端
皮皮林5518 小时前
IDEA 源码阅读利器,你居然还不会?
java·intellij idea
你的人类朋友8 小时前
什么是OpenSSL
后端·安全·程序员
bobz9659 小时前
mcp 直接操作浏览器
后端
前端小张同学11 小时前
服务器部署 gitlab 占用空间太大怎么办,优化思路。
后端
databook11 小时前
Manim实现闪光轨迹特效
后端·python·动效
武子康12 小时前
大数据-98 Spark 从 DStream 到 Structured Streaming:Spark 实时计算的演进
大数据·后端·spark
该用户已不存在12 小时前
6个值得收藏的.NET ORM 框架
前端·后端·.net