如何在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

 

 


  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值