Servlet系列(三):常见接口及会话技术

ServletConfig接口

Servlet容器会在每个Servlet初始化时为其创建一个ServletConfig对象,用于获取该Servlet初始化参数。

通常通过两种方式获取ServletConfig对象:

  1. 从带ServletConfig参数的init()方法中获得。
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
public class HelloServlet extends HttpServlet
{
private ServletConfig config;

// 从init函数参数中获取ServletConfig对象实例
@Override
public void init(ServletConfig config) throws ServletException
{
this.config = config;
}

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
{
// 通过ServletConfig实例获取该Servlet的name
String servletName = config.getServletName();
resp.getWriter().println(servletName);
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
{
doGet(req, resp);
}
}

  1. 通过GenericServlet接口提供的getServletConfig()方法获取。

由于我们编写的Servlet一般都继承自GenericServlet接口或其子接口,故可以直接通过this调用该方法。

1
ServletConfig config = this.getServletConfig();

ServletConfig接口方法:

返回类型 方法名 描述
String getInitParameter(String name) 根据name获取初始化参数
Enumeration getInitParameterNames() 获取所有初始化参数的name
ServletContext getServletContext() 获取ServletContext对象,该对象在下文介绍
String getServletName() 获取该Servlet的名称

初始化参数一般在web.xml文件中配置,或通过注解方式配置,携带了该Servlet初始化时需要的一些参数,如访问数据库的驱动、用户名、密码等:

1
2
3
4
5
6
7
8
9
<servlet>
<servlet-name>MyServlet</servlet-name>
<servlet-class>com.demo.MyServlet</servlet-class>
<!-- Servlet 初始化参数 -->
<init-param>
<param-name>name</param-name>
<param-value>root</param-value>
</init-param>
</servlet>

ServletContext接口

Servlet容器启动时为Web应用创建一个唯一的ServletContext对象,Web应用中的所有Servlet共享这个ServletContext对象,不同的Servlet之间可以通过该对象进行通信。ServletContext对象也是Servlet容器三大域对象之一。

获取ServletContext对象

  1. 通过GenericServlet提供的getServletContext()方法。
1
2
// 一般自行编写的Servlet直接或间接继承自GenericServlet
ServletContext context = this.getServletContext();
  1. 通过ServletConfig提供的getServletContext()方法。
1
ServletContext context = this.getServletConfig().getServletContext();
  1. 通过 HttpServletRequest 提供的 getServletContext() 方法。
1
ServletContext context = req.getServletContext();
  1. 通过 HttpSession 提供的 getServletContext() 方法。
1
ServletContext context = req.getSession().getServletContext();

ServletContext对象作用

ServletContext对象和ServletConfig对象类似,可以用来获取上下文初始化参数,这些初始化参数是全局的,即整个Web应用共享的。上下文初始化参数通过在web.xml中使用<context-param>标签进行配置:

1
2
3
4
5
6
7
8
<context-param>
<param-name>name</param-name>
<param-value>root</param-value>
</context-param>
<context-param>
<param-name>password</param-name>
<param-value>123456</param-value>
</context-param>

获取参数的方法和ServletConfig一致:

返回类型 方法名 描述
String getInitParameter(String name) 根据name获取初始化参数
Enumeration getInitParameterNames() 获取Web中所有初始化参数的name

ServletContext对象作为三大域对象之一,其另一重要作用是实现Servlet间的数据通信。和其它域对象类似,通过setAttribute()方法可以存放一些属性,通过getAttribute()方法能够从中取出相应的属性。

返回类型 方法名 描述
void setAttribute(String name, Object object) 向ServletContext中存入名为name的一个Java对象
void removeAttribute(String name) 从ServletContext中删除名为name的对象
Object getAttribute(String name) 根据名称name获取已存入ServletContext中的对象

需要注意,ServletContext的生命周期从Servlet容器启动开始,一直到容器关闭或者该Web应用被容器卸载为止。

之前说过通过request对象能够实现请求的转发,ServletContext同样也能够实现请求的转发:

1
2
// 获取ServletContext对象并进行请求转发
this.getServletContext().getRequestDispatcher("/url").forward(request, response);

ServletContext的另一作用是读取Web应用中的资源文件,例如在Web应用的根目录下存放了一个数据库配置文件data.properties,可以用getResourceAsStream方法读取:

1
2
3
4
5
6
7
// 相对Web应用的根目录的路径
InputStream stream = this.getServletContext().getResourceAsStream("data.properties");
// 创建属性对象
Properties properties = new Properties();
properties.load(stream);
String name = properties.getProperty("name");
String password = properties.getProperty("password");

Cookies和Session

Cookies

HTTP协议是无状态协议,也就是说服务器不会对之前发生过的请求和相应的状态进行管理,无法根据之前的状态对本次请求进行处理。对于需要登录认证才能访问的Web页面,不记录用户的登录状态将使得每次跳转新页面都需要再次登录。这十分麻烦。

为了解决上述问题,HTTP协议引入Cookie技术,Cookie技术通过在请求和响应报文中添加Cookie字段来控制客户端状态。

它的工作过程如下:

  1. 客户端初次访问服务器时,服务器在HTTP响应中添加Set-Cookie字段,用于通知客户端保存该Cookie。
  2. 客户端将该Cookie保存在磁盘中。
  3. 客户端再次访问服务器时,携带上次保存的Cookie,即在HTTP请求首部添加Cookie字段,这样服务器通过该Cookie来跟踪客户端的状态。

可以通过javax.servlet.http包中的Cookie类创建Cookie对象:

1
2
// 通过两个参数均为String的构造函数创建Cookie对象
Cookie cookie = new Cookie("name", "value");

Cookie类常用方法如下:

返回类型 方法名 描述
void setMaxAge(int expiry) 设置Cookie的最大有效时间,单位为秒;取值为0时,表示删除该Cookie,取值为负时,浏览器退出时会删除该Cookie
int getMaxAge() 获取Cookie的最大有效时间
String getName() 获取Cookie的名称
void setPath(String uri) 设置Cookie的有效路径,浏览器只会在访问该路径的子路径会携带上这个Cookie
String getPath() 获取Cookie的有效路径
void setValue(String newValue) 设置Cookie关联的值
String getValue() 获取Cookie关联的值
void setSecure(boolean flag) 设置是否只在加密的连接上发送(https)

下面通过一个示例来演示用法:

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
package com.fxd;

import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class HelloServlet extends HttpServlet
{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
{
resp.setContentType("text/html;charset=UTF-8");
// 通过request对象能够获取请求携带的cookie
Cookie[] cookies = req.getCookies();
Cookie c = getCookieByName(cookies, "testCookie");
//第一次访问
if (c == null)
{
Cookie newCookie = new Cookie("testCookie", "12345678");
//有效期1小时
newCookie.setMaxAge(60*60);
newCookie.setPath("/");
//通过response对象能够在响应中添加Cookie
resp.addCookie(newCookie);
resp.getWriter().println("你好,新朋友!第一次访问。");
}
else
{
if (c.getValue().equals("12345678"))
{
resp.getWriter().println("你好,老朋友!");
}
}
}

//遍历cookies查找名为name的cookie
private Cookie getCookieByName(Cookie[] cookies, String name)
{
if (cookies == null)
return null;
for (Cookie cookie : cookies)
{
if (cookie.getName().equals(name))
{
return cookie;
}
}
return null;
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
{
doGet(req, resp);
}
}

第一次访问时,浏览器不携带cookie,可以看到服务器响应头部带有Set-Cookie字段:

第一次访问

再次访问时,可以看到浏览器将本地保存的cookie携带在请求头中,服务器能够辨别出再次访问:

第二次访问

Session

Session是一种服务端会话技术,即由服务器来创建和维护的一个数据结构,用来跟踪和客户端的通信。当客户端初次访问服务端时,服务器为客户端创建一个Session对象,并为这个Session对象分配一个SessionID,常用的实现Session的方法是将SessionID通过Cookie的方式发送给客户端,客户端后续的请求带上这个Cookie,从而实现会话跟踪。

第三次访问

Session对象由服务器创建,可以通过request对象获取HttpSession对象:

1
HttpSession session=request.getSession();

HttpSession对象是Servlet中的第三大域对象,意味着可以在其中存储Java对象信息,实现不同Servlet间的通信。其常用的方法如下:

返回类型 方法名 描述
long getCreationTime() 返回创建 Session 的时间。
String getId() 返回获取 Seesion 的唯一的 ID。
int getMaxInactiveInterval() 返回在无任何操作的情况下,Session 失效的时间,以秒为单位。
void setMaxInactiveInterval(int interval) 设置Session失效时间
ServletContext getServletContext() 返回ServletContext对象
void invalidate() 使 Session 失效。
void setAttribute(String name, Object object) 向HttpSession中存入名为name的一个Java对象
void removeAttribute(String name) 从HttpSession中删除名为name的对象
Object getAttribute(String name) 根据名称name获取已存入HttpSession中的对象

Session最大过期时间还可以通过在web.xml配置文件中使用session-config标签设置:

1
2
3
4
<!--设置session的过期时间-->
<session-config>
<session-timeout>10</session-timeout>
</session-config>

参考文献

  1. 图解HTTP
  2. C语言中文网Servlet教程
  3. Servlet文档