SpringBoot(3)-MVC自動配置及自定義檢視控制器

做豬呢,最重要的是開森啦發表於2020-09-26

1. 主要類:

WebMvcAutoConfiguration: MVC的自動配置類
EnableWebMvcConfiguration: 啟用WebMvcConfiguration的類
DelegatingWebMvcConfiguration: WebMvcConfiguration委託的代理類
WebMvcConfigurerComposite: “具體WebMvcConfiguration的委託類”
`
WebMvcAutoConfigurationAdapter: MVC自動配置介面卡,其實現了WebMvcConfiguration
WebMvcConfigurer: MVC配置介面
WebMvcConfigurerAdapter: MVC配置介面卡(1.8後棄用,因為介面支援default預設實現)

2. 檢視解析器初始化:

 springboot啟動後會自動配置mvc的元件,即WebMvcAutoConfiguration,自動進行mvc的一些配置
 其內部靜態類WebMvcAutoConfigurationAdapter也是一個元件,通過@Import匯入EnableWebMvcConfiguration元件

	@Configuration(proxyBeanMethods = false)
	@Import(EnableWebMvcConfiguration.class)
	@EnableConfigurationProperties({ WebMvcProperties.class, ResourceProperties.class })
	@Order(0)
	public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer {

 EnableWebMvcConfiguration繼承了DelegatingWebMvcConfiguration委託代理類

	@Configuration(proxyBeanMethods = false)
	public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration implements ResourceLoaderAware {

 代理類通過setConfigurers方法來初始化mvc配置的具體委託類
 @Autowired會從容器總把實現WebMvcConfigurer介面的類都注入到configurers
 然後將所有的mvc配置類新增到WebMvcConfigurerComposite

@Configuration(proxyBeanMethods = false)
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {

	private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();


	@Autowired(required = false)
	public void setConfigurers(List<WebMvcConfigurer> configurers) {
		if (!CollectionUtils.isEmpty(configurers)) {
			this.configurers.addWebMvcConfigurers(configurers);
		}
	}
//也實現了WebMvcConfigurer當並沒有@Configuration到容器中
class WebMvcConfigurerComposite implements WebMvcConfigurer {

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


	public void addWebMvcConfigurers(List<WebMvcConfigurer> configurers) {
		if (!CollectionUtils.isEmpty(configurers)) {
			this.delegates.addAll(configurers);
		}
	}

 dubug可以知道,預設使用WebMvcAutoConfigurationAdapter

在這裡插入圖片描述
所以,要想自定義mvc的配置,只需要實現WebMvcConfigurer介面,並將該類放入容器中即可

@Configuration
public class MyConfigure implements WebMvcConfigurer {
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/testView").setViewName("success");
    }
}

在這裡插入圖片描述

3. MVC檢視功能配置:

 由上可知,mvc的配置,預設使用WebMvcAutoConfigurationAdapter來配置
 這個類在配置的時候,會往容器中新增三個檢視bean,主要關注ContentNegotiatingViewResolver

 注意其該配置resolver.setOrder(Ordered.HIGHEST_PRECEDENCE);設定最高階的Order,後續會提到

	@Configuration(proxyBeanMethods = false)
	@Import(EnableWebMvcConfiguration.class)
	@EnableConfigurationProperties({ WebMvcProperties.class, ResourceProperties.class })
	@Order(0)
	public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer {

		... ... ...
		
		@Bean
		@ConditionalOnMissingBean
		public InternalResourceViewResolver defaultViewResolver() {
			InternalResourceViewResolver resolver = new InternalResourceViewResolver();
			resolver.setPrefix(this.mvcProperties.getView().getPrefix());
			resolver.setSuffix(this.mvcProperties.getView().getSuffix());
			return resolver;
		}

		@Bean
		@ConditionalOnBean(View.class)
		@ConditionalOnMissingBean
		public BeanNameViewResolver beanNameViewResolver() {
			BeanNameViewResolver resolver = new BeanNameViewResolver();
			resolver.setOrder(Ordered.LOWEST_PRECEDENCE - 10);
			return resolver;
		}

		@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;
		}
		... ... ...

 ContentNegotiatingViewResolver這個檢視解析器主要是用來組合所有檢視解析器的
 在其內部實現方法initServletContext可以看到,將所有檢視解析器(除本類)新增到本類的中viewResolvers

在這裡插入圖片描述

 可見,想要自定義檢視解析器,可以實現ViewResolver介面,然後加入容器即可,本文不論述

4. MVC解析檢視過程:

 前端控制器(DispatcherServlet),在第一次呼叫檢視解析器之前,會通過initViewResolvers方法先初始化檢視解析器
 該方法中,獲取容器內所有檢視解析器,即容器中實現ViewResolver介面的類
 然後所有檢視解析器根據Order排序,上文提及ContentNegotiatingViewResolver設定了最高階別Order,所以它會排在第一個

在這裡插入圖片描述

 當初始化檢視解析器後,前端控制器通過resolveViewName方法遍歷所有檢視解析器來解析,能解析立即返回View
 ContentNegotiatingViewResolver排在第一個,所以會最先呼叫

在這裡插入圖片描述
 其內部方法通過getCandidateViews來解析出所有檢視,最後處理返回一個bestView給前端控制器
在這裡插入圖片描述

 在getCandidateViews方法中,this.viewResolvers就是之前ContentNegotiatingViewResolver組合的所有檢視解析器
 呼叫各檢視解析器的實現方法resolveViewName來解析檢視
 有興趣的可以研究一些這個方法,我就算了

在這裡插入圖片描述

 詳細的MVC執行過程可參考:SpringBoot(2)-MVC執行過程

相關文章