概述
Spring MVC
框架提供了一个HandlerMapping
实现类BeanNameUrlHandlerMapping
,该实现类会被DispatcherServlet
用于映射请求到实现了接口Controller
的控制器。本文使用例子讲解BeanNameUrlHandlerMapping
的应用。
应用实例讲解
首先需要说明的是,本文例子基于如下项目 :
Springboot 2.1.2.RELEASE
Spring MVC 5.1.3.RELEASE
- 缺省
Servlet
容器:Tomcat embeded 9.0.14
- 缺省端口 :
8080
- 缺省
- 服务端模板引擎 :
freemarker
Spring MVC
配置基于Configuration
+@EnableWebMVC
+WebMvcConfigurer
实现类- 注意 : 没有采用
Springboot
缺省的自动配置WebMvcAutoConfiguration
- 注意 : 没有采用
1. 实现自定义Controller
package tut.controller;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.time.LocalDateTime;
/**
* 此 Controller 配合 ControllerBeanConfig 演示
* BeanNameUrlHandlerMapping + SimpleControllerHandlerAdapter 如何工作
*
*/
public class WelcomeController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) {
ModelAndView mav = new ModelAndView("welcome");
mav.addObject("now", LocalDateTime.now().toString());
String name = request.getParameter("name");
mav.addObject("name", name == null ? "你是?" : name);
return mav;
}
}
该类WelcomeController
实现了接口Controller
,该接口约定了一个方法ModelAndView handleRequest(HttpServletRequest, HttpServletResponse)
用于处理客户请求。这里的实现只是简单地准备一些参数now
,name
,然后渲染视图welcome
。因为视图的实现不是本文的重点,所以这里不再给出。
2. 将自定义Controller
定义为容器bean
package tut.config;
import tut.controller.WelcomeController;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 本配置文件用于演示定义用于 Controller 的 bean, 这些 bean 实现了接口 Controller,
* 会被 Spring MVC HandlerMapping BeanNameUrlHandlerMapping 组件发现和进行映射登记,
* 最终会在访问 URL /welcome 时匹配到 SimpleControllerHandlerAdapter
*/
@Configuration
public class ControllerBeanConfig {
/**
* 这里定义一个 web controller bean, 注意 :
* 1. 该 bean 实现了接口 Controller,
* 2. 该 bean 没有使用注解 @Controller,
* (如果使用了注解@Controller,就会被RequestMappingHandlerMapping接管,而不是由BeanNameUrlHandlerMapping处理)
* 3. 映射到匹配 welcome* 的url
* @return
*/
@Bean(name = "/welcome*")
public WelcomeController beanWelcomeController() {
return new WelcomeController();
}
}
该配置类只有一个目的,定义一个类型为WelcomeController
的bean
组件,并且bean
名称为/welcome*
,注意,该bean
名称使用一个url pattern
的格式,因为只有使用这种格式,该bean
才会被BeanNameUrlHandlerMapping
看到。
3.运行效果
有了以上的WelcomeController
和ControllerBeanConfig
配置类之后,运行应用,然后访问http://localhost:8080/welcome
,或者http://localhost:8080/welcome.html
之类任何匹配/welcome*
的地址,可以看到如下界面 :
该结果基于两个参数和一个服务端freemarker
试图渲染得到。
4.请求处理分析
通过调试方式代码跟踪,我们发现 :
- 容器启动时,
@EnableWebMVC
对应的配置机制会注册一个BeanNameUrlHandlerMapping
bean
到容器; DispatcherServlet
启动时,会获取上面注册的BeanNameUrlHandlerMapping
bean
,该组件在被获取时也被实例化和被设置ApplicationContext
属性。该组件被设置属性ApplicationContext
时,它会过滤容器中所有/
开头的bean
名称/别名,这些bean
名称/别名其实是url pattern
,如果检测到这样的bean
名称/别名,则将每个这样的bean
名称/别名和对应的bean
名称增加为一个映射项。使用Map<String, Object> handlerMap
(定义在AbstractUrlHandlerMapping
中)保存映射关系<url pattern
,handler
对象>对。
具体到本文中的例子,该BeanNameUrlHandlerMapping
bean
就是将 </welcome*
,WelcomeController
bean
>映射关系管理起来。- 用户请求
http://localhost:8080/welcome*
时,也就是http://localhost:8080/welcome
或者http://localhost:8080/welcome.do
所有这类匹配/welcome*
的地址时,DispatcherServlet
就会根据请求的url
从所注册的各个HandlerMapping
组件中试图找到所对应的请求处理器(request handler
)。最终,DispatcherServelt
会从BeanNameUrlHandlerMapping
组件找到匹配的WelcomeController
bean
。 DispatcherServelt
找到匹配的WelcomeController
bean
之后,然后会从所登记的HandlerAdpater
中查找支持WelcomeController
组件的适配器,最终,它找到了SimpleControllerHandlerAdapter
。DispatcherServelt
找到SimpleControllerHandlerAdapter
之后,使用该请求处理器适配器针对用户请求调用WelcomeController
bean
的接口Controller
方法handleRequest
。
正如上面WelcomeController#handleRequest
的实现逻辑所示,它会准备一些参数数据,指定一个视图模板welcome
,将二者以ModelAndView
的形式返回给DispatcherServelt
。DispatcherServelt
进一步渲染给定的ModelAndView
,返回给浏览器端如上运行效果小节所示的效果。