Servlet学习总结(转)
什么是Servlet
Servlet(Server Applet)是Java Servlet的简称,称为小服务程序或服务连接器,用Java编写的服务器端程序,主要功能在于交互式地浏览和修改数据,生成动态Web内容。
狭义的Servlet是指Java语言实现的一个接口,广义的Servlet是指任何实现了这个Servlet接口的类,一般情况下,人们将Servlet理解为后者。Servlet运行于支持Java的应用服务器中。从原理上讲,Servlet可以响应任何类型的请求,但绝大多数情况下Servlet只用来扩展基于HTTP协议的Web服务器。
Servlet作用
Servlet是服务器端的一个JAVA程序,它的作用是接受客户端发来的数据请求,并处理请求将数据返回给WEB服务器。一般每个功能对应一个独立的Servlet。
Servlet生命周期
通常来说,Servlet一般与WEB服务器结合使用(这里以tomcat为例),客户端(浏览器)向Servlet发出请求之后
1. 若该请求是第一次发出,则服务器将把该Servlet加载到内存,读取配置文件初始化线程池;
2. 调用init()方法对该Servlet进行初始化
3. Servlet容器通过调度线程(Dispatchaer Thread) 调度它管理下的线程池中等待执行的线程(Worker Thread)给请求者,创建ServletRequest对象(服务请求对象)和ServletResponse对象(服务响应对象)
注:Servlet为单例多线程,即只有一个Servlet,可有多个请求!此时的ServletRequest对象包含请求头和请求体,取值均为浏览器传值,而 ServletResponse对象的响应头和相应体均为空!
4.将ServletRequest对象和ServletResponse对象作为参数传给service()方法
5.service()方法开始处理请求,并将响应信息写入ServletResponse对象
6.Servlet将ServletResponse对象返回给tomcat
7.tomcat解析ServletResponse对象,对浏览器做出响应
Servlet实现方式
三种
1. 直接实现Servlet接口,重载它的方法
2. 继承GenericServlet类
3. 继承HttpServlet类(通常采用第三种方式,因为现在主流使用的是HTTP协议)
HttpServlet类中有service()方法,我们在继承时无需覆盖它的service()方法,而是重写它的doGet()和doPost()方法
HttpServlet类源码
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //获取http request的method参数,其实就是html的form标签 //中method属性对应的字符串 String method = req.getMethod(); long errMsg; //判断请求方式 if(method.equals("GET")) { //获取最后被修改时间 errMsg = this.getLastModified(req); if(errMsg == -1L) { /**如果servlet不支持http request header的if-modified-since属性 * 则继续处理 **/ this.doGet(req, resp); } else { //如果支持这个属性 long ifModifiedSince; try { ifModifiedSince = req.getDateHeader("If-Modified-Since"); } catch (IllegalArgumentException var9) { ifModifiedSince = -1L; } /** * 如果客户端的文件最后修改时间和服务器端的文件最后修改时间一致则返回304不需要修改状态 * 这样服务器就不返回html,浏览器读取本地缓存文件,否则重新获取服务器端的对应html文件 **/ if(ifModifiedSince < errMsg / 1000L * 1000L) { this.maybeSetLastModified(resp, errMsg); this.doGet(req, resp); } else { resp.setStatus(304); } } } else if(method.equals("HEAD")) { errMsg = this.getLastModified(req); this.maybeSetLastModified(resp, errMsg); this.doHead(req, resp); } else if(method.equals("POST")) { this.doPost(req, resp); } else if(method.equals("PUT")) { this.doPut(req, resp); } else if(method.equals("DELETE")) { this.doDelete(req, resp); } else if(method.equals("OPTIONS")) { this.doOptions(req, resp); } else if(method.equals("TRACE")) { this.doTrace(req, resp); } else { //如果请求不是以上的所有请求方式,该方法就会响应501错误,也就是不支持这种请求 String errMsg1 = lStrings.getString("http.method_not_implemented"); Object[] errArgs = new Object[]{method}; errMsg1 = MessageFormat.format(errMsg1, errArgs); resp.sendError(501, errMsg1); } } public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException { HttpServletRequest request; HttpServletResponse response; try { request = (HttpServletRequest)req; response = (HttpServletResponse)res; } catch (ClassCastException var6) { throw new ServletException("non-HTTP request or response"); } this.service(request, response); }
HttpServlet中有两个Service方法,一个参数与Http相关,一个无关。在service(HttpServletRequest req, HttpServletResponse resp)方法中,Servlet通过判断浏览器请求的方法类型从而判断执行哪段代码,所以我们只需要根据不同的请求重写不同的方法即可
1. 服务器会获取当前页面的最后修改时间,若最后修改时间与服务器上页面的最后修改时间一致,则服务器返回状态码为304,即不返回html,由浏览器载入缓存页面,否则服务器向浏览器发送html
2. 请求方法不在列举方法中,则返回501
3. service(ServletRequest req, ServletResponse res)是HttpServlet的生命周期方法,在该方法中,servlet将req和res对象强转为HttpServletRequest和HttpServletResponse类型,然后调用另一个service方法
doGet()
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //获取协议 String protocol = req.getProtocol(); //获取http.method_get_not_supported的国际化字符串 String msg = lStrings.getString("http.method_get_not_supported"); if(protocol.endsWith("1.1")) { //如果是HTTP/1.1,返回405禁止访问方法错误 resp.sendError(405, msg); } else { //如果不是HTTP/1.1,返回400错误的请求错误 resp.sendError(400, msg); } }
doPost()
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非线程安全
在 Web 应用程序中,一个 Servlet 在一个时刻可能被多个用户同时访问。这时 Web 容器将为每个用户创建一个线程来执行 Servlet。如果 Servlet 不涉及共享资源的问题,不必关心多线程问题。但如果 Servlet 需要共享资源,需要保证 Servlet 是线程安全的。
解决方式
1. 用方法的局部变量保存请求中的专有数据。对方法中定义的局部变量,进入方法的每个线程都有自己的一份方法变量拷贝。任何线程都不会修改其他线程的局部变量。如果要在不同的请求之间共享数据,应该使用会话(session)来共享这类数据
2. 只用 Servlet的成员变量来存放那些不会改变的数据(无状态成员变量)。有些数据在 Servlet 生命周期中不发生任何变化,通常是在初始时确定的,这些数据可以使用成员变量保存,如数据库连接名称、其他资源的路径等
3. 可以创建只读的有状态成员变量
4. 对可能被请求修改的成员变量同步。有时数据成员变量或者环境属性可能被请求修改。当访问这些数据时应该对它们同步,以避免多个线程同时修改这些数据
Web.xml
<servlet>标签
每当我们创建一个Servlet时,就会在web.xml文件中多一个对应的servlet标签,在其中定义了Servlet的名字,对应的JAVA类,初始化变量等等。
<servlet> <servlet-name>xxx</servlet-name> <servlet-class>cn.web.servlet.AServlet</servlet-class> <init-param> <param-name>p1</param-name> <param-value>v1</param-value> </init-param> </servlet>
<servlet-mapping>标签
与<servlet>标签对应的是<servlet-mapping>标签,它通过<servlet-name>标签与Servlet绑定,并定义了用户访问的URL路径。
<servlet-mapping> <servlet-name>xxx</servlet-name> <url-pattern>/AServlet</url-pattern> </servlet-mapping>
<url-pattern>标签
1. 一个Servlet可有多个路径
2. 通配符“*”
3. 一个URL最多只能有一个通配符,通配符只能在路径开头或最后
4. 一个URL只能以“*”或“/”开头
web.xml的父文件
在tomcat的conf文件夹下有一个web.xml文件是所有工程中web.xml的父文件
在该文件中定义了一个“default”Servlet,它的<servlet-mapping>中的<url-pattern>标签内为“/”,即匹配所有路径,当一个URL匹配路径越多时,那么它的优先级就越低,所以当访问路径不存在时,就将执行“default”Servlet,返回404状态码
路径访问原理
- 用户输入访问路径
- 服务器通过路径匹配<url-pattern>
- 通过<servlet-name>得到<servlet-class>
- 通过反射调用该Servlet的service方法