转自:https://blog.csdn.net/u013815649/article/details/50435819
写了好多小项目后也没弄明白<url-pattern>的真正意义,写跳转的时候也是跳的三心二意的,今天查了一下web.xml的详细配置,看了看servlet-mapping的讲解,豁然开朗,做了做小实验,原来是这样,捂脸。下面把看到的文章的servlet片段摘抄过来,先附上链接(http://blog.csdn.net/believejava/article/details/43229361)(作者如有意见,私信删文);
servlet容器对url的匹配过程:
当一个请求发送到servlet容器的时候,容器先会将请求的url减去当前应用上下文的路径作为servlet的映射url,比如我访问的是http://localhost/test/aaa.html,我的应用上下文是test,容器会将http://localhost/test去掉,剩下的/aaa.html部分拿来做servlet的映射匹配。这个映射匹配过程是有顺序的,而且当有一个servlet匹配成功以后,就不会去理会剩下的servlet了(filter不同,后文会提到)。其匹配规则和顺序如下:
1. 精确路径匹配。例子:比如servletA 的url-pattern为 /test,servletB的url-pattern为 /* ,这个时候,如果我访问的url为http://localhost/test ,这个时候容器就会先进行精确路径匹配,发现/test正好被servletA精确匹配,那么就去调用servletA,也不会去理会其他的servlet了。
2. 最长路径匹配。例子:servletA的url-pattern为/test/*,而servletB的url-pattern为/test/a/*,此时访问http://localhost/test/a时,容器会选择路径最长的servlet来匹配,也就是这里的servletB。
3. 扩展匹配,如果url最后一段包含扩展,容器将会根据扩展选择合适的servlet。例子:servletA的url-pattern:*.action
4. 如果前面三条规则都没有找到一个servlet,容器会根据url选择对应的请求资源。如果应用定义了一个default servlet,则容器会将请求丢给default servlet(什么是default servlet?后面会讲)。
根据这个规则表,就能很清楚的知道servlet的匹配过程,所以定义servlet的时候也要考虑url-pattern的写法,以免出错。
对于filter,不会像servlet那样只匹配一个servlet,因为filter的集合是一个链,所以只会有处理的顺序不同,而不会出现只选择一个filter。Filter的处理顺序和filter-mapping在web.xml中定义的顺序相同。
<servlet></servlet>
- <!--****************************servlet配置******************************-->
- <!-- Spring view分发器 对所有的请求都由business对应的类来控制转发 -->
- <servlet>
- <servlet-name>business</servlet-name>
- <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
- <init-param>
- <param-name>publishContext</param-name>
- <param-value>false</param-value>
- </init-param>
- <load-on-startup>1</load-on-startup>
- </servlet>
- <!-- 用户登出 -->
- <servlet>
- <servlet-name>LogOutServlet</servlet-name>
- <servlet-class>com.yonyou.mcloud.cas.web.servlet.LogOutServlet</servlet-class>
- <init-param>
- <param-name>serverLogoutUrl</param-name>
- <param-value>https://dev.yonyou.com:443/sso-server/logout</param-value>
- </init-param>
- <init-param>
- <param-name>serverName</param-name>
- <param-value>http://10.1.215.40:80/business/</param-value>
- </init-param>
- </servlet>
- <!--****************************servlet映射关系配置*************************-->
- <servlet-mapping>
- <servlet-name>LogOutServlet</servlet-name>
- <url-pattern>/logout</url-pattern>
- </servlet-mapping>
- <servlet-mapping>
- <servlet-name>business</servlet-name>
- <url-pattern>/</url-pattern>
- </servlet-mapping>
- Servlet介绍:
Servlet通常称为服务器端小程序,是运行在服务器端的程序,用于处理及响应客户的请求。Servlet是个特殊的java类,继承于HttpServlet。客户端通常只有GET和POST两种请求方式,Servlet为了响应则两种请求,必须重写doGet()和doPost()方法。大部分时候,Servlet对于所有的请求响应都是完全一样的,此时只需要重写service()方法即可响应客户端的所有请求。
另外HttpServlet有两个方法
- init(ServletConfig config):创建Servlet实例时,调用该方法的初始化Servlet资源。
- destroy():销毁Servlet实例时,自动调用该方法的回收资源。
通常无需重写init()和destroy()两个方法,除非需要在初始化Servlet时,完成某些资源初始化的方法,才考虑重写init()方法,如果重写了init()方法,应在重写该方法的第一行调用super.init(config),该方法将调用HttpServlet的init()方法。如果需要在销毁Servlet之前,先完成某些资源的回收,比如关闭数据库连接,才需要重写destory方法()。
Servlet的生命周期:
创建Servlet实例有两个时机:
- 客户端第一次请求某个Servlet时,系统创建该Servlet的实例,大部分Servlet都是这种Servlet。
- Web应用启动时立即创建Servlet实例,即load-on-start Servlet。
每个Servlet的运行都遵循如下生命周期:
- 创建Servlet实例。
- Web容器调用Servlet的init()方法,对Servlet进行初始化。
- Servlet初始化后,将一直存在于容器中,用于响应客户端请求,如果客户端发送GET请求,容器调用Servlet的doGet()方法处理并响应请求;如果客户端发送POST请求,容器调用Servlet的doPost()方法处理并响应请求。或者统一使用service()方法处理来响应用户请求。
- Web容器决定销毁Servlet时,先调用Servlet的destory()方法,通常在关闭Web应用时销毁Servlet实例。
- Servlet配置:
为了让Servlet能响应用户请求,还必须将Servlet配置在web应用中,配置Servlet需要修改web.xml文件。
从Servlet3.0开始,配置Servlet有两种方式:
- 在Servlet类中使用@WebServlet Annotation进行配置。
- 在web.xml文件中进行配置。
我们用web.xml文件来配置Servlet,需要配置<servlet>和<servlet-mapping>。
<servlet>用来声明一个Servlet。<icon>、<display-name>和<description>元素的用法和<filter>的用法相同。<init-param>元素与<context-param>元素具有相同的元素描述符,可以使用<init-param>子元素将初始化参数名和参数值传递给Servlet,访问Servlet配置参数通过ServletConfig对象来完成,ServletConfig提供如下方法:
java.lang.String.getInitParameter(java.lang.String name):用于获取初始化参数
ServletConfig获取配置参数的方法和ServletContext获取配置参数的方法完全一样,只是ServletConfig是取得当前Servlet的配置参数,而ServletContext是获取整个Web应用的配置参数。
- <description>、<display-name>和<icon>
- <description>:为Servlet指定一个文本描述。
- <display-name>:为Servlet提供一个简短的名字被某些工具显示。
- <icon>:为Servlet指定一个图标,在图形管理工具中表示该Servlet。
- <servlet-name>、<servlet-class>和<jsp-file>元素
<servlet>必须含有<servlet-name>和<servlet-class>,或者<servlet-name>和<jsp-file>。 描述如下:
- <servlet-name>用来定义servlet的名称,该名称在整个应用中必须是惟一的
- <servlet-class>用来指定servlet的完全限定的名称。
- <jsp-file>用来指定应用中JSP文件的完整路径。这个完整路径必须由/开始。
如果load-on-startup元素存在,而且也指定了jsp-file元素,则JSP文件会被重新编译成Servlet,同时产生的Servlet也被载入内存。<load-on-startup>的内容可以为空,或者是一个整数。这个值表示由Web容器载入内存的顺序。
举个例子:如果有两个Servlet元素都含有<load-on-startup>子元素,则<load-on-startup>子元素值较小的Servlet将先被加载。如果<load-on-startup>子元素值为空或负值,则由Web容器决定什么时候加载Servlet。如果两个Servlet的<load-on-startup>子元素值相同,则由Web容器决定先加载哪一个Servlet。
<load-on-startup>1</load-on-startup>表示启动容器时,初始化Servlet。
- <servlet-mapping>
<servlet-mapping>含有<servlet-name>和<url-pattern>
- <servlet-name>:Servlet的名字,唯一性和一致性,与<servlet>元素中声明的名字一致。
- <url-pattern>:指定相对于Servlet的URL的路径。该路径相对于web应用程序上下文的根路径。<servlet-mapping>将URL模式映射到某个Servlet,即该Servlet处理的URL。
- 加载Servlet的过程
容器的Context对象对请求路径(URL)做出处理,去掉请求URL的上下文路径后,按路径映射规则和Servlet映射路径(<url- pattern>)做匹配,如果匹配成功,则调用这个Servlet处理请求。
- DispatcherServlet在web.xml中的配置:
- <!-- Spring view分发器 对所有的请求都由business对应的类来控制转发 -->
- <servlet>
- <servlet-name>business</servlet-name>
- <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
- <init-param>
- <param-name>publishContext</param-name>
- <param-value>false</param-value>
- </init-param>
- <load-on-startup>1</load-on-startup>
- </servlet>
配置Spring MVC,指定处理请求的Servlet,有两种方式:
l 默认查找MVC配置文件的地址是:/WEB-INF/${servletName}-servlet.xml
l 可以通过配置修改MVC配置文件的位置,需要在配置DispatcherServlet时指定MVC配置文件的位置。
我们在平台项目两个工程中分别使用了不同的配置方式,介绍如下:
我们在business-client工程中按照默认方式查找MVC的配置文件,配置文件目录为: /WEB-INF/business-servlet.xml。工程目录结构如下所示:
我们在public-base-server工程中,通过第2种方式进行配置,把spring-servlet.xml放到src/main/resources/config/spring-servlet.xml,则需要在配置DispatcherServlet时指定<init-param>标签。具体代码如下:
- <servlet>
- <servlet-name>spring</servlet-name>
- <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
- <init-param>
- <param-name>publishContext</param-name>
- <param-value>false</param-value>
- </init-param>
- <init-param>
- <param-name>contextConfigLocation</param-name>
- <param-value>classpath:config/spring-servlet.xml</param-value>
- </init-param>
- <load-on-startup>1</load-on-startup>
- </servlet>
工程目录结构如下:
其中,classpath是web项目的类路径,可以理解为classes下面。因为无论这些配置文件放在哪,编译之后如果没有特殊情况的话都直接在classes下面。jar包的话虽然放在lib文件夹里,但实际上那些类可以直接引用,比如:com.test.ABC,仿佛也在classes下面一样。
在我们的工程里,经过验证,maven工程这两个
路径经过编译后生成的文件都位于classes目录下,即这两个路径相当于类路径,在下面创建config文件夹(folder),然后创建自定义的xml配置文件即可。
classpath和classpath*区别:
同名资源存在时,classpath只从第一个符合条件的classpath中加载资源,而classpath*会从所有的classpath中加载符合条件的资源。classpath*,需要遍历所有的classpath,效率肯定比不上classpath,因此在项目设计的初期就尽量规划好资源文件所在的路径,避免使用classpath*来加载。
- ContextLoaderListener和DispatcherServlet初始化上下文关系和区别:
从上图可以看出,ContextLoaderListener初始化的上下文加载的Bean是对于整个应用程序共享的,一般如:DAO层、Service层Bean;DispatcherServlet初始化的上下文加载的Bean是只对Spring MVC有效的Bean,如:Controller、HandlerMapping、HandlerAdapter等,该初始化上下文只加载Web相关组件。
注意:用户可以配置多个DispatcherServlet来分别处理不同的url请求,每个DispatcherServlet上下文都对应一个自己的子Spring容器,他们都拥有相同的父Spring容器(业务层,持久(dao)bean所在的容器)。