(一)SpringMVC之配置DispatcherServlet的一些坑

DispatcherServlet是SpringMVC的核心控制器,就像是SpringMVC的心脏,几乎所有的请求都会经过这个控制器,通过它,大大的降低了模块之间的耦合度。所有学SpringMVC的同学们第一步肯定都是先配置这个Servlet,不然还写啥SpringMVC啊。

那其实我第一次写SpringMVC的时候就遇到了好多坑,这里记录一下,只是冰山一角,但希望能帮助一些人。

为了更好的说明情况,我先写一些代码


4.png

首先新建一个MyController继承自Controller

public class MyController implements Controller {

    @Override
    public ModelAndView handleRequest(HttpServletRequest arg0, HttpServletResponse arg1) throws Exception {
        ModelAndView mav = new ModelAndView();
        // 增加一个属性到model中,底层是调用了request.setAttribute()方法
        mav.addObject("hello", "hello springmvc");
        // 为这个模型视图起一个逻辑视图名
        mav.setViewName("hello");
        return mav;
    }
}

在WEB-INF目录下建一个springmvc-servlet.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:aop="http://www.springframework.org/schema/aop"
        xmlns:tx="http://www.springframework.org/schema/tx" 
        xmlns:jdbc="http://www.springframework.org/schema/jdbc"
        xmlns:context="http://www.springframework.org/schema/context"
        xsi:schemaLocation="http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/tx 
        http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/aop 
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/jdbc 
        http://www.springframework.org/schema/jdbc/spring-jdbc.xsd
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context.xsd"> 
        
    <!-- 这个文件默认放在WEB-INF文件夹下 ,如果不是,则需要在web.xml中进行指定-->
    <!-- 注册处理器 ,id的属性值为请求的url-->
    <bean id="/my.do" class="com.codeliu.controller.MyController"></bean>
    
    <!-- 注册视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- 视图资源url的前缀 -->
        <property name="prefix" value="/WEB-INF/jsp/"></property>
        <!-- 视图资源url的后缀 -->
        <property name="suffix" value=".jsp"></property>
    </bean>
</beans>

配置完这些,就应该去web.xml中配置DispatcherServlet了。

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
  <display-name>SpringMVC_01</display-name>
  <!-- 注册中央调度器 -->
  <servlet>
    <servlet-name>springmvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <!-- tomcat启动时自动创建servlet,数字越小优先级越高(>0) -->
    <load-on-startup>1</load-on-startup>
  </servlet>
  
  <servlet-mapping>
    <servlet-name>springmvc</servlet-name>
    <url-pattern>*.do</url-pattern>
  </servlet-mapping>
</web-app>

之后我们在WEB-INF目录下新建一个jsp文件夹,在jsp文件夹里新建一个hello.jsp

<body>
    <!-- 等价于${requestScope.hello} -->
    ${hello}
</body>

到这里,基本就完成了,我们启动tomcat,输入localhost:8080/SpringMVC_01/my.do,能正常显示。

SpringMVC配置文件的名字

如果把上面配置文件的名字由springmvc-servlet.xml改为springmvc.xml,则启动tomcat时就会报错,提示在WEB-INF目录下找不到一个springmvc-servlet.xml的文件

我们从配置文档中可以找到答案

 * <p>Passes a "contextConfigLocation" servlet init-param to the context instance,
 * parsing it into potentially multiple file paths which can be separated by any
 * number of commas and spaces, like "test-servlet.xml, myServlet.xml".
 * If not explicitly specified, the context implementation is supposed to build a
 * default location from the namespace of the servlet.

* <p>The default namespace is "'servlet-name'-servlet", e.g. "test-servlet" for a
 * servlet-name "test" (leading to a "/WEB-INF/test-servlet.xml" default location
 * with XmlWebApplicationContext). The namespace can also be set explicitly via
 * the "namespace" servlet init-param.

从这里可以知道两个信息,一个是SpringMVC配置文件的默认路径是WEB-INF下面,另一个是配置文件的name默认应该是你配置的DispatcherServlet的servlet-name+servlet.xml。

知道了这个,那么这个问题也就迎刃而解了。

  • 如果你配置文件放在WEB-INF目录下,但名字不想按照那个格式,可以通过namespace这个初始化参数进行指定。

比如我想把配置文件的名字改成springmvc.xml,但文件位置不移动,则在注册servlet时加上初始化参数如下

    <init-param>
        <!-- 如果springmvc的配置文件放在WEB-INF目录下,但不想名字的形式为*-servlet.xml,
        则可以通过namespace属性值进行指定 -->
        <param-name>namespace</param-name>
        <param-value>springmvc</param-value>
    </init-param>
  • 如果你的配置文件不想放在WEB-INF目录下,则通过contextConfigLocation这个初始化参数进行指定

比如我把springmvc.xml移动到src目录下,就进行如下配置

    <init-param>
        <!-- 默认Springmvc的配置文件是在WEB-INF目录下,如果要放在其他目录,则需要指定 -->
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:springmvc.xml</param-value>
        
        <!-- 如果springmvc的配置文件放在WEB-INF目录下,但不想名字的形式为*-servlet.xml,
        则可以通过namespace属性值进行指定 -->
        <!-- <param-name>namespace</param-name>
        <param-value>springmvc</param-value> -->
    </init-param>

配置了这个参数则namespace这个参数就不用配置了,因为通过这个参数已经把路径和名字都指定了。

(2)配置Servlet的servlet-name值

从上面的信息我们也可以看到,如果在默认情况下,你的servlet-name值必须和配置文件的前缀保持一致,但如果你通过上面的初始化参数进行了设置,那就无关紧要了。

(3)配置servlet-mapping时的url-pattern值

这个也是大有文章啊。我们上面写的是*.do的形式。我们分析一下下面三种情况

  • /*.do的形式:我们把url-pattern值的值改成/*.do,这种情况你会发现连tomcat都无法启动,所以以后遇到这种情况得想想是不是自己的url-pattern值多写了一个/。

  • /*的形式:我们把url-pattern值改成/*,这时启动tomcat没有错误,当你输入

http://localhost:8080/SpringMVC_01

或者

http://localhost:8080/SpringMVC_01/my.do

的时候,发现都是404


3.png

这个404就有点怪,因为它完全不知道你访问的是哪个资源。原因如下

当你写成这个形式的时候,DispatcherServlet会讲将动态页面(注意是动态页面)的跳转请求也当做是一个controller请求,然后它就会调用处理器映射器为其查找相应的处理器,这种情况下,毫无疑问是找不到的,所以你访问所有的jsp页面都会404。

  • /的形式:我们把url-pattern值改成/*,这时启动tomcat没有错误,当你输入
http://localhost:8080/SpringMVC_01/

或者

http://localhost:8080/SpringMVC_01/my.do

诶发现都没错,那这种情况有什么问题呢?

我们修改默认的index.jsp如下

<body>
    <img alt="picture" src="images/1.jpg">
</body>

在里面放一张图片

接着重新打开

http://localhost:8080/SpringMVC_01/

你会发现图片无法正常显示。原因就是写成这种形式,DispatcherServlet会将向静态资源的请求(注意是静态资源如css,png,jpg,js等),当前是一个controller请求,中央调度器会调用处理映射器为其查找相应的处理器,毫无疑问,这也是不可能找到的,所以这种情况下,静态资源的请求也全部是404。

总结如下

(1)/*之类的肯定不能写,因为访问啥都出错

(2)/的形式访问静态资源会出错

(4)/的救星

如果我们url-pattern的值还就成/咋的啦,也不是不行,不过就得进行另外的一些配置了。

  • 使用tomcat默认的servlet
    <servlet>
        <servlet-name>default</servlet-name>
        <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
        <init-param>
            <param-name>debug</param-name>
            <param-value>0</param-value>
        </init-param>
        <init-param>
            <param-name>listings</param-name>
            <param-value>false</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

这个servlet的介绍如下,是专门用于处理静态资源的。

<!-- The default servlet for all web applications, that serves static     -->
  <!-- resources.  It processes all requests that are not mapped to other   -->
  <!-- servlets with servlet mappings (defined either here or in your own   -->
  <!-- web.xml file).   -->

这样我们可以直接在web.xml中进行这个servlet的映射,而不用注册了。

<servlet-mapping>
    <servlet-name>default</servlet-name>
    <url-pattern>*.jpg</url-pattern>
  </servlet-mapping>
  
  <servlet-mapping>
    <servlet-name>default</servlet-name>
    <url-pattern>*.png</url-pattern>
  </servlet-mapping>
  <servlet-mapping>
    <servlet-name>default</servlet-name>
    <url-pattern>*.js</url-pattern>
  </servlet-mapping>
  <servlet-mapping>
    <servlet-name>default</servlet-name>
    <url-pattern>*.css</url-pattern>
  </servlet-mapping>

这样可以把静态资源的请求转向默认的servlet进行处理,就不会出现找不到静态资源的情况了。

  • 使用mvc:default-servlet-handler解决

我们得先在springmvc.xml中引入mvc的约束

xmlns:mvc="http://www.springframework.org/schema/mvc"
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd

只需在springmvc.xml中添加一条语句即可

<mvc:default-servlet-handler/>

通过它,静态资源也能正常访问。原理如下:该标签会将对静态资源的请求添加到SimpleUrlHandlerMapping的urlMap中,key就是请求的url,而value则是默认servlet请求处理器DefaultServletHttpRequestHandler对象,而该处理器调用了tomcat的默认servlet来处理静态资源。

所以归根到底,还是使用了tomcat的默认servlet。

  • 使用mvc:resources解决

在spring3.0.4版本之后,spring中有了专门处理静态资源访问请求的处理器ResourceHttpRequestHandler。并且添加了mvc:resources标签,专业用于处理静态资源无法访问的问题。所有我们只需要springmvc.xml中加入下面的语句

<mvc:resources location="/images/" mapping="/images/**"></mvc:resources>

location表示静态资源所在的目录,包含WEB-INF目录及其子目录。

mapping表示对该资源的请求。后面是两个星号*

该标签会把对静态资源的请求添加到SimpleUrlHandlerMapping的urlMap中,key就是与mapping属性匹配的url,而value则是静态处理器对象ResourceHttpRequestHandler

相比于第二种方式,这种方式的缺点是如果有css资源、js资源那么你还得添加标签。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,445评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,889评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,047评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,760评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,745评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,638评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,011评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,669评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,923评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,655评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,740评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,406评论 4 320
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,995评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,961评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,023评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,483评论 2 342

推荐阅读更多精彩内容