Spring?WebMVC初始化Controller流程詳解
Spring WebMVC初始化Controller流程
此篇文章開始之前先向大家介紹一個接口 InitializingBean
這個接口的作用如果了解spring生命周期的應(yīng)該知道 ,這個接口的作用就是在bean初始化之后會執(zhí)行的init方法
public interface InitializingBean { ? ? void afterPropertiesSet() throws Exception; }
當(dāng)然能實現(xiàn)這種方式的方法spring還介紹了關(guān)于注解的@PostConstruct 和xml 的 init-method = "" 的兩種方式。但是springmvc使用的是接口的方式。
這里要介紹的初始化Controller是指填充完HandlerMapping map<String,Method>即代表初始化Controller流程
我們再提一點(diǎn)小知識。聲明一個controller類有哪些方式。(現(xiàn)在應(yīng)該沒有人用第二/三種方式吧)
- 1.使用注解@Controller 和 請求路徑@RequestMapping
- 2.實現(xiàn) Controller 接口 并將該類交給spring容器管理beanName為請求路徑
- 3.實現(xiàn) HttpRequestHandler 接口并將該類交給spring容器管理beanName為請求路徑
那么我們的map填充就從實現(xiàn)了InitializingBean 接口 的afterPropertiesSet這個方法開始。
源碼中是這個類 AbstractHandlerMethodMapping(下面的代碼有刪減)
public void afterPropertiesSet() { ? ? this.initHandlerMethods(); } protected void initHandlerMethods() { ? ? String[] var1 = this.getCandidateBeanNames();//1.獲取容器初始化的所有beanName ? ? int var2 = var1.length; ? ? for(int var3 = 0; var3 < var2; ++var3) { ? ? ? ? String beanName = var1[var3]; ? ? ? ? if (!beanName.startsWith("scopedTarget.")) { ? ? ? ? ? ? this.processCandidateBean(beanName);//2.獲取所有聲明為Controller類的beanName ? ? ? ? } ? ? } ? ? this.handlerMethodsInitialized(this.getHandlerMethods()); }
獲取容器初始化的所有beanName(父子容器概念)
protected String[] getCandidateBeanNames() { ? ? return this.detectHandlerMethodsInAncestorContexts ? BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.obtainApplicationContext(), Object.class) : this.obtainApplicationContext().getBeanNamesForType(Object.class); }
獲取所有聲明為Controller類的beanName
protected void processCandidateBean(String beanName) { ? ? Class beanType = null; ? ? try { ? ? ? ? beanType = this.obtainApplicationContext().getType(beanName);//獲取bean的類型 ? ? } catch (Throwable var4) { ? ? ? ? if (this.logger.isTraceEnabled()) { ? ? ? ? ? ? this.logger.trace("Could not resolve type for bean '" + beanName + "'", var4); ? ? ? ? } ? ? } ? ? if (beanType != null && this.isHandler(beanType)) {//獲取所有聲明為Controller類的beanName ? ? ? ? this.detectHandlerMethods(beanName);//1.開始處理這種類型的beanName ? ? } }
開始處理這種類型的beanName
protected void detectHandlerMethods(Object handler) { ? ? Class<?> handlerType = handler instanceof String ? this.obtainApplicationContext().getType((String)handler) : handler.getClass(); ? ? if (handlerType != null) { ? ? ? ? Class<?> userType = ClassUtils.getUserClass(handlerType); ? ? ? ? Map<Method, T> methods = MethodIntrospector.selectMethods(userType, (method) -> { ? ? ? ? ? ? try { ? ? ? ? ? ? ? ? return this.getMappingForMethod(method, userType);//1.獲取到類類型下所有的方法 ? ? ? ? ? ? } catch (Throwable var4) { ? ? ? ? ? ? ? ? throw new IllegalStateException("Invalid mapping on handler class [" + userType.getName() + "]: " + method, var4); ? ? ? ? ? ? } ? ? ? ? }); ? ? ? ? if (this.logger.isTraceEnabled()) { ? ? ? ? ? ? this.logger.trace(this.formatMappings(userType, methods)); ? ? ? ? } ? ? ? ? methods.forEach((method, mapping) -> { ? ? ? ? ? ? Method invocableMethod = AopUtils.selectInvocableMethod(method, userType); ? ? ? ? ? ? this.registerHandlerMethod(handler, invocableMethod, mapping);//注冊并填充map ? ? ? ? }); ? ? } }
獲取到類類型下所有的方法和注冊并填充map
第一個 MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap();
urlLookup.add(url, mapping); eg:url = '/test/test.do'?
mapping是一個RequestMappingInfo 對象 RequestMappingInfo.patternsCondition = T --> /test/test.do
第二個 Map<T, AbstractHandlerMethodMapping.MappingRegistration<T>> registry = new HashMap();
eg:key = 'url' ?value = 'method'
而我們的第二種和第三種的方式基本沒有用了,因為會出現(xiàn)類爆炸,就像原始的servlet一樣每一個方法都需要寫一個類。
這兩種方式是通過beanName為路徑來實例化對象并執(zhí)行通過該對象來執(zhí)行里面的方法的。
源碼中這兩種map的填充方式是在bean的生命周期中通過實現(xiàn)beanFactory的applyBeanPostProcessorsBeforeInitialization方法來填充的。
@Controller 類中初始化問題
在Controller類中常常遇到有些參數(shù)需要初始化,甚至有些只允許初始化一次,而Controller類不像servelet類可以調(diào)用init()函數(shù)進(jìn)行初始化,這里想到的辦法是設(shè)置標(biāo)記值,讓初始化部分只調(diào)用一次。
第一種方法
設(shè)置isStart值。
private static Boolean isStart = false;
if(!isStart){ //進(jìn)行初始化 isStart=true; }
第二種方法
使用注釋@PostConstruct,該注釋的類會在類初始化時進(jìn)行調(diào)用。
@PostConstruct private void init(){ //進(jìn)行初始化 }
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
MybatisPlus分頁查詢與多條件查詢介紹及查詢過程中空值問題的解決
mybatisplus是個很好用的插件,相信小伙伴們都知道,下面這篇文章主要給大家介紹了關(guān)于mybatis-plus實現(xiàn)分頁查詢與多條件查詢介紹及查詢過程中空值問題的相關(guān)資料,需要的朋友可以參考下2022-10-10Springboot Redis?哨兵模式的實現(xiàn)示例
本文主要介紹了Springboot Redis?哨兵模式的實現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-01-01修改Maven settings.xml 后配置未生效的解決
這篇文章主要介紹了修改Maven settings.xml 后配置未生效的解決,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-10-10Spring XML Schema擴(kuò)展機(jī)制的使用示例
所謂整合,即在Spring的框架下進(jìn)行擴(kuò)展,讓框架能無縫的與Spring工程配合使用。Spring設(shè)計了良好的擴(kuò)展的機(jī)制,本文將對Spring的擴(kuò)展方法及原理進(jìn)行簡單介紹。2021-05-05