servlet(1)
一,
二,
Servlet 执行以下主要任务:
- 读取客户端(浏览器)发送的显式的数据。这包括网页上的 HTML 表单,或者也可以是来自 applet 或自定义的 HTTP 客户端程序的表单。
- 读取客户端(浏览器)发送的隐式的 HTTP 请求数据。这包括 cookies、媒体类型和浏览器能理解的压缩格式等等。
- 处理数据并生成结果。这个过程可能需要访问数据库,执行 RMI 或 CORBA 调用,调用 Web 服务,或者直接计算得出对应的响应。
- 发送显式的数据(即文档)到客户端(浏览器)。该文档的格式可以是多种多样的,包括文本文件(HTML 或 XML)、二进制文件(GIF 图像)、Excel 等。
- 发送隐式的 HTTP 响应到客户端(浏览器)。这包括告诉浏览器或其他客户端被返回的文档类型(例如 HTML),设置 cookies 和缓存参数,以及其他类似的任务。
三,ecplice里面的web.xml中的servlet的配置
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1"> <display-name>Les32WebTest</display-name> <welcome-file-list> <welcome-file>index.html</welcome-file> </welcome-file-list> <!-- 在向servlet或JSP页面制定初始化参数或定制URL时, 必须首先命名servlet或JSP页面。Servlet元素就是用来完成此项任务的。 --> <servlet> <servlet-name>userLogin</servlet-name> <servlet-class>com.mec.web_test.action.UserLoginAction</servlet-class> </servlet> <!-- 服务器一般为servlet提供一个缺省的URL:http://host/webAppPrefix/servlet/ServletName. 但是,常常会更改这个URL,以便servlet可以访问初始化参数或更容易地处理相对URL。在更改缺省URL时,使用servlet-mapping元素。 --> <servlet-mapping> <servlet-name>userLogin</servlet-name> <url-pattern>/userLogin</url-pattern> </servlet-mapping> </web-app
元素说明:
<servlet></servlet> 用来声明一个servlet的数据,主要有以下子元素: <servlet-name></servlet-name> 指定servlet的名称 <servlet-class></servlet-class> 指定servlet的类名称 <jsp-file></jsp-file> 指定web站台中的某个JSP网页的完整路径 <init-param></init-param> 用来定义参数,可有多个init-param。在servlet类中通过getInitParamenter(String name)方法访问初始化参数 <load-on-startup></load-on-startup>指定当Web应用启动时,装载Servlet的次序。 当值为正数或零时:Servlet容器先加载数值小的servlet,再依次加载其他数值大的servlet. 当值为负或未定义:Servlet容器将在Web客户首次访问这个servlet时加载它 <servlet-mapping></servlet-mapping> 用来定义servlet所对应的URL,包含两个子元素 <servlet-name></servlet-name> 指定servlet的名称 <url-pattern></url-pattern> 指定servlet所对应的URL
url-pattern配置
我们在创建servlet后,如果想要这个servlet可以被我们访问到,必须在web.xml文件中对其进行配置。
在其中有一个<url-pattern>这个标签是用于确定我们访问一个servlet的路径,接下来,我们详细介绍一下关于这个标签的配置
<url-pattern>它是用于确定我们访问一个servlet的路径.
问题:一个servlet是否可以被不同的路径映射?(多个不同配置是否可以映射同一个servlet)
可以
四,Servlet的生命周期
Servlet 生命周期可被定义为从创建直到毁灭的整个过程。以下是 Servlet 遵循的过程:
- Servlet 通过调用 init () 方法进行初始化。
- Servlet 调用 service() 方法来处理客户端的请求。
- Servlet 通过调用 destroy() 方法终止(结束)。
- 最后,Servlet 是由 JVM 的垃圾回收器进行垃圾回收的。
init():创建实例后进行初始化,针对同一个Servlet,Servlet容器会在第一次收到HTTP请求时建立一个Servlet实例,然后启动一个线程,第二次收到http请求后,Servlet容器无需创建相同Servlet ,仅开启第二个线程来处理请求。
----->多线程的方式有效提高执行效率,降低服务器负担。
service():响应客户端发出的请求。
destroy():如果不再有需要请求的对象,则释放Servlet对象;
五,线程安全问题(转自:https://www.cnblogs.com/xdp-gacl/p/3760336.html)
当多个客户端并发访问同一个Servlet时,web服务器会为每一个客户端的访问请求创建一个线程,并在这个线程上调用Servlet的service方法,因此service方法内如果访问了同一个资源的话,就有可能引发线程安全问题。例如下面的代码:
不存在线程安全问题的代码:
1 package gacl.servlet.study;
2
3 import java.io.IOException;
4
5 import javax.servlet.ServletException;
6 import javax.servlet.http.HttpServlet;
7 import javax.servlet.http.HttpServletRequest;
8 import javax.servlet.http.HttpServletResponse;
9
10 public class ServletDemo3 extends HttpServlet {
11
12
13 public void doGet(HttpServletRequest request, HttpServletResponse response)
14 throws ServletException, IOException {
15
16 /**
17 * 当多线程并发访问这个方法里面的代码时,会存在线程安全问题吗
18 * i变量被多个线程并发访问,但是没有线程安全问题,因为i是doGet方法里面的局部变量,
19 * 当有多个线程并发访问doGet方法时,每一个线程里面都有自己的i变量,
20 * 各个线程操作的都是自己的i变量,所以不存在线程安全问题
21 * 多线程并发访问某一个方法的时候,如果在方法内部定义了一些资源(变量,集合等)
22 * 那么每一个线程都有这些东西,所以就不存在线程安全问题了
23 */
24 int i=1;
25 i++;
26 response.getWriter().write(i);
27 }
28
29 public void doPost(HttpServletRequest request, HttpServletResponse response)
30 throws ServletException, IOException {
31 doGet(request, response);
32 }
33
34 }
存在线程安全问题的代码:
1 package gacl.servlet.study;
2
3 import java.io.IOException;
4
5 import javax.servlet.ServletException;
6 import javax.servlet.http.HttpServlet;
7 import javax.servlet.http.HttpServletRequest;
8 import javax.servlet.http.HttpServletResponse;
9
10 public class ServletDemo3 extends HttpServlet {
11
12 int i=1;
13 public void doGet(HttpServletRequest request, HttpServletResponse response)
14 throws ServletException, IOException {
15
16 i++;
17 try {
18 Thread.sleep(1000*4);
19 } catch (InterruptedException e) {
20 e.printStackTrace();
21 }
22 response.getWriter().write(i+"");
23 }
24
25 public void doPost(HttpServletRequest request, HttpServletResponse response)
26 throws ServletException, IOException {
27 doGet(request, response);
28 }
29
30 }
把i定义成全局变量,当多个线程并发访问变量i时,就会存在线程安全问题了,如下图所示:同时开启两个浏览器模拟并发访问同一个Servlet,本来正常来说,第一个浏览器应该看到2,而第二个浏览器应该看到3的,结果两个浏览器都看到了3,这就不正常。
线程安全问题只存在多个线程并发操作同一个资源的情况下,所以在编写Servlet的时候,如果并发访问某一个资源(变量,集合等),就会存在线程安全问题,那么该如何解决这个问题呢?
先看看下面的代码:
1 package gacl.servlet.study;
2
3 import java.io.IOException;
4
5 import javax.servlet.ServletException;
6 import javax.servlet.http.HttpServlet;
7 import javax.servlet.http.HttpServletRequest;
8 import javax.servlet.http.HttpServletResponse;
9
10
11 public class ServletDemo3 extends HttpServlet {
12
13 int i=1;
14 public void doGet(HttpServletRequest request, HttpServletResponse response)
15 throws ServletException, IOException {
16 /**
17 * 加了synchronized后,并发访问i时就不存在线程安全问题了,
18 * 为什么加了synchronized后就没有线程安全问题了呢?
19 * 假如现在有一个线程访问Servlet对象,那么它就先拿到了Servlet对象的那把锁
20 * 等到它执行完之后才会把锁还给Servlet对象,由于是它先拿到了Servlet对象的那把锁,
21 * 所以当有别的线程来访问这个Servlet对象时,由于锁已经被之前的线程拿走了,后面的线程只能排队等候了
22 *
23 */
24 synchronized (this) {//在java中,每一个对象都有一把锁,这里的this指的就是Servlet对象
25 i++;
26 try {
27 Thread.sleep(1000*4);
28 } catch (InterruptedException e) {
29 e.printStackTrace();
30 }
31 response.getWriter().write(i+"");
32 }
33
34 }
35
36 public void doPost(HttpServletRequest request, HttpServletResponse response)
37 throws ServletException, IOException {
38 doGet(request, response);
39 }
40
41 }
现在这种做法是给Servlet对象加了一把锁,保证任何时候都只有一个线程在访问该Servlet对象里面的资源,这样就不存在线程安全问题了,如下图所示:
这种做法虽然解决了线程安全问题,但是编写Servlet却万万不能用这种方式处理线程安全问题,假如有9999个人同时访问这个Servlet,那么这9999个人必须按先后顺序排队轮流访问。