JavaWeb(一)
1.Servlet
1.1 Servlet简介
Servlet(Server Applet),全称Java Servlet。是用Java编写的服务器端程序。其主要功能在于交互式地浏览和修改数据,生成动态Web内容。狭义的Servlet是指Java语言实现的一个接口,广义的Servlet是指任何实现了这个Servlet接口的类,一般情况下,人们将Servlet理解为后者。
Servlet运行于支持Java的应用服务器中。从实现上讲,Servlet可以响应任何类型的请求,但绝大多数情况下Servlet只用来扩展基于HTTP协议的Web服务器。
最早支持Servlet标准的是JavaSoft的Java Web Server。此后,一些其它的基于Java的Web服务器开始支持标准的Servlet。
Java Servlet 通常情况下与使用 CGI(Common Gateway Interface,公共网关接口)实现的程序可以达到异曲同工的效果。但是相比于 CGI,Servlet 有以下几点优势:
• 性能明显更好。
• Servlet 在 Web 服务器的地址空间内执行。这样它就没有必要再创建一个单独的进程来处理每个客户端请求。
• Servlet 是独立于平台的,因为它们是用 Java 编写的。
• 服务器上的 Java 安全管理器执行了一系列限制,以保护服务器计算机上的资源。因此,Servlet 是可信的。
• Java 类库的全部功能对 Servlet 来说都是可用的。它可以通过 sockets 和 RMI 机制与 applets、数据库或其他软件进行交互。
Servlet 架构
Servlet 任务
• 读取客户端(浏览器)发送的显式的数据。这包括网页上的 HTML 表单,或者也可以是来自 applet 或自定义的 HTTP 客户端程序的表单。
• 读取客户端(浏览器)发送的隐式的 HTTP 请求数据。这包括 cookies、媒体类型和浏览器能理解的压缩格式等等。
• 处理数据并生成结果。这个过程可能需要访问数据库,执行 RMI 或 CORBA 调用,调用 Web 服务,或者直接计算得出对应的响应。
• 发送显式的数据(即文档)到客户端(浏览器)。该文档的格式可以是多种多样的,包括文本文件(HTML 或 XML)、二进制文件(GIF 图像)、Excel 等。
• 发送隐式的 HTTP 响应到客户端(浏览器)。这包括告诉浏览器或其他客户端被返回的文档类型(例如 HTML),设置 cookies 和缓存参数,以及其他类似的任务。
1.2 Servlet生命周期
Servlet 生命周期可被定义为从创建直到毁灭的整个过程。以下是 Servlet 遵循的过程:
• Servlet 通过调用 init () 方法进行初始化。
• Servlet 调用 service() 方法来处理客户端的请求。
• Servlet 通过调用 destroy() 方法终止(结束)。
• 最后,Servlet 是由 JVM 的垃圾回收器进行垃圾回收的。
init() 方法
init 方法被设计成只调用一次。它在第一次创建 Servlet 时被调用,在后续每次用户请求时不再调用。因此,它是用于一次性初始化,就像 Applet 的 init 方法一样。
Servlet 创建于用户第一次调用对应于该 Servlet 的 URL 时,但是您也可以指定 Servlet 在服务器第一次启动时被加载。
当用户调用一个 Servlet 时,就会创建一个 Servlet 实例,每一个用户请求都会产生一个新的线程,适当的时候移交给 doGet 或 doPost 方法。init() 方法简单地创建或加载一些数据,这些数据将被用于 Servlet 的整个生命周期。
service() 方法
service() 方法是执行实际任务的主要方法。Servlet 容器(即 Web 服务器)调用 service() 方法来处理来自客户端(浏览器)的请求,并把格式化的响应写回给客户端。
每次服务器接收到一个 Servlet 请求时,服务器会产生一个新的线程并调用服务。service() 方法检查 HTTP 请求类型(GET、POST、PUT、DELETE 等),并在适当的时候调用 doGet、doPost、doPut,doDelete 等方法。
service() 方法由容器调用,service 方法在适当的时候调用 doGet、doPost、doPut、doDelete 等方法。所以,您不用对 service() 方法做任何动作,您只需要根据来自客户端的请求类型来重载 doGet() 或 doPost() 即可。
doGet() 和 doPost() 方法是每次服务请求中最常用的方法。
destroy() 方法
destroy() 方法只会被调用一次,在 Servlet 生命周期结束时被调用。destroy() 方法可以让您的 Servlet 关闭数据库连接、停止后台线程、把 Cookie 列表或点击计数器写入到磁盘,并执行其他类似的清理活动。
1.3 Servlet简单例子
Servlet 是服务 HTTP 请求并实现 javax.servlet.Servlet 接口的 Java 类。Web 应用程序开发人员通常编写 Servlet 来扩展 javax.servlet.http.HttpServlet,并实现 Servlet 接口的抽象类专门用来处理 HTTP 请求。
引入依赖:
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2</version>
<scope>provided</scope>
</dependency>
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html");
response.setCharacterEncoding("utf-8");
PrintWriter out = response.getWriter();
out.println("<html>");
out.println("<head>");
out.println("<title>hello</title>");
out.println("</head>");
out.println("<body>");
out.println("<h1>你好世界</h1>");
out.println("</body>");
out.println("</html>");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
在web.xml文件中配置映射:
<servlet>
<servlet-name>HelloServlet</servlet-name>
<servlet-class>com.wyz.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HelloServlet</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
1.4 ServletContext对象
ServletContext是一个全局的储存信息的空间,服务器开始就存在,服务器关闭才释放。为了方便大家理解,我们将ServletContext和Cookie、Session做一个简单对比,如下图:
我们可以把ServletContext当成一个公用的空间,可以被所有的客户访问,如上图,A、B、C三个客户端都可以访问。
WEB容器在启动时,它会为每个Web应用程序都创建一个对应的ServletContext,它代表当前Web应用,并且它被所有客户端共享。
由于一个WEB应用中的所有Servlet共享同一个ServletContext对象,因此Servlet对象之间可以通过ServletContext对象来实现通讯。ServletContext对象通常也被称之为context域对象。公共聊天室就会用到它。
当web应用关闭、Tomcat关闭或者Web应用reload的时候,ServletContext对象会被销毁。
主要功能:
- 共享数据
- 上下文参数
- 请求转发
- 读取资源文件
共享数据
类似于Session,通过ServletContext对象我们也可以实现数据共享,但值得注意的是,Session是只能在一个客户端中共享数据,而ServletContext中的数据是在所有客户端中都可以实现数据共享的。
//设置共享数据
public class SetAttrServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext context = this.getServletContext();//获取上下文
String username = "无涯子";
context.setAttribute("username",username);
resp.setContentType("text/html;charset=utf-8");
resp.getWriter().println("属性设置完成");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
//获取共享数据
public class GetAttrServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
final ServletContext context = this.getServletContext();
String username = (String)context.getAttribute("username");
resp.setContentType("text/html;charset=utf-8");
resp.getWriter().println("名字" + username);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
上下文参数
我们可以用
<servlet>
<servlet-name>HelloServlet</servlet-name>
<servlet-class>com.wyz.HelloServlet</servlet-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</servlet>
可以看到它配置了一个初始化参数:encoding=utf-8,那么我们在HelloServlet的源代码中需要这样去得到这个参数:
String encoding = this.getServletConfig().getInitParameter("encoding");
注意,上述的参数配置方法只针对一个特定的Servlet有效,我们可以通过ServletContext来获取全局的、整个Web应用的初始化参数,全局的初始化参数是这样配置在web.xml文件中的:
<context-param>
<param-name>name</param-name>
<param-value>wyz</param-value>
</context-param>
然后我们可以在任意一个Servlet中使用ServletContext获取这个参数:
String name = this.getServletContext().getInitParameter("name");
请求转发
后面我们也可以使用HttpServletRequest实现请求转发,更多的时候我们也是使用HttpServletRequest来实现。
this.getServletContext().getRequestDispatcher("/url").forward(request, response);
读取资源文件
读取资源文件要根据资源文件所在的位置来决定,一般分为以下两种情况:
⑴ 文件在WebRoot文件夹下,即Web应用的根目录。这时候我们可以使用ServletContext来读取该资源文件。
假设我们Web根目录下有一个配置数据库信息的dbinfo.properties文件,里面配置了name和password属性,这时候可以通过ServletContext去读取这个文件:
// 这种方法的默认读取路径就是Web应用的根目录
InputStream stream = this.getServletContext().getResourceAsStream("dbinfo.properties");
// 创建属性对象
Properties properties = new Properties();
properties.load(stream);
String name = properties.getProperty("name");
String password = properties.getProperty("password");
out.println("name="+name+";password="+password);
⑵ 如果这个文件放在了src目录下,这时就不能用ServletContext来读取了,必须要使用类加载器去读取。
// 类加载器的默认读取路径是src根目录
InputStream stream = MyServlet.class.getClassLoader().getResourceAsStream("dbinfo.properties");
如果这个文件此时还没有直接在src目录下,而是在src目录下的某个包下,比如在com.gavin包下,此时类加载器要加上包的路径,如下:
InputStream stream = MyServlet.class.getClassLoader().getResourceAsStream("com/gavin/dbinfo.properties");
补充一点,ServletContext只有在读取的文件在web应用的根目录下时,才能获取文件的全路径。比如我们在WebRoot文件夹下有一个images文件夹,images文件夹下有一个Servlet.jpg图片,为了得到这个图片的全路径,如下:
// 如何读取到一个文件的全路径,这里会得到在Tomcat的全路径
String path = this.getServletContext().getRealPath("/images/Servlet.jpg");
在网站开发中,有很多功能要使用ServletContext,比如
• 网站计数器
• 网站的在线用户显示
• 简单的聊天系统
总之,如果是涉及到不同用户共享数据,而这些数据量不大,同时又不希望写入数据库中,我们就可以考虑使用ServletContext实现。
ServletContext使用建议
因为存在ServletContext中的数据在服务器中会长时间,这样就会占用很多内存,因此在使用ServletContext时,建议不要往里面添加过大的数据!
1.4 HttpServletResponse
当一个 Web 服务器响应一个 HTTP 请求时,响应通常包括一个状态行、一些响应报头、一个空行和文档。一个典型的响应如下所示:
HTTP/1.1 200 OK
Content-Type: text/html
Header2: ...
...
HeaderN: ...
(Blank Line)
<!doctype ...>
<html>
<head>...</head>
<body>
...
</body>
</html>
状态行包括 HTTP 版本(在本例中为 HTTP/1.1)、一个状态码(在本例中为 200)和一个对应于状态码的短消息(在本例中为 OK)。
下表总结了从 Web 服务器端返回到浏览器的最有用的 HTTP 1.1 响应报头,您会在 Web 编程中频繁地使用它们:
头信息 | 描述 |
---|---|
Allow | 这个头信息指定服务器支持的请求方法(GET、POST 等)。 |
Cache-Control | 这个头信息指定响应文档在何种情况下可以安全地缓存。可能的值有:public、private 或 no-cache 等。Public 意味着文档是可缓存,Private 意味着文档是单个用户私用文档,且只能存储在私有(非共享)缓存中,no-cache 意味着文档不应被缓存。 |
Connection | 这个头信息指示浏览器是否使用持久 HTTP 连接。值 close 指示浏览器不使用持久 HTTP 连接,值 keep-alive 意味着使用持久连接。 |
Content-Disposition | 这个头信息可以让您请求浏览器要求用户以给定名称的文件把响应保存到磁盘。 |
Content-Encoding | 在传输过程中,这个头信息指定页面的编码方式。 |
Content-Language | 这个头信息表示文档编写所使用的语言。例如,en、en-us、ru 等。 |
Content-Length | 这个头信息指示响应中的字节数。只有当浏览器使用持久(keep-alive)HTTP 连接时才需要这些信息。 |
Content-Type | 这个头信息提供了响应文档的 MIME(Multipurpose Internet Mail Extension)类型。 |
Expires | 这个头信息指定内容过期的时间,在这之后内容不再被缓存。 |
Last-Modified | 这个头信息指示文档的最后修改时间。然后,客户端可以缓存文件,并在以后的请求中通过 If-Modified-Since 请求头信息提供一个日期。 |
Location | 这个头信息应被包含在所有的带有状态码的响应中。在 300s 内,这会通知浏览器文档的地址。浏览器会自动重新连接到这个位置,并获取新的文档。 |
Refresh | 这个头信息指定浏览器应该如何尽快请求更新的页面。您可以指定页面刷新的秒数。 |
Retry-After | 这个头信息可以与 503(Service Unavailable 服务不可用)响应配合使用,这会告诉客户端多久就可以重复它的请求。 |
Set-Cookie | 这个头信息指定一个与页面关联的 cookie。 |
设置 HTTP 响应报头的方法
下面的方法可用于在 Servlet 程序中设置 HTTP 响应报头。这些方法通过 HttpServletResponse 对象可用。
序5 | 方法 & 描述 |
---|---|
1 | String encodeRedirectURL(String url)为 sendRedirect 方法中使用的指定的 URL 进行编码,或者如果编码不是必需的,则返回 URL 未改变。 |
2 | String encodeURL(String url)对包含 session 会话 ID 的指定 URL 进行编码,或者如果编码不是必需的,则返回 URL 未改变。 |
3 | boolean containsHeader(String name)返回一个布尔值,指示是否已经设置已命名的响应报头。 |
4 | boolean isCommitted()返回一个布尔值,指示响应是否已经提交。 |
5 | void addCookie(Cookie cookie)把指定的 cookie 添加到响应。 |
6 | void addDateHeader(String name, long date)添加一个带有给定的名称和日期值的响应报头。 |
7 | void addHeader(String name, String value)添加一个带有给定的名称和值的响应报头。 |
8 | void addIntHeader(String name, int value)添加一个带有给定的名称和整数值的响应报头。 |
9 | void flushBuffer()强制任何在缓冲区中的内容被写入到客户端。 |
10 | void reset()清除缓冲区中存在的任何数据,包括状态码和头。 |
11 | void resetBuffer()清除响应中基础缓冲区的内容,不清除状态码和头。 |
12 | void sendError(int sc)使用指定的状态码发送错误响应到客户端,并清除缓冲区。 |
13 | void sendError(int sc, String msg)使用指定的状态发送错误响应到客户端。 |
14 | void sendRedirect(String location)使用指定的重定向位置 URL 发送临时重定向响应到客户端。 |
15 | void setBufferSize(int size)为响应主体设置首选的缓冲区大小。 |
16 | void setCharacterEncoding(String charset)设置被发送到客户端的响应的字符编码(MIME 字符集)例如,UTF-8。 |
17 | void setContentLength(int len)设置在 HTTP Servlet 响应中的内容主体的长度,该方法设置 HTTP Content-Length 头。 |
18 | void setContentType(String type)如果响应还未被提交,设置被发送到客户端的响应的内容类型。 |
19 | void setDateHeader(String name, long date)设置一个带有给定的名称和日期值的响应报头。 |
20 | void setHeader(String name, String value)设置一个带有给定的名称和值的响应报头。 |
21 | void setIntHeader(String name, int value)设置一个带有给定的名称和整数值的响应报头。 |
22 | void setLocale(Locale loc)如果响应还未被提交,设置响应的区域。 |
23 | void setStatus(int sc)为该响应设置状态码。 |
请求重定向
response.sendRedirect("/web01/db");
自动下载
在服务器端准备一张图片
public class DownServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取资源路径
String path = DownServlet.class.getClassLoader().getResource("无涯子.jpg").getPath();
System.out.println("path"+path);
String filename = path.substring(path.lastIndexOf('/')+1);
System.out.println("文件名:" + filename);
//设置响应头可以自动下载文件
resp.setHeader("Content-Disposition","attachment;filename=" + URLEncoder.encode(filename,"utf-8"));
//输出文件
byte[] buffer = new byte[1024];
int len;
FileInputStream is = new FileInputStream(path);
OutputStream outputStream = resp.getOutputStream();
while((len = is.read(buffer)) != -1){
outputStream.write(buffer,0,len);
}
is.close();
outputStream.close();
System.out.println("输出文件成功");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
1.5 HttpServletRequest
当浏览器请求网页时,它会向 Web 服务器发送特定信息,这些信息不能被直接读取,因为这些信息是作为 HTTP 请求的头的一部分进行传输的。您可以查看 HTTP 协议 了解更多相关信息。
以下是来自于浏览器端的重要头信息,您可以在 Web 编程中频繁使用:
头信息 | 描述 |
---|---|
Accept | 这个头信息指定浏览器或其他客户端可以处理的 MIME 类型。值 image/png 或 image/jpeg 是最常见的两种可能值。 |
Accept-Charset | 这个头信息指定浏览器可以用来显示信息的字符集。例如 ISO-8859-1。 |
Accept-Encoding | 这个头信息指定浏览器知道如何处理的编码类型。值 gzip 或 compress 是最常见的两种可能值。 |
Accept-Language | 这个头信息指定客户端的首选语言,在这种情况下,Servlet 会产生多种语言的结果。例如,en、en-us、ru 等。 |
Authorization | 这个头信息用于客户端在访问受密码保护的网页时识别自己的身份。 |
Connection | 这个头信息指示客户端是否可以处理持久 HTTP 连接。持久连接允许客户端或其他浏览器通过单个请求来检索多个文件。值 Keep-Alive 意味着使用了持续连接。 |
Content-Length | 这个头信息只适用于 POST 请求,并给出 POST 数据的大小(以字节为单位)。 |
Cookie | 这个头信息把之前发送到浏览器的 cookies 返回到服务器。 |
Host | 这个头信息指定原始的 URL 中的主机和端口。 |
If-Modified-Since | 这个头信息表示只有当页面在指定的日期后已更改时,客户端想要的页面。如果没有新的结果可以使用,服务器会发送一个 304 代码,表示 Not Modified 头信息。 |
If-Unmodified-Since | 这个头信息是 If-Modified-Since 的对立面,它指定只有当文档早于指定日期时,操作才会成功。 |
Referer | 这个头信息指示所指向的 Web 页的 URL。例如,如果您在网页 1,点击一个链接到网页 2,当浏览器请求网页 2 时,网页 1 的 URL 就会包含在 Referer 头信息中。 |
User-Agent | 这个头信息识别发出请求的浏览器或其他客户端,并可以向不同类型的浏览器返回不同的内容。 |
读取 HTTP 头的方法
下面的方法可用在 Servlet 程序中读取 HTTP 头。这些方法通过 HttpServletRequest 对象可用。
序号 | 方法 & 描述 |
---|---|
1 | Cookie[] getCookies()返回一个数组,包含客户端发送该请求的所有的 Cookie 对象。 |
2 | Enumeration getAttributeNames()返回一个枚举,包含提供给该请求可用的属性名称。 |
3 | Enumeration getHeaderNames()返回一个枚举,包含在该请求中包含的所有的头名。 |
4 | Enumeration getParameterNames()返回一个 String 对象的枚举,包含在该请求中包含的参数的名称。 |
5 | HttpSession getSession()返回与该请求关联的当前 session 会话,或者如果请求没有 session 会话,则创建一个。 |
6 | HttpSession getSession(boolean create)返回与该请求关联的当前 HttpSession,或者如果没有当前会话,且创建是真的,则返回一个新的 session 会话。 |
7 | Locale getLocale()基于 Accept-Language 头,返回客户端接受内容的首选的区域设置。 |
8 | Object getAttribute(String name)以对象形式返回已命名属性的值,如果没有给定名称的属性存在,则返回 null。 |
9 | ServletInputStream getInputStream()使用 ServletInputStream,以二进制数据形式检索请求的主体。 |
10 | String getAuthType()返回用于保护 Servlet 的身份验证方案的名称,例如,"BASIC" 或 "SSL",如果JSP没有受到保护则返回 null。 |
11 | String getCharacterEncoding()返回请求主体中使用的字符编码的名称。 |
12 | String getContentType()返回请求主体的 MIME 类型,如果不知道类型则返回 null。 |
13 | String getContextPath()返回指示请求上下文的请求 URI 部分。 |
14 | String getHeader(String name)以字符串形式返回指定的请求头的值。 |
15 | String getMethod()返回请求的 HTTP 方法的名称,例如,GET、POST 或 PUT。 |
16 | String getParameter(String name)以字符串形式返回请求参数的值,或者如果参数不存在则返回 null。 |
17 | String getPathInfo()当请求发出时,返回与客户端发送的 URL 相关的任何额外的路径信息。 |
18 | String getProtocol()返回请求协议的名称和版本。 |
19 | String getQueryString()返回包含在路径后的请求 URL 中的查询字符串。 |
20 | String getRemoteAddr()返回发送请求的客户端的互联网协议(IP)地址。 |
21 | String getRemoteHost()返回发送请求的客户端的完全限定名称。 |
22 | String getRemoteUser()如果用户已通过身份验证,则返回发出请求的登录用户,或者如果用户未通过身份验证,则返回 null。 |
23 | String getRequestURI()从协议名称直到 HTTP 请求的第一行的查询字符串中,返回该请求的 URL 的一部分。 |
24 | String getRequestedSessionId()返回由客户端指定的 session 会话 ID。 |
25 | String getServletPath()返回调用 JSP 的请求的 URL 的一部分。 |
26 | String[] getParameterValues(String name)返回一个字符串对象的数组,包含所有给定的请求参数的值,如果参数不存在则返回 null。 |
27 | boolean isSecure()返回一个布尔值,指示请求是否使用安全通道,如 HTTPS。 |
28 | int getContentLength()以字节为单位返回请求主体的长度,并提供输入流,或者如果长度未知则返回 -1。 |
29 | int getIntHeader(String name)返回指定的请求头的值为一个 int 值。 |
30 | int getServerPort()返回接收到这个请求的端口号。 |
读取请求参数
简单一个前端登录页面:
<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="UTF-8" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>用户登录</h1>
<form action="${pageContext.request.contextPath}/login" method="post">
用户名:<input type="text" name="username"><br/>
密码:<input type="password" name="password"><br/>
爱好:<input type="checkbox" name="hobby" value="女孩">女孩
<input type="checkbox" name="hobby" value="编程">编程
<input type="checkbox" name="hobby" value="阅读">阅读
<input type="checkbox" name="hobby" value="看电影">看电影<br/>
<input type="submit" value="提交">
</form>
</body>
</html>
使用Servlet读取参数,并输出参数信息,进行相应的请求转发:
public class LoginServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("utf-8");//设置请求响应编码
resp.setCharacterEncoding("utf-8");
//获取请求参数
final String username = req.getParameter("username");
final String password = req.getParameter("password");
final String[] hobbies = req.getParameterValues("hobby");
System.out.println("用户名:" + username);
System.out.println("密码:" + password);
System.out.println("爱好:" + Arrays.toString(hobbies));
//请求转发
req.getRequestDispatcher("/success.jsp").forward(req,resp);
}
}
请求转发
request.getRequestDispatcher("/success.jsp").forward(req,resp);
1.6 Cookie和Session
Cookies 是存储在客户端计算机上的文本文件,并保留了各种跟踪信息。Java Servlet 显然支持 HTTP Cookies。
public class CookieDemo1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//设置请求响应乱码问题
req.setCharacterEncoding("utf-8");
resp.setContentType("text/html;charset=utf-8");
final PrintWriter out = resp.getWriter();
//获取Cookie
final Cookie[] cookies = req.getCookies();
if (cookies != null){
for (Cookie cookie : cookies) {
if("lastTime".equals(cookie.getName())){
final String value = cookie.getValue();
final Date date = new Date(Long.parseLong(value));
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd HH:mm:ss");
final String format = sdf.format(date);
System.out.println(format);
out.println("最后一次登录的时间为:" + format);
}
}
}
final Cookie cookie = new Cookie("lastTime", System.currentTimeMillis() + "");
cookie.setMaxAge(24*60*60);//一天
//设置响应cookie
resp.addCookie(cookie);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
HTTP 是一种"无状态"协议,这意味着每次客户端检索网页时,客户端打开一个单独的连接到 Web 服务器,服务器会自动不保留之前客户端请求的任何记录。
但是仍然有以下三种方式来维持 Web 客户端和 Web 服务器之间的 session 会话:
Cookies
一个 Web 服务器可以分配一个唯一的 session 会话 ID 作为每个 Web 客户端的 cookie,对于客户端的后续请求可以使用接收到的 cookie 来识别。
这可能不是一个有效的方法,因为很多浏览器不支持 cookie,所以我们建议不要使用这种方式来维持 session 会话。
隐藏的表单字段
一个 Web 服务器可以发送一个隐藏的 HTML 表单字段,以及一个唯一的 session 会话 ID,如下所示:
<input type="hidden" name="sessionid" value="12345">
该条目意味着,当表单被提交时,指定的名称和值会被自动包含在 GET 或 POST 数据中。每次当 Web 浏览器发送回请求时,session_id 值可以用于保持不同的 Web 浏览器的跟踪。
这可能是一种保持 session 会话跟踪的有效方式,但是点击常规的超文本链接()不会导致表单提交,因此隐藏的表单字段也不支持常规的 session 会话跟踪。
URL 重写
您可以在每个 URL 末尾追加一些额外的数据来标识 session 会话,服务器会把该 session 会话标识符与已存储的有关 session 会话的数据相关联。
例如,http://www.baidu.com/file.htm;sessionid=12345,session 会话标识符被附加为 sessionid=12345,标识符可被 Web 服务器访问以识别客户端。
URL 重写是一种更好的维持 session 会话的方式,它在浏览器不支持 cookie 时能够很好地工作,但是它的缺点是会动态生成每个 URL 来为页面分配一个 session 会话 ID,即使是在很简单的静态 HTML 页面中也会如此。
HttpSession 对象
除了上述的三种方式,Servlet 还提供了 HttpSession 接口,该接口提供了一种跨多个页面请求或访问网站时识别用户以及存储有关用户信息的方式。
Servlet 容器使用这个接口来创建一个 HTTP 客户端和 HTTP 服务器之间的 session 会话。会话持续一个指定的时间段,跨多个连接或页面请求。
您会通过调用 HttpServletRequest 的公共方法 getSession() 来获取 HttpSession 对象,如下所示:
HttpSession session = request.getSession();
你需要在向客户端发送任何文档内容之前调用 request.getSession()。下面总结了 HttpSession 对象中可用的几个重要的方法:
序号 | 方法&描述 |
---|---|
1 | public Object getAttribute(String name)该方法返回在该 session 会话中具有指定名称的对象,如果没有指定名称的对象,则返回 null。 |
2 | public Enumeration getAttributeNames()该方法返回 String 对象的枚举,String 对象包含所有绑定到该 session 会话的对象的名称。 |
3 | public long getCreationTime()该方法返回该 session 会话被创建的时间,自格林尼治标准时间 1970 年 1 月 1 日午夜算起,以毫秒为单位。 |
4 | public String getId()该方法返回一个包含分配给该 session 会话的唯一标识符的字符串。 |
5 | public long getLastAccessedTime()该方法返回客户端最后一次发送与该 session 会话相关的请求的时间自格林尼治标准时间 1970 年 1 月 1 日午夜算起,以毫秒为单位。 |
6 | public int getMaxInactiveInterval()该方法返回 Servlet 容器在客户端访问时保持 session 会话打开的最大时间间隔,以秒为单位。 |
7 | public void invalidate()该方法指示该 session 会话无效,并解除绑定到它上面的任何对象。 |
8 | public boolean isNew(如果客户端还不知道该 session 会话,或者如果客户选择不参入该 session 会话,则该方法返回 true。 |
9 | public void removeAttribute(String name)该方法将从该 session 会话移除指定名称的对象。 |
10 | public void setAttribute(String name, Object value)该方法使用指定的名称绑定一个对象到该 session 会话。 |
11 | public void setMaxInactiveInterval(int interval)该方法在 Servlet 容器指示该 session 会话无效之前,指定客户端请求之间的时间,以秒为单位。 |
//设置Session
public class SessionDemo1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("utf-8");
resp.setContentType("text/html;charset=utf-8");
final HttpSession session = req.getSession();//获取session
session.setAttribute("name","无涯子");
//获取sessionId
final String id = session.getId();
//判断session是不是新建的
if(session.isNew()){
resp.getWriter().write("session创建成功,sessionID:" + id);
}else{
resp.getWriter().write("session在服务器已经存在,sessionID:" + id);
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
//获取Session
public class SessionDemo2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("utf-8");
resp.setContentType("text/html;charset=utf-8");
//获取session
final HttpSession session = req.getSession();
String name = (String)session.getAttribute("name");
resp.getWriter().write("name:" + name);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
//删除Session
public class SessionDemo3 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("utf-8");
resp.setContentType("text/html;charset=utf-8");
//删除session
final HttpSession session = req.getSession();
session.removeAttribute("name");
session.invalidate();//移出删除
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具