什么是Servet?
Servlet (Server Applet),从字面上看,就是Java服务器小程序的意思。它确实就像字面意思一样,是在服务中用于处理网络请求的小程序。
在Web的世界中,客户端会提交各种请求到服务端,服务端如何处理客户端的请求呢?
常规的Java编程方法,好像很难完成这项任务,但是没有事情是难倒程序猿的。
于是,他们设计了Servlet规范,用来处理网络的各种请求。
具体网络的请求呢,无非就是get、post等等,这在HTTP规范系列里面有讲到。
Servlet规范也没有想象中的那么高大上,其实它就是一个Java接口,里面一共就定义了五个方法,如图:
其中:
- init() 规定了Servet如何初始化。
- getServletConfig() 获取Sevvlet的配置。
- service(ServletRequest, ServletResponse) 接收到请求怎么处理。
- getServletInfo() 提供有关servlet的信息,如作者、版本、版权等。
- destroy() 销毁Servlet。
Servlet的运行
任何合理实现了Servlet接口的类都具有处理HTTP请求的能力,但是就像java类的运行需要在JVM环境中一样,Servlet的运行也要环境,这里称为容器。
即Servlet的运行需要特定的容器,该容器负责实现对端口的监听,将请求内容解析,然后实例Servlet对象,给Servlet提供运行环境,然后将Servlet的处理结果发给客户端。
Tomcat就是一个开源的Servlet的容器,它也是一个Web服务器。
HttpServlet就是已经实现好的一个Servlet类,他对一些方法进行了详细的补充,我们通过doGet()、doPost()等方法很方便地实现处理HTTP请求功能。
它的一些源码如下:
1. service()实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
| protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String method = req.getMethod(); long errMsg; if(method.equals("GET")) { errMsg = this.getLastModified(req); if(errMsg == -1L) {
this.doGet(req, resp); } else { long ifModifiedSince; try { ifModifiedSince = req.getDateHeader("If-Modified-Since"); } catch (IllegalArgumentException var9) { ifModifiedSince = -1L; }
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 { 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); }
|
2. doGet()实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| 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); } }
|
2. doPost()实现:
1 2 3 4 5 6 7 8 9 10
| 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开发的,如大名鼎鼎的[Spring](https://spring.io/)**
# 如何实现?
1. 首先我写一个Servlet类,作用是得到get()请求,然后返回“Hello Servlet!”与打印当前时间。
1 2 3 4 5 6 7 8 9 10 11 12 13
| public class HelloServlet extends HttpServlet{ public void doGet(HttpServletRequest request, HttpServletResponse response){ try { response.getWriter().println("<h1>Hello Servlet!</h1>"); response.getWriter().println(new Date().toLocaleString()); } catch (IOException e) { e.printStackTrace(); } } }
|
2. 然后配置web.xml文件,建立URL与Servlet处理类 **HelloServlet** 之间的关联关系。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <?xml version="1.0" encoding="UTF-8"?> <web-app>
<servlet> <servlet-name>HelloServlet</servlet-name> <servlet-class>HelloServlet</servlet-class> </servlet>
<servlet-mapping> <servlet-name>HelloServlet</servlet-name> <url-pattern>/hello</url-pattern> </servlet-mapping>
</web-app>
|
其中**servlet-name**是类名简写,**servlet-class** 是类名。
**servlet-mapping**实现URL与类的映射,这里实现了访问路径 http://127.0.0.1//hello 时调用HelloServlet类的相应方法。
http://127.0.0.1 是一个监听地址,需要服务器实现监听,此时就需要Tomcat。
然后修改Tomcat的conf/server.xml的路径配置。
1
| <Context path="/" docBase="F:\\eclipse-workspace\\j2ee\\web" debug="0" reloadable="false" />
|
# Servlet的特点
**1. Servlet是单实例多线程。**
>1) 当Web服务器启动或者用户请求抵达服务器时,Servlet被实例化且只存在一个实例。
>2) 当请求抵达时,Servlet容器(如Tomcat)会调度线程 (Dispathchaer Thread)调度它管理下的线程池中等待执行的线程 (Worker Thread) 给请求者。
>3) 线程执行Servlet中的sercive方法。
>4) 请求结束,该线程放回线程池,等待被调用。
**2. Servet使用标准API,可被更多Web服务器调用。**
## 线程安全
由于Servlet是单实例多线程,当多个线程的用户同时访问共享资源时,就会出现线程安全的问题。
**解决方法:**
首先,定义在 doPost() 和 doGet()里的方法由于是**局部变量**,再每个用户调用实例方法时都会初始化,所以**不存在线程安全**。(一些属性尽量定义在实例的局部方法中)
实在需要共享的资源,只需加synchronized同步机制,在共享资源被某一线程占用后,该线程就拥有锁,其它线程只有等待该线程执行完毕才能使用该资源(抢占锁后一一执行)。
# Servlet的常用方法
在HttpServlet中sercive方法中,参数列表接受两个对象,一个是HttpServletResponse对象,一个是HttpServletRequest对象。
## HttpServletRequest常用方法
**常见方法**
方法名 | 作用
:-: | :-:
request.getRequestURL() | 浏览器发出请求时的完整URL,包括协议 主机名 端口(如果有)"
request.getRequestURI() | 浏览器发出请求的资源名部分,去掉了协议和主机名"
request.getQueryString() | 请求行中的参数部分,只能显示以get方式发出的参数,post方式的看不到
request.getRemoteAddr() | 浏览器所处于的客户机的IP地址
request.getRemoteHost() | 浏览器所处于的客户机的主机名
request.getRemotePort() | 浏览器所处于的客户机使用的网络端口
request.getLocalAddr() | 服务器的IP地址
request.getLocalName() | 服务器的主机名
request.getMethod() | 得到客户机请求方式一般是GET或者POST
**获取参数**
方法名 | 作用
:-: | :-:
request.getParameter() | 是常见的方法,用于获取单值的参数
request.getParameterValues() | 用于获取具有多值的参数,比如注册时候提交的 "hobits",可以是多选的。
request.getParameterMap() | 用于遍历所有的参数,并返回Map类型。
**获取头信息**
方法名 | 作用
:-: | :-:
request.getHeader() | 获取浏览器传递过来的头信息。
比如getHeader("user-agent") | 可以获取浏览器的基本资料,这样就能判断是firefox、IE、chrome、或者是safari浏览器
request.getHeaderNames() | 获取浏览器所有的头信息名称,根据头信息名称就能遍历出所有的头信息
头信息含义:
>host: 主机地址
>user-agent: 浏览器基本资料
>accept: 表示浏览器接受的数据类型
>accept-language: 表示浏览器接受的语言
>accept-encoding: 表示浏览器接受的压缩方式,是压缩方式,并非编码
>connection: 是否保持连接
>cache-control: 缓存时限
## HttpServletResponse常用方法
1. 设置相应内容
PrintWriter pw= response.getWriter();
通过`response.getWriter();` 获取一个PrintWriter 对象
可以使用`println()`,`append()`,`write()`,`format()`等等方法设置返回给浏览器的html内容。
设置相应内容
response.setContentType("text/html");
设置相应编码
response.setContentType("text/html; charset=UTF-8");
不仅发送到浏览器的内容会使用UTF-8编码,而且还通知浏览器使用UTF-8编码方式进行显示。所以总能正常显示中文
response.setCharacterEncoding("UTF-8");
仅仅是发送的浏览器的内容是UTF-8编码的,至于浏览器是用哪种编码方式显示不管。 所以当浏览器的显示编码方式不是UTF-8的时候,就会看到乱码,需要手动再进行一次设置。
301或者302客户端跳转
客户端有两种跳转:
302 表示临时跳转
response.sendRedirect("fail.html");
301 表示永久性跳转
response.setStatus(301);
response.setHeader("Location", "fail.html");
设置不使用缓存
1 2 3
| response.setDateHeader("Expires",0 ); response.setHeader("Cache-Control","no-cache"); response.setHeader("pragma","no-cache");
|
参考: http://how2j.cn/k/servlet/servlet-upload/587.html#nowhere