Servlet运行原理解析/在web.xml中的配置
Servlet机制:
Servlet是服务器端用来处理用户请求并相应返回结果的应用程序,它的生命周期由web容器来负责维护,它的多线程体系建立在java多线程的机制之上;Servlet被设计为多线程的,当web应用程序初始化的时候,Servlet会根据web.xml中的配置文件实例化一个Servlet类,每当一个用户请求通过Tomucat获其他服务器进来直接调用已经实例化的Servlet对应的doGet/doPost来处理请求,而不会再去实例化一个Servlet,也就是多个线程在使用这个servlet,一般会由一个线程池来支持,线程池在初始化的期间就会创建一定数量的线程对象来避免频繁的创建线程、销毁线程对象,提高性能;(由线程来执行servlet的service方法,Servlet在tomcat中是以单例的模式存在的,但是在大量并发的情况下依然会出现Servlet线程安全的问题,而且很难发现。一般造成线程安全主要问题在于实例变量造成的,因此在编写servlet是尽量避免使用实例变量,在必须使用到实例变量的时候尽量使用同步锁的保护使用的实例变量)。
客户端client向服务器端发送请求,服务器(tomcat)收到请求之后会创建两个对象(HttpRequest和HttpResponse对象)并将他们的引用出给线程池中分配的线程,该线程访问Servlet处理用户请求,处理完之后,将结果返回Tomcat并将该线程返还线程池或者销毁;tomcat将结果以http响应发送回客户端
Servlet生命周期:
1、默认情况下,在第一次访问Servlet时,服务器(tomcat/jetty)会创建并调用init()方法实例化Servlet(单例),也可以修改配置文件web.xml,使得Tomcat启动时就直接创建Servlet(启动时创建Servlet,数字1代表创建的次序:<load-on-startup>1</load-on-startup>);
2、之后请求进来之后Tomcat通过线程对象访问servlet处理请求并响应结果,这个过程中实例化的servlet会被反复调用
3、当服务停机之后,调用destroy()方法销毁servlet实例,整个生命周期结束
web.xml中Servlet配置:
编译好的Servlet只能运行在web容器中,客户端不可以直接访问Servlet,所以需要在web.xml中配置一下,
<?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" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
id="TAPWebService" version="2.5">
<!-- 定义web应用的名称 -->
<display-name>boss-trace</display-name>
<!-- 声明初始化参数,这里初始化对应的xml文件 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath*:/config/spring/applicationContext.xml,
classpath*:/config/spring/applicationContext-repository.xml,
classpath*:/config/spring/applicationContext-dubbo.xml,
classpath*:/config/spring/applicationContext-hbase.xml,
classpath*:/config/spring/applicationContext-rest.xml,
classpath*:/config/spring/applicationContext-tbschedule.xml,
</param-value>
</context-param>
<!-- 设置事件监听器 ,事件监听程序在建立、修改和删除会话或servlet环境时得到通知。 Listener元素指出事件监听程序类 -->
<!-- <listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>-->
<!-- 设置过滤器,过滤器能够对目标资源的请求和响应进行截取 -->
<!--
<filter>
<filter-name>TraceFilter</filter-name>
<filter-class>com.zxq.iov.cloud.trace.filter.servlet.TraceAppFilterWeb</filter-class>
</filter>
// 一旦命名了一个过滤器,就要利用filter-mapping元素把它与一个或多个servlet或JSP页面相关联。
<filter-mapping>
<filter-name>TraceFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
-->
<!-- 命名指定servlet来处理对应URL的请求,SpringMVC中配置DispatcherServlet -->
<servlet>
<!-- 别名 -->
<servlet-name>zxq</servlet-name>
<!-- 指定servlet类 -->
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 1表示容器启动时就初始化servlet -->
<load-on-startup>1</load-on-startup>
</servlet>
<!-- 配置对应servlet 拦截url与应用中contrller的url的映射规则 -->
<servlet-mapping>
<servlet-name>zxq</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- **tomcat中defaultServlet 来拦截静态资源的请求,配置后这些拓展名结尾的不被收录为以上zxq servlet的访问请求 ,直接使用defaultServlet 来处理-->
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/swagger-ui.html</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.jpg</url-pattern>
</servlet-mapping>
</web-app>
我们先了解两个一个URL的组成:例如:http://localhost:8080/appDemo/user/users.html
该地址中包含通信协议http+服务器地址localhost+服务器端口+contextPath+servletPath;这里主要说一下contextPath和servletPath,contextPath一般就是你配置的应用名,servletPath则就是tomcat根据请求URL匹配规则处理之后映射的地址,一般会截取contextPath之后的的字符串与你在<url-pattern>定义的匹配规则做验证,验证通过则用那段字符串去做servlet的地址映射,映射到对应的servlet去处理请求;
<url-pattern>匹配规则:
- 精确匹配:在<url-pattern>中配置的项必须与url完全精确匹配
<servlet-mapping> <servlet-name>MyServlet</servlet-name> <url-pattern>/index.html</url-pattern> </servlet-mapping>
则在浏览器中输入http://localhost:8080/appDemo/index.html才会被匹配到该servlet;另外精确匹配在url后面加查询条件也可以被匹配:http://localhost:8080/appDemo/index.html?username=Tom&age=23也是可以的
- 路径匹配:以“/”字符开头,并以“/*”结尾的字符串用于路径匹配
<servlet-mapping> <servlet-name>MyServlet</servlet-name> <url-pattern>/user/*</url-pattern> </servlet-mapping>
路径以/user/开始,后面的路径可以任意。比如下面的url都会被匹配。例如:http://localhost:8080/appDemo/user/users.html;
http://localhost:8080/appDemo/user/addUser.action;http://localhost:8080/appDemo/user/updateUser.actionl -
扩展名匹配:以“*.”开头的字符串被用于扩展名匹配
<servlet-mapping> <servlet-name>MyServlet</servlet-name> <url-pattern>*.jsp</url-pattern> <url-pattern>*.action</url-pattern> </servlet-mapping>
则任何扩展名为jsp或action的url请求都会匹配,比如下面的url都会被匹配
http://localhost:8080/appDemo/user/users.jsp
http://localhost:8080/appDemo/toHome.action -
缺省匹配
<servlet-mapping> <servlet-name>MyServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
则所有请求都可以匹配
使用时需要注意的是:
-
路径匹配和扩展名匹配无法同时设置, 如<url-pattern>/user/*.action</url-pattern>是非法的,另外注意:<url-pattern>/aa/*/bb</url-pattern>是精确匹配,合法,这里的*不是通配的含义
-
"/*"和"/"含义并不相同;“/*”属于路径匹配,并且可以匹配所有request,由于路径匹配的优先级仅次于精确匹配,所以“/*”会覆盖所有的扩展名匹配,很多404错误均由此引起,所以这是一种特别恶劣的匹配模式,一般只用于filter的url-pattern;“/”是servlet中特殊的匹配模式,切该模式有且仅有一个实例,优先级最低,不会覆盖其他任何url-pattern,只是会替换servlet容器的内建default servlet ,该模式同样会匹配所有request。