1. 程式人生 > >SpringBoot——web開發之SpringMVC自動配置原理

SpringBoot——web開發之SpringMVC自動配置原理

一、SpringBoot為SpringMVC提供的自動配置

2、SpringBoot為SpringMVC提供的自動配置:參考類WebMvcAutoConfiguration

①Inclusion of ContentNegotiatingViewResolver and BeanNameViewResolver beans

    SpringBoot為SpringMVC自動配置了ViewResolver(檢視解析器:根據方法的返回值得到檢視物件——View,檢視物件決定如何渲染,是轉發還是重定向),SpringBoot通過ContentNegotiatingViewResolver組合所有的檢視解析器(遍歷容器中所有的檢視解析器,並將這些檢視解析器新增到viewResolvers中):

protected void initServletContext(ServletContext servletContext) {
	Collection<ViewResolver> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(this.obtainApplicationContext(), ViewResolver.class).values();
	ViewResolver viewResolver;
	if (this.viewResolvers == null) {
		this.viewResolvers = new ArrayList(matchingBeans.size());
		Iterator var3 = matchingBeans.iterator();

		while(var3.hasNext()) {
			viewResolver = (ViewResolver)var3.next();
			if (this != viewResolver) {
				this.viewResolvers.add(viewResolver);
			}
		}
	} else {
		for(int i = 0; i < this.viewResolvers.size(); ++i) {
			viewResolver = (ViewResolver)this.viewResolvers.get(i);
			if (!matchingBeans.contains(viewResolver)) {
				String name = viewResolver.getClass().getName() + i;
				this.obtainApplicationContext().getAutowireCapableBeanFactory().initializeBean(viewResolver, name);
			}
		}
	}

	if (this.viewResolvers.isEmpty()) {
		this.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);
}

我們可以自己定製檢視解析器,並將自定義的檢視解析器新增到容器中:ContentNegotiatingViewResolver會自動的將其組合進來,例如:這裡定義了一個靜態內部類,使其實現ViewResolver介面

@SpringBootApplication
public class SpringBootWebApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringBootWebApplication.class, args);
    }

    @Bean
    public ViewResolver myViewResolver(){
        return new MyViewResolver();
    }

    public static class MyViewResolver implements ViewResolver{

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

②Support for serving static resources, including support for WebJars (see below):SpringBoot支援靜態資原始檔夾路徑,包括webjars

③Static index.html support:SpringBoot提供了靜態首頁訪問

④Custom Favicon support (see below):自定義的 favicon.ico

⑤自動註冊了Converter , GenericConverter , Formatter beans等轉換器(轉換器:頁面傳給後臺的值都是文字型別的,後臺拿到這些資料封裝物件的時候可能需要進行型別轉換,比如將"true"轉換為Boolean型別,將“18”轉為Integer型別等)和格式化器

@Bean
@ConditionalOnProperty(prefix = "spring.mvc", name = "date‐format")//在檔案中配置日期格式化的規則
public Formatter<Date> dateFormatter() {
	return new DateFormatter(this.mvcProperties.getDateFormat());//日期格式化元件
}

我們也可以自定義格式化器,方式類似於自定義檢視解析器

⑥Support for HttpMessageConverters (see below):HTTP訊息轉換器,比如報文是物件還是JSON格式,也可以自定義

⑦Automatic registration of MessageCodesResolver (see below):定義錯誤程式碼生成規則

⑧Automatic use of a ConfigurableWebBindingInitializer bean (see below):自動將請求資料轉化為JavaBean,可自定義

SpringBoot為SpringMVC做的所有自動配置都在org.springframework.boot.autoconfigure.web下

另外:

If you want to keep Spring Boot MVC features, and you just want to add additional MVC configuration(interceptors, formatters, view controllers etc.)you can add your own @Configuration class of type WebMvcConfigurerAdapter,but without @EnableWebMvc . If you wish to provide custom instances of RequestMappingHandlerMapping , RequestMappingHandlerAdapter or ExceptionHandlerExceptionResolver you can declare a WebMvcRegistrationsAdapter instance providing such components.If you want to take complete control of Spring MVC, you can add your own @Configuration annotated with @EnableWebMvc.

二、如何修改SpringBoot的預設配置

模式:

1、SpringBoot在自動配置很多元件的時候,會先看容器中有沒有使用者自己配置的(@Bean、@Component)對應元件,如 果有就用使用者配置的,如果沒有,才自動配置;如果有些元件可以有多個(比如ViewResolver)SpringBoot會將使用者配置的和默 認的組合起來:通過註解@ConditionalOnMissingBean來判斷使用者有沒有自定義元件

@Bean
@ConditionalOnMissingBean(HiddenHttpMethodFilter.class)
public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() {
	return new OrderedHiddenHttpMethodFilter();
}

2、在SpringBoot中會有非常多的XxxConfigurer幫助我們進行擴充套件配置

3、在SpringBoot中會有很多的XxxCustomizer幫助我們進行定製配置

三、擴充套件SpringMVC

    比如:配置某些請求的檢視或者某些請求的攔截器,在之前我們可以這麼配置

<mvc:view-controller path="/hello" view-name="success"/>
<mvc:interceptors>
	<mvc:interceptor>
		<mvc:mapping path="/hello"/>
		<bean></bean>
	</mvc:interceptor>
</mvc:interceptors>

現在我們可以自定義一個配置類:使用@Configuration標註,是WebMvcConfigurerAdapter型別,不能標註@EnableWebMvc,然後重寫相應的方法即可,這樣我們既保留了SpringBoot中所有的自動配置,也啟用了我們擴充套件的配置

//通過重寫WebMvcConfigurerAdapter的相應方法來擴充套件SpringMVC
@Configuration
public class MyMvcConfig extends WebMvcConfigurerAdapter {
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        //對於一些簡單的請求,比如不需要從後臺獲取資料的請求,可以採用這種方式直接返回頁面,而不需要在Controller中寫一些空方法
        registry.addViewController("/bdm").setViewName("success");
    }
}

擴充套件原理:

在做其他自動配置時會匯入EnableWebMvcConfiguration:@Import(EnableWebMvcConfiguration.class)

@Configuration
public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration {
    ...
}

在該類中會將所有的WebMvcConfigurer組合起來:使容器中所有的WebMvcConfigurer一起起作用

//從容器中獲取所有的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);
	}
	...
}

四、全盤接管SpringgMVC

當我們不需要使用SpringBoot為SpringMVC提供的所有自動配置的時候,可以全面接管SpringMVC(不推薦這樣做),只需要在配置類中新增@EnableWebMvc即可:

//通過重寫WebMvcConfigurerAdapter的相應方法來擴充套件SpringMVC
@EnableWebMvc
@Configuration
public class MyMvcConfig extends WebMvcConfigurerAdapter {
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        //對於一些簡單的請求,比如不需要從後臺獲取資料的請求,可以採用這種方式直接返回頁面,而不需要在Controller中寫一些空方法
        registry.addViewController("/bdm").setViewName("success");
    }
}

原理:

SpringBoot中為SpringMVC提供的自動配置生效的前提是容器中不存在WebMvcConfigurationSupport:

@Configuration @ConditionalOnWebApplication(     type = Type.SERVLET ) @ConditionalOnClass({Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class})@ConditionalOnMissingBean({WebMvcConfigurationSupport.class}) @AutoConfigureOrder(-2147483638) @AutoConfigureAfter({DispatcherServletAutoConfiguration.class, ValidationAutoConfiguration.class}) public class WebMvcAutoConfiguration {    ... }

一旦容器中存在了WebMvcConfigurationSupport,則WebMvcAutoConfiguration元件不會被載入到容器中,而@EnableWebMvc會@Import({DelegatingWebMvcConfiguration.class}),DelegatingWebMvcConfiguration繼承自WebMvcConfigurationSupport,因此會導致WebMvcAutoConfiguration失效

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import({DelegatingWebMvcConfiguration.class})
public @interface EnableWebMvc {
}
@Configuration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
}

WebMvcConfigurationSupport中只實現了SpringMVC最基本的功能,所以不推薦全面接管SpringMVC,除非專案中很少使用web模組