如何在spring boot中獲取所有RequestMapping的URL路徑列表集?

本文是“著”模式。但是本文寫完之後,我直接搜索了一下結論的核心,發現好些達人都已經實現了,無知好尷尬……

 

爲什麼要做這事兒?

自動化、可視化、授權……總之,誰用誰知道

 

如何在Controller類的方法內獲取到系統內已存的所有RequestMapping的路徑列表集?

簡單來說:在帶有@Controller註解的類中,添加以下的代碼:

@Autowired
ApplicationContext applicationContext;
public void getAllRequestMappingInfo() {
    AbstractHandlerMethodMapping<RequestMappingInfo> objHandlerMethodMapping = (AbstractHandlerMethodMapping<RequestMappingInfo>)application.getBean(“requestMappingHandlerMapping”);
    Map<RequestMappingInfo, HandlerMethod> mapRet = objHandlerMethodMapping.getHandlerMethods();
}

實現過程,沒仔細看書,只能蠻力調試了。

本文基於的主要庫版本:

spring-boot:1.5.2.RELEASE

*spring-xx:4.3.7.RELEASE

*:spring-xx中的xx包含core、context、beans等

 

以下是調試流程:

1、在項目的App.main方法(App類有@SpringBootApplication註解)中,使用SpringApplication.run方法,最終會創建一個SpringApplication類的對象,並調用其run(String…args)方法;

2、在run方法中,使用SpringApplication.createApplicationContext()方法,創建了ApplicationContext對象,其具體類型由SpringApplication對象屬性的webEnvironment布爾值所決定,由於後者的值爲true,因此ApplicationContext對象被初始化爲org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext類型;run方法內將創建的對象臨時賦值給了方法體內定義的context(ConfigurableApplicationContext類型)局部變量;再具體的細節這裏不表;

3、run方法內,繼續到this.refreshContext(context),在後者內調用refresh方法;

4、refresh最終會調用org.springframework.context.support.AbstractApplicationContext類的refresh方法;

5、在AbstractApplicationContext.refresh方法內,調用finishBeanFactoryInitialization方法對GenericApplicationContext.beanFactory成員變量進行初始化(GenericApplicationContext類是直接從AbstractApplicationContext類派生出來的,後者並沒有定義beanFactory成員變量,但是定義了一個抽象方法getBeanFactory,其實獲取的就是前者的成員變量,因爲前者不僅定義了成員變量,而且也實現了這個抽象方法),這個beanFactory成員變量在GenericApplicationContext類的構造函數裏被創建,其類型爲DefaultListableBeanFactory;

6、再回到finishBeanFactoryInitialization方法內,beanFactory會調用DefaultListableBeanFactory.preInstantiateSingletons方法,暫停一下,我們下面說一個beanFactory;

6.1、在第2步的run方法內使用createApplicationContext方法創建上下文時,會通過org.springframework.beans.BeanUtils類,最終在org.springframework.context.annotation.AnnotationConfigUtils類內的registerAnnotationsConfigProcessors方法中,對beanFactory進行一些初始化。beanFactory對象的beanDefinitionNames成員變量(ArrayList)保存着系統內的bean名稱(這裏,我覺得使用GenericApplicationContext.getDefaultListableBeanFactory()方法可以獲取到beanFactory,然後進行獲取到bean名稱集)

6.2、接上面,在AnnotationConfigUtils.registerAnnotationsConfigProcessors方法內,會對beanFactory對象實際添加幾個系統內最初的bean,將其名稱保存在DefaultListableBeanFactory的beanDefinitionNames成員變量內,將<bean名稱,對象>保存在beanDefinitionMap成員變量內。整個系統內的第一個bean名稱是org.springframework.context.annotation.internalConfigurationAnnotationProcessor,其值爲org.springframework.beans.factory.support.RootBeanDefinition類型的對象,且後者傳進去的參數爲org.springframework.context.annotation.ConfigurationClassPostProcessor類型。

6.3、whatever,最終在DefaultListableBeanFactory.registerBeanDefinition方法內,對bean進行添加。總之,在AnnotationConfigUtils.registerAnnotationsConfigProcessors方法內,會最終人爲的添加6個bean:

名稱

對象類型

org.springframework.context.annotation.internalConfigurationAnnotationProcessor

org.springframework.beans.factory.support.RootBeanDefinition類型,參數爲org.springframework.context.annotation.ConfigurationClassPostProcessor

org.springframework.context.annotation.internalAutowiredAnnotationProcessor

org.springframework.beans.factory.support.RootBeanDefinition類型,參數爲org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor

org.springframework.context.annotation.internalRequiredAnnotationProcessor

org.springframework.beans.factory.support.RootBeanDefinition類型,參數爲org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor

org.springframework.context.annotation.internalCommonAnnotationProcessor

org.springframework.beans.factory.support.RootBeanDefinition類型,參數爲org.springframework.beans.factory.annotation.CommonAnnotationBeanPostProcessor

org.springframework.context.annotation.internalPersistenceAnnotationProcessor

org.springframework.beans.factory.support.RootBeanDefinition類型,參數爲org.springframework.beans.factory.annotation.PersistenceAnnotationBeanPostProcessor

org.springframework.context.event.internalEventListenerProcessor

org.springframework.beans.factory.support.RootBeanDefinition類型,參數爲org.springframework.context.event.EventListenterMethodProcessor

org.springframework.context.event.internalEventListenerFactory

org.springframework.beans.factory.support.RootBeanDefinition類型,參數爲org.springframework.context.event.DefaultEventListenerFactory

 

7、回到AbstractApplicationContext.refresh方法內,在其中的this.invokeBeanFactoryPostProcessors步驟,會掃描獲取到系統內自定義的所有的bean名稱,並將其裝入到beanFactory對象內。

8、在AbstractApplicationContext.finishBeanFactoryInitialization方法內,beanFactory對象使用preInstantiateSingletons方法,對單件bean對象進行預初始化;

9、單件的bean對象很多,比如requestMappingHandlerMapping就是其中之一,還有error、beanNameHandlerMapping等等;

10、這裏我們主要看名稱爲requestMappingHandlerMapping的bean,這個名稱的bean最終通過一系列手段會對應org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping類型的對象;

11、RequestMappingHandlerMapping類對象在創建後,會調用bean的一個約定方法(它本身改寫了)afterPropertiesSet(),然後在其中又調用了它的父類AbstractHandlerMethodMapping的afterPropertiesSet()方法,後者內調用了initHandlerMethods()方法;

11.1、在AbstractHandlerMethodMapping.initHandlerMethods方法內,首先會獲取到系統內所有的bean names,然後一一枚舉這些bean,主要是爲了判斷bean對象的類上有沒有@Controller註解,或者方法上是不是有@RequestMapping註解。這個實現是通過這種方式達成的:RequestMappingHandlerMapping類實現了AbstractHandlerMethodMapping定義的抽象方法isHandler(),在前者的實體內,

12、在AbstractHandlerMethodMapping.initHandlerMethods方法內,會通過detectHandlerMethods()方法->registerHandlerMethods()方法,最終調用內部類AbstractHandlerMethodMapping.MappingRegistry的register方法,將@RequestMapping註解後的mapping字符串、Controller類對象、Mehod對象三者存放在MappingRegistry類對象的成員變量mappingLookup這個Map<T,HandlerMethod>類型的映身內,後者是一個LinkedHashMap類型的映射;

13、MappingRegistry類提供了getMapping()方法返回mappingLookup成員變量的引用;

14、AbstractHandlerMethodMapping類提供了getMappingRegistry()方法,返回MappingRegistry類對象,但是這個方法並不是一個public方法,因此只能在org.springframework.web.servlet.handler包內被使用了。

15、RequestMappingHandlerMapping類從AbstractHandlerMethodMapping類派生而來,通過getBean()方法,能夠獲取到其對象,但由於14步的影響,還是無法獲取到@RequestMapping的路徑;

16、另外一種方法,AbstractHandlerMethodMapping類內提供了一個公共的方法getHandlerMethods(),這個方法返回MappingRegistry類的mappingLookup成員變量的不可更改視圖;AbstractHandlerMethodMapping類還提供了其他幾個有用的方法,暫時略過;

17、ApplicationContext可使用ApplicationObjectSupport.getApplicationContext()方法獲取;所有的bean names可通過org.springframework.beans.factory.ListableBeanFactory.getBeansNamesForType()方法獲取到(此方法真正在org.springframework.beans.factory.support.DefaultListableBeanFactory類內被實現;

 

 

其他有用的方法:

1、如何通過bean名稱獲取bean的實際類型?

使用org.springframework.beans.factory.BeanFactory接口提供的getType方法(此方法在ApplicationContext被實現了)

 

最後,其實,在使用springboot調試時,控制檯的輸出信息裏,已經打印出了所有映射路徑及對應的處理方法信息,比本文最開始的方法打印的更全(本文僅做了RequestMappingHandlerMapping)

 

 

事後發現的一些達人的文章:

SpringMVC項目中獲取所有URL到Controller Method的映射:http://www.cnblogs.com/yuananyun/archive/2014/08/25/3934371.html

Spring 反射得到所有controller與method:http://183615215-qq-com.iteye.com/blog/1866281

 

 


發佈了40 篇原創文章 · 獲贊 9 · 訪問量 12萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章