上一篇我们讲解了DispatcherServlet的url-pattern配置详解,今天我们来真正对SpringMVC进行配置式开发。
所谓配置式开发是指“处理器类是程序员自己定义的、实现了特定接口的类,然后在SpringMVC配置文件中对该类进行显式的,明确的注册”的开发方式。今天我们的开发还是将中央调度器的url-pattern配置成*.do。然后将springmvc.xml的静态资源访问先取消。
一、处理器映射器(BeanNameUrlHandlerMapping)
handlerMapping接口负责根据request请求找到对应的Handler处理器及Interceptor拦截器,并将它们封装在HandlerExecutionChain对象中,返回给中央调度器。其常用的实现类有两种:
1BeanNameUrlHandlerMapping
2SimpleUrlHandlerMapping
这里着重说明SimpleUrlHandlerMapping。BeanNameUrlHandlerMapping我们简单来看一下源码:
1public class BeanNameUrlHandlerMapping extends AbstractDetectingUrlHandlerMapping {
2
3 /**
4 * Checks name and aliases of the given bean for URLs, starting with "/".
5 */
6 @Override
7 protected String[] determineUrlsForHandler(String beanName) {
8 List<String> urls = new ArrayList<String>();
9 if (beanName.startsWith("/")) {
10 urls.add(beanName);
11 }
12 String[] aliases = getApplicationContext().getAliases(beanName);
13 for (String alias : aliases) {
14 if (alias.startsWith("/")) {
15 urls.add(alias);
16 }
17 }
18 return StringUtils.toStringArray(urls);
19 }
20
21}
上方BeanNameUrlHandlerMapping类中只有一个方法determineUrlsForHandler()。该方法中把所有以斜杠开头的请求的BeanName都放到了一个名叫urls的List中。然后返回给了中央调度器。但是有一个弊端,比如我有两个请求都由一个Controller来处理,这时我们的springmvc.xml文件的配置方式如下:
1<!-- 注册SpringMVC处理器 -->
2<bean id="/my.do" class="cn.wechatbao.controller.MyController"></bean>
3<bean id="/you.do" class="cn.wechatbao.controller.MyController"></bean>
这样Spring容器在创建MyController实例的时候,一次性创建了两个,而对于我们来说,其实一个实例就可以对两个或多个请求进行处理。所以我们继续来讲解SimpleUrlHandlerMapping方法。
二、处理器映射器(SimpleUrlHandlerMapping)
要使用SimpleUrlHandlerMapping我们需要将其注册到SpringMVC中,如图,我们在默认的处理器映射器中并没有发现该实现类。所以需要注册。
注册方式:(springmvc.xml如下)
1<?xml version="1.0" encoding="UTF-8"?>
2<beans xmlns="http://www.springframework.org/schema/beans"
3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4 xmlns:context="http://www.springframework.org/schema/context"
5 xmlns:aop="http://www.springframework.org/schema/aop"
6 xmlns:tx="http://www.springframework.org/schema/tx"
7 xmlns:mvc="http://www.springframework.org/schema/mvc"
8 xsi:schemaLocation="http://www.springframework.org/schema/beans
9 http://www.springframework.org/schema/beans/spring-beans.xsd
10 http://www.springframework.org/schema/context
11 http://www.springframework.org/schema/context/spring-context.xsd
12 http://www.springframework.org/schema/aop
13 http://www.springframework.org/schema/aop/spring-aop.xsd
14 http://www.springframework.org/schema/tx
15 http://www.springframework.org/schema/tx/spring-tx.xsd
16 http://www.springframework.org/schema/mvc
17 http://www.springframework.org/schema/mvc/spring-mvc.xsd">
18
19 <!-- 注册视图解析器 -->
20 <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
21 <property name="prefix" value="/WEB-INF/jsp/" />
22 <property name="suffix" value=".jsp" />
23 </bean>
24
25 <bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
26 <property name="mappings">
27 <props>
28 <prop key="/my.do">myController</prop>
29 <prop key="/you.do">myController</prop>
30 <prop key="/he.do">myController</prop>
31 </props>
32 </property>
33 <!--
34 这种方式也可以实现多个请求交由一个Controller处理的需求
35 <property name="urlMap">
36 <map>
37 <entry key="/my.do" value="myController"></entry>
38 <entry key="/you.do" value="myController"></entry>
39 <entry key="/he.do" value="myController"></entry>
40 </map>
41 </property>
42 -->
43 </bean>
44
45 <!-- 注册SpringMVC处理器 -->
46 <bean id="myController" class="cn.wechatbao.controller.MyController"></bean>
47</beans>
注意:该种配置方式只针对多个请求用一个Controller处理才使用。
三、处理器映射器源码分析
1、当客户端发送请求到达中央调度器(DispatcherServlet)时,DispatcherServlet首先进入到doService方法在doService()方法里,对request设计一些属性,然后又进入到了doDispatch()方法,在doDispatch()方法里,我们着重来看以下代码:
1// Determine handler for the current request.
2mappedHandler = getHandler(processedRequest);
2、从doDispatch()方法又进入了一个getHandler()的方法,如下
1protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
2 for (HandlerMapping hm : this.handlerMappings) {
3 if (logger.isTraceEnabled()) {
4 logger.trace(
5 "Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
6 }
7 HandlerExecutionChain handler = hm.getHandler(request);
8 if (handler != null) {
9 return handler;
10 }
11 }
12 return null;
13}
在该方法中,使用了一个forEach循环,来循环所有的处理器映射器,根据每个处理器映射器(HandlerMapping)来获取与之对应的处理器执行链(HandlerExecutionChain)。
进入hm.getHandler(request);方法来看。
3、继续hm.getHandler(request);方法
1@Override
2public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
3 Object handler = getHandlerInternal(request);
4 if (handler == null) {
5 handler = getDefaultHandler();
6 }
7 if (handler == null) {
8 return null;
9 }
10 // Bean name or resolved handler?
11 if (handler instanceof String) {
12 String handlerName = (String) handler;
13 handler = getApplicationContext().getBean(handlerName);
14 }
15
16 HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
17 if (CorsUtils.isCorsRequest(request)) {
18 CorsConfiguration globalConfig = this.corsConfigSource.getCorsConfiguration(request);
19 CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
20 CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
21 executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
22 }
23 return executionChain;
24}
4、接下来又走到了HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);这个方法。我们就不继续深究了。有兴趣的朋友可以自己再研究下去。我们得出的结论是经过了一系列的方法,最终返回给中央调度器一个HandlerExecutionChain对象。明白这个我们的目的就已经达到了。