Spring管理Controller可行性原理示例分析
Spring 容器中的父子容器
上篇文章和小伙伴們聊了 Spring 容器中的父子容器問題,也和小伙伴們梳理了 Spring 容器和 SpringMVC 容器之間的關(guān)系,其中,Spring 容器是父容器,SpringMVC 是子容器,子容器可以訪問父容器中的 Bean,但是父容器無法訪問子容器中的 Bean。
在一個(gè) SSM 項(xiàng)目中,你可以單純使用 SpringMVC 容器,這個(gè)沒問題,項(xiàng)目可以正常運(yùn)行。但是,有的小伙伴可能要問了,如果把所有的 Bean 都掃描到 Spring 容器中行不行?
先來說結(jié)論:可以!但是需要額外配置。
閱讀本文需要先了解 Spring 容器的父子容器哦,如果還不了解的話建議先閱讀上篇文章。
為什么不能把所有 Bean 都注冊(cè)到 Spring 容器中呢?按照我們上篇文章中的分析,所有 Bean 都注冊(cè)到 Spring 容器之后,Spring 容器作為父容器,SpringMVC 作為子容器,按理說,由于子容器可以訪問父容器中的 Bean,所以 SpringMVC 是可以正常訪問 Spring 容器中的 Bean 的,所以,似乎把所有的 Bean 都掃描到 Spring 容器應(yīng)該是沒有問題的?
其實(shí)不然!
問題就出在 SpringMVC 容器查找 Controller 的方式上,SpringMVC 容器查找 Controller,默認(rèn)情況下,只在當(dāng)前容器中查找,并不會(huì)去父容器中查找,所以如果把 Controller 都掃描到父容器的話,對(duì)于 SpringMVC 來說,相當(dāng)于系統(tǒng)中就沒有 Controller 了,所以你一訪問,直接就 404 了。
接下來,我結(jié)合源碼和小伙伴們分析一下。
SpringMVC 如何查找 Controller
首先,小伙伴們知道,在 SpringMVC 中,當(dāng)請(qǐng)求到達(dá)服務(wù)端之后,需要由處理器映射器 HandlerMapping 來確定這個(gè)請(qǐng)求應(yīng)該由哪個(gè)處理器來處理,所以,按理說,HandlerMapping 中就會(huì)記錄所有的處理器信息,也就是 Controller 的信息。一般我們?cè)?SpringMVC 中使用的 HandlerMapping 都是 RequestMappingHandlerMapping,所以這里我們就通過 RequestMappingHandlerMapping 的初始化來看一下,SpringMVC 到底是如何查找 Controller 的。
在 RequestMappingHandlerMapping#afterPropertiesSet 方法中,調(diào)用了父類的 afterPropertiesSet 方法,我們來看下:
AbstractHandlerMethodMapping#afterPropertiesSet:
@Override public void afterPropertiesSet() { initHandlerMethods(); } protected void initHandlerMethods() { for (String beanName : getCandidateBeanNames()) { if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) { processCandidateBean(beanName); } } handlerMethodsInitialized(getHandlerMethods()); }
initHandlerMethods 方法就是初始化處理器的方法,也就是在這個(gè)方法中,去嘗試找到所有的 Controller,并且把每一個(gè)接口方法都封裝成 HandlerMethod 對(duì)象。
我們來看下 getCandidateBeanNames 方法,這個(gè)方法用來找到所有的候選的 Bean:
protected String[] getCandidateBeanNames() { return (this.detectHandlerMethodsInAncestorContexts ? BeanFactoryUtils.beanNamesForTypeIncludingAncestors(obtainApplicationContext(), Object.class) : obtainApplicationContext().getBeanNamesForType(Object.class)); }
關(guān)鍵點(diǎn)就在這了,這里首先去判斷 detectHandlerMethodsInAncestorContexts 變量的值,如果這個(gè)變量為 true,則調(diào)用 BeanFactoryUtils.beanNamesForTypeIncludingAncestors 方法去查詢 Bean,這個(gè)方法在上篇文章中松哥和大家分享過,用來查找 Bean 的名稱,包括父容器中的 Bean 都會(huì)查找到并返回;
如果 detectHandlerMethodsInAncestorContexts 變量為 false,則調(diào)用 getBeanNamesForType 方法去查找 Bean,getBeanNamesForType 方法我們上篇文章也講過,這個(gè)方法只找當(dāng)前容器的 Bean,不會(huì)去父容器中查找。
SpringMVC 容器查找 Bean
所以現(xiàn)在問題的關(guān)鍵就在于 detectHandlerMethodsInAncestorContexts 變量了,這個(gè)變量默認(rèn)是 false,即,默認(rèn)情況下,只去當(dāng)前容器(SpringMVC 容器)查找 Bean。
這里找到的 beanName 是當(dāng)前容器中所有的 beanName,所以接下來還要去 processCandidateBean 方法走一圈,這個(gè)方法會(huì)去判斷這個(gè) Bean 是否是一個(gè) Controller,如果是就將之收集到一起:
protected void processCandidateBean(String beanName) { Class<?> beanType = null; beanType = obtainApplicationContext().getType(beanName); if (beanType != null && isHandler(beanType)) { detectHandlerMethods(beanName); } } @Override protected boolean isHandler(Class<?> beanType) { return AnnotatedElementUtils.hasAnnotation(beanType, Controller.class); }
可以看到,只有這類上有 @Controller
注解,這個(gè)類才會(huì)被留下來。
好啦,剩下的邏輯我們就不看了。
現(xiàn)在大家已經(jīng)了解到這樣一個(gè)情況:
SpringMVC 容器在初始化 HandlerMapping 的時(shí)候,會(huì)去查找所有的 Controller 并完成初始化,但是在默認(rèn)情況下,只會(huì)去當(dāng)前容器中查找,并不會(huì)去父容器中查找。
所以,如果把 Controller 讓 Spring 容器掃描并管理,那么就會(huì)導(dǎo)致在默認(rèn)情況下,SpringMVC 容器找不到 Controller,進(jìn)而導(dǎo)致所有的請(qǐng)求 404。
在前面的講解中,松哥都強(qiáng)調(diào)了默認(rèn)情況,意思就是說這個(gè)事情還有轉(zhuǎn)圜的余地,看了前面源碼的小伙伴應(yīng)該也發(fā)現(xiàn)了,只要我們把 detectHandlerMethodsInAncestorContexts 變量改為 true,那么 HandlerMapping 就會(huì)去父容器中查找 Bean,這樣即使被 Spring 容器掃描并管理的 Bean,也就能夠查找到了。
修改方式
spring-servlet.xml:
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"> <property name="detectHandlerMethodsInAncestorContexts" value="true"/> </bean>
在 Spring 容器中直接掃描所有 Bean:
<context:component-scan base-package="org.javaboy.web"/>
web.xml 中加載這兩個(gè)配置文件:
<context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <servlet> <servlet-name>springmvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-servlet.xml</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>springmvc</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
這樣配置之后,就可以把所有 Bean 都掃描到 Spring 容器中了。
好啦,今天這篇文章目的不是為了讓小伙伴們?nèi)ピ?Spring 容器中管理 Controller,只是想借這樣一個(gè)契機(jī),一起來捋一捋 SpringMVC 中 HanderMapping 的原理。
如果感覺本文閱讀有點(diǎn)吃力,可以先看看上篇文章哦
以上就是Spring管理Controller可行性原理示例分析的詳細(xì)內(nèi)容,更多關(guān)于Spring管理Controller的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- Spring?Boot將@RestController誤用于視圖跳轉(zhuǎn)問題解決
- springboot項(xiàng)目完整后端請(qǐng)求Controller層優(yōu)雅處理
- 解析Spring中@Controller@Service等線程安全問題
- spring?controller層引用service報(bào)空指針異常nullpointExceptio問題
- SpringMVC中事務(wù)是否可以加在Controller層的問題
- SpringMVC實(shí)現(xiàn)Controller的三種方式總結(jié)
- springboot controller 增加指定前綴的兩種實(shí)現(xiàn)方法
相關(guān)文章
使用Java獲取List交集數(shù)據(jù)的實(shí)現(xiàn)方案小結(jié)
今天遇到一個(gè)小需求,當(dāng)用戶上傳了一個(gè)關(guān)于用戶數(shù)據(jù)的列表,我們需要將其與數(shù)據(jù)庫中已有的用戶數(shù)據(jù)進(jìn)行比較,所以本文給大家介紹了使用Java獲取List交集數(shù)據(jù)的實(shí)現(xiàn)方案小結(jié),文中有詳細(xì)的代碼示例供大家參考,需要的朋友可以參考下2024-03-03Java 定時(shí)器(Timer,TimerTask)詳解及實(shí)例代碼
這篇文章主要介紹了 Java 定時(shí)器(Timer,TimerTask)詳解及實(shí)例代碼的相關(guān)資料,需要的朋友可以參考下2017-01-01Java中使用MyBatis-Plus操作數(shù)據(jù)庫的實(shí)例
本文主要介紹了Java中使用MyBatis-Plus操作數(shù)據(jù)庫的實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-02-02Java中對(duì)象?和?json?互轉(zhuǎn)四種方式?json-lib、Gson、FastJson、Jackson
這篇文章主要介紹了Java中對(duì)象?和?json?互轉(zhuǎn)?四種方式?json-lib、Gson、FastJson、Jackson,需要的朋友可以參考下2023-11-11Java 隊(duì)列實(shí)現(xiàn)原理及簡(jiǎn)單實(shí)現(xiàn)代碼
這篇文章主要介紹了Java 隊(duì)列實(shí)現(xiàn)原理及簡(jiǎn)單實(shí)現(xiàn)代碼的相關(guān)資料,需要的朋友可以參考下2016-10-10SpringCloud hystrix服務(wù)降級(jí)學(xué)習(xí)筆記
什么是服務(wù)降級(jí)?當(dāng)服務(wù)器壓力劇增的情況下,根據(jù)實(shí)際業(yè)務(wù)情況及流量,對(duì)一些服務(wù)和頁面有策略的不處理或換種簡(jiǎn)單的方式處理,從而釋放服務(wù)器資源以保證核心交易正常運(yùn)作或高效運(yùn)作2022-10-10