1. 程式人生 > >【Spring Boot】(13)、Spring Boot自動配置SpringMVC

【Spring Boot】(13)、Spring Boot自動配置SpringMVC

1、SpringMVC自動配置官方文件

2、Spring MVC auto-configuration

Spring Boot 提供了大多數SpringMVC應用常用的自動配置項。

以下是Spring Boot對SpringMVC的預設配置(來自官網,自行翻譯):

  • 自動配置了 ContentNegotiatingViewResolverBeanNameViewResolver 的Beans.

    • 給容器自定義新增一個檢視解析器,該ContentNegotiatingViewResolver 的bean會自動組合進來。

    • 自動配置了ViewResolver:ContentNegotiatingViewResolver

      是組合了所有的檢視解析器

public class WebMvcAutoConfiguration {
    //other code...
    
    @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));
        // ContentNegotiatingViewResolver uses all the other view resolvers to locate
        // a view so it should have a high precedence
        resolver.setOrder(Ordered.HIGHEST_PRECEDENCE);
        return resolver;
    }
}

其中ContentNegotiatintViewResolver類實現ViewResoler介面:

public class ContentNegotiatingViewResolver extends WebApplicationObjectSupport
		implements ViewResolver, Ordered, InitializingBean {
	//other code...
	
	@Override
	public View resolveViewName(String viewName, Locale locale) throws Exception {
		RequestAttributes attrs = RequestContextHolder.getRequestAttributes();
		Assert.state(attrs instanceof ServletRequestAttributes, "No current ServletRequestAttributes");
		//獲取所有的MediaType,例如application/json,text/html等等。。。
		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;
			}
		}
		//other code...
	}
	
	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);
					}
				}
			}
		}
		//other code...
		
		return candidateViews;
	}
}
  • 支援靜態資原始檔夾和webjars

  • 靜態首頁的訪問。

  • 自定義favicon圖示

  • 自動註冊了 Converter, GenericConverter, Formatter Beans.

    • Converter:轉換器,用於型別轉換

    • Formatter:格式化器

@Bean
//如果配置了spring.mvc.date-format,則自動註冊Formatter<Date>的Bean
@ConditionalOnProperty(prefix = "spring.mvc", name = "date-format")
public Formatter<Date> dateFormatter() {
    return new DateFormatter(this.mvcProperties.getDateFormat());
}

        自動新增的格式化轉換器,只需新增到容器中即可。

  • 支援HttpMessageConverters 訊息轉換器。

    • HttpMessageConverter:SpringMVC用來轉換Http請求和響應的

    • HttpMessageConverters 是從容器中獲取的所有的HttpMessageConverter如果需要給容器中新增HttpMessageConverter,只需要將自定義的元件註冊在容器中即可。

  • 自動配置 MessageCodesResolver 用於錯誤程式碼的生成規則。

    • PREFIX_ERROR_CODE: error_code + "." + object name + "." + field

    • POSTFIX_ERROR_CODE:object name + "." + field + "." + error_code

  • 自動使用 ConfigurableWebBindingInitializerBean。

    • 可以自定義配置一個ConfigurableWebBindingInitializer來替換預設的,需要新增到容器中。

    • 初始化WebDataBinder:用於將請求資料繫結到資料模型等。

    包org.springframework.boot.autoconfigure.web是web的所有自動配置場景。

    來自官網:

    If you want to keep Spring Boot MVC features, and you just want to add additional (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.

3、擴充套件SpringMVC

編寫一個配置類,是WebMvcConfigurerAdapter類的子類,但是不能標註@EnableWebMvc註解。

​這樣既保留了所有的自動配置,也能使用自定義的擴充套件配置。

//使用WebMvcConfigurerAdapter可以來擴充套件SpringMVC的功能
@Configuration
public class MyMvcConfig extends WebMvcConfigurerAdapter{

	@Override
	public void addViewControllers(ViewControllerRegistry registry) {
//		super.addViewControllers(registry);
        //瀏覽器傳送/cay請求,會直接跳到success頁面。
		registry.addViewController("/cay").setViewName("success");
	}
}

原理:

1)、WebMvcAutoConfiguration是SpringMVC的自動配置類,在內部維護了一個內部類WebMvcAutoConfigurationAdapter,該類又繼承自WebMvcConfigurerAdapter,看定義:

@Configuration
@Import(EnableWebMvcConfiguration.class)
@EnableConfigurationProperties({ WebMvcProperties.class, ResourceProperties.class })
public static class WebMvcAutoConfigurationAdapter extends WebMvcConfigurerAdapter {}

    而WebMvcConfigurerAdapter又實現了WebMvcConfigurer介面:

public abstract class WebMvcConfigurerAdapter implements WebMvcConfigurer {}
    所以自定義的配置類(此例為MyMvcConfig)是個WebMvcConfigurer介面的實現類。

     2)、在做WebMvcAutoConfigurationAdapter自動配置時會匯入@Import(EnableWebMvcConfiguration.class)

@Configuration
public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration {}

父類:

@Configuration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {

	private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();

	//從容器中獲取所有的WebMvcConfigurer
	@Autowired(required = false)
	public void setConfigurers(List<WebMvcConfigurer> configurers) {
		if (!CollectionUtils.isEmpty(configurers)) {
			this.configurers.addWebMvcConfigurers(configurers);
		}
	}
}

  3)、容器中所有的WebMvcConfigurer都會一起起作用。

class WebMvcConfigurerComposite implements WebMvcConfigurer {

	private final List<WebMvcConfigurer> delegates = new ArrayList<WebMvcConfigurer>();

	public void addWebMvcConfigurers(List<WebMvcConfigurer> configurers) {
		if (!CollectionUtils.isEmpty(configurers)) {
			this.delegates.addAll(configurers);
		}
	}
    
    @Override
	public void addFormatters(FormatterRegistry registry) {
		for (WebMvcConfigurer delegate : this.delegates) {
			delegate.addFormatters(registry);
		}
	}

	@Override
	public void addInterceptors(InterceptorRegistry registry) {
		for (WebMvcConfigurer delegate : this.delegates) {
			delegate.addInterceptors(registry);
		}
	}
    
    @Override
	public void addViewControllers(ViewControllerRegistry registry) {
		for (WebMvcConfigurer delegate : this.delegates) {
			delegate.addViewControllers(registry);
		}
	}
    
    //other code...
}

從原始碼中可以看到,從容器中獲取的所有WebMvcConfigurer物件都會被呼叫對應的配置方法。

       4)、最後可以結合第2點和第3點看出,自定義的配置類也會被呼叫。

總結:SpringMVC的自動配置和自定義的擴充套件配置都會起作用。

4、全面接管SpringMVC

    在配置類上使用@EnableWebMvc註解,這樣Spring Boot對SpringMVC的自動配置就失效了,所有都需要自定義配置。

原理:為什麼使用了@EnableWebMvc後自動配置就失效了?

​         1)、EnableWebMvc註解的定義

@Import(DelegatingWebMvcConfiguration.class)
public @interface EnableWebMvc {}

          2)、匯入了DelegatingWebMvcConfiguration元件配置

@Configuration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {}

          3)、檢視WebMvcAutoConfiguration自動配置類的簽名

@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)、@EnableWebMvc將WebMvcConfigurationSupport元件匯入進來了,導致Spring Boot對SpringMVC的自動配置失效,即WebMvcAutoonfiguration配置類未註冊成功。

5、修改SpringBoot的預設配置

模式:

​ 1)、Spring Boot在自動配置很多元件的時候,會先檢查容器中是否有使用者自定義配置的Bean或者元件。如果有,就使用使用者自定義的;如果沒有,Spring Boot才自動配置;如果有些元件可以有多個,使用者可以自定義新增元件,並加入到容器中,這樣Spring Boot會自動將使用者配置的和預設的組合起來。

​ 2)、在Spring Boot中會有很多的Configurer幫助使用者進行擴充套件配置。

====================打個廣告,歡迎關注====================