自满是成功的杀手,唯有持续的奋斗才是成长的源泉
--苏格拉底
最近在学习spring-mvc,在成功搭建spring-mvc的环境后,使用html写了一个简单的登陆页面,但是在启动服务后,访问相应的页面,报404,找不到对应的资源。百思不得其解,于是乎跟了一下源码,尝试一下自己解决。
我使用的版本为:
- tomcat : 8.5.9
- spring-webmvc : 4.2.3.RELEASE
- jstl : 1.2
web.xml中DispatcherServlet
的配置如下:
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/spring-mvc-config.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
跟源码后发现,在请求对应的html页面的时候,请求被DispatcherServlet
处理了,在由其doDispatch处理的时候,无法获取到对应的handler。于是乎自作聪明,想着对对应的html页面写一个Controller不就好了吗?最后发现是不行的,暂时发现对应Controller的页面需要是jsp(这个可能是自己试用jstl解析视图的原因,具体细节还要学)。
可是之前自己在学习servlet的时候,是可以访问静态的html页面的。为什么在使用spring-mvc的框架后会提示无法访问呢?问题就出在DispatcherServlet
的路径映射上了。
servlet的路径匹配规则
当键入一个url后,servlet容器是如何进行路径的匹配的呢?具体的匹配规则是什么呢?匹配的顺序是怎样的?
首先,serlvet容器会用请求的url减去当前应用的上下文路径,然后以剩余的字符串进行匹配,假设现在我们请求的url为http://localhost:8080/servlet/index/index.html
,他的应用上下文为servlet
,容器就会将http://localhost:8080/servlet
去掉,使用/index/index.html
进行匹配,以选择对应处理请求的serlvet。
servlet 的匹配规则有四种,按匹配顺序排列。
精确匹配。精确匹配的就是请求的路径与servlet定义的url-pattern完全匹配。
路径匹配。假如一个资源路径下的所有资源页面对应的请求都需要有一个serlvet进行处理,这可以使用路径匹配规则。路径匹配规则以
/
开口,并以/*
结尾
<!-- 对应的servlet会处理以 /index/ 开头的请求 -->
<servlet-mapping>
<servlet-name>servlet</servlet-name>
<url-pattern>/index/*</url-pattern>
</servlet-mapping>
- 扩展名匹配。匹配指定扩展名字的文件,路径匹配规则以
*.
开头
<!-- 对应的servlet会处理对html文件的请求 -->
<servlet-mapping>
<servlet-name>servlet</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>
- 默认匹配。默认路径匹配规则的路径就一种:
/
⚠️注意:在一个请求经由servlet容器处理的时候,servlet容器按照顺序进行匹配,当匹配中的时候,就将请求交给对应的servlet进行处理,单一匹配。
问题再次分析
很显然,我将DispatcherServlet
的匹配路径定义为了默认匹配,然后没有定义其他的servlet,导致所有的请求都有DispatcherServlet
进行处理了。所有,如果定义一个可以处理静态资源的serlvet就可以了,由此出发,回想最初使用servlet的时候,为什么可以处理静态资源的访问呢?抱着疑问上网搜索了一下,发现原来在tomcat的conf目录下由一个web.xml文件,打开这个web.xml文件,其中定义了DefaultServlet
。我们看一下这个文件:
其对应定义的匹配路径为:
在我后续将DispatcherServlet
的路径映射定义为默认后,我定义的覆盖了tomcat的默认的servlet,这就是问题原因了。找到问题原因了,怎么解决呢?
解决办法
解决办法一:
为tomcat的DefaultServlet
定义一个优先级更高的匹配。扩展名匹配或者是路径匹配。
<!-- 让DefaultServlet来处理html,js,css文件 -->
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.html</url-pattern>
<url-pattern>*.css</url-pattern>
<url-pattern>*.js</url-pattern>
</servlet-mapping>
<!-- 假设所有的资源都放在resource这个目录下 -->
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/resource/*</url-pattern>
</servlet-mapping>
解决办法二:
让spring-webmvc来为我们解决静态资源访问的问题。spring中定义了一个DefaultServletHttpRequestHandler
来处理静态资源。我们可以看一下官方对它的介绍:
大概的意思就是,这个DefaultServletHttpRequestHandler
是spring提供用来处理静态资源的。可以在spring的配置文件中对它进行配置就好了。
<mvc:default-servlet-handler/>
<!-- 两种选其中一种就好了 -->
<bean class="org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler"></bean>
自此,静态资源的访问解决了,但是这其实解决了配置问题,其中具体的细节自己还是不了解,还是要深入进行学习。学无止境啊!