《SpringMVC从入门到放肆》四、SpringMVC配置式开发(处理器映射器)

上一篇我们讲解了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对象。明白这个我们的目的就已经达到了。


关于作者: 王俊南(Jonas)

昨夜寒蛩不住鸣。惊回千里梦,已三更。起来独自绕阶行。人悄悄,帘外月胧明。 白首为功名。旧山松竹老,阻归程。欲将心事付瑶琴。知音少,弦断有谁听。

热门文章