servlet的两种配置方式
若想让Servlet正确地运行在服务器中并处理请求信息,必须进行适当的配置,关于Servlet的配置主要有两种方式,分别是通过Web应用的配置文件web.xml来完成配置和使用@WebServlet注解的方式完成。
1.xml方式
<servlet>
<servlet-name>HelloServlet</servlet-name>
<servlet-class>web.controller.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HelloServlet</servlet-name>
<url-pattern>/servlet/HelloServlet</url-pattern>
</servlet-mapping>
2.注解方式
@WebServlet(name = "HelloServlet",urlPatterns = "/HelloServlet")
public class HelloServlet extends HttpServlet{
//处理GET方法请求的方法
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {}
//处理POST方法请求的方法
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {}
}
servlet 的生命周期
当客户端向Servlet容器发出HTTP请求访问Servlet时,Servlet容器首先会解析请求,检查内存中是否已经有了该Servlet对象,如果有,直接使用该Servlet对象;如果没有,就创建Servlet实例对象,然后通过调用init()方法完成Servlet的初始化。需要注意的是,在Servlet的整个生命周期内,它的init()方法只被调用一次。
这是Servlet生命周期中最重要的阶段,在这个阶段,Servlet容器会为客户端请求创建代表HTTP请求的ServletRequest对象和代表HTTP响应的ServletResponse对象,然后将它们作为参数传递给Servlet的service()方法。service()方法从ServletRequest对象中获得客户端请求信息并处理该请求,通过ServletResponse对象生成响应结果。在Servlet的整个生命周期内,对于Servlet的每一次访问请求,Servlet容器都会调用一次Servlet的service()方法,并且创建新的ServletRequest和ServletResponse对象,也就是说,service()方法在Servlet的整个生命周期中会被调用多次。
当服务器关闭或web应用被移除出容器时,Servlet随着web应用的销毁而销毁。在销毁Servlet之前,Servlet容器会调用Servlet的destroy()方法,以便让Servlet对象释放它所占用的资源。在Servlet的整个生命周期中,destroy()方法也只被调用一次。
需要注意的是,Servlet对象一旦创建就会驻留在内存中等待客户端的访问,直到服务器关闭,或web应用被移除出容器时Servlet对象才会销毁。
示例:
package com.miao;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
// 配置servlet 采用注解的方式
//@WebServlet(name = "ServletDemo03" , value = "/ServletDemo03")
//采用web.xml的方式配置
public class ServletDemo03 extends HttpServlet {
@Override
public void init(ServletConfig config) throws ServletException {
System.out.println("init方法执行");
}
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("service方法执行了!!!!");
}
@Override
public void destroy() {
System.out.println("destroy方法执行了!!!!");
}
}
<servlet>
<servlet-name>ServletDemo03</servlet-name>
<servlet-class>com.miao.ServletDemo03</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ServletDemo03</servlet-name>
<url-pattern>/ServletDemo03</url-pattern>
</servlet-mapping>
ServletConfig接口
在Servlet运行期间,经常需要一些配置信息,例如,文件使用的编码等,这些信息都可以在@WebServlet注解的属性中配置。当Tomcat初始化一个Servlet时,会将该Servlet的配置信息封装到一个ServletConfig对象中,通过调用init(ServletConfig config)方法将ServletConfig对象传递给Servlet。ServletConfig定义了一系列获取配置信息的方法。
方法说明 | 功能描述 |
---|---|
String getInitParameter(String name) | 根据初始化参数名返回对应的初始化参数值 |
Enumeration getInitParameterNames() | 返回一个Enumeration对象,其中包含了所有的初始化参数名 |
ServletContext getServletContext() | 返回一个代表当前Web应用的ServletContext对象 |
String getServletName() | 返回Servlet的名字 |
package com.miao;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Enumeration;
// 配置初始化参数
//配置单个参数
//@WebServlet(name = "ServletDemo05", value = "/ServletDemo05",
// initParams = @WebInitParam(name = "encoding" , value = "UTF-8"))
//配置多个参数
@WebServlet(name = "ServletDemo05", value = "/ServletDemo05",
initParams = {@WebInitParam(name = "encoding", value = "UTF-8"),
@WebInitParam(name="name" , value = "zhangsan")}
)
public class ServletDemo05 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String encoding = this.getServletConfig().getInitParameter("encoding");
System.out.println(encoding);
String name = this.getServletConfig().getInitParameter("name");
System.out.println(name);
// 看一看其他属性里边是什么内容
// servletContext
ServletConfig servletConfig = this.getServletConfig();
ServletContext servletContext = servletConfig.getServletContext();
System.out.println(servletContext.toString());
// initParameterNames
Enumeration<String> initParameterNames = servletConfig.getInitParameterNames();
while (initParameterNames.hasMoreElements()) {
String element = initParameterNames.nextElement();
System.out.print(element + "==>");
String parameter = servletConfig.getInitParameter(element);
System.out.println(parameter);
}
String servletName = servletConfig.getServletName();
System.out.println(servletName);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
05servletconfig接口_哔哩哔哩_bilibili
ServletContext接口
当Servlet容器启动时,会为每个Web应用创建一个唯一的ServletContext对象代表当前Web应用。ServletContext对象不仅封装了当前Web应用的所有信息,而且实现了多个Servlet之间数据的共享。
在web.xml文件中,可以配置Servlet的初始化信息,还可以配置整个Web应用的初始化信息。Web应用初始化参数的配置方式具体如下所示。
<context-param>元素位于根元素<web-app>中,它的子元素<param-name>和<param-value>分别用来指定参数的名字和参数值。可以通过调用ServletContext接口中定义的getInitParameterNames()和getInitParameter(String name)方法,分别获取参数名和参数值。
由于一个Web应用中的所有Servlet共享同一个ServletContext对象,所以ServletContext对象的域属性可以被该Web应用中的所有Servlet访问。ServletContext接口中定义了用于增加、删除、设置ServletContext域属性的四个方法。
ServletContext接口定义了一些读取Web资源的方法,这些方法是依靠Servlet容器来实现的。Servlet容器根据资源文件相对于Web应用的路径,返回关联资源文件的IO流、资源文件在文件系统的绝对路径等。
1.获取Web应用程序的初始化参数
<context-param>
<param-name>参数名</param-name>
<param-value>参数值</param-value>
</context-param>
<context-param>
<param-name>参数名</param-name>
<param-value>参数值</param-value>
</context-param>
package com.miao;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Enumeration;
// 配置初始化参数
//配置单个参数
//@WebServlet(name = "ServletDemo05", value = "/ServletDemo05",
// initParams = @WebInitParam(name = "encoding" , value = "UTF-8"))
//配置多个参数
//@WebServlet(name = "ServletDemo06", value = "/ServletDemo06",
// initParams = {@WebInitParam(name = "encoding", value = "UTF-8"),
// @WebInitParam(name="name" , value = "zhangsan")}
//)
public class ServletDemo06 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=utf-8");
ServletContext context = this.getServletContext();
Enumeration<String> initParameterNames = context.getInitParameterNames();
while (initParameterNames.hasMoreElements()) {
String element = initParameterNames.nextElement();
System.out.print(element + " ==> ");
System.out.println(context.getInitParameter(element));
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
2.实现多个Servlet对象共享数据
方法说明 | 功能描述 |
---|---|
Enumeration getAttributeNames() | 返回一个Enumeration对象,该对象包含了所有存放在ServletContext中的所有域属性名 |
Object getAttibute(String name) | 根据参数指定的属性名返回一个与之匹配的域属性值 |
void removeAttribute(String name) | 根据参数指定的域属性名,从ServletContext中删除匹配的域属性 |
void setAttribute(String name,Object obj) | 设置ServletContext的域属性,其中name是域属性名,obj是域属性值 |
package com.miao;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Enumeration;
// 配置初始化参数
//配置单个参数
//@WebServlet(name = "ServletDemo05", value = "/ServletDemo05",
// initParams = @WebInitParam(name = "encoding" , value = "UTF-8"))
//配置多个参数
//@WebServlet(name = "ServletDemo06", value = "/ServletDemo06",
// initParams = {@WebInitParam(name = "encoding", value = "UTF-8"),
// @WebInitParam(name="name" , value = "zhangsan")}
//)
public class ServletDemo06 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=utf-8");
ServletContext context = this.getServletContext();
Enumeration<String> initParameterNames = context.getInitParameterNames();
while (initParameterNames.hasMoreElements()) {
String element = initParameterNames.nextElement();
System.out.print(element + " ==> ");
System.out.println(context.getInitParameter(element));
}
context.setAttribute("cookie","123456");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
package com.miao;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Enumeration;
// 配置初始化参数
//配置单个参数
//@WebServlet(name = "ServletDemo05", value = "/ServletDemo05",
// initParams = @WebInitParam(name = "encoding" , value = "UTF-8"))
//配置多个参数
//@WebServlet(name = "ServletDemo06", value = "/ServletDemo06",
// initParams = {@WebInitParam(name = "encoding", value = "UTF-8"),
// @WebInitParam(name="name" , value = "zhangsan")}
//)
public class ServletDemo07 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=utf-8");
ServletContext context = this.getServletContext();
Object cookie = context.getAttribute("cookie");
resp.getWriter().println( "cookie ==> " + cookie);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
3.读取Web应用下的资源文件
方法说明 | 功能描述 |
---|---|
Set getResourcePaths(String path) | 返回一个Set集合,集合中包含资源目录中子目录和文件的路径名称。参数path必须以正斜线(/)开始,指定匹配资源的部分路径 |
String getRealPath(String path) | 返回资源文件在服务器文件系统上的真实路径(文件的绝对路径)。参数path代表资源文件的虚拟路径,它应该以正斜线(/)开始,“/”表示当前Web应用的根目录,如果Servlet容器不能将虚拟路径转换为文件系统的真实路径,则返回null |
URL getResource(String path) | 返回映射到某个资源文件的URL对象。参数path必须以正斜线(/)开始,“/”表示当前Web应用的根目录 |
InputStream getResourceAsStream(String path) | 返回映射到某个资源文件的InputStream输入流对象。参数path传递规则和getResource()方法完全一致 |
package com.miao;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
// 配置初始化参数
//配置单个参数
//@WebServlet(name = "ServletDemo05", value = "/ServletDemo05",
// initParams = @WebInitParam(name = "encoding" , value = "UTF-8"))
//配置多个参数
//@WebServlet(name = "ServletDemo06", value = "/ServletDemo06",
// initParams = {@WebInitParam(name = "encoding", value = "UTF-8"),
// @WebInitParam(name="name" , value = "zhangsan")}
//)
public class ServletDemo08 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=utf-8");
ServletContext context = this.getServletContext();
InputStream stream = context.getResourceAsStream("/WEB-INF/classes/demo.properties");
Properties properties = new Properties();
properties.load(stream);
System.out.println(properties.getProperty("name"));
System.out.println(properties.getProperty("age"));
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
06ServletContext接口_哔哩哔哩_bilibili
HttpServletResponse对象
1.HttpServletResponse接口定义的状态码方法
当Servlet向客户端回送响应消息时,需要在响应消息中设置状态码,状态码代表着客户端请求服务器的结果。为此,HttpServletResponse接口定义了3个
发送状态码的方法。
HttpServletResponse接口—setStatus(int status)方法
setStatus(int status)方法用于设置HTTP响应消息的状态码,并生成响应状态行。由于响应状态行中的状态描述信息直接与状态码相关,而HTTP版本由服
务器确定,所以,只要通过setStatus(int status)方法设置了状态码,即可实现状态行的发送。例如,正常情况下,Web服务器会默认产生一个状态码为
200的状态行。
HttpServletResponse接口—sendError(int sc)方法
sendError(int sc)方法用于发送表示错误信息的状态码,例如,404状态码表示找不到客户端请求的资源。
HttpServletResponse接口—sendError(int code,String message)方法
sendError(int code, String message)方法除了设置状态码,还会向客户端发出一条错误信息。服务器默认会创建一个HTML格式的错误服务页面作为响
应结果,其中包含参数message指定的文本信息,这个HTML页面的内容类型为“text/html”,保留cookies和其他未修改的
响应头信息。如果一个对应于传入
的错误码的错误页面已经在web.xml中声明,那么这个声明的错误页面会将优先建议的message参数服务于客户端。
2.HttpServletResponse接口设置HTTP响应头字段的方法
方法说明 | 功能描述 |
---|---|
void addHeader(String name, String value) | 这两个方法都是用来设置HTTP协议的响应头字段,其中,参数name用于指定响应头字段的名称,参数value用于指定响应头字段的值。不同的是,addHeader()方法可以增加同名的响应头字段,而setHeader()方法则会覆盖同名的头字段 |
void setHeader(String name, String value) | |
void addIntHeader(String name,int value) | 这两个方法专门用于设置包含整数值的响应头。避免了调用addHeader()与setHeader()方法时,需要将int类型的设置值转换为String类型的麻烦 |
void setIntHeader(String name,int value) | |
void setContentLength(int len) | 该方法用于设置响应消息的实体内容的大小,单位为字节。对于HTTP协议来说,这个方法就是设置Content-Length响应头字段的值 |
void setContentType(String type) | 该方法用于设置Servlet输出内容的MIME类型,对于HTTP协议来说,就是设置Content-Type响应头字段的值。例如,如果发送到客户端的内容是jpeg格式的图像数据,就需要将响应头字段的类型设置为“image/jpeg”。需要注意的是,如果响应的内容为文本,setContentType()方法的还可以设置字符编码,如:text/html;charset=UTF-8 |
void setLocale(Locale loc) | 该方法用于设置响应消息的本地化信息。对HTTP来说,就是设置Content-Language响应头字段和Content-Type头字段中的字符集编码部分。需要注意的是,如果HTTP消息没有设置Content-Type头字段,setLocale()方法设置的字符集编码不会出现在HTTP消息的响应头中,如果调用setCharacterEncoding()或setContentType()方法指定了响应内容的字符集编码,setLocale()方法将不再具有指定字符集编码的功能 |
void setCharacterEncoding(String charset) | 该方法用于设置输出内容使用的字符编码,对HTTP 协议来说,就是设置Content-Type头字段中的字符集编码部分。如果没有设置Content-Type头字段,setCharacterEncoding方法设置的字符集编码不会出现在HTTP消息的响应头中。setCharacterEncoding()方法比setContentType()和setLocale()方法的优先权高,setCharacterEncoding()方法的设置结果将覆盖setContentType()和setLocale()方法所设置的字符码表 |
需要注意的是,addHeader()、setHeader()、addIntHeader()、setIntHeader()方法都是用于设置各种头字段的,而setContetType()、setLoacale()和setCharacterEncoding()方法用于设置字符编码,这些设置字符编码的方法可以有效解决中文字符乱码问题。
3.发送响应消息体相关的方法
由于在HTTP响应消息中,大量的数据都是通过响应消息体传递的,所以,ServletResponse遵循IO流传递大量数据的设计理念。在发送响应消息体时,定义了两个与输出流相关的方法。
getOutputStream()方法
getOutputStream()方法所获取的字节输出流对象为ServletOutputStream类型。由于ServletOutputStream是OutputStream的子类,它可以直接输出字节数组中的二进制数据。所以,要想输出二进制格式的响应正文,就需要调用getOutputStream()方法。
getWriter()方法
getWriter()方法所获取的字符输出流对象为PrintWriter类型。由于PrintWriter类型的对象可以直接输出字符文本内容,所以,要想输出内容为字符文本的网页文档,需要调用getWriter()方法。
07HttpServletResponse对象_哔哩哔哩_bilibili
案例:
package com.miao.response;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
/**
* \* Created with IntelliJ IDEA.
* \* User: maomao
* \* Date: 2022/3/18
* \* Time: 17:11
* \* Description:
* \
*/
@WebServlet(name = "PrintServletDemo", value = "/PrintServletDemo")
public class PrintServletDemo extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String data = "同学们,好好学习JavaWeb!!!!!";
// 获取字节流输出对象
// ServletOutputStream outputStream = resp.getOutputStream();
// outputStream.write(data.getBytes());
// 获取字符流输出对象
resp.setContentType("text/html;charset=utf-8");
PrintWriter writer = resp.getWriter();
writer.println(data);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
注意:路径的问题
HttpServletResponse应用
1.实现请求重定向
08HttpServletResponse应用_哔哩哔哩_bilibili
在某些情况下,针对客户端的请求,一个Servlet类可能无法完成全部工作。这时,可以使用请求重定向来完成。所谓请求重定向,指的是Web服务器接收到客户端的请求后,可能由于某些条件限制,不能访问当前请求URL所指向的Web资源,而是指定了一个新的资源路径,让客户端重新发送请求。
HttpServletResponse接口—sendRedirect()方法
为了实现请求重定向,HttpServletResponse接口定义了一个sendRedirect()方法,该方法用于生成302响应码和Location响应头,从而通知客户端重新访问Location响应头中指定的URL。sendRedirect()方法的完整声明如下所示:
public void sendRedirect(java.lang.String location) throws java.io.IOException
需要注意的是,参数location可以使用相对URL,Web服务器会自动将相对URL翻译成绝对URL,再生成Location头字段。
login.html
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<!--把表单内容提交到chapter04工程下的LoginServlet-->
<form action="LoginServlet" method="post">
用户名: <input type="text" name="username" /><br />
密 码:<input type="password" name="password"/><br />
<input type="submit" value="登录" />
</form>
</body>
</html>
welcome.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
欢迎你,登录成功!
</body>
</html>
LoginServletDemo
package com.miao.login;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet(name = "LoginServletDemo", value = "/LoginServletDemo")
public class LoginServletDemo extends HttpServlet {
// 在这里可以用doGet 和 doPost两个方法
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
resp.setContentType("text/html;charset=utf-8");
String username = req.getParameter("username");
String password = req.getParameter("password");
final String root = "/chapter04_war_exploded";
if (username.equals("zahngsan") && password.equals("123456")) {
resp.sendRedirect(root + "/welcome.html");
} else {
resp.sendRedirect(root + "/login.html");
}
}
}
2.页面中的乱码问题
HttpServletResponse接口提供了一个setCharacterEncoding()方法,该方法用于设置字符的编码方式,接下来对chineseServlet类进行修改,在代码String data = “中国”;前增加一行代码,设置字符编码使用的码表为UTF-8。
response.setCharacterEncoding("utf-8");
HttpServletRequest对象
1. HttpServletRequest接口获取请求行的相关方法
方法声明 | 功能描述 |
---|---|
String getMethod( ) | 该方法用于获取HTTP请求消息中的请求方式(如GET、POST等) |
String getRequestURI( ) | 该方法用于获取请求行中资源名称部分,即位于URL的主机和端口之后、参数部分之前的数据 |
String getQueryString( ) | 该方法用于获取请求行中的参数部分,也就是资源路径后面问号(?)以后的所有内容 |
String getProtocol( ) | 该方法用于获取请求行中的协议名和版本,例如HTTP/1.0或HTTP/1.1 |
String getContextPath( ) | 该方法用于获取请求URL中属于Web应用程序的路径,这个路径以“/”开头,表示相对于整个Web站点的根目录,路径结尾不含“/”。如果请求URL属于Web站点的根目录,那么返回结果为空字符串("") |
String getServletPath( ) | 该方法用于获取Servlet的名称或Servlet所映射的路径 |
String getRemoteAddr( ) | 该方法用于获取请求客户端的IP地址,其格式类似于“192.168.0.3” |
String getRemoteHost( ) | 该方法用于获取请求客户端的完整主机名,其格式类似于“pc1.itcast.cn”。需要注意的是,如果无法解析出客户机的完整主机名,该方法将会返回客户端的IP地址 |
int getRemotePort() | 该方法用于获取请求客户端网络连接的端口号 |
String getLocalAddr() | 该方法用于获取Web服务器上接收当前请求网络连接的IP地址 |
String getLocalName() | 该方法用于获取Web服务器上接收当前网络连接IP所对应的主机名 |
int getLocalPort() | 该方法用于获取Web服务器上接收当前网络连接的端口号 |
String getServerName() | 该方法用于获取当前请求所指向的主机名,即HTTP请求消息中Host头字段所对应的主机名部分 |
int getServerPort() | 该方法用于获取当前请求所连接的服务器端口号,即如果HTTP请求消息中Host头字段所对应的端口号部分 |
String getScheme() | 该方法用于获取请求的协议名,例如http、https或ftp |
StringBuffer getRequestURL() | 该方法用于获取客户端发出请求时的完整URL,包括协议、服务器名、端口号、资源路径等信息,但不包括后面的查询参数部分。注意,getRequestURL()方法返回的结果是StringBuffer类型,而不是String类型,这样更便于对结果进行修改 |
package com.miao;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
@WebServlet(name = "RequestmethodsServlet", value = "/RequestmethodsServlet")
public class RequestmethodsServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=utf-8");
PrintWriter writer = resp.getWriter();
writer.println("String getMethod( ) : 该方法用于获取HTTP请求消息中的请求方式(如GET、POST等)" + req.getMethod() + "<br />");
writer.println("String getRequestURI( ) : 该方法用于获取请求行中资源名称部分,即位于URL的主机和端口之后、参数部分之前的数据" + req.getRequestURI() + "<br />");
writer.println("String getQueryString( ) : 该方法用于获取请求行中的参数部分,也就是资源路径后面问号(?)以后的所有内容" + req.getQueryString() + "<br />");
writer.println("String getProtocol( ) : 该方法用于获取请求行中的协议名和版本,例如HTTP/1.0或HTTP/1.1" + req.getProtocol() + "<br />");
writer.println("String getContextPath( ) 该方法用于获取请求URL中属于Web应用程序的路径,这个路径以“/”开头,表示相对于整个Web站点的根目录,路径结尾不含“/”。" +
"如果请求URL属于Web站点的根目录,那么返回结果为空字符串(\"\")" +req.getContextPath() + "<br />");
writer.println("String getServletPath( ) : 该方法用于获取Servlet的名称或Servlet所映射的路径" + req.getServletPath() + "<br />");
writer.println("String getRemoteAddr( ) : 该方法用于获取请求客户端的IP地址,其格式类似于192.168.0.3" + req.getRemoteAddr() + "<br />");
writer.println("String getRemoteHost( ) : 该方法用于获取请求客户端的完整主机名,其格式类似于“pc1.itcast.cn”。需要注意的是," +
"如果无法解析出客户机的完整主机名,该方法将会返回客户端的IP地址" + req.getRemoteHost() + "<br />");
writer.println("int getRemotePort() : 该方法用于获取请求客户端网络连接的端口号" + req.getRemotePort() + "<br />");
writer.println("iString getLocalAddr() : 该方法用于获取Web服务器上接收当前请求网络连接的IP地址" + req.getLocalAddr() + "<br />");
writer.println("String getLocalName() : 该方法用于获取Web服务器上接收当前网络连接IP所对应的主机名" + req.getLocalName() + "<br />");
writer.println("int getLocalPort() : 该方法用于获取Web服务器上接收当前网络连接的端口号" + req.getLocalPort() + "<br />");
writer.println("String getServerName() : 该方法用于获取请求客户端网络连接的端口号" + req.getServerName() + "<br />");
writer.println("int getServerPort() : 该方法用于获取当前请求所连接的服务器端口号,即如果HTTP请求消息中Host头字段所对应的端口号部分" + req.getServerPort() + "<br />");
writer.println("String getScheme() : 该方法用于获取请求客户端网络连接的端口号" + req.getScheme() + "<br />");
writer.println("StringBuffer getRequestURL() : 该方法用于获取客户端发出请求时的完整URL,包括协议、服务器名、端口号、资源路径等信息,但不包括后面的查询参数部分。" +
"注意,getRequestURL()方法返回的结果是StringBuffer类型,而不是String类型,这样更便于对结果进行修改" + req.getRequestURL() + "<br />");
}
}
结果:
2. 获取请求头的相关方法
方法声明 | 功能描述 |
---|---|
String getHeader(String name) | 该方法用于获取一个指定头字段的值,如果请求消息中没有包含指定的头字段,getHeader()方法返回null;如果请求消息中包含有多个指定名称的头字段,getHeader()方法返回其中第一个头字段的值 |
Enumeration getHeaders(String name) | 该方法返回一个Enumeration集合对象,该集合对象由请求消息中出现的某个指定名称的所有头字段值组成。在多数情况下,一个头字段名在请求消息中只出现一次,但有时候可能会出现多次 |
Enumeration getHeaderNames() | 该方法用于获取一个包含所有请求头字段的Enumeration对象 |
int getIntHeader(String name) | 该方法用于获取指定名称的头字段,并且将其值转为int类型。需要注意的是,如果指定名称的头字段不存在,返回值为-1;如果获取到的头字段的值不能转为int类型,将发生NumberFormatException异常 |
long getDateHeader(String name) | 该方法用于获取指定头字段的值,并将其按GMT时间格式转换成一个代表日期/时间的长整数,这个长整数是自1970年1月1日0点0分0秒算起的以毫秒为单位的时间值 |
String getContentType() | 该方法用于获取Content-Type头字段的值,结果为String类型 |
int getContentLength() | 该方法用于获取Content-Length头字段的值,结果为int类型 |
String getCharacterEncoding() | 该方法用于返回请求消息的实体部分的字符集编码,通常是从Content-Type头字段中进行提取,结果为String类型 |
09HttpServletRequest接口获取请求行的相关方法_哔哩哔哩_bilibili
package com.miao;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;
@WebServlet(name = "RequestmethodsServlet02", value = "/RequestmethodsServlet02")
public class RequestmethodsServlet02 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=utf-8");
PrintWriter writer = resp.getWriter();
Enumeration<String> names = req.getHeaderNames();
// 循环便利请求头
while(names.hasMoreElements()){
String element = names.nextElement();
writer.println(element + " : " + req.getHeader(element) + "<br />");
}
}
}
3.请求转发
getRequestDispatcher()方法
Servlet之间可以相互跳转,利用Servlet的跳转可以很容易地把一项任务按模块分开,例如,使用一个Servlet实现
用户登录,然后跳转到另外一个Servlet实现用户资料修改。Servlet的跳转要通过RequestDispatcher接口的实例
对象实现。HttpServletRequest接口提供了getRequestDispatcher()方法用于获取RequestDispatcher对象,
getRequestDispatcher()方法的具体格式如下所示。
RequestDispatcher getRequestDispatcher(String path)
getRequestDispatcher()方法返回封装了某条路径所指定资源的RequestDispatcher对象。其中,参数 path 必须
以“/”开头,用于表示当前 Web 应用的根目录。需要注意的是,WEB-INF目录中的内容对RequestDispatcher对象也
是可见的。因此,传递给 getRequestDispatcher(String path) 方法的资源可以是 WEB-INF 目录中的文件。
forward()方法
获取到RequestDispatcher对象后,如果当前 Web 资源不想处理请求,RequestDispatcher接口提供了一个
forward()方法,该方法可以将当前请求传递给其他 Web 资源对这些信息进行处理并响应给客户端,这种方式称为请
求转发。forward()方法的具体格式如下所示。
forward(ServletRequest request,ServletResponse response)
forward()方法用于将请求从一个 Servlet 传递给另一个 Web 资源。在 Servlet 中,可以对请求做一个初步处
理,然后通过调用forward()方法,将请求传递给其他资源进行响应。需要注意的是,该方法必须在响应提交给客户端
之前被调用,否则将抛出 IllegalStateException 异常。
10请求转发、获取请求参数、通过Request对象传递数据_哔哩哔哩_bilibili
package com.miao;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
@WebServlet(name = "HelloServlet" ,value = "/HelloServlet")
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String serverName = request.getServerName();
System.out.println(serverName);
// 不配置 servlet-name 时 打印servlet-name
System.out.println(this.getServletConfig().toString());
String username = (String) request.getAttribute("username");
response.getWriter().println("Hello Servlet!!!" + username);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
package com.miao.request;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;
/**
* \* Created with IntelliJ IDEA.
* \* User: maomao
* \* Date: 2022/3/19
* \* Time: 11:27
* \* Description:
* \
*/
@WebServlet(name = "RequestMethodServlet" , value = "/RequestMethodServlet")
public class RequestMethodServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=utf-8");
req.setCharacterEncoding("UTF-8");
PrintWriter writer = resp.getWriter();
// 请求转发
// 1.将数据设置到req对象中
// 2.得到RequestDispatcher对象,封装要跳转到的servlet路径
// 3.带着req 和 resp 通过forward方法跳转
req.setAttribute("username" , "zs");
RequestDispatcher requestDispatcher = req.getRequestDispatcher("/HelloServlet");
requestDispatcher.forward(req,resp);
}
}
4.获取请求参数
方法声明 | 功能描述 |
---|---|
String getParameter(String name) | 该方法用于获取某个指定名称的参数值,如果请求消息中没有包含指定名称的参数,getParameter()方法返回null;如果指定名称的参数存在但没有设置值,则返回一个空串;如果请求消息中包含有多个该指定名称的参数,getParameter()方法返回第一个出现的参数值 |
String[] getParameterValues(String name) | 该方法用于返回一个String类型的数组,HTTP请求消息中可以有多个相同名称的参数(通常由一个包含有多个同名的字段元素的form表单生成),如果要获得HTTP请求消息中的同一个参数名所对应的所有参数值,那么就应该使用getParameterValues()方法 |
Enumeration getParameterNames() | 该方法用于返回一个包含请求消息中所有参数名的Enumeration对象,在此基础上,可以对请求消息中的所有参数进行遍历处理 |
Map getParameterMap() | 该方法用于将请求消息中的所有参数名和值装入进一个Map对象中返回 |
5. 通过Request对象传递数据
方法声明 | 功能描述 |
---|---|
setAttribute()方法 | setAttribute()方法用于将一个对象与一个name关联后存储进ServletRequest对象中,其完整声明定义如下: public void setAttribute(String name,Object o) setAttribute()方法的参数列表的第一个参数接收的是一个String类型的name,第二个参数接收的是一个Object类型的对象o。需要注意的是,如果ServletRequest对象中已经存在指定名称的属性,setAttribute()方法将会先删除原来的属性,然后再添加新的属性。如果传递给setAttribute()方法的属性值对象为null,则删除指定名称的属性,这时的效果等同于removeAttribute()方法。 |
getAttribute()方法 | getAttribute()方法用于从ServletRequest对象中返回指定名称的属性对象,其完整声明如下: public Object getAttribute(String name); |
removeAttribute()方法 | removeAttribute()方法用于从ServletRequest对象中删除指定名称的属性,其完整声明如下:public void removeAttribute(String name); |
getAttributeNames()方法 | getAttributeNames()方法用于返回一个包含ServletRequest对象中的所有属性名的Enumeration对象,在此基础上,可以对ServletRequest对象中的所有属性进行遍历处理。getAttributeNames()方法声明如下:public Enumeration getAttributeNames(); 需要注意的是,只有属于同一个请求中的数据才可以通过ServletRequest对象传递数据。 |
会话及会话技术
当用户通过浏览器访问Web应用时,通常情况下,服务器需要对用户的状态进行跟踪。例如,用户在网站结算商品时,Web服务器必须根据
请求用户的身份,找到该用户所购买的商品。在Web开发中,服务器跟踪用户信息的技术称为会话技术,下面将针对会话及会话技术进行详细讲
解。
1. 会话概述
在日常生活中,从拨通电话到挂断电话之间的一连串的你问我答的过程就是一个会话。在打电话过程中,通话双方会有通话内容,同样,在
客户端与服务器交互的过程中,也会产生一些数据。例如,用户甲和乙分别登录了购物网站,甲购买了一个iPhone手机,乙购买了一个iPad,当
这两个用户结账时,Web服务器需要对用户甲和乙的信息分别进行保存。为了保存会话过程中产生的数据,Servlet提供了两个用于保存会话数据
的对象,分别是Cookie和Session。
2. Cookie对象
在现实生活中,当顾客在购物时,商城经常会赠送顾客一张会员卡,卡上记录用户的个人信息(姓名,手机号等)、消费额度和积分额度
等。顾客一旦接受了会员卡,以后每次光临该商场时,都可以使用这张会员卡,商场也将根据会员卡上的消费记录计算会员的优惠额度和累加积
分。在Web应用中,Cookie的功能类似于会员卡,当用户通过浏览器访问Web服务器时,服务器会给客户端发送一些信息,如用户信息和商品信
息,这些信息都保存在Cookie中。这样,当该浏览器再次访问服务器时,会在请求头中将Cookie发送给服务器,方便服务器对浏览器做出正确地
响应。
服务器向客户端发送Cookie时,会在HTTP响应头字段中增加Set-Cookie响应头字段。Set-Cookie头字段中设置的Cookie的具体示例如
下:
Set-Cookie: user=itcast; Path=/;
user表示Cookie的名称,itcast表示Cookie的值,Path表示Cookie的属性。Cookie必须以键值对的形式存在,Cookie属性可以有多个,
属性之间用分号“;”和空格分隔。
当用户第一次访问服务器时,服务器会在响应消息中增加Set-Cookie头字段,将用户信息以Cookie的形式发送给浏览器。一旦用户浏览器
接受了服务器发送的Cookie信息,就会将它保存在浏览器的缓冲区中,这样,当浏览器后续访问该服务器时,都会在请求消息中将用户信息以
Cookie的形式发送给服务器,从而使服务器分辨出当前请求是由哪个用户发出的。
Cookie类有且仅有一个构造方法,具体语法格式如下:
public Cookie(java.lang.String name,java.lang.String value);
在Cookie的构造方法中,参数name用于指定Cookie的名称,value用于指定Cookie的值。需要注意的是,Cookie一旦创建,它的名称就不
能再更改,Cookie的值可以为任何值,创建后允许被修改。
1. Cookie类的常用方法
方法声明 | 功能描述 |
---|---|
String getName() | 用于返回Cookie的名称 |
void setValue(String newValue) | 用于为Cookie设置一个新的值 |
String getValue() | 用于返回Cookie的值 |
void setMaxAge(int expiry) | 用于设置Cookie在浏览器客户机上保持有效的秒数 |
int getMaxAge() | 用于返回Cookie在浏览器客户机上保持有效的秒数 |
void setPath(String uri) | 用于设置该Cookie项的有效目录路径 |
String getPath() | 用于返回该Cookie项的有效目录路径 |
void setDomain(String pattern) | 用于设置该Cookie项的有效域 |
String getDomain() | 用于返回该Cookie项的有效域 |
void setVersion(int v) | 用于设置该Cookie项采用的协议版本 |
int getVersion() | 用于返回该Cookie项采用的协议版本 |
void setComment(String purpose) | 用于设置该Cookie项的注解部分 |
String getComment() | 用于返回该Cookie项的注解部分 |
void setSecure(boolean flag) | 用于设置该Cookie项是否只能使用安全的协议传送 |
boolean getSecure() | 用于返回该Cookie项是否只能使用安全的协议传送 |
2. setMaxAge(int expiry)方法和getMaxAge()方法
setMaxAge(int expiry)和getMaxAge()方法分别用于设置和返回Cookie在浏览器上保持有效的秒数。如果设置的值为一个正整数,浏览
器会将Cookie信息保存在本地硬盘中。从当前时间开始,在没有超过指定的秒数之前,这个Cookie都保持有效,并且同一台计算机上运行的该浏
览器都可以使用这个Cookie信息。如果设置值为负整数,浏览器会将Cookie信息保存在浏览器的缓存中,当浏览器关闭时,Cookie信息会被删
除。如果设置值为0,则浏览器会立即删除这个Cookie信息。
3. setPath(String uri)方法和getPath()方法
setPath(String uri)方法和getPath()方法是针对Cookie的Path属性的。如果创建的某个Cookie对象没有设置Path属性,那么该
Cookie只对当前访问路径所属的目录及其子目录有效。如果想让某个Cookie项对站点的所有目录下的访问路径都有效,应调用Cookie对象的
setPath()方法将其Path属性设置为“/”。
4. setDomain(String pattern)方法和getDomain()方法
setDomain(String pattern)方法和getDomain()方法是针对Cookie的domain属性的。domain属性用于指定浏览器访问的域。例如,传
智播客的域为“itcast.cn”。设置domain属性时,其值必须以“.”开头,如domain=.itcast.cn。默认情况下,domain属性的值为当前主机
名,浏览器在访问当前主机下的资源时,都会将Cookie信息发送给服务器(当前主机)。需要注意的是,domain属性的值不区分大小写。
案例:显示用户上次访问时间
当用户访问某些Web应用时,经常会显示出该用户上一次的访问时间。例如,QQ登录成功后,会显示用户上次的登录时间。本案例要求使用Cookie技术实现显示用户上次的访问时间。显示用户上次访问时间效果如下图所示。
package com.miao;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.Date;
@WebServlet(name = "ServletDemo11",value = "/ServletDemo11")
public class ServletDemo11 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 指定服务器输出内容的编码方式为UTF-8,防止发生乱码
resp.setContentType("text/html;charset=utf-8");
// 获取所有的cookie
Cookie[] cookies = req.getCookies();
// 定义一个flag变量用于判断cookie是否为空
boolean flag = false;
// 遍历cookies数组
if (cookies != null && cookies.length > 0) {
// 定义一个字符串,用于保存 lastTime
String lastTime = "lastTime";
for (Cookie cookie : cookies) {
// 获取cookie的名称
String name = cookie.getName();
// 判断是否为lastTime
if (lastTime.equals(name)) {
// 该cookie不是第一次登录,显示上次登录的时间
flag = true;
// 获取cookie的value
String value = cookie.getValue();
System.out.println(value);
System.out.println("------------");
// 解码
value = URLDecoder.decode(value, "utf-8");
System.out.println(value);
resp.getWriter().write("欢迎回来 , 您上次访问的时间是: " + value);
// 重新设置访问的时间
Date date = new Date();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy年mm月dd日 HH:mm:ss");
String format = simpleDateFormat.format(date);
System.out.println(format);
System.out.println("-------------");
String encode = URLEncoder.encode(format,"utf-8");
System.out.println(encode);
// 保存cookie的值
cookie.setValue(encode);
// 设置cookie的存活时间 一个月
cookie.setMaxAge(60 * 60 * 24 * 30);
// 加入当前cookie请求时间
resp.addCookie(cookie);
break;
}
}
if (cookies == null || cookies.length == 0 || flag == false) {
Date date = new Date();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy年mm月dd日 HH:mm:ss");
String format = simpleDateFormat.format(date);
System.out.println(format);
System.out.println("-------------");
String encode = URLEncoder.encode(format,"utf-8");
System.out.println(encode);
// 创建一个cookie
Cookie cookie = new Cookie(lastTime,encode);
// 设置cookie的存活时间 一个月
cookie.setMaxAge(60 * 60 * 24 * 30);
// 加入当前cookie请求时间
resp.addCookie(cookie);
resp.getWriter().write("您好欢迎首次登陆!!!");
}
}
}
}
刷新之后:
3. Session对象
当人们去医院就诊时,就诊病人需要办理医院的就诊卡,就诊卡上只有卡号,没有其他信息。但病人每次去该医院就诊时,只要出示就诊
卡,医务人员便可根据卡号查询到病人的就诊信息。Session技术类似医院办理就诊卡和医院为每个病人保留病历档案的过程。当浏览器访问Web
服务器时,Servlet容器就会创建一个Session对象和ID属性, Session对象就相当于病历档案,ID就相当于就诊卡号。当客户端后续访问服务
器时,只要将ID传递给服务器,服务器就能判断出该请求是哪个客户端发送的,从而选择与之对应的Session对象为其服务。
用户甲和用户乙都调用buyServlet将商品添加到购物车,调用payServlet进行商品结算。由于甲和乙购买商品的过程类似,在此,以用户
甲为例进行详细说明。当用户甲访问购物网站时,服务器为甲创建了一个Session对象(相当于购物车)。当甲将iPhone手机添加到购物车时,
iPhone手机的信息便存放到了Session对象中。同时,服务器将Session对象的ID属性以Cookie (Set-Cookie: JSESSIONID=111)的形式返回
给甲的浏览器。当甲完成购物进行结账时,需要向服务器发送结账请求,这时,浏览器自动在请求消息头中将Cookie (Cookie:
JSESSIONID=111)信息发送给服务器,服务器根据ID属性找到为用户甲所创建的Session对象,并将Session对象中所存放的iPhone手机信息取
出进行结算。
Session还具有更高的安全性,它将关键数据保存在服务器。cookie则是将数据存在客户端的浏览器中。因此cookie是较为危险的,若客
户端遭遇黑客攻击,cookie信息容易被窃取,数据也可能被篡改,而运用Session可以有效避免这种情况的发生。
1. Session对象的getSession()方法
Session是与每个请求消息紧密相关的,为此,HttpServletRequest定义了用于获取Session对象的getSession()方法,该方法有两种重
载形式,具体如下:
public HttpSession getSession(boolean create)//第一个
public HttpSession getSession()//第二个
第一个getSession()方法根据传递的参数判断是否创建新的HttpSession对象,如果参数为true,则在相关的HttpSession对象不存在时
创建并返回新的HttpSession对象,否则不创建新的HttpSession对象,而是返回null。
第二个getSession()方法相当于第一个方法参数为true时的情况,在相关的HttpSession对象不存在时总是创建新的HttpSession对象。
需要注意的是,由于getSession()方法可能会产生发送会话标识号的Cookie头字段,所以必须在发送任何响应内容之前调用getSession()方
法。
2. HttpSession接口中的常用方法
方法声明 | 功能描述 |
---|---|
String getId() | 用于返回与当前HttpSession对象关联的会话标识号 |
long getCreationTime() | 用于返回Session创建的时间,这个时间是创建Session的时间与1970年1月1日00:00:00之间时间差的毫秒表示形式 |
long getLastAccessedTime() | 用于返回客户端最后一次发送与Session相关请求的时间,这个时间是发送请求的时间与1970年1月1日00:00:00之间时间差的毫秒表示形式,时间戳 |
void setMaxInactiveInterval(int interval) | 用于设置当前HttpSession对象可空闲的以秒为单位的最长时间,也就是修改当前会话的默认超时间隔 |
boolean isNew() | 判断当前HttpSession对象是否是新创建的 |
void invalidate() | 用于强制使Session对象无效 |
ServletContext getServletContext() | 用于返回当前HttpSession对象所属于的Web应用程序对象,即代表当前Web应用程序的ServletContext对象 |
void setAttribite(String name,Object value) | 用于将一个对象与一个名称关联后存储到当前的HttpSession对象中 |
String getAttribute() | 用于从当前HttpSession对象中返回指定名称的属性对象 |
void removeAttribute(String name) | 用于从当前HttpSession对象中删除指定名称的属性 |
3. Session生效
Sessinon在用户第一次访问服务器时创建,需要注意只有访问JSP(JSP将在第6章讲解)、Servlet等程序时才会创建Session。此外,还
可调用request.getSession(true)强制生成Session。只访问HTML、IMAGE等静态资源并不会创建Session。
4. Session失效—“超时限制”判断Session是否生效
Web服务器采用“超时限制”判断客户端是否还在继续访问。在一定时间内,如果某个客户端一直没有请求访问,那么,Web服务器就会认为
该客户端已经结束请求,并且将与该客户端会话所对应的HttpSession对象变成垃圾对象,等待垃圾收集器将其从内存中彻底清除。反之,如果
浏览器超时后,再次向服务器发出请求访问,那么,Web服务器会创建一个新的HttpSession对象,并为其分配一个新的ID属性。
5. Session失效—强制Session失效
invalidate()方法,该方法可以强制使Session对象失效,具体用法如下所示:
HttpSession session = request.getSession();
session.invalidate();//注销该request的所有session
有时默认的Session失效时间并不能满足我们的需求。这时我们需要自定义Session的失效时间,自定义Session的失效时间有3种:
第1种
在项目的web.xml文件中配置Session的失效时间,具体代码如下所示:
<session-config>
<session-timeout>30</session-timeout>
</session-config>
需要注意的是,在web.xml配置的Session失效时间默认是分钟,所以上述代码设置Session失效时间为30分钟。
第2种
在Servlet程序中手动设置Session的失效时间,具体代码如下所示:
session.setMaxInactiveInterval(30 * 60);//设置单位为秒,设置为-1永不过期
第3种
上述代码设置了Session的失效时间为30分钟,如果将值设置为-1,则该Session永不过期。在<Tomcat安装目录>\conf\web.xml文件
中,可以找到如下一段配置信息:
<session-config>
<session-timeout>30</session-timeout>
</session-config>
在上述代码的配置信息中,设置的时间值是以分钟为单位的,即Tomcat服务器的默认会话超时间隔为30分钟。如果将<session-timeout>
元素中的时间值设置成0或负数,则表示会话永不超时。需要注意的是<Tomcat安装目录>\conf\web.xml文件对站点内的所有Web应用程序都起
作用。
案例:实现购物车功能
1.ServletDemo12 展示购物车中的商品
package com.miao;
import com.miao.DB.CakeDB;
import com.miao.pojo.Cake;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Collection;
/**
* 展示所有可以购买的蛋糕的列表
*/
@WebServlet(name = "ServletDemo12", value = "/ServletDemo12")
public class ServletDemo12 extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 设置响应的格式编码
resp.setContentType("text/html;charset=utf-8");
PrintWriter writer = resp.getWriter();
Collection<Cake> cakes = CakeDB.getAll();
// 遍历输出cakes列表
writer.write("本店提供的蛋糕有:<br />" );
for (Cake cake : cakes) {
String url = "PurchaseServlet?id=" + cake.getId();
writer.write(cake.getName() + "<a href=" + url + ">" +" 购买 </a> <br />");
}
}
}
2.PurchaseServlet 用于处理购买的操作
package com.miao;
import com.miao.DB.CakeDB;
import com.miao.pojo.Cake;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@WebServlet(name = "PurchaseServlet", value = "/PurchaseServlet")
public class PurchaseServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String id = req.getParameter("id");
// 重定向,如果id为空,重新跳转到ServletDemo12
if (id == null || id.equals("")) {
String url = "/ServletDemo01_war_exploded/ServletDemo12";
resp.sendRedirect(url);
return;
}
Cake cake = CakeDB.getCake(id);
// 创建或者获得用户的session对象
HttpSession session = req.getSession();
// 从session对象中过去用胡的购物车
List<Cake> cart = (List) session.getAttribute("cart");
if (cart == null) {
// 首次购买,为用户创建一个购物车List模拟购物车
cart = new ArrayList<Cake>();
// 将购物车存进session对象
session.setAttribute("cart",cart);
}
// 将cake放入session
cart.add(cake);
// 创建cookie存放session的标识号
Cookie cookie = new Cookie("JSESSIONID", session.getId());
// 设置cookie的过期时间
cookie.setMaxAge(60 * 30);
// 设置有效的路径
cookie.setPath("/");
// 将cookie添加到相应体
resp.addCookie(cookie);
// 重定向到购物车页面
String url = "/ServletDemo01_war_exploded/CartServlet";
resp.sendRedirect(url);
}
}
3.CartServlet 购物车展示
package com.miao;
import com.miao.pojo.Cake;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
@WebServlet(name = "CartServlet", value = "/CartServlet")
public class CartServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=utf-8");
PrintWriter writer = resp.getWriter();
// 变量cart引用用的购物车
List<Cake> cart = null;
// 标记用户是否购买过商品
boolean purFlag = true;
// 获得用户的session
HttpSession session = req.getSession();
// 如果session为空,将purFlag设置为false
if (session == null) {
purFlag = false;
} else {
// 获得用户的购物车
cart = (List) session.getAttribute("cart");
// 如果购物车为空 purFlag置为false
if (cart == null) {
purFlag = false;
}
}
String url = "/ServletDemo01_war_exploded/ServletDemo12";
// 如果pueFlag为false,则表示用户没有购买商品,返回商品列表页面
if (purFlag == false) {
writer.write("对不起,您没有购买任何商品!!! <a href=" + url + ">点击返回列表页面</a>");
} else {
writer.write("您购买的蛋糕有<br />");
for (Cake cake : cart) {
writer.write(cake.getName() + "<br />");
}
writer.write("<a href=" + url + ">点击返回列表页面,继续添加</a>");
}
}
}
4.Cake实例
package com.miao.pojo;
import java.io.Serializable;
public class Cake implements Serializable {
private static final long serialVersionUID = 1L;
private String id;
private String name;
public Cake() {
}
public Cake(String id, String name) {
this.id = id;
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
5. CakeDB 模拟数据库
package com.miao.DB;
import com.miao.pojo.Cake;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
public class CakeDB {
private static Map<String, Cake> cake = new LinkedHashMap<String, Cake>();
static {
cake.put("1", new Cake("1", "A蛋糕"));
cake.put("2", new Cake("2", "B蛋糕"));
cake.put("3", new Cake("3", "C蛋糕"));
cake.put("4", new Cake("4", "D蛋糕"));
}
// 获得所有的蛋糕
public static Collection<Cake> getAll(){
return cake.values();
}
// 根据id获得指定的蛋糕
public static Cake getCake(String id) {
return cake.get(id);
}
}
案例:实现用户登录注册功能
1 login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="http://localhost:8080/ServletDemo01_war_exploded/LoginServlet" method="post">
username: <input type="text" name="username" id="username">
password: <input type="password" name="password" id="password">
<input type="submit" value="提交">
</form>
</body>
</html>
2. LoginServlet 处理登录的操作
package com.miao;
import com.miao.pojo.User;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet(name = "LoginServlet",value = "/LoginServlet")
public class LoginServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=utf-8");
String username = req.getParameter("username");
String password = req.getParameter("password");
// 模拟数据库的验证
if (username.equals("zhangsan") && password.equals("123456")) {
User user = new User();
user.setUsername(username);
user.setPassword(password);
req.getSession().setAttribute("user", user);
resp.sendRedirect("/ServletDemo01_war_exploded/IndexServlet");
} else {
resp.sendRedirect("login.html");
}
}
}
3. IndexServlet 展示用户登录的结果
package com.miao;
import com.miao.pojo.User;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
import java.io.IOException;
@WebServlet(name = "IndexServlet",value = "/IndexServlet")
public class IndexServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=utf-8");
// 获取session
HttpSession session = req.getSession();
User user = (User) session.getAttribute("user");
if (user == null) {
String url = "login.html";
resp.getWriter().write("您还没有登录,请点击<a href=" + url + ">登录</a>");
} else {
String url = "LogoutServlet";
resp.getWriter().write("您已经登录,欢迎" + user.getUsername());
resp.getWriter().write("<a href=" +url +">退出 </a>");
Cookie cookie = new Cookie("JSESSIONID", session.getId());
cookie.setMaxAge(60 * 30);
cookie.setPath("/");
resp.addCookie(cookie);
}
}
}
4. LogoutServlet 登出用户
package com.miao;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet(name = "LogoutServlet",value = "/LogoutServlet")
public class LogoutServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=utf-8");
req.getSession().removeAttribute("user");
resp.sendRedirect("/ServletDemo01_war_exploded/IndexServlet");
}
}
JSP技术
在动态网页开发中,经常需要动态生成HTML内容,例如,一篇新闻报道的浏览次数需要动态生成。如果使用Servlet实现HTML页面数据的
统计,需要调用大量的Servlet输出语句,使静态内容和动态内容混合在一起,导致程序非常臃肿。为了克服Servlet的这些缺点,Oracle
(Sun)公司推出了JSP技术。本章将围绕JSP技术进行详细讲解。
1. JSP概述
JSP全名是Java Server Pages,即Java服务器页面。它是Servlet更高级别的扩展。在JSP文件中,HTML代码与Java代码共同存在,其中,
HTML代码用来实现网页中静态内容的显示,Java代码用来实现网页中动态内容的显示。最终,JSP文件会通过Web服务器的Web容器编译成一
个Servlet,用来处理各种请求。
由于JSP是基于Java语言的,使用JSP开发的Web应用是跨平台的,可以应用于不同的系统中,如Windows、Linux等。当从一个平台移植到
另一个平台时,JSP和JavaBean的代码并不需要重新编译,这是因为Java的字节码是与平台无关的,这也符合了Java语言“一次编译,到处运行”
的特点。
在使用JSP技术开发Web应用时,可以将界面的开发与应用程序的开发分离开。开发人员使用HTML设计界面,使用JSP标签和脚本动态生成页
面上的内容。在服务器端,JSP容器负责解析JSP标签和脚本程序,生成所请求的内容,并将执行结果以HTML页面的形式返回给浏览器。
JSP中可以使用JavaBean编写业务组件,也就是使用一个JavaBean封装业务处理代码或者作为一个数据存储模型,在JSP页面中,甚至在整
个项目中,都可以重复使用这个JavaBean,同时,JavaBean也可以应用到其他Java应用程序中。
预编译就是在用户第一次通过浏览器访问JSP页面时,服务器将对JSP页面代码进行编译,并且仅执行一次编译。编译好的代码将被保存,
在用户下一次访问时,会直接执行编译好的代码。这样不仅节约了服务器的CPU资源,还大大提升了客户端的访问速度。
创建一个jsp文件
<%--
Created by IntelliJ IDEA.
User: maomao
Date: 2022/3/22
Time: 10:18
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title> </title>
</head>
<body>
Hello JSP
</body>
</html>
注意:新创建的JSP文件与传统的HTML文件几乎没有什么区别,唯一的区别是默认创建时,页面代码最上方多了一条page指令,并且该文件的后缀名是jsp,而不是html。Demo.jsp的标签中添加的内容已被显示出来,这说明HTML元素可以被JSP容器解析。实际上,JSP只是在原有的HTML文件中加入了一些具有Java特点的代码,这些称为JSP的语法元素。
访问:localhost:8080/ServletDemo01_war_exploded/jsp/Demo.jsp
1. JSP运行原理
JSP的工作模式是请求/响应模式,客户端首先发出HTTP请求,JSP程序收到请求后进行处理并返回处理结果。一个JSP文件第一次被请求
时,JSP容器把该JSP文件转换成为一个Servlet,而这个容器本身也是一个Servlet。
第一步: 客户端发出请求,请求访问JSP文件。
第二步:JSP容器先将JSP文件转换成一个Java源文件(Java Servlet源程序),在转换过程中,如果发现JSP文件中存在任何语法错误,
则中断转换过程,并向服务端和客户端返回出错信息。
第三步:如果转换成功,则JSP容器将生成的Java源文件编译成相应的字节码文件*.class。该class文件就是一个Servlet,Servlet容器
会像处理其他Servlet一样来处理它。
第四步:由Servlet容器加载转换后的Servlet类(.class文件)创建一个该Servlet(JSP页面的转换结果)的实例,并执行Servlet的
第五步:jspInit()方法完成初始化。jspInit()方法在Servlet的整个生命周期中只会执行一次。
第六步:JSP容器执行jspService()方法处理客户端的请求。对于每一个请求,JSP容器都会创建一个新的线程来处理它。如果多个客户端
同时请求该JSP文件,则JSP容器会创建多个线程,使得每一个客户端请求都对应一个线程。JSP运行过程中采用的这种多线程的执行方式
可以极大地降低对系统资源的消耗,提高系统的并发量并缩短响应时间。需要注意的是,由于第 4 步生成的Servlet实例是常驻内存的,所以
响应速度非常快。
第七步:如果JSP文件被修改了,则服务器将根据新的设置决定是否对该文件进行重新编译。如果需要重新编译,则使用重新编译后的结果
取代内存中常驻的Servlet实例,并继续上述处理过程。
第八步:虽然JSP效率很高,但在第一次调用的时候往往由于需要转换和编译,会产生一些轻微的延迟。此外,由于系统资源不足等原因,
JSP容器可能会以某种不确定的方式将Servlet实例从内存中移除,发生这种情况时,JSP容器首先会调用jspDestroy()方法,然后Servlet实例
会被加入“垃圾收集”处理。当请求处理完成后,响应对象由JSP容器接收,并将HTML格式的响应信息发送回客户端。
了解了JSP的运行原理后,完全可以利用其中的一些步骤来做一些工作,例如,可以在jspInit()中进行一些初始化工作(建立数据库的连
接、建立网络连接、从配置文件中获取一些参数等),可以在jspDestroy()中释放相应的资源等。
2. JSP基本语法
一个JSP页面可以包括指令标识、HTML代码、JavaScript代码、嵌入的Java代码、注释和JSP动作标识等内容。
JSP的脚本元素
JSP脚本元素是指嵌套在“<%”和“%>”之中的一条或多条Java程序代码。通过JSP脚本元素可以将Java代码嵌入HTML页面中,所有可执行的
Java代码,都可以通过JSP脚本执行。
JSP脚本元素主要包含如下三种类型:
(1)JSP Scriptlets 。
(2)声明标识 。
(3)JSP表达式。
JSP Scriptlets
JSP Scriptlets 是一段代码片段。所谓代码片段,就是在JSP页面中嵌入的Java代码或脚本代码。代码片段将在页面请求的处理期间被执
行,通过Java代码可以定义变量或流程控制语句等;而脚本代码可以应用JSP的内置对象在页面输出内容、处理请求和访问session会话等。JSP
Scriptlets的语法格式如下所示:
<% java 代码(变量、方法、表达式等)%>
声明标识
在JSP Scriptlets中可以进行属性的定义,也可以输出内容,但是它不可以进行方法的定义。如果想在脚本元素中定义方法,可以使用声
明标识。声明标识用于在JSP页面中定义全局变量或方法,它以“<%!”开始,以“%>”结束。通过声明标识定义的变量和方法可以被整个JSP页面访
问,所以通常使用该标识定义整个JSP页面需要引用的变量或方法。声明标识的语法格式如下所示:
<%! 定义变量或方法等 %>
在JSP声明语句中定义的都是成员方法、成员变量、静态方法、静态变量、静态代码块等。
在JSP声明语句中声明的方法在整个JSP页面内有效,但是在方法内定义的变量只在该方法内有效。当声明的方法被调用时,会为方法内定
义的变量分配内存,而调用结束后立刻会释放所占的内存。
注意:在一个JSP页面中可以有多个JSP声明标识,单个声明中的Java语句可以是不完整的,但是多个声明组合后的结果必须是完整的Java
语句。
<%--
Created by IntelliJ IDEA.
User: maomao
Date: 2022/3/22
Time: 10:34
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="java.util.Date" %>
<%@ page import="java.text.SimpleDateFormat" %>
<html>
<head>
<title>JSP--显示时间</title>
</head>
<body>
<%
Date date = new Date();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String format = simpleDateFormat.format(date);
%>
当前时间:<%=format %>
</body>
</html>
案例:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%
int a = 10;
int b = 30;
%>
a: <%=a %> <br>
b: <%=b %>
</body>
</html>
案例:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title> 撒地方 </title>
</head>
<body>
<%!
public String print(){
System.out.println("打印内容");
return "打印内容";
}
%>
<%=print() %>
</body>
</html>
需要注意的是,“<%!”和“%>”里面定义的属性是成员属性,相当于类的属性,方法相当于是全局的方法,也相当于是类里面的方法,但是在
“<%!”和“%>”里面是不可以进行输出的,只能在里面进行方法的定义和属性的定义。总之,“<%!”和“%>”是用来定义属性和方法的,“<%”和“%>”
主要是用来输出内容的,因此如果涉及到了成员变量的操作,那么就应该使用<%!和%>,而如果是涉及到了输出内容的时候,就使用<%和%>。
注意:通过声明标识创建的变量和方法在当前JSP页面中有效,它的生命周期是从创建开始到服务器结束;代码片段创建的变量或方法,也
是在当前JSP页面有效,但它的生命周期是页面关闭后就会被销毁。
JSP表达式
JSP表达式(expression)用于向页面输出信息,它以“<%=”开始,以“%>”结束,其基本的语法格式如下所示:
<%= expression %>
上述语法格式中,参数expression可以是任何Java语言的完整表达式,该表达式的最终运算结果将被转换成一个字符串。
注意:“<%=”是一个完整的符号,“<%”和“=”之间不能有空格,且JSP表达式中的变量或表达式后面不能有分号(;)。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%
int a = 1, b = 4;
%>
<%=a+b %>
</body>
</html>
JSP注释
带有JSP表达式的注释—单行注释
在JSP页面中可以嵌入代码片段,在代码片段中也可以加入注释。代码片段中的注释同Java的注释相同,其中单行注释以“//”开头,后面接注释内容,其语法格式如下: //注释内容
带有JSP表达式的注释—多行注释
多行注释以“/*”开头,以“*/”结束。在这个标识中间的内容为注释内容,并且注释内容可以换行。其语法格式如下:
/*
注释内容1
注释内容2
......
*/
带有JSP表达式的注释—提示文档注释
提示文档注释在被Javadoc文档工具生成文档时读取,文档是对代码结构和功能的描述。其语法格式如下:
/**
提示信息1
提示信息2
......
*/
隐藏注释
在文档中添加的HTML注释虽然在浏览器页面中不显示,但是可以通过查看源代码的方式看到这些注释信息。所以严格来说,这些注释是不安
全的。为此,JSP提供了隐藏注释,隐藏注释不仅在浏览器页面中看不到,在查看HTML源代码时也看不到,所以隐藏注释有着较高的安全性。隐
藏注释的语法格式如下:
<%-- 注释内容 --%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<% // 单行注释 %>
<% /*
多行注释
*/
%>
<% /**
文档注释
*/
%>
<!-- html注释 -->
<%-- 隐藏注释 --%>
</body>
</html>
JSP表达式
注意:在上图中,网页源代码只显示出了HTML注释,而没有显示JSP的注释信息。这是因为Tomcat编译JSP文件时,会将HTML注释当成普通
文本发送到客户端,而JSP页面中格式为“<%-- 注释信息 --%>”的内容则会被忽略,不会发送到客户端。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<!-- <%="// hello " %> -->
</body>
</html>
3.JSP指令
1. page指令的格式
在JSP页面中,经常需要对页面的某些特性进行描述,例如,页面的编码方式,JSP页面采用的语言等,这些特性的描述可以通过page指令
实现。page指令的具体语法格式如下所示:
<%@ page 属性名1= "属性值1" 属性名2= "属性值2" ...%>
page用于声明指令名称,属性用来指定JSP页面的某些特性。page指令还提供了一系列与JSP页面相关的属性。
2. page指令的常用属性
属性名称 | 取值范围 | 描述 |
---|---|---|
language | java | 指定JSP页面所用的脚本语言,默认为Java |
import | 任何包名、类名 | 指定在JSP页面翻译成的Servlet源文件中导入的包或类。import是唯一可以声明多次的page指令属性。一个import属性可以引用多个类,中间用英文逗号隔开 |
session | true、false | 指定该JSP内是否内置Session对象,如果为true,则说明内置Session对象,可以直接使用,否则没有内置Session对象。默认情况下,session属性的值为true。需要注意的是,JSP 容器自动导入以下4个包:java.lang.*javax.servlet.*javax.servlet.jsp.javax.servlet.http. |
isErrorPage | true、false | 指定该页面是否为错误处理页面,如果为true,则该JSP内置有一个Exception对象的exception,可直接使用。默认情况下,isErrorPage的值为false |
errorPage | 某个JSP页面的相对路径 | 指定一个错误页面,如果该JSP程序抛出一个未捕捉的异常,则转到errorPage指定的页面。errorPage指定页面的isErrorPage属性为true,且内置的exception对象为未捕捉的异常 |
contentType | 有效的文档类型 | 指定当前JSP页面的MIME类型和字符编码,例如:HTML格式为text/html纯文本格式为text/plainJPG图像为image/jpegGIF图像为image/gifWord文档为application/msword |
pageEnCoding | 当前页面 | 指定页面编码格式 |
3. page指令的示例
page指令的常见属性中除了import属性外,其他的属性都只能出现一次,否则会编译失败。下面列举两个使用page指令的示例:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ page import="java.awt.*" %>
<%@ page import="java.util.*","java.awt.*"%>
上面代码中使用了page指令的language、contentType、pageEncoding和import属性。需要注意的是,page指令对整个页面都有效,而
与其书写的位置无关,但是习惯上把page指令写在JSP页面的最前面。
4. include指令的格式
在实际开发时,有时需要在JSP页面中包含另一个JSP页面,这时,可以通过include指令实现,include指令的具体语法格式如下所示:
<%@ include file="被包含的文件地址"%>
include指令只有一个file属性,用于指定要包含文件的路径。需要注意的是,插入文件的路径一般不以“/”开头,而是使用相对路径。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="java.util.Date" %>
<%@ page import="java.text.SimpleDateFormat" %>
<html>
<head>
<title>JSP--显示时间</title>
</head>
<body>
<%
Date date = new Date();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String format = simpleDateFormat.format(date);
%>
当前时间:<%=format %>
</body>
</html>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%@ include file="timeinfo.jsp"%>
</body>
</html>
5. 使用include指令的常见问题
关于include指令的具体应用,有很多问题需要注意,下面介绍几个常见的问题。
(1)被引入的文件必须遵循JSP语法,其中的内容可以包含静态HTML、JSP脚本元素和JSP指令等普通JSP页面所具有的一切内容。
(2)除了指令元素之外,被引入的文件中的其他元素都被转换成相应的Java源代码,然后插入进当前JSP页面所翻译成的Servlet源文件
中,插入位置与include指令在当前JSP页面中的位置保持一致。
(3)file属性的设置值必须使用相对路径,如果以“/”开头,表示相对于当前Web应用程序的根目录(注意不是站点根目录);否则,表示
相对于当前文件。需要注意的是,这里的file属性指定的相对路径是相对于文件(file),而不是相对于页面(page)。
(4)在应用include指令进行文件包含时,为了使整个页面的层次结构不发生冲突,建议在被包含页面中将<html>,<body>等标签删除,
因为在包含页面的文件中已经指定了这些标签。
6. taglib指令的格式
在JSP文件中,可以通过taglib指令标识该页面中所使用的标签库,同时引用标签库,并指定标签的前缀。在页面中引用标签库后,就可以
通过前缀来引用标签库中的标签。taglib指令的具体语法格式如下:
<%@ taglib prefix="tagPrefix" uri="tagURI" %>
prefix:用于指定标签的前缀,该前缀不能命名为jsp、jspx、java、sun、servlet和sunw。
uri:用于指定标签库文件的存放位置。
·在页面中引用JSTL中的核心标签库,示例代码如下:
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
4. JSP动作元素
1. jsp:include动作元素的格式
在JSP页面中,<jsp:include>动作元素用于向当前页面引入其他的文件,被引入的文件可以是动态文件,也可以是静态文件。<jsp:include>动作元素的具体语法格式如下所示:
<jsp:include page="URL" flush="true|false" />
page:用于指定被引入文件的相对路径。例如,指定属性值为top.jsp,则表示将当前JSP文件相同文件夹下的top.jsp文件引入到当前JSP
页面中。
flush:用于指定是否将当前页面的输出内容刷新到客户端,默认情况下,flush属性的值为false。
<jsp:include>包含的原理是将被包含页面编译处理后的结果包含在当前页面中。例如,在页面1中使用<jsp:include>元素包含了页面
2,当浏览器第一次请求页面1时,Web容器首先会编译页面2,然后将编译处理后的返回结果包含在页面1中,之后编译页面1,最后将两个页面组
合的结果回应给浏览器。为了使读者更好地理解<jsp:include>动作元素,下面通过一个案例演示<jsp:include>动作元素的使用。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
demo06.jsp内容<br>
<jsp:include page="included.jsp" flush="false" />
</body>
</html>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%Thread.sleep(5000);%>
included.jsp内的中文<br />
</body>
</html>
注意:第二行文本会演示5s再显示
include指令与<jsp:include>动作元素的区别
include指令通过file属性指定被包含的文件,file属性不支持任何表达式; <jsp:include>动作元素通过page属性指定被包含的文件,
page属性支持JSP表达式。
使用include指令时,被包含的文件内容会原封不动地插入到包含页中,然后JSP编译器再将合成后的文件最终编译成一个Java文件;使用
<jsp:include>动作元素包含文件时,当该元素被执行时,程序会将请求转发到被包含的页面,并将执行结果输出到浏览器中,然后返回包含
页,继续执行后面的代码。因为服务器执行的是多个文件,所以如果一个页面包含了多个文件,JSP编译器会分别对被包含的文件进行编译。
在应用include指令包含文件时,由于被包含的文件最终会生成一个文件,所以在被包含文件、包含文件中不能有重复的变量名或方法;而
在应用<jsp:include>动作元素包含文件时,因为每个文件是单独编译的,所以被包含文件和包含文件中的重名变量和方法是不冲突的。
注意:<jsp:include>动作元素对包含的动态文件和静态文件的处理方式是不同的,如果被包含的是静态文件,则包含页面执行后,在使用
了<jsp:include>动作元素的位置将会输出被包含文件的内容。如果<jsp:include>动作元素包含的是一个动态文件,那么JSP编译器将编译并
执行被包含文件。
2. 请求转发元素jsp:forward
<jsp:forward>动作元素可以将当前请求转发到其他Web资源(HTML页面、JSP页面和Servlet等),执行请求转发之后,当前页面将不再
执行,而是执行该元素指定的目标页面。<jsp:forward>具体语法格式如下所示:
<jsp:forward page="relativeURL" />
page属性用于指定请求转发到的资源的相对路径,该路径的目标文件必须是当前应用中的内部资源。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<jsp:forward page="welcome.jsp" />
</body>
</html>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
Hello !!!!!
</body>
</html>
5. JSP隐式对象
1. 隐式对象
在JSP页面中,有一些对象需要频繁使用,如果每次都重新创建这些对象则会非常麻烦。为了简化Web应用程序的开发,JSP2.0规范中提供了9个隐式(内置)对象,它们是JSP默认创建的,可以直接在JSP页面中使用。
下表中列举了JSP的9个隐式对象及它们各自对应的类型。其中,由于request、response、config、session和application所属的类及其用法在前面的章节都已经讲解过,而page对象在JSP页面中很少被用到。
2. JSP中的9个隐式对象
名称 | 类型 | 描述 |
---|---|---|
out | javax.servlet.jspJspWriter | 用于页面输出 |
request | javax.servlet.http.HttpServletRequest | 得到用户请求信息 |
response | javax.servlet.http.HttpServletResponse | 服务器向客户端的回应信息 |
config | javax.servlet.ServletConfig | 服务器配置,可以取得初始化参数 |
session | javax.servlet.http.HttpSession | 用来保存用户的信息 |
application | javax.servlet.ServletContext | 所有用户的共享信息 |
page | java.lang.Object | 指当前页面转换后的Servlet类的实例 |
pageContext | javax.servlet.jsp.PageContext | JSP的页面容器 |
exception | java.lang.Throwable | 表示JSP页面所发生的异常,在错误页中才起作用 |
3. out对象的作用
在JSP页面中,经常需要向客户端发送文本内容,向客户端发送文本内容可以使用out对象实现。out对象javax.servlet.jsp.JspWriter
类的实例对象,它的作用与ServletResponse.getWriter()方法返回的PrintWriter对象非常相似,都是用来向客户端发送文本形式的实体内
容。不同的是,out对象的类型为JspWriter,它相当于带缓存功能的PrintWriter。
在JSP页面中,通过out隐式对象写入数据相当于将数据插入到JspWriter对象的缓冲区中,只有调用了ServletResponse.getWriter()方
法,缓冲区中的数据才能真正写入到Servlet引擎所提供的缓冲区中。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%
out.print("第一行");
response.getWriter().write("第二行");
%>
</body>
</html>
由上图可知,程序先输出了第二行,后输出了第一行。这是因为out对象通过print语句写入数据后,直到整个JSP页面结束,out对象中输
入缓冲区的数据(即:第一行)才真正写入到Serlvet引擎提供的缓冲区中,而response.getWriter().println()语句则是直接把内容(即:
第二行)写入Servlet引擎提供的缓冲区中,Servlet引擎按照缓冲区中的数据存放顺序输出内容。
4. 使用page指令设置out对象的缓冲区大小
有时候,开发人员希望out对象可以直接将数据写入Servlet引擎提供的缓冲区中,这时,可以通过page指令中操作缓冲区的buffer属性来实现。接下来对文件out.jsp进行修改,修改后的代码如下所示。
<%@ page contentType=“text/html;charset=UTF-8” language=“java” buffer=“0kb” %>
<%@ page contentType="text/html;charset=UTF-8" language="java" buffer="0kb" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%
out.print("第一行");
response.getWriter().write("第二行");
%>
</body>
</html>
由上图可知,out对象输出的内容在response.getWriter().println()语句输出的内容之前,由此可见,out对象中的数据直接写入了Servlet引擎提供的缓冲区中。此外,当写入到out对象中的内容充满了out对象的缓冲区时,out对象中输入缓冲区的数据也会真正写入到Servlet引擎提供的缓冲区中。
5. pageContext对象
在JSP页面中,使用pageContext对象可以获取JSP的其他8个隐式对象。pageContext对象是javax.servlet.jsp.PageContext类的实例对象,它代表当前JSP页面的运行环境,并提供了一系列用于获取其他隐式对象的方法。
方法名 | 功能描述 |
---|---|
JspWriter getOut() | 用于获取out隐式对象 |
Object getPage() | 用于获取page隐式对象 |
ServletRequest getRequest() | 用于获取request隐式对象 |
ServletResponse getResponse() | 用于获取response隐式对象 |
HttpSession getSession() | 用于获取session隐式对象 |
Exception getException() | 用于获取exception隐式对象 |
ServletConfig getServletConfig() | 用于获取config隐式对象 |
ServletContext getServletContext() | 用于获取application隐式对象 |
void setAttribute(String name,Object value,int scope) | 用于设置pageContext对象的属性 |
Object getAttribute(String name,int scope) | 用于获取pageContext对象的属性 |
void removeAttribute(String name,int scope) | 用于删除指定范围内名称为name的属性 |
void removeAttribute(String name) | 用于删除所有范围内名称为name的属性 |
Object findAttribute(String name) | 用于从4个域对象中查找名称为name的属性 |
pageContext对象的作用范围
pageContext操作属性的相关方法中参数name指定的是属性名称,参数scope指定的是属性的作用范围。pageContext对象的作用范围有4
个值,具体如下:
pageContext.PAGE_SCOPE:表示页面范围。
pageContext.REQUEST_SCOPE:表示请求范围。
pageContext.SESSION_SCOPE:表示会话范围。
pageContext.APPLICATION_SCOPE:表示Web应用程序范围。
需要注意的是,当调用findAttribute()方法查找名称为name的属性时,会按照page、request、session和application的顺序依次进
行查找,如果找到,则返回属性的名称,否则返回null。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%
//获取request对象
HttpServletRequest req = (HttpServletRequest) pageContext.getRequest();
//设置page范围内属性
pageContext.setAttribute("str", "Java",pageContext.PAGE_SCOPE);
//设置request范围内属性
req.setAttribute("str", "Java Web");
//获得的page范围属性
String str1 = (String)pageContext.getAttribute("str", pageContext.PAGE_SCOPE);
//获得的request范围属性
String str2 = (String)pageContext.getAttribute("str", pageContext.REQUEST_SCOPE);
%>
<%="page范围:"+str1 %><br />
<%="request范围:"+str2 %><br />
</body>
</html>
6. exception对象
在JSP页面中,经常需要处理一些异常信息,处理异常信息可以通过exception对象实现。exception对象是java.lang.Exception类的实例对象,它用于封装JSP中抛出的异常信息。需要注意的是,exception对象只有在错误处理页面才可以使用,即page指令中指定了属性<%@ page isErrorPage="true"%>的页面。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%
int a = 3;
int b = 0;
%>
输出结果为:<%=(a / b)%><!--此处会产生异常 -->
</body>
</html>
修改:
<%@ page contentType="text/html;charset=UTF-8" language="java" isErrorPage="true" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<!-- 显示异常信息 -->
<%=exception.getMessage()%><br />
</body>
</html>
<%@ page contentType="text/html;charset=UTF-8" language="java" errorPage="err.jsp" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%
int a = 3;
int b = 0;
%>
输出结果为:<%=(a / b)%><!--此处会产生异常 -->
</body>
</html>