0%

JavaWeb学习(三)——Servlet(一)

Servlet

是用Java编写的服务器端程序。其主要功能在于交互式地浏览和修改数据,生成动态Web内容。

狭义的Servlet是指Java语言实现的一个接口,广义的Servlet是指任何实现了这个Servlet接口的类。

Servlet运行于支持Java的应用服务器中。从实现上讲,Servlet可以响应任何类型的请求,但绝大多数情况下Servlet只用来扩展基于HTTP协议的Web服务器。

Servlet带给我们最大的作用就是能够处理浏览器带来HTTP请求,并返回一个响应给浏览器,从而实现浏览器和服务器的交互

Servlet的工作模式

  • 客户端发送请求至web服务器
  • web服务器收到客户端的Servlet访问请求后:
    1. web服务器检查是否已经装载并创建了该Servlet的实例对象。如果是,则直接执行第④步,否则,执行第②步。
    2. 装载并创建该Servlet的一个实例对象。
    3. 调用Servlet实例对象的init()方法。
    4. 创建一个用于封装HTTP请求消息的HttpServletRequest对象和一个代表HTTP响应消息的HttpServletResponse对象,然后调用Servlet的service()方法并将请求和响应对象作为参数传递进去。
    5. WEB应用程序被停止或重新启动之前,Servlet引擎将卸载Servlet,并在卸载之前调用Servlet的destroy()方法。
  • 服务器将响应返回客户端

Servlet的生命周期

  1. 加载Servlet。当Tomcat第一次访问Servlet的时候,Tomcat会负责创建Servlet的实例
  2. 初始化。当Servlet被实例化后,Tomcat会调用init()方法初始化这个对象
  3. 处理服务。当浏览器访问Servlet的时候,Servlet 会调用service()方法处理请求
  4. 销毁。当Tomcat关闭时或者检测到Servlet要从Tomcat删除的时候会自动调用destroy()方法,让该实例释放掉所占的资源。一个Servlet如果长时间不被使用的话,也会被Tomcat自动销毁
  5. 卸载。当Servlet调用完destroy()方法后,等待垃圾回收。如果有需要再次使用这个Servlet,会重新调用init()方法进行初始化操作
  • 简单总结:只要访问Servlet,service()就会被调用。init()只有第一次访问Servlet的时候才会被调用。destroy()只有在Tomcat关闭的时候才会被调用。

Servlet的特点

映射关系

  • 一个已经注册的Servlet可以被多次映射同一个Servlet可以被映射到多个URL上。

    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
    <!--
    当浏览器访问http://localhost:8080/Demo1时:
    1、发现Tomcat中的web.xml有配置这个映射路径 <url-pattern>/Demo1</url-pattern>
    2、查看映射路径的名字 <servlet-name>Demo1</servlet-name>
    3、通过映射路径的名字找到配置servlet的名字 <servlet-name>Demo1</servlet-name>
    4、通过servlet名找到servlet编译后class文件存放的位置
    5、执行MyServlet <servlet-class>
    -->
    <servlet>
    <!--为Demo1配一个名字,一般都会和类名一致-->
    <servlet-name>Demo1</servlet-name>
    <!--类的存放位置在哪里【有包名的话需要添加上包名】-->
    <servlet-class>zhongfucheng.web.Demo1</servlet-class>
    </servlet>
    <!--为Demo1配置映射路径-->
    <servlet-mapping>
    <servlet-name>Demo1</servlet-name>
    <!--外界访问Demo1的路径-->
    <url-pattern>/Demo1</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
    <servlet-name>Demo1</servlet-name>
    <url-pattern>/ouzicheng</url-pattern>
    </servlet-mapping>


    无论我访问的是http://localhost:8080/Demo1还是http://localhost:8080/ouzicheng。我访问的都是Demo1。
  • Servlet映射的URL可以使用通配符

  • 在servlet3.0以后,web.xml中对Servlet配置,同样可以在@WebServlet注解中配置.
    下面是@WebServlet的属性列表:

    属性名 类型 描述
    name String 指定Servlet 的 name 属性,等价于 <servlet-name>。如果没有显式指定,则该 Servlet 的取值即为类的全限定名。
    value String[] 该属性等价于 urlPatterns 属性。两个属性不能同时使用。
    urlPatterns String[] 指定一组 Servlet 的 URL 匹配模式。等价于<url-pattern>标签。
    loadOnStartup int 指定 Servlet 的加载顺序,等价于 <load-on-startup>标签。
    initParams WebInitParam[] 指定一组 Servlet 初始化参数,等价于<init-param>标签。
    asyncSupported boolean 声明 Servlet 是否支持异步操作模式,等价于<async-supported> 标签。
    description String 该 Servlet 的描述信息,等价于 <description>标签。
    displayName String 该 Servlet 的显示名,通常配合工具使用,等价于 <display-name>标签。

Servlet————单例模式

  • 浏览器多次对Servlet的请求,一般情况下,服务器只创建一个Servlet对象,也就是说,Servlet对象一旦创建了,就会驻留在内存中,为后续的请求做服务,直到服务器关闭
  • 在Servlet的整个生命周期内,Servlet的init方法只被调用一次。而对一个Servlet的每次访问请求都导致Servlet引擎调用一次servlet的service方法。
  • 对于每次访问请求,Servlet引擎都会创建一个新的HttpServletRequest请求对象和一个新的HttpServletResponse响应对象,然后将这两个对象作为参数传递给它调用的Servlet的service()方法service方法再根据请求方式分别调用doXXX方法

通配符

  • 通配符有两种格式:

    1. *.扩展名
    2. 正斜杠(/)开头并以“/*”结尾。
    3. *.扩展名的优先级最低

线程安全问题

当多个用户访问Servlet的时候,服务器会为每个用户创建一个线程当多个用户并发访问Servlet共享资源的时候就会出现线程安全问题

原则:

  1. 如果一个变量需要多个用户共享,则应当在访问该变量的时候,加同步机制synchronized (对象){}
  2. 如果一个变量不需要共享,则直接在 doGet() 或者 doPost()定义.这样不会存在线程安全问题

web资源的访问都需要访问Servlet

缺省Servlet用于处理所有其他Servlet都不处理的访问请求。

web站点信息配置

  • web.xml文件支持对整个站点进行配置参数信息所有Servlet都可以取到该参数信息
1
2
3
4
<context-param>
<param-name>name</param-name>
<param-value>XXX</param-value>
</context-param>

web工程中URL地址的写法

如果”/“是给服务器用的,则代表当前的web工程,如果”/“是给浏览器用的,则代表webapps目录。

ServletConfig对象

此对象可以读取web.xml中配置的初始化参数。

————当servlet配置了初始化参数后,web容器在创建servlet实例对象时,会自动将这些初始化参数封装到ServletConfig对象中,并在调用servlet的init方法时,将ServletConfig对象传递给servlet。

参数信息放置在web.xml中可以方便修改。

ServletContext对象

当Tomcat启动的时候,就会创建一个ServletContext对象。它代表着当前web站点

  • 所有Servlet都共享着一个ServletContext对象,所以Servlet之间可以通过ServletContext实现通讯
  • ServletConfig获取的是配置的是单个Servlet的参数信息,ServletContext可以获取的是配置整个web站点的参数信息
  • 利用ServletContext读取web站点的资源文件
  • 实现Servlet的转发【用ServletContext转发不多,主要用request转发】

应用

多个Servlet通过ServletContext对象实现数据共享

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
//ServletContextDemo1:
public class ServletContextDemo1 extends HttpServlet {

public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String data = "xdp_gacl";
/**
* ServletConfig对象中维护了ServletContext对象的引用,开发人员在编写servlet时,
* 可以通过ServletConfig.getServletContext方法获得ServletContext对象。
*/
ServletContext context = this.getServletConfig().getServletContext();//获得ServletContext对象
context.setAttribute("data", data); //将data存储到ServletContext对象中
}

public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}

//ServletContextDemo2:
public class ServletContextDemo2 extends HttpServlet {

public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
ServletContext context = this.getServletContext();
String data = (String) context.getAttribute("data");//从ServletContext对象中取出数据
response.getWriter().print("data="+data);
}

public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
//实现的功能:先运行ServletContextDemo1,将数据data存储到ServletContext对象中,然后运行ServletContextDemo2就可以从ServletContext对象中取出数据了,这样就实现了数据共享

获取WEB应用的初始化参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<display-name></display-name>
<!-- 配置WEB应用的初始化参数 -->
<context-param>
<param-name>url</param-name>
<param-value>jdbc:mysql://localhost:3306/test</param-value>
</context-param>

<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class ServletContextDemo3 extends HttpServlet {

public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {

ServletContext context = this.getServletContext();
//获取整个web站点的初始化参数
String contextInitParam = context.getInitParameter("url");
response.getWriter().print(contextInitParam);
}

public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}

}

用servletContext实现请求转发

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
//ServletContextDemo4:
public class ServletContextDemo4 extends HttpServlet {

public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String data = "<h1><font color='red'>abcdefghjkl</font></h1>";
response.getOutputStream().write(data.getBytes());
ServletContext context = this.getServletContext();//获取ServletContext对象
RequestDispatcher rd = context.getRequestDispatcher("/servlet/ServletContextDemo5");//获取请求转发对象(RequestDispatcher)
rd.forward(request, response);//调用forward方法实现请求转发
}

public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
}
}

//ServletContextDemo5:
public class ServletContextDemo5 extends HttpServlet {

public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.getOutputStream().write("servletDemo5".getBytes());
}

public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
this.doGet(request, response);
}
}
//浏览器访问ServletContextDemo4,浏览显示的却是ServletContextDemo5的内容

利用ServletContext对象读取资源文件

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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
/**
* 使用servletContext读取资源文件
*
* @author gacl
*
*/
public class ServletContextDemo6 extends HttpServlet {

public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
/**
* response.setContentType("text/html;charset=UTF-8");目的是控制浏览器用UTF-8进行解码;
* 这样就不会出现中文乱码了
*/
response.setHeader("content-type","text/html;charset=UTF-8");
readSrcDirPropCfgFile(response);//读取src目录下的properties配置文件
response.getWriter().println("<hr/>");
readWebRootDirPropCfgFile(response);//读取WebRoot目录下的properties配置文件
response.getWriter().println("<hr/>");
readPropCfgFile(response);//读取src目录下的db.config包中的db3.properties配置文件
response.getWriter().println("<hr/>");
readPropCfgFile2(response);//读取src目录下的gacl.servlet.study包中的db4.properties配置文件

}

/**
* 读取src目录下的gacl.servlet.study包中的db4.properties配置文件
* @param response
* @throws IOException
*/
private void readPropCfgFile2(HttpServletResponse response)
throws IOException {
InputStream in = this.getServletContext().getResourceAsStream("/WEB-INF/classes/gacl/servlet/study/db4.properties");
Properties prop = new Properties();
prop.load(in);
String driver = prop.getProperty("driver");
String url = prop.getProperty("url");
String username = prop.getProperty("username");
String password = prop.getProperty("password");
response.getWriter().println("读取src目录下的gacl.servlet.study包中的db4.properties配置文件:");
response.getWriter().println(
MessageFormat.format(
"driver={0},url={1},username={2},password={3}",
driver,url, username, password));
}

/**
* 读取src目录下的db.config包中的db3.properties配置文件
* @param response
* @throws FileNotFoundException
* @throws IOException
*/
private void readPropCfgFile(HttpServletResponse response)
throws FileNotFoundException, IOException {
//通过ServletContext获取web资源的绝对路径
String path = this.getServletContext().getRealPath("/WEB-INF/classes/db/config/db3.properties");
InputStream in = new FileInputStream(path);
Properties prop = new Properties();
prop.load(in);
String driver = prop.getProperty("driver");
String url = prop.getProperty("url");
String username = prop.getProperty("username");
String password = prop.getProperty("password");
response.getWriter().println("读取src目录下的db.config包中的db3.properties配置文件:");
response.getWriter().println(
MessageFormat.format(
"driver={0},url={1},username={2},password={3}",
driver,url, username, password));
}

/**
* 通过ServletContext对象读取WebRoot目录下的properties配置文件
* @param response
* @throws IOException
*/
private void readWebRootDirPropCfgFile(HttpServletResponse response)
throws IOException {
/**
* 通过ServletContext对象读取WebRoot目录下的properties配置文件
* “/”代表的是项目根目录
*/
InputStream in = this.getServletContext().getResourceAsStream("/db2.properties");
Properties prop = new Properties();
prop.load(in);
String driver = prop.getProperty("driver");
String url = prop.getProperty("url");
String username = prop.getProperty("username");
String password = prop.getProperty("password");
response.getWriter().println("读取WebRoot目录下的db2.properties配置文件:");
response.getWriter().print(
MessageFormat.format(
"driver={0},url={1},username={2},password={3}",
driver,url, username, password));
}

/**
* 通过ServletContext对象读取src目录下的properties配置文件
* @param response
* @throws IOException
*/
private void readSrcDirPropCfgFile(HttpServletResponse response) throws IOException {
/**
* 通过ServletContext对象读取src目录下的db1.properties配置文件
*/
InputStream in = this.getServletContext().getResourceAsStream("/WEB-INF/classes/db1.properties");
Properties prop = new Properties();
prop.load(in);
String driver = prop.getProperty("driver");
String url = prop.getProperty("url");
String username = prop.getProperty("username");
String password = prop.getProperty("password");
response.getWriter().println("读取src目录下的db1.properties配置文件:");
response.getWriter().println(
MessageFormat.format(
"driver={0},url={1},username={2},password={3}",
driver,url, username, password));
}

public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
this.doGet(request, response);
}

}

在客户端缓存Servlet的输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class ServletDemo5 extends HttpServlet {

public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String data = "abcddfwerwesfasfsadf";
/**
* 设置数据合理的缓存时间值,以避免浏览器频繁向服务器发送请求,提升服务器的性能
* 这里是将数据的缓存时间设置为1天
*/
response.setDateHeader("expires",System.currentTimeMillis() + 24 * 3600 * 1000);
response.getOutputStream().write(data.getBytes());
}

public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {

this.doGet(request, response);
}

}