一.Servlet简介
Servlet是serverapplet的简写,意为服务器端程序。用来处理客户端浏览器发来的请求,并相应给浏览器动态资源
Servlet是JavaWeb三大组件(Servlet,Filter,Listener)
1.1 Servlet的功能
Servlet用来处理动态资源,其根本任务是,处理请求,生成响应。
1.2 Servlet的工作方式
Servlet是一个供其他Java程序(Servlet引擎)调用的Java类,它不能独立运行,它的运行完全由Servlet引擎(即服务器,如:Tomcat)来控制和调度。
1.3 Servlet的生命周期
如果客户端的多次发出Servlet请求,通常情况下,服务器只会创建一个Servlet实例对象,也就是说Servlet实例对象一旦创建,它就会驻留在内存中,为后续的其它请求服务,直至web容器退出,Servlet实例对象才会销毁。
Servlet的生命周期方法的执行:
1)第一次调用Servlet时,将执行初始化方法,init(ServletConfig),创建Serlvet的实例,以单实例的形式创建(Struts2的动作类则是多实例的)。
2)每一次调用都执行service(ServletResquest , ServletResponse)方法。
3)服务器关闭(或项目移除),调用destroy( )方法销毁Servlet的实例。
二.Servlet的基本使用
2.1 Servlet的创建
方式一:实现servlet接口.
方式二:继承javax.servlet.GenericServlet
方式三:继承javax.servlet.http.HttpServlet
2.2 Servlet的生命周期方法
生命周期方法指的是调用之后必须要执行的方法。对于servlet来讲,有3个生命周期方法。
声明周期 | 方法 | 功能 |
---|---|---|
出生 | init() | 在构造方法调用之后调用 |
使命 | service() | 当请求发来时,处理请求使用 |
销毁 | destory() | 当服务器关闭时,会销毁servlet,在销毁之前调用该方法释放资源 |
2.3 Servlet中的其他方法
Servlet对象一共有5个方法,除了3个和生命周期有关的方法外,还有2个不常用的方法:
getServletInfo()方法,获得servlet的信息(版本,作者,版权..),基本不用
getServletConfig()方法,返回servletConfig对象
2.4 Servlet技术中各对象的作用范围
对象 | 作用范围 |
---|---|
servlet | 项目启动期间一个servlet只有一个servlet实例 |
request | 项目启动期间,request对象的数量,要看当前有多少个请求正在处理 |
response | 项目启动期间,reponse对象的数量,要看当前有多少个请求正在处理 |
servletConfig | 一个servlet实例对应一个servletConfig对象 |
servletContext | 整个项目中只有一个servletContext实例 |
三.ServletConfig接口
功能:封装了servlet在web.xml中的配置,ServletConfig的API:
1.getServletName 获得配置文件中< servlet-name >元素的内容
2.getInitParameter 根据< init-param >中的 < param-name > 获得 < /param-value >
<init-param><param-name>name</param-name><param-value>tom</param-value>
</init-param>
getInitParameterNames返回所有< param-name >
getServletContext返回ServletContext对象
四.ServletContext接口
服务器启动的时候会为每一个WEB应用创建一个单独的ServletContext对象,ServletContext对象中的数据可以在整个WEB应用中获取(需要使用ServletContext对象的获取值的方法)
4.1 ServletContext封装了web.xml中的配置
<context-param><param-name>name</param-name><param-value>jerry</param-value>
</context-param>
<context-param><param-name>password</param-name><param-value>1234</param-value>
</context-param>
可以使用ServletContext中的方法获得web.xml中的配置内容:
getInitParameterNames(); 获得所有键
getInitParameter(key); 根据键获得对应的值
4.2 Servlet技术中三大域对象(application域,session域,request域)之一
“域”即存储数据的区域,Request,Session和ServletContext对象都有存取数据的功能,所以又被称为“域”。ServletContext对应着application域。application域中的数据可以再整个项目范围内存取。(注:JSP中为四大域,多了一个页面域—-page域)
关于域的操作:
操作 | 对应方法 |
---|---|
放入键值对 | setAttribute(key,value) |
通过键取值 | getAttribute(key) |
通过键删除 | removeAttribute(key) |
遍历所有键 | getAttributeNames() |
ServletContext的常用功能:
ServletContext中的数据可以在整个项目中存取,所以可以用来做Servlet对象之间的通信
4.3 获得项目中资源
所有servletContext中关于路径的获得,相对路径都是相对的 WebRoot(项目根)下
getRealPath 通过相对路径获得绝对路径
getResourceAsStream 根据相对路径获得指定资源流
五.实现了Servlet接口的类
在Servlet的API中有2个实现了Servlet接口的类,对Servlet进行了封装和优化,方便开发人员的使用
5.1 GenericServlet
实现了ServletConfig接口,方便调用(在init方法中做的优化)
GenericServlet有两个init方法,使用时调用空参的init方法,含参的init方法做了优化
public void init(ServletConfig config) throws ServletException {this.config = config;this.init();}
public void init() throws ServletException {}
GenericServlet源码
public abstract class GenericServlet implements Servlet, ServletConfig, Serializable {private static final long serialVersionUID = 1L;private transient ServletConfig config;public GenericServlet() {}public void destroy() {}public String getInitParameter(String name) {return this.getServletConfig().getInitParameter(name);}public ServletConfig getServletConfig() {return this.config;}public ServletContext getServletContext() {return this.getServletConfig().getServletContext();}public void init(ServletConfig config) throws ServletException {this.config = config;this.init();}public abstract void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;.......
}
5.2 HTTPServlet
1.HTTPServlet是在GenericServelt的基础上做的进一步的优化,是一个抽象类。
2.因为我们WEB项目都是基于HTTP协议,所以Service方法中的request,response对象都是基于HTTP协议的。也就是HttpServletReueqst和HttpServletResponse。它帮我们进行了强转.
3.根据不同的请求采取不同的方法进行处理。有专门处理GET和POST请求的方法。
HTTPServlet部分源码
public abstract class HttpServlet extends GenericServlet {private static final long serialVersionUID = 1L;private static final String METHOD_GET = "GET";private static final String METHOD_POST = "POST";private static final String METHOD_PUT = "PUT";private static final String HEADER_IFMODSINCE = "If-Modified-Since";private static final String HEADER_LASTMOD = "Last-Modified";private static final String LSTRING_FILE = "javax.servlet.http.LocalStrings";private static ResourceBundle lStrings = ResourceBundle.getBundle("javax.servlet.http.LocalStrings");public HttpServlet() {}protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {String protocol = req.getProtocol();String msg = lStrings.getString("http.method_get_not_supported");if (protocol.endsWith("1.1")) {resp.sendError(405, msg);} else {resp.sendError(400, msg);}}protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {String protocol = req.getProtocol();String msg = lStrings.getString("http.method_post_not_supported");if (protocol.endsWith("1.1")) {resp.sendError(405, msg);} else {resp.sendError(400, msg);}}
六.Servlet线程安全问题
因为在servlet运行期间只有一个servlet实例存在.可能会同时处理多个请求.那么我们在servlet中声明成员变量来存储用户数据是有线程安全问题的.
解决方法:
6.1 实现SigleThreadModel (不推荐)
SigleThreadModel本质上是一个标记接口,类似Serializable,标记接口的主要作用就是给某个对象打上一个标志,告诉JVM,这个对象可以做什么,比如实现了”Serializable”接口的类的对象就可以被序列化,而实现” SigleThreadModel ” 接口的类的对象则可以创建多个实例,所以这种方法解决线程安全的原理是创建多个Servlet实例,每个线程分配一个Servlet的实例对象,会使占用很多内存,所以这种解决办法不好,很少用
6.2 使用局部变量保存用户数据(推荐)
若service()方法里有一个局部变量n,当有多个线程并发访问service()方法时,Servlet不会创建新的实例,而是每次访问都调用一次service()方法,所以每一个线程里面都有自己的n变量,各个线程操作的都是自己的n变量,所以不存在线程安全问题。
多线程并发访问某一个方法的时候,如果在方法内部定义了一些资源(变量,集合等),那么每一个线程都有这些东西,所以就不存在线程安全问题了。
七.Servlet随着服务器的启动而创建
Servlet实例默认是在第一次访问时创建,在web.xml文件中可以设置更改Servlet实例的创建时机,改为服务器启动后直接创建Servlet的实例
使用< load-on-startup >配置来实现.
例如:
<servlet><servlet-name>AServlet</servlet-name><servlet-class>cn.itcast.servlet.hello.AServlet</servlet-class><load-on-startup>3</load-on-startup>
</servlet>
< load-on-startup >标签内填写一个整数,每个Servlet都可以独立设置一个启动常数,这个常数数值越小代表优先级越高,创建时优先创建。如果优先级一样,启动顺序按照配置顺序。
八.关于Servlet路径配置问题详解
web.xml文件中的< url-pattern > 标签配置目标资源路径
完全路径匹配:
特点:路径完整,指定Servlet对象
XML中的配置 | 访问时使用路径 |
---|---|
/AServlet | http://localhost:8080/Day07-servlet/AServlet |
/ABC/AServlet | http://localhost:8080/Day07-servlet/ABC/AServlet |
目录匹配:
特点:以 * 结束,代表匹配一定范围内的资源
XML中的配置 | 访问时使用路径 |
---|---|
/ABC/ABC/* | http://localhost:8080/Day07-servlet/ABC/ABC/ServletName |
/* | http://localhost:8080/Day07-servlet/ServletName |
/ | /* (即可以是项目下的任意资源) |
后缀名匹配:
特点:匹配后缀名
后缀名 | 使用环境 |
---|---|
*.do | struts |
*.action | struts2 |
*.html | 自定义匹配(很少用) |
注意:
1.关于路径,配置的路径匹配范围越大优先级越低
优先级排序:完全路径匹配>目录匹配>后缀名匹配
2.两种匹配模式不能混用.例如错误的例子: /*.do
缺省Servlet
缺省Servlet的映射路径为“/”,若一个请求在web.xml文件中匹配不到Servlet,那么这个请求就会交给缺省Servlet处理
在Tomcat默认的配置文件/conf/web.xml中就配置有缺省Servlet
当访问Tomcat服务器中的静态文件时,底层就是调用这个默认的缺省Servelt进行处理的
<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>