赞
踩
我们发出的请求,SpringMVC是如何精准定位到那个Controller以及具体方法?其实这都是 HandlerMapping 发挥的作用,这篇博文我们以 RequestMappingHandlerMapping 为例并结合源码一步步进行分析。
默认 HandlerMapping 主要定义在 EnableWebMvcConfiguration 和其祖父类 WebMvcConfigurationSupport 中
- @Bean
- @Primary
- @Override
- public RequestMappingHandlerMapping requestMappingHandlerMapping(
- @Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
- @Qualifier("mvcConversionService") FormattingConversionService conversionService,
- @Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {
- // Must be @Primary for MvcUriComponentsBuilder to work
- return super.requestMappingHandlerMapping(contentNegotiationManager, conversionService,
- resourceUrlProvider);
- }
-
- @Bean
- public WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext applicationContext,
- FormattingConversionService mvcConversionService, ResourceUrlProvider mvcResourceUrlProvider) {
- WelcomePageHandlerMapping welcomePageHandlerMapping = new WelcomePageHandlerMapping(
- new TemplateAvailabilityProviders(applicationContext), applicationContext, getWelcomePage(),
- this.mvcProperties.getStaticPathPattern());
- welcomePageHandlerMapping.setInterceptors(getInterceptors(mvcConversionService, mvcResourceUrlProvider));
- welcomePageHandlerMapping.setCorsConfigurations(getCorsConfigurations());
- return welcomePageHandlerMapping;
- }
- @Bean
- public BeanNameUrlHandlerMapping beanNameHandlerMapping(
- @Qualifier("mvcConversionService") FormattingConversionService conversionService,
- @Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {
-
- BeanNameUrlHandlerMapping mapping = new BeanNameUrlHandlerMapping();
- mapping.setOrder(2);
-
- PathMatchConfigurer pathConfig = getPathMatchConfigurer();
- if (pathConfig.getPatternParser() != null) {
- mapping.setPatternParser(pathConfig.getPatternParser());
- }
- else {
- mapping.setUrlPathHelper(pathConfig.getUrlPathHelperOrDefault());
- mapping.setPathMatcher(pathConfig.getPathMatcherOrDefault());
- }
-
- mapping.setInterceptors(getInterceptors(conversionService, resourceUrlProvider));
- mapping.setCorsConfigurations(getCorsConfigurations());
- return mapping;
- }
-
- @Bean
- public RouterFunctionMapping routerFunctionMapping(
- @Qualifier("mvcConversionService") FormattingConversionService conversionService,
- @Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {
-
- RouterFunctionMapping mapping = new RouterFunctionMapping();
- mapping.setOrder(3);
- mapping.setInterceptors(getInterceptors(conversionService, resourceUrlProvider));
- mapping.setCorsConfigurations(getCorsConfigurations());
- mapping.setMessageConverters(getMessageConverters());
-
- PathPatternParser patternParser = getPathMatchConfigurer().getPatternParser();
- if (patternParser != null) {
- mapping.setPatternParser(patternParser);
- }
-
- return mapping;
- }
-
- @Bean
- @Nullable
- public HandlerMapping resourceHandlerMapping(
- @Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
- @Qualifier("mvcConversionService") FormattingConversionService conversionService,
- @Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {
-
- Assert.state(this.applicationContext != null, "No ApplicationContext set");
- Assert.state(this.servletContext != null, "No ServletContext set");
-
- PathMatchConfigurer pathConfig = getPathMatchConfigurer();
-
- ResourceHandlerRegistry registry = new ResourceHandlerRegistry(this.applicationContext,
- this.servletContext, contentNegotiationManager, pathConfig.getUrlPathHelper());
- addResourceHandlers(registry);
-
- AbstractHandlerMapping handlerMapping = registry.getHandlerMapping();
- if (handlerMapping == null) {
- return null;
- }
- if (pathConfig.getPatternParser() != null) {
- handlerMapping.setPatternParser(pathConfig.getPatternParser());
- }
- else {
- handlerMapping.setUrlPathHelper(pathConfig.getUrlPathHelperOrDefault());
- handlerMapping.setPathMatcher(pathConfig.getPathMatcherOrDefault());
- }
- handlerMapping.setInterceptors(getInterceptors(conversionService, resourceUrlProvider));
- handlerMapping.setCorsConfigurations(getCorsConfigurations());
- return handlerMapping;
- }
PS:WebMvcConfigurationSupport 中定义的 HandlerMapping 不止上述三个,但是有效的只有三个 (SpringBoot 版本 2.6.13),有的 HandlerMapping 需要满足一定条件才生效。
- private void initHandlerMappings(ApplicationContext context) {
- this.handlerMappings = null;
-
- if (this.detectAllHandlerMappings) {
- // Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
- Map<String, HandlerMapping> matchingBeans =
- BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
- if (!matchingBeans.isEmpty()) {
- this.handlerMappings = new ArrayList<>(matchingBeans.values());
- // We keep HandlerMappings in sorted order.
- AnnotationAwareOrderComparator.sort(this.handlerMappings);
- }
- }
- else {
- try {
- HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
- this.handlerMappings = Collections.singletonList(hm);
- }
- catch (NoSuchBeanDefinitionException ex) {
- // Ignore, we'll add a default HandlerMapping later.
- }
- }
-
- // Ensure we have at least one HandlerMapping, by registering
- // a default HandlerMapping if no other mappings are found.
- if (this.handlerMappings == null) {
- this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
- if (logger.isTraceEnabled()) {
- logger.trace("No HandlerMappings declared for servlet '" + getServletName() +
- "': using default strategies from DispatcherServlet.properties");
- }
- }
-
- for (HandlerMapping mapping : this.handlerMappings) {
- if (mapping.usesPathPatterns()) {
- this.parseRequestPath = true;
- break;
- }
- }
- }
默认的 HandlerMapping 定义在 DispatcherServlet.properties 文件中
默认HandlerMapping为
一共有五个 HandlerMapping (SpringBoot 版本 2.6.13,最新版本貌似有6个,大家自行验证一下),即我们上文所分析的在类EnableWebMvcConfiguration、WebMvcConfigurationSupport 中定义的 HandlerMapping
如果某个 HandlerMapping 的 getHandler 方法返回了一个有效的 HandlerExecutionChain,我们就可以认为这个 HandlerMapping 可以处理这个请求。接下里以 RequestMappingHandlerMapping 为例进行分析,大部分请求也都是由这个 HandlerMapping 处理的
PS : WelcomePageHandlerMapping 就是处理欢迎页的
通过上图,我们知道其祖父类(AbstractHandlerMethodMapping) 继承 InitializingBean 接口,继承 InitializingBean 接口的类会在bean的实例化过程中执行 afterPropertiesSet 方法
获取所有类型是 Object 的 beans,并且 beanName 不以 scopedTarget. 开头
根据 beanName 获取 beanType
如果 beanType 上存在 @Controller 、@RequestMapping 注解则进行处理
主要有三个方法 selectMethods、getMappingForMethod、registerHandlerMethod
PS : 经过测试,如果存在 @RequestMapping 注解,即使方法的描述符是 private,也可以接受请求
假设有一个UserController,明细由下方所示,我们来看一下这个 mappingRegistry 属性的结构
- @RestController
- public class UserController {
-
- @GetMapping("/user")
- public String getUser() {
- return "get user";
- }
-
- @PostMapping("/user")
- public String postUser() {
- return "post user";
- }
-
- @PutMapping("/user")
- public String putUser() {
- return "put user";
- }
-
- @DeleteMapping("/user")
- public String deleteUser() {
- return "delete user";
- }
- }
URI 和具体方法的映射关系,都存储在 mappingRegistry 这个属性中
- public class CustomHandlerMapping implements HandlerMapping, PriorityOrdered {
-
- private ApplicationContext applicationContext;
-
- public CustomHandlerMapping(ApplicationContext applicationContext) {
- this.applicationContext = applicationContext;
- }
-
- @Override
- public HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
- String beanName = request.getParameter("beanName");
- String methodName = request.getParameter("method");
-
- if (StringUtils.isBlank(beanName) || StringUtils.isBlank(methodName) || !applicationContext.containsBean(beanName)) {
- return null;
- }
-
- Object object = applicationContext.getBean(beanName);
-
- Method method = null;
- Method[] declaredMethods = object.getClass().getDeclaredMethods();
- for (Method declaredMethod : declaredMethods) {
- if (declaredMethod.getName().equals(methodName)) {
- method = declaredMethod;
- break;
- }
- }
-
- if (method == null) {
- return null;
- }
-
- HandlerMethod handlerMethod = new HandlerMethod(object, method);
-
- return new HandlerExecutionChain(handlerMethod);
- }
-
- @Override
- public int getOrder() {
- return 0;
- }
- }
继承 PriorityOrdered 接口,让我们自定义的 HandlerMapping 优先级最高
- @Configuration
- public class HandlerMappingConfig implements ApplicationContextAware {
-
- private ApplicationContext applicationContext;
-
- @Override
- public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
- this.applicationContext = applicationContext;
- }
-
- @Bean
- public CustomHandlerMapping customHandlerMapping() {
- return new CustomHandlerMapping(applicationContext);
- }
- }
- @Component("chmc")
- public class CustomHandlerMappingController {
-
- @ResponseBody
- public String hello() {
- return "hello HandlerMapping! ";
- }
- }
自定义 handlerMapping 生效
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。