自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

用 Spring 管理 Controller,你覺得可行嗎?

開發(fā) 前端
今天這篇文章目的不是為了讓小伙伴們?nèi)ピ?Spring 容器中管理 Controller,只是想借這樣一個契機,一起來捋一捋 SpringMVC 中 HanderMapping 的原理。?

上篇文章和小伙伴們聊了 Spring 容器中的父子容器問題,也和小伙伴們梳理了 Spring 容器和 SpringMVC 容器之間的關(guān)系,其中,Spring 容器是父容器,SpringMVC 是子容器,父容器可以訪問子容器中的 Bean,但是子容器無法訪問父容器中的 Bean。

在一個 SSM 項目中,你可以單純使用 SpringMVC 容器,這個沒問題,項目可以正常運行。但是,有的小伙伴可能要問了,如果把所有的 Bean 都掃描到 Spring 容器中行不行?

先來說結(jié)論:可以!但是需要額外配置。

閱讀本文需要先了解 Spring 容器的父子容器哦,如果還不了解的話建議先閱讀上篇文章。

為什么不能把所有 Bean 都注冊到 Spring 容器中呢?按照我們上篇文章中的分析,所有 Bean 都注冊到 Spring 容器之后,Spring 容器作為父容器,SpringMVC 作為子容器,按理說,由于子容器可以訪問父容器中的 Bean,所以 SpringMVC 是可以正常訪問 Spring 容器中的 Bean 的,所以,似乎把所有的 Bean 都掃描到 Spring 容器應(yīng)該是沒有問題的?

其實不然!

問題就出在 SpringMVC 容器查找 Controller 的方式上,SpringMVC 容器查找 Controller,默認情況下,只在當前容器中查找,并不會去父容器中查找,所以如果把 Controller 都掃描到父容器的話,對于 SpringMVC 來說,相當于系統(tǒng)中就沒有 Controller 了,所以你一訪問,直接就 404 了。

接下來,我結(jié)合源碼和小伙伴們分析一下。

首先,小伙伴們知道,在 SpringMVC 中,當請求到達服務(wù)端之后,需要由處理器映射器 HandlerMapping 來確定這個請求應(yīng)該由哪個處理器來處理,所以,按理說,HandlerMapping 中就會記錄所有的處理器信息,也就是 Controller 的信息。一般我們在 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 方法就是初始化處理器的方法,也就是在這個方法中,去嘗試找到所有的 Controller,并且把每一個接口方法都封裝成 HandlerMethod 對象。

我們來看下 getCandidateBeanNames 方法,這個方法用來找到所有的候選的 Bean:

protected String[] getCandidateBeanNames() {
 return (this.detectHandlerMethodsInAncestorContexts ?
   BeanFactoryUtils.beanNamesForTypeIncludingAncestors(obtainApplicationContext(), Object.class) :
   obtainApplicationContext().getBeanNamesForType(Object.class));
}

關(guān)鍵點就在這了,這里首先去判斷 detectHandlerMethodsInAncestorContexts 變量的值,如果這個變量為 true,則調(diào)用 BeanFactoryUtils.beanNamesForTypeIncludingAncestors 方法去查詢 Bean,這個方法在上篇文章中松哥和大家分享過,用來查找 Bean 的名稱,包括父容器中的 Bean 都會查找到并返回;如果 detectHandlerMethodsInAncestorContexts 變量為 false,則調(diào)用 getBeanNamesForType 方法去查找 Bean,getBeanNamesForType 方法我們上篇文章也講過,這個方法只找當前容器的 Bean,不會去父容器中查找。

所以現(xiàn)在問題的關(guān)鍵就在于 detectHandlerMethodsInAncestorContexts 變量了,這個變量默認是 false,即,默認情況下,只去當前容器(SpringMVC 容器)查找 Bean。

這里找到的 beanName 是當前容器中所有的 beanName,所以接下來還要去 processCandidateBean 方法走一圈,這個方法會去判斷這個 Bean 是否是一個 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 注解,這個類才會被留下來。

好啦,剩下的邏輯我們就不看了。

現(xiàn)在大家已經(jīng)了解到這樣一個情況:

SpringMVC 容器在初始化 HandlerMapping 的時候,會去查找所有的 Controller 并完成初始化,但是在默認情況下,只會去當前容器中查找,并不會去父容器中查找。

所以,如果把 Controller 讓 Spring 容器掃描并管理,那么就會導(dǎo)致在默認情況下,SpringMVC 容器找不到 Controller,進而導(dǎo)致所有的請求 404。

在前面的講解中,松哥都強調(diào)了默認情況,意思就是說這個事情還有轉(zhuǎn)圜的余地,看了前面源碼的小伙伴應(yīng)該也發(fā)現(xiàn)了,只要我們把 detectHandlerMethodsInAncestorContexts 變量改為 true,那么 HandlerMapping 就會去父容器中查找 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 中加載這兩個配置文件:

<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,只是想借這樣一個契機,一起來捋一捋 SpringMVC 中 HanderMapping 的原理。

責任編輯:武曉燕 來源: 江南一點雨
相關(guān)推薦

2024-01-03 09:40:01

QA軟件測試開發(fā)

2010-05-11 10:41:21

職業(yè)IT人

2020-03-28 14:36:36

機器學(xué)習備忘單AI

2012-07-24 12:47:37

軟件設(shè)計架構(gòu)設(shè)計

2016-04-18 12:58:42

菜鳥程序員跳槽

2024-05-24 14:04:04

2023-03-03 16:38:28

JavaSpring框架

2022-12-06 08:45:33

數(shù)據(jù)庫OS產(chǎn)品

2022-04-02 15:04:46

Windows 11Linux微軟

2014-11-10 09:20:36

空中網(wǎng)絡(luò)谷歌Facebook

2018-04-24 08:42:19

2023-09-08 08:23:29

Servlet程序MVC

2021-08-31 10:52:30

容量背包物品

2022-12-08 19:20:11

開源用戶使用軟件

2023-08-10 13:57:50

模型AI

2012-04-13 18:52:45

諾基亞

2021-03-23 10:08:02

編程互聯(lián)網(wǎng)數(shù)據(jù)科學(xué)

2022-10-09 09:38:10

高可用設(shè)計

2021-10-26 09:40:29

人工智能AI機器人

2016-08-08 15:22:11

課程
點贊
收藏

51CTO技術(shù)棧公眾號