當前位置:
首頁 >
前端技术
> javascript
>内容正文
javascript
SpringBoot_web开发-SpringMVC自动配置原理
生活随笔
收集整理的這篇文章主要介紹了
SpringBoot_web开发-SpringMVC自动配置原理
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
我們自己分析Springboot的源碼,里面有什么功能,第二個我們來參照官方文檔,官方文檔里面寫了什么功能,我們看官方文檔告訴我們自動配置了哪些,再來帶大家來翻一翻源碼,希望通過這一次的分析,后來的模塊原理都是一樣的,我們打開這個文檔https://docs.spring.io/spring-boot/docs/1.5.22.RELEASE/reference/html/有一個開發WEB應用Developing web applications,這里是說了我們怎么來使用SpringMVC,SpringMVC的自動配置都在這,https://docs.spring.io/spring-boot/docs/1.5.22.RELEASE/reference/html/
boot-features-developing-web-applications.html我們來看一下,首先如果我們要用SpringMVC,就直接上手用就行了,所有的東西都自動配置好了,我們可以非常簡單的使用SpringMVC應用,那他到底用了哪些自動配置呢,這里就是SpringBoot對SpringMVC配置的精髓
我們跟著官方文檔慢慢的來分析,Springboot提供SpringMVC要工作時,大多數場景的自動配置,那就一句話,自動配置好了SpringMVC,都配好了哪些,下面有列舉,以下是SpringBoot的默認配置,我們要對SpringMVC的原理掌握的比較清楚,如果這一塊還不清楚的,那我就來給大家一個一個來分析一下,他說我們自動配置,ContentNegotiatingViewResolver和BeanNameViewResolver,就是自動配置了視圖解析器,而這個視圖解析器呢,在SpringMVC中,功能就是根據我們方法的返回值,我們得到視圖對象,視圖對象就是Viewer對象,視圖對象絕對是轉發還是重定向,視圖對象決定如何渲染,所謂的渲染,是否要轉發到頁面,還是重定向到頁面,我們再來看一下他的底層原理,我們打開WebMvcAutoConfiguration,SpringMVC的自動配置,我們看一下有沒有這個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));// ContentNegotiatingViewResolver uses all the other view resolvers to locate// a view so it should have a high precedenceresolver.setOrder(Ordered.HIGHEST_PRECEDENCE);return resolver;
}這一塊確實有這個配置,@Bean給我們容器中添加這個組件,添加了這個ContentNegotiatingViewResolver,這個ViewResolver的功能,既然他是視圖解析器,他就要解析視圖,他怎么解析視圖呢,獲取候選的視圖對象,然后選擇一個最適合的視圖對象,然后把這個視圖對象返回,而它怎么樣選擇視圖對象呢,他其實是把所有視圖解析器拿來,挨個來解析,ContentNegotiatingViewResolver它是組合所有的視圖解析器的,那既然是這樣,我們來看一下他的組合邏輯,ContentNegotiatingViewResolver resolver = new ContentNegotiatingViewResolver();他先new了一個ContentNegotiatingViewResolver,他要用到這些視圖解析器,它是從哪拿的呢,我們來看一下視圖解析器,這里有一個初始化方法,它是用BeanFactory工具,從容器中獲取所有的視圖解析器,把這個視圖解析器作為所有的視圖解析器了,如果我們要寫視圖解析器,我們只需要知道如何定制,我們可以自己給容器中添加一個視圖解析器,ContentNegotiatingViewResolver就會將其自動的組合進來,是不是我們分析的這樣呢,可以給大家來測試一下,比如我們在主類里邊,myViewResolver還沒有,我在這里就寫一個靜態的內部類,既然是視圖解析器,那我們要實現ViewResolver接口,然后添加未實現的方法,那這個方法我就不實現了,我主要來new一個MyViewResolver,我們看有沒有起作用就行了,怎么看起作用呢,我們來看DispatcherServlet,請求一進來就會來到doDispatch方法,我們在這里打一個斷點,看一下視圖解析器是什么,我以debug的方式來運行,啟動起來以后,我們隨便來訪問一個請求,我們的確實進來了,我們就是給容器中添組件就行了,它自動就加進來了,后面的邏輯基本上都一樣了,說明我們的這個猜想是正確的
我們以后看東西,就看關鍵字,不用分析的那么細節,支持靜態資源的,靜態資源首頁,包括favicon,這是靜態首頁訪問的,靜態資源文件夾路徑,webjars,之前都來用過,我們就不說了,自動注冊了Converter這些東西,什么Formatter,Converter翻譯過來就是轉換器,假設有一個方法public String hello(User user),我們發請求來到這個方法,如果頁面的數據和User里的屬性正好一一對應,SpringMVC是不是要自動封裝,但是在自動封裝期間,一定會出現類型轉換問題,而頁面提交的數據都是文本,所以我們要把18轉成Integer類型,true也是文本,要轉成布爾類型,我們類型轉換要用Converter組件,而對應的還有一個Formatter,這個就叫格式化器,比如頁面帶來一個數據2017年,12月17號,我們就要對應轉成一個日期類型,所以這就牽涉到兩個方面,一個是把字符串轉成日期類型,第二種要按照這種格式轉過來,因為有些國家寫法不一樣,所以我們要按照一定的格式轉過來,他也自動注冊了,自動注冊的在哪呢,@Bean
@ConditionalOnProperty(prefix = "spring.mvc", name = "date-format")
public Formatter<Date> dateFormatter() {return new DateFormatter(this.mvcProperties.getDateFormat());
}日期格式調整,我們用spring.mvc.date-format來調整,他確實注冊了格式化器,而注冊的條件,他在這注冊的條件是什么呢,如果我們配了日期格式化,他就會注到格式化器,如果我們沒配,我們就不注冊了,在配置文件中要配置日期要用哪種方式,在文件中配置日志格式化的規則,而我們配了這個以后呢,加一個DateFormatter日期格式化對象,那如果我們沒配,自然就沒有了,當然要注意,下面還有一個方法,addFormatters@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);}
}添加格式化器,格式化器里要加一些注冊,東西添的時候怎么添呢,叫getBeansOfType,這個方法就在他的下邊,private <T> Collection<T> getBeansOfType(Class<T> type) {return this.beanFactory.getBeansOfType(type).values();
}他拿到beanFactory,也就是我們的容器,從容器中獲得所有的Converter,然后把它們挨個遍歷遷過去,registry.addConverter(converter);又看到這個關鍵字,又得出一個結論,我們自己想要做,能不能做,自己添加的格式化器,包括轉換器,我們只需要放在容器中即可,這個東西我們就不用像以前那樣測試了,添進來肯定能用,他還幫我們添加了HttpMessageConverters功能,我得說一下MessageConverters,他的作用是什么呢,MessageConverters叫消息轉換器,這是SpringMVC用來轉換Http請求和響應的,舉一個例子,比如我們有一個方法,返回了User,我想讓他以JSON的形式寫出去,那我們就得有一個使User以JSON的形式寫出去的MessageConverters,而MessageConverters是怎么在容器里面配的呢,來搜索一下HttpMessageConverters,private final HttpMessageConverters messageConverters;這個也是在@Configuration類中,配置類和之前的寫法一樣,我們這個大配置類中有一個小配置類,這個配置類只有一個有參構造器,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();
}傳入messageConverters,而且這里標了@Lazy,@Lazy是懶加載,用的時候才會自動注入進來,而這個messageConverters,是什么大家來注意,如果只有一個有參構造器的情況下,每一個參數的值都是要從容器中拿的,相當于HttpMessageConverters怎么確定值,是從容器中確定的,確定什么呢,我們來看一下messageConverters,public class HttpMessageConverters implements Iterable<HttpMessageConverter<?>> {他其實是一個Iterable<HttpMessageConverter<?>>,是HttpMessageConverter的數組或者集合,需要從容器中獲取所有的messageConverters,獲取所有的HttpMessageConverter,也就是如果我們要自己來獲取一個HttpMessageConverter,我們還是那句話,自己給容器中添加HttpMessageConverter,只需要將自己的組件放到容器中,我們就注冊在容器中,用@Bean的方法,或者@Component,反正你把它掃描到容器里面都行,那是不是這樣呢,也可以看一下官方文檔,有做JSON的,有做XML的,但是如果我們自己想要用一個,怎么辦呢,而且是我們自己添加一個,他來new一個HttpMessageConverter,放到容器中,我們給容器中添加就可以了
就是我們這個的自動配置,接下來的兩個自動配置我們來看看就行了,一個叫MessageCodesResolver,我們看一下這是什么東西呢,看有沒有人配他,@Override
public MessageCodesResolver getMessageCodesResolver() {if (this.mvcProperties.getMessageCodesResolverFormat() != null) {DefaultMessageCodesResolver resolver = new DefaultMessageCodesResolver();resolver.setMessageCodeFormatter(this.mvcProperties.getMessageCodesResolverFormat());return resolver;}return null;
}他能從一個配置類里哪一個配置,我們不看這個組件了,我們來看這個配置,/*** Common message code formats.* @see MessageCodeFormatter* @see DefaultMessageCodesResolver#setMessageCodeFormatter(MessageCodeFormatter)*/
public enum Format implements MessageCodeFormatter {/*** Prefix the error code at the beginning of the generated message code. e.g.:* {@code errorCode + "." + object name + "." + field}*/PREFIX_ERROR_CODE {@Overridepublic String format(String errorCode, String objectName, String field) {return toDelimitedString(errorCode, objectName, field);}},/*** Postfix the error code at the end of the generated message code. e.g.:* {@code object name + "." + field + "." + errorCode}*/POSTFIX_ERROR_CODE {@Overridepublic String format(String errorCode, String objectName, String field) {return toDelimitedString(objectName, field, errorCode);}};它里面是這種Format,定義錯誤代碼的生成規則的,比如我們在做JSR303數據校驗的時候,某一個字段發生錯誤以后,是怎么樣產生錯誤代碼的,這里一個錯誤代碼,對象名,屬性名,這是第一種規則,第二種是對象名+屬性名+錯誤代碼,這是定義錯誤代碼生成規則的,知道一下就行了,定義錯誤代碼生成規則的,我們來看一下ConfigurableWebBindingInitializer,我們先搜一下他在哪里有這個配置,@Override
protected ConfigurableWebBindingInitializer getConfigurableWebBindingInitializer() {try {return this.beanFactory.getBean(ConfigurableWebBindingInitializer.class);}catch (NoSuchBeanDefinitionException ex) {return super.getConfigurableWebBindingInitializer();}
}我們別的不看,我們看這里有一句話叫什么,beanFactory,從容器中拿這個東西,我們相當于也可以來配一個,來替換默認的,因為它是從容器中拿的,我們也只需要從容器中添一個組件,添加到容器中,那這個是什么作用呢,我們看一下這段代碼,而它是從容器中拿,拿不到它會調用super,super的這個方法,/*** Return the {@link ConfigurableWebBindingInitializer} to use for* initializing all {@link WebDataBinder} instances.*/
protected ConfigurableWebBindingInitializer getConfigurableWebBindingInitializer() {ConfigurableWebBindingInitializer initializer = new ConfigurableWebBindingInitializer();initializer.setConversionService(mvcConversionService());initializer.setValidator(mvcValidator());initializer.setMessageCodesResolver(getMessageCodesResolver());return initializer;
}幫我們來創建一個Initializer,而這個Initializer的作用呢,來初始化我們的WEB數據綁定器,WEB數據綁定器的功能,就是把請求數據綁定到JavaBean中,我們發過來的請求帶了幾個參數,我們要封裝到我們的User對象里邊,那我們就需要用到我們的數據綁定器,牽涉到數據類型格式化等等,會用到前面的組件,這都是SpringMVC底層的原理,包括ConfigurableWebBindingInitializer,他這里有一個方法,initBinder@Override
public void initBinder(WebDataBinder binder, WebRequest request) {binder.setAutoGrowNestedPaths(this.autoGrowNestedPaths);if (this.directFieldAccess) {binder.initDirectFieldAccess();}if (this.messageCodesResolver != null) {binder.setMessageCodesResolver(this.messageCodesResolver);}if (this.bindingErrorProcessor != null) {binder.setBindingErrorProcessor(this.bindingErrorProcessor);}if (this.validator != null && binder.getTarget() != null &&this.validator.supports(binder.getTarget().getClass())) {binder.setValidator(this.validator);}if (this.conversionService != null) {binder.setConversionService(this.conversionService);}if (this.propertyEditorRegistrars != null) {for (PropertyEditorRegistrar propertyEditorRegistrar : this.propertyEditorRegistrars) {propertyEditorRegistrar.registerCustomEditors(binder);}}
}就是用來初始化WEB數據綁定,當然我們的核心就在這一塊,要想放,我們給容器中放一個就行了,但是我們自動配置的功能,可不止有這么多,是分析了WebMvcAutoConfiguration這個類,然后Springboot對我們整個WEB的配置呢,我們都可以來到這個包里面,我們有一個autoconfig,我們說這個自動配置,這里面用的組件功能可能不是很了解,org.springframework.boot.autoconfigure.webweb所有自動配置場景,但是希望能夠通過說自動配置,大家提供一種模式和思想,什么思想呢,我們如何修改這個默認配置呢,首先這些都是一個統一的一個模式,我總結的這個模式呢,不只是用于WEB模塊,其他模塊都這樣,那我們想修改默認配置的第一個配置,看我們之前,我們想要添格式化器,放容器中就行了,我們要添MessagerConverter,我們放容器中就行了,好多就是給容器中放一個就行了,因為SpringBoot大量用到了這個細節,比如在WebMvc里邊,我們想給容器中添加HiddenHttpMethodFilter,這個是支持REST風格的過濾器@Bean
@ConditionalOnMissingBean(HiddenHttpMethodFilter.class)
public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() {return new OrderedHiddenHttpMethodFilter();
}但是他要給容器中注冊這個bean,他怎么做呢,它是這樣的,@ConditionalOnMissingBean,先判斷容器中如果沒有這個組件,沒有這個類型的組件,然后他就給容器中new一個放到容器中,我們還是之前說的那句話,只要這個組件在容器中了,SpringBoot就能想辦法讓他生效,至于是怎么生效的,我們后來可以慢慢來體會,第一個模式就是,SpringBoot在用很多組件的時候,在自動配置很多組件的時候,都有一個模式,先看容器中有沒有用戶自己配置,如果我們用戶用@Bean了,或者@Component,掃描了一個相關的組件,已經進入容器中了,如果有就用用戶配置了,如果沒有呢,才自動配置,好多代碼都是這樣,包括我們容器中的組件,如果說有些組件要確定多個,他把用戶配置的組件,會和他默認的合并起來,如果有些組件可以有多個,比如我們的視圖解析器,ViewResolver,將用戶配置的,和自己默認的,組合起來,所以這就是第一種模式,我們想要修改默認配置,那在很多情況下,我們給容器中添加一個組件就行了,Springboot就會優先使用我們的,這不是所有的情況,比如人家這段話就說,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.我們還想添加一些額外的功能特性,比如我們要填View Controller,我們這些攔截器
?
總結
以上是生活随笔為你收集整理的SpringBoot_web开发-SpringMVC自动配置原理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SpringBoot_web开发-web
- 下一篇: SpringBoot_web开发-扩展与