文章目录
Tomcat
服务器
概述
Tomcat
是一款实现了Servlet
标准和JSP
标准的JavaWeb
容器。Tomcat
本身使用Java
语言开发,因此使用Tomcat
必须安装JVM
.
Tomcat
安装
使用压缩包方式,解压完成即Tomcat
安装完成。双击安装目录下的bin/startup.bat
即可启动Tomcat
,使用浏览器访问http://localhost:8080
,出现Tomcat
页面即Tomcat
安装成功。双击安装目录下的bin/shutdown.bat
即可关闭Tomcat
。
也可使用命令行启动Tomcat
服务器:
1.cd C:\DevKits\Tomcat - 使用cd命令定位到Tomcat的安装(解压)目录
2.catalina run - 使用"catalina run"命名启动Tomcat服务器
Tomcat
基础配置
1.修改端口号:记事本打开 安装目录/conf/server.xml,将<Connector.../>中的端口号改为8080
2.修改用户名和密码:记事本打开 安装目录/conf/tomcat-users.xml,
在<tomcat-users>...</tomcat-users>中添加以下内容:
<role rolename="manager-gui"/>
<!-- 仅修改用户名和密码 -->
<user username="your name" password="your password" roles="manager-gui"/>
3.重启Tomcat服务器
Tomcat
项目部署
Tomcat
目录介绍
bin : 存放可执行文件
conf : 存放配置文件
lib : 存放Tomcat服务器的jar包
logs : 存放Tomcat服务器运行时输出的日志
temp : 存放临时数据
webapps : 存放部署的web工程
work : 用来存放Tomcat运行时jsp翻译为Servlet的源码以及Session钝化(序列化)的目录
项目部署方式
1.将工程拷贝至 Tomcat安装(解压)目录/webapps 中
2.在 Tomcat安装(解压)目录/conf/Catalina/localhost 中添加xml文件并输入以下内容:
<!-- path指定实际工程路径的映射路径,浏览器访问时使用此映射路径;docBase指定实际工程路径
注意xml文件必须使用utf-8编码格式 -->
<Context path="映射路径" docBase="实际工程路径"/>
3.在浏览器中使用 http://ip:port/路径 访问页面
IDEA
整合Tomcat
1.依次点击IDEA的File>Build,Execution,Deployment>Application Servers
2.点击"+"选择"Tomcat Server",在弹出的窗口中为"Tomcat Home"输入框选择Tomcat安装(解压)路径
3.新建模块,选择"Java Enterprise项目类型",勾选"Web Application",勾选"Create web.xml"
4.模块新建成功后,在 模块/web/WEB-INF 中添加lib文件夹,用于存放jar包
HTTP
协议
HTTP
协议数据又称为HTTP
报文,HTTP
报文又分为请求报文和响应报文,客户端向服务器发送的数据为请求报文,服务器向客户端回传的数据为响应报文。
HTTP
请求
GET
请求
常见的GET
请求:
1.form标签中的method=GET的form表单
2.a标签
3.link标签引入CSS
4.Script标签引入JavaScript
5.img标签引入图片
6.iframe标签引入html页面
7.在浏览器地址栏输入地址的搜索行为
GET
请求的报文组成为:
1.请求行
(1).请求方式:GET
(2).请求资源路径+参数:资源路径?name1=value&name2=value...
(3).请求的协议版本号:HTTP/1.1
2.请求头
key1:value
key2:value
...
例:
GET /ServletDemo/hello HTTP/1.1
Accept:image/jpeg,*/*
Accept-Language:zh-CN
User-Agent:Mozilla/4.0
Accept-Encoding:gzip,deflate
Host:localhost:8080
Connection:Keep-Alive
...
POST
请求
POST
请求的报文组成为:
1.请求行
(1).请求方式:POST
(2).请求资源路径+参数:资源路径?name1=value&name2=value...
(3).请求的协议版本号:HTTP/1.1
2.请求头
(1).多个请求头键值对
(2).空行(请求头末尾与请求体之间有一个额外的空行)
3.请求体
发送给服务器的数据
例:
POST /ServletDemo/login HTTP/1.1
Accept:image/jpeg,*/*
Referer:http://localhost:8080/ServletDemo/login.html
Accept-Language:zh-CN
User-Agent:Mozilla/4.0
Content-Type:application/x-www-form-urlencoded
Accept-Encoding:gzip,deflate
Host:localhost:8080
Content-Length:100
Connection:Keep-Alive
Cache-Control:no-cache
action=login&username=root&password=123456
常用请求头
GET与POST都使用的请求头
Accept:声明服务器客户端可以接收的数据类型,"*/*"表示可以接收任意类型
Accept-Language:声明服务器客户端可以接收的语言类型,"zh_CN"表示中文中国,"en_US"表示英文美国
User-Agent:用户代理,即浏览器信息
Accept-Encoding:声明服务器客户端可以接收的数据压缩或编码方式
Host:表示服务器的IP和端口号
Connection:声明服务器如何处理当前连接,"Keep-Alive"表示回传数据后不要马上关闭,
继续保持一定时间的连接,"Closed"表示回传数据后立即关闭连接
仅POST使用的请求头
Referer:请求发起时浏览器地址栏的输入的地址(可以记录用户的访问链接,防止盗链)
Content-Type:表示客户端发送的数据类型,有两种类型:
application/x-www-form-urlencoded(以name=value&name=value...
的形式提交数据至服务器,然后进行URL编码)
multipart/form-data(以流的形势提交数据至服务器,用于文件上传)
Content-Length:表示发送的数据的字符长度
Cache-Control:指定缓存的控制方式
HTTP
响应
响应报文的格式:
1.响应行
(1).响应的协议及版本号
(2).响应状态码
(3).响应状态描述符
2.响应头
(1).多个响应头键值对
(2).空行(响应头末尾与响应体之间有一个额外的空行)
3.响应体
服务器回传给客户端的数据
例:
HTTP/1.1 200 OK
Server:Apache-Coyote/1.1
Accept-Ranges:bytes
ETag:W:/"375-..."
Last-Modified:Mon,13 Jan 2020 08:22:53 GMT
Content-Type:text/html
Content-Length:500
Data:Mon,13 Jan 2020 08:34:42 GMT
<!DOCTYPE html>
<html lang="zh_CN">
<head>
<meta charset="UTF-8">
<title>Hello world</title>
</head>
<body>
Hello world!
</body>
</html>
常见的响应码:
200:表示请求成功
302:表示请求重定向
404:表示服务器已经收到请求,但请求数据不存在
500:表示服务器已经收到请求,但服务器内部出现错误
MIME
类型
MIME
是HTTP
中的数据类型,格式为"大类型/小类型"
:
文件类型 | MIME类型 |
---|---|
超文本标记语言文本 | text/html |
普通文本 | text/plain |
RTF文本 | application/rtf |
GIF图形 | image/gif |
JPEG图形 | image/jpeg |
au声音文件 | audio/basic |
MIDI音乐文件 | audio/midi,audio/x-midi |
RealAudio音乐文件 | audio/x-pn-realaudio |
MPEG文件 | video/mpeg |
AVI | video/x-msvideo |
GZIP文件 | application/x-gzip |
TAR文件 | application/x-tar |
Servlet
概述
使用Servlet
必须有servlet-api.jar
的支持,可在Tomcat安装(解压)路径/lib
中引用。
Servlet
是JavaEE
的三大核心组件(Servlet,Filter,Listener
)之一,是运行在服务器上的小程序,用于处理及响应浏览器或客户端对服务器的HTTP
请求。Servlet
是标准Web
容器必须支持的规范。
Servlet
接口介绍
Servlet
接口在jakarta.servlet
包下(新版Tomcat
),实现Servlet
接口用于处理及响应浏览器或客户端对具体Web
资源的请求。Servlet
中包含了处理各种资源请求行为的方法。
package jakarta.servlet;
import java.io.IOException;
public interface Servlet {
/* Servlet接口实现类初始化时由Web容器调用,定义初始化时刻的行为 */
void init(ServletConfig var1) throws ServletException;
/* Web容器获取Servlet配置信息 */
ServletConfig getServletConfig();
/* 用户发出请求时由Web容器调用,定义具体的业务逻辑 */
void service(ServletRequest var1,ServletResponse var2)
throws ServletException,IOException;
/* Web容器获取Servlet对象信息 */
String getServletInfo();
/* Web容器关闭/Webapp重新部署/该Servlet对象长时间未被访问时由Web容器调用 */
void destroy();
}
Servlet
接口的简单使用
第1步,创建Java Enterprise
工程。
第2步,指定webapp
工程名,依次点击IDEA
的按钮Run>Edit Configurations
,在弹出的窗口中选择正在使用的Tomcat
服务器,点击Deployment
选项卡,在Application context
输入框中输入以"/"
开头的字符串作为webapp
工程名,例如"/ServletDemo"
。
第3步,实现Servlet
接口并实现方法。
HelloServlet.java
public class HelloServlet implements Servlet {
@Override
public void init(ServletConfig servletConfig) throws ServletException {}
@Override
public ServletConfig getServletConfig() {return null;}
@Override
public void service(ServletRequest servletRequest,ServletResponse servletResponse)
throws ServletException,IOException {
System.out.println("Servlet is accessed.");
}
@Override
public String getServletInfo() {return null;}
@Override
public void destroy() {}
}
第4步,编辑web/WEB-INF/web.xml
.
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app ... >
<!-- 上下文参数 -->
<context-param>
<param-name>name1</param-name>
<param-value>value1</param-value>
</context-param>
<context-param>
<param-name>name2</param-name>
<param-value>value2</param-value>
</context-param>
...
<servlet>
<!-- servlet-name可任意取字符串,但必须与servlet-mapping中的servlet-name保持一致;
此servlet-name是Servlet接口实现类的标识,用于与同名的资源访问路径对应 -->
<servlet-name>HelloServlet</servlet-name>
<!-- 对应的servlet实现类路径 -->
<servlet-class>com.lishaoyin.servletdemo.HelloServlet</servlet-class>
<!-- 可添加若干初始化参数 -->
<init-param>
<param-name>name1</param-name>
<param-value>value1</param-value>
</init-param>
<init-param>
<param-name>name1</param-name>
<param-value>value1</param-value>
</init-param>
...
</servlet>
<servlet-mapping>
<!-- servlet-name可任意取字符串,但必须与servlet-mapping中的servlet-name保持一致;
此servlet-name是资源访问路径的标识,用于与同名的Servlet接口实现类对应 -->
<servlet-name>HelloServlet</servlet-name>
<!-- 资源访问路径名,可取任意字符串,但必须以"/"开头;
"/"指代"http://ip:port/project/",
因此"/hello"即为"http://ip:port/project/hello" -->
<url-pattern>/hello</url-pattern>
<!-- 可以编写多个url-pattern,从而实现访问多个资源调用同一个Servlet接口实现类 -->
<url-pattern>...</url-pattern>
<url-pattern>...</url-pattern>
...
</servlet-mapping>
</web-app>
servlet
标签和servlet-mapping
标签将浏览器或客户端对资源的访问映射到指定的Servlet
接口实现类,从而浏览器或客户端访问该资源时,服务器将调用对应的Servlet
接口实现类中的方法。
第5步,访问资源,在浏览器中输入:
http://localhost:8080/ServletDemo/hello
使用注解进行Servlet
映射
以上对Servlet
的简单实用中,通过添加servlet
标签和servlet-mapping
标签实现Servlet
接口实现类与资源访问的映射,也可以使用@WebServlet
注解指定Servlet
接口实现类与资源访问的映射。@WebServlet
的属性:
public @interface WebServlet {
/* 指定Servlet的名字,等价于web.xml中的<servlet-name> */
String name() default "";
/* 与urlPatterns属性等价,但两者不可同时使用,等价于web.xml中的<url-pattern> */
String[] value() default {};
/* 与value属性等价,但两者不可同时使用,等价于web.xml中的<url-pattern> */
String[] urlPatterns() default {};
/* 指定服务器开启时Servlet加载的顺序,默认为-1指定服务器开启时默认不加载Servlet */
int loadOnStartup() default -1;
/* 指定一组Servlet初始化参数,等价于web.xml中的<init-param>参数 */
WebInitParam[] initParams() default {};
/* 指定是否支持异步操作模式,等价于web.xml中的<async-supported>标签 */
boolean asyncSupported() default false;
/* Servlet的描述信息,等价于web.xml中的<description> */
String description() default "";
/* 该Servlet的显示名,等价于web.xml中的<display-name> */
String displayName() default "";
/* 指定Servlet显示的小图标的图标地址 */
String smallIcon() default "";
/* 指定Servlet显示的大图标的图标地址 */
String largeIcon() default "";
}
示例:
/* 访问http://ip:port/project/hello时将调用此Servlet实现类 */
@WebServlet(value="/hello")
public class HelloServlet implements Servlet {...}
映射多个资源访问时,可以使用数组:
@WebServlet(name="Servlet1",value={"/hello1","/hello2","/hello3"})
public class HelloServlet implements Servlet {...}
@WebServlet
注解与web.xml
并存使用使,如果web.xml
中的根标签(<web-app>
)中的metadata-complete
属性为true
,表示所有配置信息完全由web.xml
完成,@WebServlet
注解将失效,需将metadata-complete
属性设置为false
。
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0"
metadata-complete="false">
...
</web-app>
Servlet
对象生命周期
Servlet
对象的生命周期由Web
容器(例如Tomcat
)管理,Servlet
对象中的方法也由Web
容器调用。
1.用户发出请求
2.Web容器截取请求路径
3.Web容器根据容器上下文寻找Servlet对象
4.如果未找到Servlet对象:
4.1.Web容器读取web.xml文件,读取Servlet实现类路径名,使用反射机制创建Servlet对象
4.2.Web容器调用Servlet对象的init()方法完成初始化操作
4.3.Web容器调用Servlet对象的service()方法提供服务
5.如果找到Servlet对象,直接调用Servlet对象的service()方法提供服务
6.当Web容器关闭/Webapp重新部署/该Servlet对象长时间未被访问时,Web容器会将该Servlet对象销毁,
销毁前调用Servlet对象的destroy()方法
总结:
1.Servlet接口实现类的构造器只执行一次
2.Servlet对象的init()方法只执行一次
3.Servlet对象的service()方法,只要用户请求一次,就被执行一次
4.Servlet对象的destroy()方法只执行一次
5.默认情况下,服务器启动阶段不会实例化Servlet接口实现类,可以在web.xml文件中的
servlet标签添加以下内容开启服务器启动阶段实例化:
<servlet>
...
<!-- load-on-startup中填写自然数,值越小,服务器启动阶段实例化优先级越高 -->
<load-on-startup>0</load-on-startup>
</servlet>
Servlet
相关接口
ServletConfig
接口
在Servlet
接口实现类中,ServletConfig
接口的实例由Web
容器通过init()
方法传给Servlet
接口实现类,ServletConfig
对象封装了Servlet
接口实现类的配置信息,等同于web.xml
或@WebServlet
注解中的信息。
常用的方法
/* 获取Servlet名,与web.xml中的<servlet-name>对应 */
String getServletName();
/* 获取Servlet上下文对象 */
ServletContext getServletContext();
/* 获取指定初始化参数的值,与web.xml中的<init-param>对应 */
String getInitParameter(String name);
/* 获取所有初始化参数的名字,与web.xml中的<init-param>对应 */
Enumeration<String> getInitParameterNames();
getServletConfig()
Servlet
接口中的getServletConfig()
方法用于间接获取Web
容器传给Servlet
对象的ServletConfig
对象。
public class HelloServlet implements Servlet {
private ServletConfig servletConfig;
@Override
public void init(ServletConfig servletConfig) throws ServletException {
this.servletConfig = servletConfig;
}
/* 此方法供子类使用获取ServletConfig对象 */
@Override
public ServletConfig getServletConfig() {
return servletConfig;
}
...
}
ServletContext
接口
ServletContext
用于封装Servlet
上下文信息,Web
容器将ServletConfig
对象通过init()
方法传入Servlet
对象,通过此ServletConfig
对象可获取ServletContext
对象,所有Servlet
对象共享同一个ServletContext
对象。ServletContext
对象代表web.xml
文件。
需要所有用户共享的数据,可放在ServletContext
对象中,即ServletContext
对象可实现跨用户传递数据。ServletContext
对象是多线程共享的,因此不建议修改其中的数据。
常用方法
/* 获取指定的属性值,对应web.xml中的<context-param>中的值 */
Object getAttribute(String name);
/* 设置指定的属性值 */
void setAttribute(String name,Object value);
/* 移除指定的属性值 */
void removeAttribute(String name);
/* 获取指定初始化参数的值,与web.xml中的<init-param>对应 */
String getInitParameter(String name);
/* 获取所有初始化参数的名字,与web.xml中的<init-param>对应 */
Enumeration<String> getInitParameterNames();
/* 获取指定资源的绝对路径 */
String getRealPath(String path);
Servlet
的实现方式
实现Servlet
接口
public class HelloServlet implements Servlet {
@Override
public void init(ServletConfig servletConfig) throws ServletException {}
@Override
public ServletConfig getServletConfig() {return null;}
@Override
public void service(ServletRequest servletRequest,ServletResponse servletResponse)
throws ServletException,IOException {}
@Override
public String getServletInfo() {return null;}
@Override
public void destroy() {}
}
继承HttpServlet
抽象类
HttpServlet
的继承体系为:
public abstract class HttpServlet extends GenericServlet {...}
继承HttpServlet
抽象类以实现Servlet
接口是最常用的方式。
public class HelloServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req,HttpServletResponse resp)
throws ServletException,IOException {...}
}
继承GenericServlet
抽象类
GenericServlet
的继承体系为:
public abstract class GenericServlet implements Servlet,ServletConfig,Serializable {...}
与HttpServlet
相比,GenericServlet
的方法更少。
public class HelloServlet extends GenericServlet {
@Override
public void service(ServletRequest req,ServletResponse resp)
throws ServletException,IOException {...}
}
Servlet
请求与响应
HttpServletRequest
接口
HttpServletRequest
接口是ServletRequest
接口的子接口,用于获取各种请求信息。service()
方法的第一个形参即为ServletRequest
类型,且ServletRequest
实例由Web
容器创建后传入service()
方法,所有关于请求的处理都围绕此实例进行。
常用方法
/* 获取完整请求URL(从"http"开始,到"?"之前结束):http://ip:port/project/... */
StringBuffer getRequestURL();
/* 获取请求URL中的资源名称部分(从项目名开始,到"?"之前结束):project/... */
String getRequestURI();
/* 获取请求行中的参数部分(从"?"开始到整个URL结尾):name1=value&name2=value... */
String getQueryString();
/* 获取请求方法("GET"和"POST") */
String getMethod();
/* 获取HTTP版本号 */
String getProtocol();
/* 获取webapp名称(项目对外访问路径,即servlet-mapping标签中的url-pattern指定的字符串) */
String getContextPath();
/* 获取指定参数的值,name对应HTML表单中元素的name属性,返回值对应value属性 */
String getParamater(String name);
/* 获取指定参数的所有值,某些表单元素(例如复选框)具有多个值 */
String[] getParamaterValues(String name);
/* 设置请求编码,可解决乱码问题,例如:
req.setCharacterEncoding("UTF-8"); */
void setCharacterEncoding(String env);
/* 获取请求转发器,传入转发目的地的路径,可以是Servlet实现类,也可以是jsp或html页面 */
RequestDispatcher getRequestDispatcher(String var1);
/* 设置指定属性的值 */
void setAttribute(String name,Object value);
/* 读取指定属性的值 */
Object getAttribute(String name);
/* 移除指定属性 */
void removeAttribute(String name);
解决请求乱码
直接使用setCharacterEncoding()
方法:
req.setCharacterEncoding("UTF-8");
setCharacterEncoding()
方法仅对POST
方法有效,如需对所有方法有效,可以采用如下方法:
/* Tomcat的请求接收编码默认使用ISO-8859-1标准,此标准不支持中文,
可以先获取所有字节,再使用UTF-8编码 */
String value = new String(req.getParameter(name).getBytes("ISO-8859-1"),"UTF-8");
请求转发
请求转发指将一个Servlet
截取的请求转发至另一个Servlet
,或转发至前台(jsp
页面或html
页面)。请求转发使用HttpServletRequest(ServletRequest)
接口中的方法实现:
/* 获取请求转发器,传入转发目的地的路径,可以是Servlet实现类,也可以是jsp或html页面 */
RequestDispatcher getRequestDispatcher(String var1);
/* 使用RequestDispatcher的forward()方法可将请求和响应信息转发至目的地 */
void forward(ServletRequest var1, ServletResponse var2)
throws ServletException, IOException;
示例:
@Override
protected void service(HttpServletRequest req,HttpServletResponse resp)
throws ServletException,IOException {
/* 将请求转发至另一个Servlet */
req.getRequestDispatcher("/hello1").forward(req,resp);
}
@Override
protected void service(HttpServletRequest req,HttpServletResponse resp)
throws ServletException,IOException {
/* 将请求转发至html页面 */
req.getRequestDispatcher("index.html").forward(req,resp);
}
@Override
protected void service(HttpServletRequest req,HttpServletResponse resp)
throws ServletException,IOException {
/* 将请求转发至jsp页面 */
req.getRequestDispatcher("login.jsp").forward(req,resp);
}
请求转发是服务器端的行为,不会改变浏览器地址栏中的url
字符串。请求转发可能调用了多个Servlet
,但请求转发过程中,只有一个请求,即请求可以被多个Servlet
共享。
域对象
请求被从一个Servlet
转发到另一个Servlet
时,这两个Servlet
拥有一个公共的作用域,Servlet
为这个公共的作用域创建了一个公有对象,称为域对象。转发发起方在转发之前可以设置域对象中指定属性的值,转发接收方可以读取域对象中指定的值。通过HttpServletRequest(ServletRequest)
接口中的以下方法实现:
/* 设置指定属性的值 */
void setAttribute(String name,Object value);
/* 读取指定属性的值 */
Object getAttribute(String name);
/* 移除指定属性 */
void removeAttribute(String name);
域对象中的数据只在一次请求转发中有效,一次转发中只在转发发起方和转发接收方之间有效。例:
/* 转发发起方 */
@Override
protected void service(HttpServletRequest req,HttpServletResponse resp)
throws ServletException,IOException {
req.setAttribute("name","Tony");
req.setAttribute("password","123456")
/* 将请求转发至另一个Servlet */
req.getRequestDispatcher("/hello1").forward(req,resp);
}
/* 转发接收方 */
@Override
protected void service(HttpServletRequest req,HttpServletResponse resp)
throws ServletException,IOException {
System.out.println("user is " + req.getAttribute("name")
+ ",password is " + req.getAttribute("password"));
}
HttpServletResponse
接口
HttpServletResponse
接口是ServletResponse
接口的子接口,用于请求响应。service()
方法的第二个形参即为ServletResponse
类型,且ServletResponse
实例由Web
容器创建后传入service()
方法,所有关于请求响应的处理都围绕此实例进行。
常用方法
/* 获取字节输出流,可向前台输出任何类型的数据 */
ServletOutputStream getOutputStream() throws IOException;
/* 获取字符输出流,可向前台输出字符类型的数据 */
PrintWriter getWriter() throws IOException;
/* 获取服务器端输出流编码格式 */
String getCharacterEncoding();
/* 设置服务器端输出流编码格式 */
String setCharacterEncoding(String charset);
/* 获取响应体数据类型 */
String getContentType();
/* 设置响应体数据类型 */
void setContentType(String type);
/* 设置响应体数据字符长度 */
void setContentLength(int len);
/* 获取指定响应头的值 */
String getHeader(String name);
/* 获取指定响应头的多个值 */
Collection<String> getHeaders(String name);
/* 设置指定响应头的值 */
void setHeader(String name,String value);
/* 添加响应头并设置值 */
void addHeader(String name,String value);
/* 判断是否设置了指定响应头 */
boolean containsHeader(String name);
/* 设置响应状态码 */
void setStatus(int sc);
/* 发送重定向地址 */
void sendRedirect(String location) throws IOException;
/* 添加指定Cookie */
void addCookie(Cookie cookie);
解决响应乱码
服务器端编码方式与客户端编码方式不一致时将出现乱码现象。方式一:先设置服务器端编码方式,在设置相关响应头,指定客户端应当使用的编码方式:
/* 设置服务器端编码方式 */
resp.setCharacterEncoding("UTF-8");
/* 设置Content-Type响应头,指定客户端应当使用的编码方式 */
resp.setHeader("Content-Type","charset=UTF-8");
/* 如果回传内容是html页面,需加上对应的MIME类型,否则所有字符将原样展示 */
resp.setHeader("Content-Type","text/html;charset=UTF-8");
方式二:直接设置响应内容的编码方式:
/* 设置回传内容的编码方式 */
resp.setContentType("charset=UTF-8");
/* 如果回传内容是html页面,需加上对应的MIME类型,否则所有字符将原样展示 */
resp.setContentType("text/html;charset=UTF-8");
响应重定向
响应重定向是由服务器引导,客户端的行为。客户端发送第一个请求,服务器处理后向客户端发送一个新地址,客户端接收到新地址后会立刻自动跳转至该地址,浏览器地址栏发生改变,服务器再次接受请求并作出响应,重定向完成。一次响应重定向中,有两次请求和两次响应。响应重定向使用的方法:
/* 发送重定向地址 */
void sendRedirect(String location) throws IOException;
响应重定向有非常广泛的用途,例如客户端未登录却试图访问个人信息时,服务器可以根据页面的登录状态引导客户端重定向到登录地址。
Cookie
Cookie
是浏览器提供的技术,用于将少量的需要存储在客户端的数据存储在计算机本地,不需要经过网络传输,可以提高网页的运行效率。例如浏览器中的记住密码即使用Cookie
技术实现。Cookie
中的常用方法:
/* 创建Cookie对象 */
public Cookie(String name,String value) {...}
/* 获取Cookie名 */
public String getName() {...}
/* 获取Cookie值 */
public String getValue() {...}
/* 为Cookie设置新名称 */
public void setValue(String newValue) {...}
/* 获取Cookie的有效时间(秒) */
public int getMaxAge() {...}
/* 设置Cookie的有效时间(秒) */
public void setMaxAge(int enpiry) {...}
/* 获取Cookie的允许访问路径 */
public String getPath() {...}
/* 设置Cookie的允许访问路径 */
public void setPath(String uri) {...}
Cookie
的创建:
/* Cookie构造器,传入key和value */
public Cookie(String name,String value);
Cookie cookie = new Cookie("password","123456789");
Cookie
的发送是一种服务器端响应行为,需使用service()
方法中的HttpServletResponse
参数进行发送:
@Override
protected void service(HttpServletRequest req,HttpServletResponse resp)
throws ServletException,IOException {
/* 发送Cookie */
resp.addCookie(cookie);
}
Cookie
的接收是一种客户端请求行为,需使用service()
方法中的HttpServletRequest
参数进行接收:
@Override
protected void service(HttpServletRequest req,HttpServletResponse resp)
throws ServletException,IOException {
Cookie[] cookies = req.getCookies();
for(Cookie cookie : cookies) {
System.out.println(cookie.getName() + " : " + cookie.getValue());
}
}
Cookie
的有效时间默认为浏览器关闭后即失效,可以通过方法设置Cookie
的有效时间:
/* 设置Cookie的有效时间,传入的值为以下时:
1.负数,Cookie不存活,仅在浏览器内存中存活,maxAge的默认值为-1,关闭浏览器后Cookie失效
2.正整数,Cookie存活指定的时间(单位是秒)
3.0,Cookie的有效时间设置为0时,Cookie被删除!!!
通过使用cookie.setMaxAge(0)删除用户内存及硬盘上的指定Cookie!!! */
public void setMaxAge(int expiry);
public int getMaxAge();
通过设置Cookie
路径可指定服务器下的哪些项目或资源可以访问该Cookie
。访问路径中包含Cookie
路径时,可访问该Cookie
,不包含则不能访问。
Cookie cookie = new Cookie("password","123456789");
/* 设置Cookie路径为"/"表示当前服务器下的任何项目都可访问此Cookie */
cookie.setPath("/");
resp.addCookie(cookie);
Cookie cookie = new Cookie("password","123456789");
/* 设置Cookie路径为"/项目名"表示仅当前项目可访问此Cookie,这是默认情况,无需显式设置 */
cookie.setPath("/Project1");
resp.addCookie(cookie);
Cookie cookie = new Cookie("password","123456789");
/* 设置Cookie路径为"/当前项目名"表示仅当前项目可访问此Cookie,这是默认情况,无需显式设置 */
cookie.setPath("/Project1");
resp.addCookie(cookie);
Cookie cookie = new Cookie("password","123456789");
/* 设置Cookie路径为"/其它项目名"表示仅指定的其它项目可访问此Cookie */
cookie.setPath("/Project2");
resp.addCookie(cookie);
Cookie cookie = new Cookie("password","123456789");
/* 设置Cookie路径为"资源路径"表示仅指定的资源可访问此Cookie */
cookie.setPath("/Project1/src1");
resp.addCookie(cookie);
Cookie
的使用注意点:
1.Cookie存储在当前浏览器中,如果更换浏览器则无法访问Cookie数据
2.Cookie不支持存储中文数据,如果有相关需求,发送时使用URLEncoder.encode()进行编码,
获取时使用URLDecoder.decode()进行解码
3.同一域名下Cookie的name不重复,如果服务器发送重复名称的Cookie,将覆盖之前的Cookie
4.浏览器可存储的Cookie数量是有限的
Session
Session
表示客户端与服务器的会话,是一种维护客户端与服务器关联的技术,Session
是HTTP
规范之一。每个客户端拥有不同的Session
,Session
常用于保存用户登录之后的信息,例如用户关闭浏览器后再打开登陆页面可自动登录并展示信息。Session
底层使用Cookie
技术实现,常用的Session-API
为HttpSession
接口。
获取Session
使用HttpServlet
对象中service()
方法的HttpServletRequest
参数获取HttpSession
对象:
/* 获取HttpSession对象时,如果存在HttpSession对象则直接获取,不存在则创建HttpSession对象 */
req.getSession();
对于JSP
页面,必须设置page
指令的session
属性为true
(默认为true
)才会创建Session
对象。
常用方法
/* 获取Session创建时间 */
long getCreateTime();
/* 获取会话标识,即Session对象的唯一标识 */
String getId();
/* 获取最后一次访问时间 */
long getLastAccessedTime();
/* 获取Servlet上下文 */
ServletContext getServletContext();
/* 判断Session是否为本次新创建 */
boolean isNew();
/* 获取指定属性 */
Object getAttribute(String name);
/* 获取所有属性名 */
Enumeration<String> getAttributeNames();
/* 设置属性值,如果不存在该属性,创建新属性 */
void setAttribute(String name,Object value);
/* 移除指定属性 */
void removeAttribute(String name);
/* 设置Session的超时时长(秒),会话超过指定时长,Session将被销毁;
负数指定Session永不超时 */
void setMaxInactiveInterval(int interval);
/* 获取Session的超时时长(秒) */
int getMaxInactiveInterval();
/* 立即销毁Session */
void invalidate();
Session
域对象
Session
对象表示服务器与客户端的会话,会话中可包含多次访问,Session
域对象用于存储共享数据,会话中的所有访问均可使用该共享数据。
/* 获取会话 */
HttpSession session = req.getSesson();
/* 设置属性值 */
session.setAttribute("user","Lishaoyin");
session.setAttribute("password","123456789");
/* 获取属性值 */
String user = (String)session.getAttribute()
/* 移除属性 */
session.removeAttribute("password");
Session
超时时长
会话存活时间超过超时时长后,Session
将销毁。Tomcat
的默认Session
最大不活动时间为30min
,可以在Tomcat安装(解压)目录/conf/web.xml
中全局修改。
<session-config>
<!-- 修改全局HttpSession的超时时长,单位为min -->
<session-timeout>30</session-timeout>
</session-config>
通过web.xml
修改的是所有Session
的超时时长,通过方法可以设置指定Session
的超时时长。
/* 设置HttpSession的超时时长(秒) */
session.setMaxInactiveInterval(1000);
/* 获取HttpSession的超时时长(秒) */
int time = session.getMaxInactiveInterval();
/* 立即销毁HttpSession对象 */
session.invalidate();
客户端每次访问服务器的资源时,资源对应的Session
都会更新访问时间,Session
的超时时间是以客户端的最新一次访问为起点(并非以Session
创建时间为起点),下一次访问之前超过超时时长,Session
失效。
Session
生命周期
1.Session的创建
(1).Servlet或JSP第一次被访问时,调用req.getSession()将创建新的Session
(2).Servlet或JSP上一次Session超时后,调用req.getSession()将创建新的Session
(3).Servlet或JSP未超时,但调用invalidate()方法手动销毁Session后,
调用req.getSession()将创建新的Session
2.Session的访问
(1).客户端访问Servlet或JSP时,Session将自动更新访问时间,Session的失效时间为
最新访问时间+Session超时时长
(2).关闭浏览器或客户端行为不会销毁Session对象
3.Session的销毁
(1).Session超时
(2).调用invalidate()方法手动销毁Session
(3).服务器端webapp关闭
Servlet
示例
文件上传
前台页面
upload.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Upload</title>
</head>
<body>
<!-- 1.文件上传的表单方法必须使用"POST"
2.enctype必须设置为"multipart/form-data"才能支持文件上传
3.action指定资源访问路径,与@WebServlet设置的路径对应 -->
<form method="POST"
enctype="multipart/form-data"
action="http://localhost:8080/ServletDemo/UploadServlet">
用户<input type="text" name="user"/><br/>
密码<input type="text" name="password"/><br/>
<input type="file" name="file"/><br/>
<button type="submit">上传</button>
</form>
</body>
</html>
后台代码
UploadServlet.java
/* 必须添加@MultipartConfig注解,Servlet才能支持文件上传 */
@MultipartConfig
@WebServlet(value="/UploadServlet")
public class UploadServlet extends HttpServlet {
private ServletConfig config;
@Override
public void init(ServletConfig config) {
this.config = config;
}
@Override
public ServletConfig getServletConfig() {
return config;
}
@Override
protected void service(HttpServletRequest request,HttpServletResponse response)
throws ServletException,IOException {
/* 设置请求编码 */
request.setCharacterEncoding("UTF-8");
/* 获取普通数据,传入表单输入框的"name"属性 */
String user = request.getParameter("user");
String password = request.getParameter("password");
System.out.println("user : " + user + " , " + "password : " + password);
/* 获取上传的文件必须首先获取Part(接口)对象;
获取文件数据,传入表单输入框的"name"属性 */
Part part = request.getPart("file");
/* 获取文件名 */
String fileName = part.getSubmittedFileName();
/* 获取服务器上指定URI的实际计算机路径 */
String serverPath = config.getServletContext().getRealPath("/");
String filePath = serverPath + File.separator + fileName;
System.out.println(filePath);
/* 将文件上传至指定路径 */
part.write(filePath);
/* 设置回传内容类型及编码 */
response.setContentType("text/html;charset=UTF-8");
/* 获取响应输出流 */
OutputStream os = response.getOutputStream();
/* 响应 */
os.write("上传完成".getBytes("UTF-8"));
/* 关闭流 */
os.close();
}
}
文件下载
超链接下载
download-a.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Upload</title>
</head>
<body>
<!-- 对于服务器可以识别的文件格式,点击超链接时将原样展示 -->
<a href="/download/file1.txt">文件1</a><br/>
<a href="/download/file2.png">文件2</a><br/>
<!-- 对于服务器不能识别的文件格式,点击超链接时将自动下载 -->
<a href="/download/file3.rar">文件3</a><br/>
<hr/>
<!-- 使用"download"属性可是服务器下载指定文件,download属性值指定下载后的文件名 -->
<a href="/download/file1.txt" download="test.txt">文件1</a><br/>
<a href="/download/file2.png" download="test.png">文件2</a>
</body>
</html>
Servlet
下载
前台页面:
download.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Download</title>
</head>
<body>
<div>
<form method="GET"
action="http://localhost:8080/ServletDemo/file-download">
<!-- 告知服务器需要下载哪个文件 -->
<button type="submit" name="file" value="file1.txt">文件1下载</button>
</form>
</div>
<div>
<form method="GET"
action="http://localhost:8080/ServletDemo/file-download">
<button type="submit" name="file" value="file2.png">文件2下载</button>
</form>
</div>
<div>
<form method="GET"
action="http://localhost:8080/ServletDemo/file-download">
<button type="submit" name="file" value="file3.rar">文件3下载</button>
</form>
</div>
</body>
</html>
后台代码:
DownloadServlet.java
@WebServlet(value="/file-download")
public class DownloadServlet extends HttpServlet {
private ServletConfig config;
@Override
public void init(ServletConfig config) {
this.config = config;
}
@Override
public ServletConfig getServletConfig() {
return config;
}
@Override
protected void service(HttpServletRequest request,HttpServletResponse response)
throws ServletException,IOException {
/* 设置请求编码 */
request.setCharacterEncoding("UTF-8");
/* 获取客户端请求的文件名 */
String fileName = request.getParameter("file");
/* 获取文件在服务器上的完整路径 */
String filePath = config.getServletContext().getRealPath("/download/")
+ File.separator + fileName;
File file = new File(filePath);
/* 判断指定文件是否存在以及是否是标准的文件格式 */
if(file.exists() && file.isFile()) {
/* 设置响应内容类型为浏览器不可识别的应用类型,否则页面将原样打印输出流内容 */
response.setContentType("application/x-msdownload");
/* 设置响应头,此部分内容在浏览器的下载图标上展示 */
response.setHeader("Content-Disposition","attachment;filename=" + fileName);
/* 获取文件输入流 */
InputStream is = new FileInputStream(file);
/* 获取响应输出流 */
OutputStream os = response.getOutputStream();
byte[] bytes = new byte[1024];
int len;
/* 响应输出 */
while((len = is.read(bytes)) != -1) {
os.write(bytes,0,len);
}
/* 关闭流 */
is.close();
os.close();
}
else {
response.setContentType("text/html;charset=UTF-8");
OutputStream os = response.getOutputStream();
os.write("文件不存在!".getBytes("UTF-8"));
os.close();
}
}
}
JSP
概述
使用JSP
必须有jsp-api.jar
和servlet-api.jar
的支持,可在Tomcat安装(解压)路径/lib
中引用。
JSP(Java Server Page)
是SUN
公司提供的动态网页编程技术,是服务器端的动态资源。JSP
用于代替Servlet
程序回传数据,因为Servlet
回传HTML
页面是一项非常繁琐的工作。
JSP
是可以内嵌Java
代码的HTML
页面,服务器会将JSP
翻译为Servlet
源码再编译运行,JSP
实质就是Servlet
程序。根据目前已有的技术,不建议将Java
代码嵌入JSP
页面,这种做法将增加代码的耦合度。
JSP
示例
JSP
的编写方式与HTML
基本一致。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title$</title>
</head>
<body>
$END$
</body>
</html>
JSP
的作用
JSP
之所以称为动态网页,是因为JSP
同时具有排版网页和响应请求的功能。JSP
将服务器对请求的响应输出转换为网页标签的形式,通过在JSP
中编写的逻辑,可以实现在网页中指定位置响应输出指定内容。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Hello world</title>
</head>
<body>
<h1>Hello world,Hello coder!</h1>
<table>
<tr>
<td>姓名</td>
<td>学号</td>
<td>班级</td>
</tr>
<tr>
<!-- 在网页的指定位置响应输出指定内容 -->
<td><%=request.getParameter("name") %></td>
<td><%=request.getParameter("gender") %></td>
<td><%=request.getParameter("class") %></td>
</tr>
</table>
</body>
</html>
JSP
基本使用
page
指令
JSP
文档开头的<%@ page ... %>
称为page
指令,通过修改其中的属性,可以配置很多重要信息。
<%@ page contentType="text/html;charset=UTF-8" language="java"%>
常用属性:
language:表示JSP翻译后的语言类型
contentType:表示JSP返回的数据类型,与JSP翻译后Servlet对象中response.setContentType(...)等价
pageEncoding:表示JSP页面本身的字符集
import:用于导入Java的包或类
errorPage:设置当JSP页面运行出错时,自动跳转的页面路径,路径字符串一般由"/"开头
isErrorPage:设置当前JSP页面是否是错误信息页面,默认为false,如果设置为true,可以打印错误异常信息
session:设置访问当前JSP页面,是否会创建HttpSession对象,默认是true
extends:设置JSP翻译为Java代码后的Servlet对象类默认继承的Servlet实现类
autoFlush:设置out输出流缓冲区满后,是否自动刷新缓冲区,默认值是true
buffer:设置out缓冲区大小,默认是8kb
注释
1.<!-- 显式注释,继承自HTML,浏览器控制台可见 -->
2.<%-- 隐式注释,JSP自身的注释,浏览器控制台不可见 --%>
3.// 隐式注释,单行注释,继承自Java,浏览器控制台不可见
4./* 隐式注释,多行注释,继承自Java,浏览器控制台不可见 */
脚本小程序
JSP
页面中嵌入的Java
代码称为脚本小程序,JSP
中共有三种脚本小程序编写方法:
1.Java代码段,用于声明局部变量,编写语句
<%
// Java代码段
%>
2.声明代码段,用于声明全局变量、方法、类等,以及静态代码块
<%!
// 声明代码段
%>
3.输出表达式,用于输出变量或字面量到浏览器页面
<%=数值 %>
示例:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Test</title>
</head>
<body>
<!-- 声明代码段 -->
<%!
static {
System.out.println("Hello world!");
}
private Map<String,Object> map;
%>
<!-- Java代码段 -->
<%
map.put("key1",20);
map.put("key2",30);
System.out.println(map.get("key1"));
%>
<!-- 输出表达式 -->
<%=new String("Hello world!") %>
</body>
</html>
每种脚本小程序都可以编写多个,且多个脚本小程序编译时会拼接为一段完整的Java
程序。
<%
for(int i=0;i<100;i++) {
System.out.println("Hello world!");
}
%>
<%-- 等价于 --%>
<%
for(int i=0;i<100;i++) {
%>
<%
System.out.println("Hello world!");
}
%>
可将Java
代码段与输出表达式结合使用,实现非常灵活的功能:
<!-- 以下代码可以循环生成表格单元 -->
<table border="1" cellspacing="0">
<%
for(int i=0;i<10;i++) {
%>
<tr>
<td>第<%=i+1%>行第1列</td>
<td>第<%=i+1%>行第2列</td>
<td>第<%=i+1%>行第3列</td>
</tr>
<%
}
%>
</table>
每执行一次<%=...%>
相当于使用response
打印一次数据至页面,因此可以使用Java
代码段与输出表达式结合使用简化重复繁琐的工作。
JSP
运行原理
Web
容器第一次运行时将JSP
翻译为类似于Servlet
接口实现类的Java
源码,再将翻译后的Java
源码编译为class
文件放在特定位置交给JVM
运行。JSP
中的三种脚本小程序对应翻译为Servlet
接口实现类的不同部分:
<!-- <%! ... %> -->
<%! ... %>中的静态代码块 -> Servlet实现类的静态代码块
<%! ... %>中的变量 -> Servlet实现类的成员变量
<%! ... %>中的方法 -> Servlet实现类的成员方法
<!-- <% ... %> -->
<% ... %>中的变量 -> service()方法(实际是jspService()方法)中的局部变量
<% ... %>中的语句 -> service()方法(实际是jspService()方法)方法中的语句
<!-- <%= ... %> -->
<%= ... %>中的内容使用out.write()方法写入到网页的指定位置
可重写JSP
的内置方法实现Servlet
的其它方法:
<!-- 必须在<%! ... %>中重写,jspService()方法不可自定义 -->
<%!
/* Servlet对象初始化调用,对应init()方法,
ServletConfig参数已被JSP全局化为config内置对象 */
public void jspInit() {
super.jspInit();
System.out.println("Hello.");
}
/* Servlet对象销毁调用,对应destroy()方法 */
public void jspDestroy() {
super.jspDestroy();
System.out.println("Bye.");
}
%>
JSP
翻译后的Servlet
实现类的service()
方法(实际是jspService()
方法)中包含了很多out.write()
调用,这些调用从HTML
的文档声明开始,结合翻译后<%= ... %>
中的内容逐字符的输出网页内容到缓冲区中,网页输出完毕后,调用out.flush()
方法将缓冲区的内容响应到客户端的网页上。
/* 逐字符输出HTML页面内容至缓冲区 */
out.write("<!DOCTYPE html ...>\r\n");
out.write("<html>\r\n");
out.write(" <head>\r\n");
out.write(" <title>...</title>\r\n");
...
/* 将缓冲区内容响应至客户端页面 */
out.flush();
服务器只在第一次运行时翻译JSP
代码并编译产物,以后运行不需要再重复,因此JSP
只在第一次运行时比传统Servlet
效率低。
JSP
语法
JSP
内置对象
编写JSP
页面嵌入Java
代码时,可以直接使用Servlet
相关的某些对象,这些对象称为JSP
内置对象。
request(HttpServletRequest类):请求对象
response(HttpServletResponse类):响应对象
pageContext(PageContextImpl类):JSP的上下文对象
session(HttpSession类):会话对象
application(ServletContext类):Servlet上下文
config(ServletConfig)类:Servlet配置对象
out:JSP输出流
page:指向当前JSP页面的对象
exception:异常对象,需开启isErrorPage=true
JSP
域对象
JSP
内置对象中包含了四个JSP
域对象,这些域对象都能像Map
一样存取或者移除数据,功能一致,只是数据的作用范围不同。
pageContext:数据在当前JSP页面内有效(最小范围域)
request:数据在一次请求内有效
session:数据在一个会话内有效(打开浏览器访问服务器,直到关闭浏览器)
application:数据在整个Web工程内有效(最大范围域)
例:
<%
<%-- 添加数据 --%>
pageContext.setAttribute("key","pageContext");
request.setAttribute("key","request");
session.setAttribute("key","session");
application.setAttribute("key","application");
%>
<!-- 获取数据 -->
<%=pageContext.getAttribute("key") %><br/>
<%=request.getAttribute("key") %><br/>
<%=session.getAttribute("key") %><br/>
<%=application.getAttribute("key") %><br/>
JSP
常用标签
静态包含
对于某些被重复使用的页面内容,可以使用静态包含将外部JSP
内容引用到当前JSP
页面中。静态包含的底层原理是将被引用JSP
文件使用out.write()
原样添加到引用位置。
<!-- file属性指定被引用的JSP文件地址 -->
<%@ include file="/..." %>
例:
index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Hello</title>
</head>
<body>
I am index.jsp<br/>
<!-- "/"表示"http://ip:port/project" -->
<%@ include file="/test.jsp" %>
</body>
</html>
test.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Test</title>
</head>
<body>
Hello world!
</body>
</html>
动态包含
动态包含同样可以引用外部JSP
文件,不同的是被引入的JSP
页面的内置对象会被当前JSP
的内置对象替代,因此当前JSP
页面可以向被引入的JSP
页面传递数据。
<!-- page指定引入页面的路径 -->
<jsp:include page="/...">
<%-- jsp:param标签用于向被引入页面传递数据 --%>
<jsp:param name="..." value="..."/>
<jsp:param name="..." value="..."/>
<jsp:param name="..." value="..."/>
...
</jsp:include>
例:
index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Hello</title>
</head>
<body>
I am index.jsp<br/>
<!-- 引用外部JSP文件 -->
<jsp:include page="/test.jsp">
<%-- 向外部JSP文件传递数据 --%>
<jsp:param name="name" value="Lishaoyin"/>
<jsp:param name="number" value="1703181407"/>
</jsp:include>
</body>
</html>
test.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Test</title>
</head>
<body>
Hello world!<br/>
<!-- 接收数据 -->
<%=request.getParameter("name") %><br/>
<%=request.getParameter("number") %>
</body>
</html>
请求转发
可以使用专门的请求转发标签代替request.getRequestDispatcher(...).forward(...)
实现请求转发。
<jsp:forward page="/...">
<%-- jsp:param标签用于向转发页面传递数据 --%>
<jsp:param name="..." value="..."/>
<jsp:param name="..." value="..."/>
<jsp:param name="..." value="..."/>
</jsp:forward>
例:
index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Hello</title>
</head>
<body>
<!-- 请求转发 -->
<jsp:forward page="/test.jsp">
<%-- 向外部JSP文件传递数据 --%>
<jsp:param name="name" value="Lishaoyin"/>
<jsp:param name="number" value="1703181407"/>
</jsp:forward>
</body>
</html>
test.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Test</title>
</head>
<body>
Hello world!<br/>
<!-- 接收数据 -->
<%=request.getParameter("name") %><br/>
<%=request.getParameter("number") %>
</body>
</html>
EL
表达式
基础
EL(Expression Language)
用于替代JSP
中的表达式脚本进行数据的输出,EL
表达式主要用于输出JSP
域对象中的数据。EL
表达式输出示例:
<%
/* 在pageContext域中添加属性 */
pageContext.setAttribute("name","Lishaoyin");
%>
<%-- 获取域中的属性并输出 --%>
EL表达式输出的结果是:${name}
JSP
拥有四个不同作用范围的域对象,EL
表达式按照JSP
域对象的作用范围从小到大搜索指定数据,并输出第一个搜索到的数据:
<%
/* pageContext的作用范围只在当前页面有效,是JSP中作用范围最小的域对象 */
pageContext.setAttribute("key","pageContext");
/* request的作用范围在一次请求中有效,包括请求转发至其它页面,作用范围大于pageContext */
request.setAttribute("key","request");
/* session的作用范围在一次会话中有效,作用范围大于request */
session.setAttribute("key","session");
/* application的作用范围在整个Web工程有效,是JSP中作用范围最大的域对象 */
application.setAttribute("key","application");
%>
<%-- EL表达式按照JSP域对象的作用范围从小到大搜索指定数据,输出"pageContext" --%>
EL表达式输出的结果是:${key}
EL
表达式对null
值的处理比JSP
表达式脚本更加严谨:
<%
pageContext.setAttribute("name",null);
%>
<%-- 输出"null"字符串 --%>
表达式脚本的输出结果是:<%=pageContext.getAttribute("name") %><br/>
<%-- 输出空串 --%>
EL表达式的输出结果是:${name}
EL
表达式输出对象:
<%
Person person = new Person("Tony","m",1.75f,140);
/* 域对象添加属性 */
pageContext.setAttribute("person",person);
%>
${person.getName()}的名字是${person.getName()}<br/>
${person.getName()}的性别是${person.getSex()}<br/>
${person.getName()}的身高是${person.getHeight()}<br/>
${person.getName()}的体重是${person.getWeight()}
EL
表达式还可以调用对象中的私有属性,实际调用的是私有属性对应的getXXX()
方法,因此必须为对象定义符合命名规范的get/set
方法。
<%
Person person = new Person("Tony","m",1.75f,140);
/* 域对象添加属性 */
pageContext.setAttribute("person",person);
%>
${person.name}的名字是${person.name}<br/>
${person.name}的性别是${person.sex}<br/>
${person.name}的身高是${person.height}<br/>
${person.name}的体重是${person.weight}
EL
表达式运算
<%
int a = 10;
int b = 20;
boolean c = true;
boolean d = false;
pageContext.setAttribute("a",a);
pageContext.setAttribute("b",b);
pageContext.setAttribute("c",c);
pageContext.setAttribute("d",d);
%>
<%-- 关系运算,输出"true/false" --%>
a等于b?:${a == b}<br/>
a等于b?:${a eq b}<br/>
a不等b?:${a != b}<br/>
a不等b?:${a ne b}<br/>
a大于b?:${a > b}<br/>
a大于b?:${a gt b}<br/>
a小于b?:${a < b}<br/>
a小于b?:${a lt b}<br/>
a大于等于b?:${a >= b}<br/>
a大于等于b?:${a ge b}<br/>
a小于等于b?:${a <= b}<br/>
a小于等于b?:${a le b}<br/>
<%-- 逻辑运算,输出"true/false" --%>
c与d:${c && d}<br/>
c与d:${c and d}<br/>
c或d:${c || d}<br/>
c或d:${c or d}<br/>
非c:${!c}<br/>
非c:${not c}<br/>
<%-- 算数运算,输出运算结果 --%>
a+b=${a + b}<br/>
a-b=${a - b}<br/>
a*b=${a * b}<br/>
a/b=${a / b}<br/>
a/b=${a div b}<br/>
a%b=${a % b}<br/>
a%b=${a mod b}<br/>
<%-- empty运算,判断对象是否为空,输出"true/false" --%>
<%
pageContext.setAttribute("null",null);
pageContext.setAttribute("string","");
pageContext.setAttribute("array",new Byte[0]);
pageContext.setAttribute("list",new ArrayList<String>());
pageContext.setAttribute("map",new HashMap<String,Object>());
%>
null值判断为空:${empty null}<br/>
空字符串判断空:${empty string}<br/>
数组长度为0判断为空:${empty array}<br/>
list无元素判断为空:${empty list}<br/>
map无元素判断为空:${empty map}<br/>
<%-- 三元运算,输出运算结果 --%>
求最大:${a>b ? a : b}
<%-- .运算,输出bean类的成员 --%>
<%
pageContext.setAttribute("person",new Person("Tony","m",1.75f,140));
%>
输出属性(调get()方法):${person.name}<br/>
输出方法(输出方法返回):${person.getName()}<br/>
<%-- []运算,输出有序集合指定索引的成员值 --%>
<%
pageContext.setAttribute("array",new String[]{"Tony","Steven","Jack"});
ArrayList<String> list = new ArrayList<String>();
list.add("Tony");
list.add("Steven");
list.add("Jack");
pageContext.setAttribute("list",list);
%>
输出数组成员值:${array[1]}<br/>
输出集合成员值:${list[0]}
EL
隐含对象
EL
表达式定义了一些隐含对象,可以在EL
表达式中直接使用:
pageContext : PageContextImpl类型,用于获取JSP内置对象
pageScope : Map<String,Object>类型,用于获取pageContext域中的数据
requestScope : Map<String,Object>类型,用于获取request域中的数据
sessionScope : Map<String,Object>类型,用于获取session域中的数据
applicationScope : Map<String,Object>类型,用于获取application域中的数据
param : Map<String,String>类型,用于获取请求参数值
paramValues : Map<String,String[]>类型,用于获取请求参数值(一个参数有多个值时使用)
header : Map<String,String>类型,用于获取请求头值
headerValues : Map<String,String[]>类型,用于获取请求头值(一个请求头有多个值时使用)
cookie : Map<String,Cookie>类型,用于获取当前请求的Cookie信息
initParam : Map<String,String>类型,用于获取初始化参数,
对应web.xml中的<init-param>或@WebServlet注解中的initParams
获取域中的数据:
<%
pageContext.setAttribute("key","pageContext");
request.setAttribute("key","request");
session.setAttribute("key","session");
application.setAttribute("key","application");
%>
获取pageContext域的数据:${pageScope.key}<br/>
获取request域的数据:${requestScope.key}<br/>
获取session域的数据:${sessionScope.key}<br/>
获取application域的数据:${applicationScope.key}
pageContext
获取JSP
内置对象(表达式脚本与EL
表达式对比):
<%-- 获取request --%>
获取请求协议:<%=request.getScheme() %><br/>
获取请求协议:${pageContext.request.scheme}<br/>
获取服务器IP或域名:<%=request.getServerName() %><br/>
获取服务器IP或域名:${pageContext.request.serverName}<br/>
获取服务器端口号:<%=request.getServerPort() %><br/>
获取服务器端口号:${pageContext.request.serverPort}<br/>
获取工程路径:<%=request.getContextPath() %><br/>
获取工程路径:${pageContext.request.contextPath}<br/>
获取请求方法:<%=request.getMethod() %><br/>
获取请求方法:${pageContext.request.method}<br/>
获取客户端IP:<%=request.getRemoteHost() %><br/>
获取客户端IP:${pageContext.request.remoteHost}<br/>
获取客户端端口号:<%=request.getRemotePort() %><br/>
获取客户端端口号:${pageContext.request.remotePort}<br/>
获取会话ID:<%=session.getId() %><br/>
获取会话ID:${pageContext.session.id}<br/>
JSTL
标签库
基础
JSTL(JSP Standard Tag Library)
用于替换JSP
中的代码脚本。JSTL
标签库:
功能范围 | URI | 前缀 |
---|---|---|
核心标签库 | http://java.sun.com/jsp/jstl/core | c |
格式化标签库 | http://java.sun.com/jsp/jstl/fmt | fmt |
函数标签库 | http://java.sun.com/jsp/jstl/functions | fn |
使用JSTL
需要先引入JSTL
:
<%-- 引入核心标签库,prefix指定前缀,uri指定路径 --%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%-- 引入格式化标签库,prefix指定前缀,uri指定路径 --%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%-- 引入函数标签库,prefix指定前缀,uri指定路径 --%>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
https://jakarta.ee/zh/specifications/tags/2.0
jakarta-servlet-jsp-jstl-xxx.jar
JSTL
依赖包
使用JSTL
需要相关jar
包的支持,可以在Tomcat(Tomcat10)
的安装路径中获取相关jar
包:
Tomcat安装(解压)路径/webapps/examples/WEB-INF/lib
--taglibs-standard-impl-xxx-migrated-xxx.jar
--taglibs-standard-spec-xxx-migrated-xxx.jar
JSTL-core
标签库
使用core
标签库需要引入:
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
set
标签
set
标签用于向指定域中添加指定属性或修改已有属性。
<%-- scope属性指定域对象,var属性指定key,value属性指定value;
set标签相当于:scope.setAttribute(var,value); --%>
<c:set scope="..." var="..." value="..." />
<%-- 示例 --%>
<%-- 添加或修改属性,scope可以取值:"page","request","session","application" --%>
<c:set scope="page" var="name" value="Lishaoyin"/>
<%-- 获取属性 --%>
你的名字是:${pageScope.name}
remove
标签
remove
标签用于移除指定域中的属性。
<%-- scope属性指定域对象,var属性指定key,
remove标签相当于scope.removeAttribute(key); --%>
<c:remove scope="..." var="..." />
<%-- 示例 --%>
<%-- 添加属性 --%>
<c:set scope="page" var="test" value="page" />
<c:set scope="request" var="test" value="request" />
<div>${pageScope.test}</div>
<div>${requestScope.test}</div>
<%-- 移除属性 --%>
<c:remove scope="request" var="test" />
<div>${test}</div>
if
标签
if
标签用于进行单分支判断。
<%-- test属性传递EL表达式,if标签内部为HTML语句,
test属性值为true,if内部的HTML语句展示,否则不展示 --%>
<c:if test="EL表达式">
HTML内容
</c:if>
<%-- 示例 --%>
<%-- 添加属性 --%>
<c:set scope="page" var="test" value="100"/>
<c:if test="${pageScope.test > 50}">
<div>test大于50</div>
</c:if>
<c:if test="${pageScope.test < 50}">
<div>test小于50</div>
</c:if>
<c:if test="${pageScope.test == 50}">
<div>test等于50</div>
</c:if>
choose
标签
choose
标签用于进行多分支判断,相当于switch
语句。
<%-- <c:when>标签指定判断分支,test属性传递EL表达式的值,
test属性值为true则展示标签内的HTML语句,否则不展示;
otherwise为其余判断情况 --%>
<c:choose>
<c:when test="EL表达式1">HTML语句1</c:when>
<c:when test="EL表达式2">HTML语句2</c:when>
...
<c:otherwise>HTML语句</c:otherwise>
</c:choose>
<%-- 示例 --%>
<%-- 添加属性 --%>
<c:set scope="page" var="test" value="100"/>
<c:choose>
<c:when test="${pageScope.test > 50}"><div>test大于50</div></c:when>
<c:when test="${pageScope.test == 50}"><div>test等于50</div></c:when>
<c:otherwise><div>test小于50</div></c:otherwise>
</c:choose>
forEach
标签
forEach
标签用于遍历循环输出。
<%-- forEach标签的属性:
var(String类型)指定遍历变量名,
begin(int类型)指定遍历初始索引,
end(int类型)指定遍历结束索引,
step(int类型)指定每次增加的步长(默认为1),
items(Object类型)指定被循环的数据 --%>
<%-- forEach用法1,相当于:
for(int var=begin;var<=end;var+=step){...} --%>
<c:forEach var="..." begin="..." end="..." step="...">
...
</c:forEach>
<%-- 示例 --%>
<%
String[] names = new String[]{"Tony","Tom","Steven","Ellen"};
pageContext.setAttribute("array",names);
/* 数组的长度 */
pageContext.setAttribute("length",names.length);
%>
<c:forEach var="index" begin="0" end="${pageScope.length-1}" step="1">
<%-- 使用EL表达式取值 --%>
<div>name is ${array[index]}</div>
</c:forEach>
<%-- forEach用法2,相当于:
for(Object var : items){...} --%>
<c:forEach var="..." items="...">
...
</c:forEach>
<%-- 示例 --%>
<%
ArrayList<String> names = new ArrayList<String>();
names.add("Tony");
names.add("Tom");
names.add("Steven");
names.add("Ellen");
pageContext.setAttribute("list",names);
%>
<%-- items使用EL表达式传入需要遍历的集合 --%>
<c:forEach var="name" items="${pageScope.list}">
<%-- 使用EL表达式取值 --%>
<div>name is ${name}</div>
</c:forEach>
JSTL-fmt
标签库
使用fmt
标签库需要引入:
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
formatNumber
标签
formatNumber
标签用于将指定数字转换成指定格式或类型的数据,再将转换后的数据添加到指定域对象中。
<%-- value指定要显示的数字,
type指定需要转换为的数据类型,
可取number(数字,默认值),percent(百分比),currency(货币类型,带¥/$符号)
scope指定数据输出的域对象,
var指定数据在域对象中引用的key,如果不设置var,
标签会在JSP页面中直接展示转换结果,如果设置了var,需要使用域对象获取结果 --%>
<fmt:formatNumber value="..." type="..." scope="..." var="..."/>
<%-- 示例 --%>
<fmt:formatNumber value="3.1415" type="number" scope="page" var="PI"/>
<div>value of PI is ${pageScope.PI}</div>
<fmt:formatNumber value="0.1" type="percent" scope="page" var="per"/>
<div>percent is ${pageScope.per}</div>
<%-- setLocale标签用于设置时区,设置为"ch_CN"带上"¥",设置为"en_US"带上"$" --%>
<fmt:setLocale value="en_US"/>
<fmt:formatNumber value="1000000" type="currency" scope="page" var="money"/>
<div>money is ${pageScope.money}</div>
formateDate
标签
formatNumber
标签用于将日期数据转换成指定格式,再将转换后的结果添加到指定域对象中。
<%-- value指定要显示的日期,
type指定要显示的数据类型,可取"DATE"/"TIME"/"BOTH",
dateStyle指定要显示的日期类型,可取"FULL"/"LONG"/"MEDIUM"/"SHORT"/"DEFAULT",
timeStyle指定要显示的时间类型,可取"FULL"/"LONG"/"MEDIUM"/"SHORT"/"DEFAULT",
pattern指定自定义时间格式,
timeZone指定时区,
scope指定数据输出的域对象,
var指定数据在域对象中引用的key,如果不设置var,标签会在JSP页面中直接展示转换结果,
如果设置了var,需要使用域对象获取结果 --%>
<fmt:formatDate value="..."
type="..."
dateStyle="..."
timeStyle="..."
pattern="..."
timeZone="..."
scope="..."
var="..."/>
<%-- 示例 --%>
<%
pageContext.setAttribute("myDate",new Date());
%>
<fmt:formatDate value="${myDate}" type="BOTH" var="date" scope="page"/>
<div>date is ${date}</div>
<%-- 使用pattern自定义日期时间格式 --%>
<fmt:formatDate value="${myDate}" pattern="yyyy-MM-dd" var="date" scope="page"/>
<div>date is ${date}</div>
Filter
Filter
是JavaWeb
的三大组件之一,作用是拦截请求,过滤响应。Filter
可用于权限检查、事务管理等场景。Filter
是JavaEE
的规范之一,具体为Filter
接口。具有Filter
的Web
工程运行流程为:
请求->Filter->Servlet->响应->Filter->客户端
概述
Filter
接口
package jakarta.servlet;
public interface Filter {
/* Filter初始化时由Web容器调用 */
void init(FilterConfig filterConfig) throws ServletException;
/* Filter核心方法,客户端请求映射资源时由Web容器调用 */
void doFilter(ServletRequest request,ServletResponse response,FilterChain chain)
throws IOException,ServletException;
/* Filter销毁前调用 */
void destroy();
}
Filter
演示
实现以下功能:在工程中创建资源文件夹logged
用于存储已登录用户可访问的资源,客户端请求其中的资源时,检查用户是否已登录,如果未登录则跳转至登录页面。使用Filter
需要进行配置:
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app ... >
...
<!-- 为Filter起名,指定Filter实现类的全类名 -->
<filter>
<filter-name>login-filter</filter-name>
<filter-class>com.lishaoyin.servletdemo.LoginFilter</filter-class>
</filter>
<!-- 路径映射,url-pattern指定拦截资源路径 -->
<filter-mapping>
<filter-name>login-filter</filter-name>
<!-- 拦截http://ip:port/project/logged下的所有资源 -->
<url-pattern>/logged/*</url-pattern>
</filter-mapping>
...
</web-app>
LoginFilter.java
public class LoginFilter implements filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request,ServletResponse response,FilterChain chain)
throws IOException,ServletException {
/* session域用于存储会话信息,可使用session域中的状态变量判断用户的登录状态 */
HttpSession session = ((HttpServletRequest)request).getSession();
if(session.getAttribute("user") == null) {
/* 如果未登录则将请求转发至登录页面 */
request.getRequestDispatcher("/login.jsp").forward(request,response);
} else {
/* 如果已登录则继续执行请求和响应 */
chain.doFilter(request,response);
}
}
@Override
public void destroy() {
}
}
login.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<div>please login!</div>
<form action="http://localhost:8080/ServletDemo/login" method="POST">
<div>用户:<input type="text" name="user"/></div>
<div>密码:<input type="password" name="password"/></div>
<div><button type="submit">确认</button></div>
</form>
</body>
</html>
LoginServlet.java
@WebServlet(value="/login")
public class LoginServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request,HttpServletResponse response)
throws ServletException,IOException {
request.setCharacterEncoding("UTF-8");
/* 获取表单数据 */
String user = request.getParameter("user");
String password = request.getParameter("password");
if("Lishaoyin".equals(user) && "123456".equals(password)) {
/* 设置登录标志 */
request.getSession().setAttribute("user",user);
/* 设置登录信息的过期时间 */
request.getSession().setMaxInactiveInterval(60);
/* 解决乱码 */
response.setContentType("text/html;charset=UTF-8");
response.getOutputStream().write("登录成功".getBytes("UTF-8"));
}
}
}
Filter
深入
Filter
的生命周期
1.Filter的创建:服务器启动时由服务器创建
(1).调用构造器创建Filter
(2).调用init()方法
2.Filter过滤:每次客户端发出请求时,服务器都会调用doFilter()方法对请求进行拦截
3.Web工程关闭,Filter被销毁
Filter
的配置方法
使用配置文件
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app ... >
...
<!-- 为Filter起名,指定Filter实现类的全类名 -->
<filter>
<filter-name>login-filter</filter-name>
<filter-class>com.lishaoyin.servletdemo.LoginFilter</filter-class>
<!-- 可自定义多个Filter初始化参数 -->
<init-param>
<param-name>...</param-name>
<param-value>...</param-value>
</init-param>
<init-param>
<param-name>...</param-name>
<param-value>...</param-value>
</init-param>
...
</filter>
<!-- 路径映射,url-pattern指定拦截资源路径 -->
<filter-mapping>
<filter-name>login-filter</filter-name>
<url-pattern>url1</url-pattern>
</filter-mapping>
<!-- 一个Filter可映射多个资源 -->
<filter-mapping>
<filter-name>login-filter</filter-name>
<url-pattern>url2</url-pattern>
</filter-mapping>
...
...
</web-app>
使用注解
使用@WebFilter
注解也可实现Filter
的配置,与配置文件等效,并且可与配置文件一起使用。
WebFilter.java
public @interface WebFilter {
String description() default "";
String displayName() default "";
/* Filter初始化参数 */
WebInitParam[] initParams() default {};
/* Filter名,与<filter-name>对应 */
String filterName() default "";
String smallIcon() default "";
String largeIcon() default "";
String[] servletNames() default {};
/* 拦截路径,与<url-pattern>对应,与urlPatterns等效,但不能同时使用 */
String[] value() default {};
/* 拦截路径,与<url-pattern>对应,与value等效,但不能同时使用 */
String[] urlPatterns() default {};
DispatcherType[] dispatcherTypes() default {DispatcherType.REQUEST};
boolean asyncSupported() default false;
}
例:
@WebFilter(value="/logged/file.png") // 指定拦截路径为http://ip:port/project/logged/file.png
public class LoginFilter implements filter {...}
Filter
相关的类
FilterConfig
接口
FilterConfig
类用于封装Filter
的配置信息,对应配置文件中的信息。
public interface FilterConfig {
/* 获取Filter名,对应<filter-name> */
String getFilterName();
/* 获取Servlet上下文 */
ServletContext getServletContext();
/* 获取Filter初始化参数,对应<filter>标签中<init-param>中的内容 */
String getInitParameter(String name);
/* 获取所有Filter初始化参数名 */
Enumeration<String> getInitParameterNames();
}
FilterChain
接口
public interface FilterChain {
/* 执行过滤器链,请求路径中可能包含映射的多个Filter,多个Filter从外层路径开始链式执行,
如果有下一个Filter,执行下一个Filter的filterChain.doFilter();
如果没有下一个Filter,执行资源请求 */
void doFilter(ServletRequest request, ServletResponse response)
throws IOException,ServletException;
}
doFilter()
中编写在filterChain.doFilter()
之前的代码称为前置代码,之后的代码成为后置代码,整个过滤器链执行的过程中,前置代码顺序执行,后置代码倒序执行:
Filter1.doFilter()->前置代码1->chainFilter.doFilter()->
Filter2.doFilter()->前置代码2->chainFilter.doFilter()->
...
Filtern.doFilter()->前置代码n->chainFilter.doFilter()->
后置代码n->
后置代码n-1->
...
后置代码1->执行客户端请求
FilterChain
的特点
1.FilterChain中的所有Filter在同一个线程中执行,即内层Filter会阻塞外层Filter的运行
2.FilterChain中的所有filterChain.doFilter()的前置代码顺序执行,后置代码倒序执行
3.FilterChain中的所有Filter共享同一个request
Filter
拦截路径
1.精确匹配:
<!-- 拦截指定的一个资源 -->
<url-pattern>http://ip:port/project/resource</url-pattern>
2.目录匹配:
<!-- 拦截http://ip:port/project/dir下的所有资源 -->
<url-pattern>/dir/*</url-pattern>
3.后缀匹配
<!-- 拦截http://ip:port/project/dir下的所有后缀为.jsp的资源 -->
<url-pattern>/dir/*.jsp</url-pattern>
Listener
Listener
是JavaWeb
三大组件之一,用于监听对象的创建和销毁,或监听域对象的改变等。
ServletContextListener
ServletContextListener
用于监听Servlet
上下文创建与销毁,可以简单理解为Web
工程的开启与关闭。
ServletContextListener.java
package jakarta.servlet;
public interface ServletContextListener extends EventListener {
/* Servlet上下文初始化时由Web容器调用,可以简单理解为Web工程开启时调用 */
void contextInitialized(ServletContextEvent sce);
/* Servlet上下文销毁时由Web容器调用,可以简单理解为Web工程关闭或服务器关闭时调用 */
void contextDestroyed(ServletContextEvent sce);
}
ServletContextEvent
类
ServletContextEvent
类是ServletContextListener
中监听上下文初始化和销毁方法的参数,常用方法有:
/* 获取ServletContext */
public ServletContext getServletContext();
配置
使用配置文件配置:
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app ... >
<listener>
<!-- Listener的配置仅需指明实现类路径信息 -->
<listener-class>com.lishaoyin.servletdemo.MyContextListener</listener-class>
</listener>
</web-app>
使用@WebListener
注解配置:
@WebListener
public class MyContextListener implements ServletContextListener {
...
}
示例
@WebListener
public class MyContextListener implements ServletContextListener {
/* Servlet上下文初始化时由Web容器调用,可以简单理解为Web工程开启时调用 */
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("Web工程开启");
}
/* Servlet上下文销毁时由Web容器调用,可以简单理解为Web工程关闭或服务器关闭时调用 */
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("Web工程关闭");
}
}
ServletRequestListener
ServletRequestListener
用于监听客户端请求的创建与销毁。
ServletRequestListener.java
package jakarta.servlet;
public interface ServletRequestListener extends EventListener {
/* 客户端请求初始化时由Web容器调用,客户端发起请求时,请求被创建并初始化 */
void requestInitialized(ServletRequestEvent sre);
/* 客户端请求被销毁时由Web容器调用,服务器响应请求后,请求被销毁 */
void requestDestroyed(ServletRequestEvent sre);
}
ServletRequestEvent
类
ServletRequestEvent
类是ServletRequestListener
中监听request
初始化和销毁方法的参数,常用方法有:
/* 获取ServletRequest对象 */
public ServletRequest getServletRequest();
/* 获取ServletContext对象 */
public ServletContext getServletContext();
配置
使用配置文件配置:
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app ... >
<listener>
<!-- Listener的配置仅需指明实现类路径信息 -->
<listener-class>com.lishaoyin.servletdemo.MyRequestListener</listener-class>
</listener>
</web-app>
使用@WebListener
注解配置:
@WebListener
public class MyRequestListener implements ServletRequestListener {
...
}
示例
@WebListener
public class MyRequestListener implements ServletRequestListener {
/* 客户端请求初始化时由Web容器调用,客户端发起请求时,请求被创建并初始化 */
@Override
public void requestInitialized(ServletRequestEvent sre) {
System.out.println("客户端发起新的请求");
ServletRequest request = sre.getServletRequest();
try {
/* 设置请求的编码 */
request.setCharacterEncoding("UTF-8");
} catch(Exception e) {
e.printStackTrace();
}
}
/* 客户端请求被销毁时由Web容器调用,服务器响应请求后,请求被销毁 */
@Override
public void requestDestroyed(ServletRequestEvent sre) {
System.out.println("服务器已处理请求");
}
}
HttpSessionListener
HttpSessionListener
用于监听session
的创建和销毁。
HttpSessionListener.java
public interface HttpSessionListener extends EventListener {
/* 新的session创建时由Web容器调用 */
void sessionCreated(HttpSessionEvent se);
/* session过期或被手动销毁时由web容器调用 */
void sessionDestroyed(HttpSessionEvent se);
}
HttpSessionEvent
类
HttpSessionEvent
类是HttpSessionListener
中监听session
创建和销毁方法的参数,常用方法有:
/* 获取session */
public HttpSession getSession();
配置
使用配置文件配置:
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app ... >
<listener>
<!-- Listener的配置仅需指明实现类路径信息 -->
<listener-class>com.lishaoyin.servletdemo.MySessionListener</listener-class>
</listener>
</web-app>
使用@WebListener
注解配置:
@WebListener
public class MySessionListener implements HttpSessionListener {
...
}
示例
MySessionListener.java
@WebListener
public class MySessionListener implements HttpSessionListener {
/* 统计在线人数 */
private int onlineNumber = 0;
/* 新的session创建时由Web容器调用 */
@Override
public void sessionCreated(HttpSessionEvent se) {
System.out.println("成员加入");
onlineNumber++;
/* 更新ServletContext域中的在线人数 */
se.getSession().getServletContext().setAttribute("online-number",onlineNumber);
}
/* session过期或被手动销毁时由web容器调用 */
@Override
public void sessionDestroyed(HttpSessionEvent se) {
System.out.println("成员离开");
onlineNumber--;
/* 更新ServletContext域中的在线人数 */
se.getSession().getServletContext().setAttribute("online-number",onlineNumber);
}
}
LoginServlet.java
@WebServlet(value="/login")
public class LoginServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request,HttpServletResponse response)
throws ServletException,IOException {
request.setCharacterEncoding("UTF-8");
/* 获取或创建Session */
HttpSession session = request.getSession();
/* 设置登录过期时间 */
session.setMaxInactiveInterval(10);
response.setContentType("text/html;charset=UTF-8");
String message = "当前在线人数 : "
+ session.getServletContext().getAttribute("online-number")
+ "人";
response.getOutputStream().write(message.getBytes("UTF-8"));
}
}
JSON
JSON(JavaScript Object Notation)
是一种轻量级客户端与服务器之间的数据交换格式。
JSON
格式
JSON
对象
JSON
是一组键值对,可包含各种类型的数据。JSON
可以是对象格式(JavaScript
对象),也可以是字符串形式。JSON
对象格式为:
/* 键与值之间使用":"分隔,多个键值对之间使用","分隔 */
var jsonObj = {
key1:value,
key2:value,
...
}
/* JSON的数据类型可以是普通类型、数组和JSON类型 */
var jsonObj = {
key1:123,
key2:"abc",
key3:false,
key4:[123,"abc"],
key5:{
key5_1:246,
key5_2:"abc"
}
}
/* JSON的访问:JSON对象.键 */
jsonObj.key1
示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JSON</title>
</head>
<body>
<script type="text/javascript">
var json = {
name:"Lishaoyin",
age:22,
skill:["c","java","javascript"],
department:{
id:1,
salary:1000000
}
}
console.log(json.name);
for(var i=0;i<json.skill.length;i++) {
console.log(json.skill[i]);
}
console.log(json.department.salary);
</script>
</body>
</html>
JSON
字符串
JSON
为字符串形式时,JSON
字符串必须以{}
包括,key
必须以""
包括。
/* JSON为字符串形式时,JSON字符串必须以{}包括,key必须以""包括,\"是"的转义 */
String json = "{\"key1\":value,\"key2\":value...}";
/* 例 */
String json = "{\"username\":\"Lishaoyin\",\"password\":\"123456789\"}";
客户端JSON
JSON
对象形式在客户端(JavaScript
)需要对数据进行处理时使用,字符串形式在客户端与服务器交换数据时使用。客户端(JavaScript
)中JSON
的内置对象名即为JSON
.客户端JSON
的常用方法:
/* 将JSON对象转换为JSON字符串 */
JSON.stringify(jsonObject);
/* 将JSON字符串转换为JSON对象 */
JSON.parse(jsonString);
/* JSON对象示例 */
var json = {
name:"Lishaoyin",
age:22,
skill:["c","java","javascript"],
department:{
id:1,
salary:1000000
}
}
/* JSON字符串示例,
JSON字符串必须使用"{}"包括,key必须使用""包括,\"为"的转义 */
var jsonString = "{\"key1\":value,\"key2\":value...}"
/* JSON对象转换为JSON字符串 */
var jsonString = JSON.stringify(json);
console.log(jsonString);
/* JSON字符串转换为JSON对象,\"是"的转义 */
var jsonObj = JSON.parse("{\"name\":\"Lishaoyin\",\"age\":22}");
console.log(jsonObj.name);
客户端发送JSON
客户端发送JSON
将在之后的AJAX
章节介绍。
客户端接收JSON
客户端接收JSON
将在之后的AJAX
章节介绍。
服务器JSON
服务器(Java)
使用JSON
可以使用谷歌提供的GSON
包,也可以直接使用JSON
字符串。
JSON
与JavaBean
的转换
Person person = new Person("Tony",1.75f,140);
Gson gson = new Gson();
/* JavaBean对象转换为JSON字符串 */
String jsonString = gson.toJson(person);
/* JSON字符串转换为JavaBean对象 */
Person person_1 = gson.fromJson("{\"name\" : \"Tony\",\"height\" : 1.75,\"weight\" : 140}",
Person.class);
JSON
与List
的转换
List<Person> list = new ArrayList<Person>();
list.add(new Person("Tony",1.75f,140));
list.add(new Person("Steven",1.85f,150));
list.add(new Person("Ellen",1.80f,130));
Gson gson = new Gson();
/* 将List转换为JSON字符串数组 */
String jsonString = gson.toJson(list);
String jsons = {"{\"name\":\"Tony\",\"height\":1.75,\"weight\":140}",
"{\"name\":\"Steven\",\"height\":1.85,\"weight\":150}",
"{\"name\":\"Ellen\",\"height\":1.80,\"weight\":130}"};
/* 将JSON字符串数组转换为泛型List,参数1传入JSON字符串数组,参数2传入Type类型 */
List<Person> persons = gson.fromJson(jsons,new PersonListType().getType());
将JSON
字符串数组转换为对应的泛型List
需要定义一个空类,并继承TypeToken
类:
/* PersonListType是一个中转工具类 */
public class PersonListType extends TypeToken<List<Person>> {
// 空类
}
JSON
与Map
的转换
Map<String,Person> map = new HashMap<String,Person>();
map.put("Tony",new Person("Tony",1.75f,140));
map.put("Steven",new Person("Steven",1.85f,150));
map.put("Ellen",new Person("Ellen",1.80f,130));
Gson gson = new Gson();
/* 将Map转换为JSON字符串数组 */
String jsonString = gson.toJson(map);
/* key对应Map的key */
String jsons = {"\"Tony\":{\"name\":\"Tony\",\"height\":1.75,\"weight\":140}",
"\"Steven\":{\"name\":\"Steven\",\"height\":1.85,\"weight\":150}",
"\"Ellen\":{\"name\":\"Ellen\",\"height\":1.80,\"weight\":130}"};
/* 将JSON字符串转换为Map,参数1传入JSON字符串,参数2传入Type类型 */
Map<String,Person> persons = gson.fromJson(jsons,new PersonMapType().getType());
将JSON
字符串数组转换为对应的Map
需要定义一个空类,并继承TypeToken
类:
/* PersonMapType是一个中转工具类 */
public class PersonMapType extends TypeToken<Map<String,Person>> {
// 空类
}
服务器接收JSON
JSON
用于向服务器传递参数,JavaScript
发送的JSON
数据将以"?key1=value&key2=value..."
的形式追加至请求url
之后,因此服务器端相应的使用request.getParameter()
方法获取JSON
数据。
String username = request.getParameter("username");
String password = request.getParameter("password");
服务器响应JSON
服务器只能使用字符串形式响应JSON
,但可以指定响应内容的数据类型。
/* 设置响应数据类型为JSON并设置编码格式,
"application"指定响应数据为应用类型,不在页面上直接展示 */
response.setContentType("application/json;charset=UTF-8");
/* JSON字符串必须使用"{}"包括,key必须使用""包括,\"是"的转义 */
String json = "{\"username\":\"Lishaoyin\",\"password\":\"123456789\"}";
response.getOutputStream().write(json.getBytes("UTF-8"));
/* 设置响应数据类型为普通文本并设置编码格式,
"application"指定响应数据为应用类型,不在页面上直接展示 */
response.setContentType("application/text;charset=UTF-8");
/* JSON字符串必须使用"{}"包括,key必须使用""包括,\"是"的转义 */
String json = "{\"username\":\"Lishaoyin\",\"password\":\"123456789\"}";
response.getOutputStream().write(json.getBytes("UTF-8"));
服务器端响应指定的数据类型必须与客户端一致。
AJAX
AJAX(Asynchronous JavaScript And XML)
是客户端与服务器交换数据的另一种格式,并且是目前最常用的服务器与客户端数据交换格式。AJAX
的原理为浏览器通过JavaScript
异步向服务器发起请求,因此可以在不刷新网页的情况下向服务器发送请求,并且可实现网页局部数据刷新。W3School-AJAX教程
原生AJAX
AJAX
请求步骤
1.创建XMLHttpRequest对象
/* XMLHttpRequest对象是AJAX的核心,AJAX实际是XMLHttpRequest对象的别名 */
var xhr = new XMLHttpRequest(method,url,async)
/* 例 */
var xhr = new XMLHttpRequest();
2.调用XMLHttpRequest对象的open()方法设置请求参数
/* 设置请求参数,
参数method指定请求方法("GET"/"POST");
url指定请求资源的地址;
async指定是否为异步请求,为true则为异步请求 */
xhr.open(method,url,async);
/* 例 */
xhr.open("GET","http://ip:port/project?key1=value&key2=value",true);
3.绑定接收响应方法
/* 为onreadystatechange绑定函数,在绑定的函数中可以根据不同状态码实现不同的功能;
readyState :
0 - 请求未初始化
1 - 服务器连接已建立
2 - 请求已接收
3 - 请求处理中
4 - 请求已完成,且响应已就绪
status :
404 - 找不到指定资源
500 - 服务器内部错误
200 - "OK" */
xhr.onreadystatechange = function() {
// 根据不同的状态码实现不同的功能
if(xhr.readyState == 4 && xhr.status == 200) {
// 接收响应数据
}
}
4.调用XMLHttpRequest对象的send()方法发送请求
/* 例 */
xhr.send();
XMLHttpRequest
中的重要方法
/* 取消当前请求 */
abort();
/* 以字符串形式返回所有响应头 */
getAllResponseHeaders();
/* 设置请求参数,
参数method指定请求方法("GET"/"POST");
url指定请求资源的地址;
async指定是否为异步请求,为true则为异步请求 */
open(method,url,async);
/* 设置请求参数同时指定用户名和密码 */
open(method,url,async,userName,password);
/* 发送请求 */
send();
/* 发送请求内容,请求内容可以是任何内容,仅用于POST请求 */
send(content);
/* 设置指定请求头的值 */
setRequestHeader(label,value);
AJAX
请求参数
GET
请求
AJAX
发送GET
请求需要在请求的url
之后追加请求参数。
/* 请求指定资源 */
xhr.open("GET","http://localhost:8080/ServletDemo/src",true);
/* 发送请求 */
xhr.send();
/* 设置请求参数 */
xhr.open("GET","http://localhost:8080/ServletDemo?name=Lishaoyin&password=123456",true);
/* 发送请求 */
xhr.send();
POST
请求
AJAX
发送POST
请求需要使用send()
方法,并且需要设置请求头。
/* 设置请求地址 */
xhr.open("POST","http://localhost:8080/ServletDemo/src",true);
/* 设置请求头,设置请求内容类型 */
xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
/* 发送请求参数 */
xhr.send("name=Lishaoyin&password=123456");
AJAX
接收响应
xhr.onreadystatechange = function() {
// 根据不同的状态码实现不同的功能
if(xhr.readyState == 4 && xhr.status == 200) {
/* 获取响应数据(非XML数据,例如JSON) */
var text = console.log(xhr.responseText);
/* 获取响应数据(XML数据) */
var xml = console.log(xhr.responseXML);
}
}
jQuery
使用AJAX
实际开发中不会使用原生AJAX
,而是使用JavaScript
框架实现的AJAX
,例如jQuery
.jQuery
在线教程
基础
jQuery
使用AJAX
的方式一:
/* 使用jQuery对象中的ajax()函数,函数中传入JSON对象 */
$.ajax({
param1:value,
param2:value,
...
paramn:value
});
/* ajax()参数 */
type : 请求方式"GET"/"POST"
url : 请求地址
async : 是否异步请求,true/false,默认为true
data : 发送至服务器的数据,如果没有参数则不需要设置
dataType : 预期服务器返回的数据类型
contentType : 设置请求头
success : 请求成功回调函数
error : 请求失败回调函数
/* 示例 */
$.ajax({
type:"GET",
url:"http://localhost:8080/ServletDemo/src",
async:true,
/* 设置返回的数据类型为JSON,此时jQuery接收到数据后会将数据自动封装为JSON格式 */
dataType:"json",
/* JSON对象格式数据 */
data:{
name:"Lishaoyin",
age:22
},
/* success回调函数的参数data为响应数据 */
success:function(data) {
var json = JSON.parse(data);
console.log(json.name + " , " + json.age);
}
});
jQuery
使用AJAX
的方式二:
/* 使用"GET"方法请求指定资源,传入资源地址,请求参数和请求响应回调函数 */
$.get(url,param,function(data){
// ...
});
/* 例:请求指定资源 */
$.get("http://localhost:8080/ServletDemo/file.png");
/* 例:请求指定动作 */
$.get("http://localhost:8080/ServletDemo/login",
{user:"Lishaoyin",password:"123456"},
function(data){
// ...
});
/* 使用"POST"方法请求指定资源,传入资源地址,请求参数和请求响应回调函数 */
$.post(url,param,function(data){
// ...
});
/* 例:请求指定资源 */
$.post("http://localhost:8080/ServletDemo/file.png");
/* 例:请求指定动作 */
$.post("http://localhost:8080/ServletDemo/login",
{user:"Lishaoyin",password:"123456"},
function(data){
// ...
});
jQuery
使用AJAX
的方式三:
/* 使用"GET"方法,要求回传数据为JSON格式,传入请求地址,请求参数和响应回调函数 */
$.getJSON(url,param,function(data){
// ...
});
AJAX
发送JSON
$.ajax()
$.ajax({
...
url:"http://ip:port/project/src",
/* 发起请求时data中的参数直接追加至url之后:
"http://ip:port/project/src?key1=value&key2=value..." */
data:{
key1:value,
key2:value,
...
}
...
});
/* 例 */
$.ajax({
type:"GET",
url:"http://localhost:8080/ServletDemo/login",
async:true,
dataType:"json",
/* 请求参数追加:
"http://localhost:8080/ServletDemo/login?username=Lishaoyin&password=123456789" */
data:{
username:"Lishaoyin",
password:"123456789"
}
});
$.get()/post()/getJSON()
/* 发起请求时参数直接追加至url之后:
"http://ip:port/project/src?key1=value&key2=value..." */
$.get("http://ip:port/project/src",{key1:value,key2:value...});
/* 例 */
/* 请求参数追加:
"http://localhost:8080/ServletDemo/login?username=Lishaoyin&password=123456789" */
$.get("http://ip:port/project/src",{
username:"Lishaoyin",
password:"123456789"});
AJAX
接收JSON
JSON
对象形式
服务器端发送数据:
/* 设置响应内容为JSON并设置编码格式,
"application"指定响应内容为应用类型,不在页面上直接展示 */
response.setContentType("application/json;charset=UTF-8");
/* JSON字符串必须使用"{}"包括,且key必须使用""包括 */
String json = "{\"key1\":value,\"key2\":value...}";
/* 发送响应 */
response.getOutputStream().write(json.getBytes("UTF-8"));
客户端接收数据:
$.ajax({
...
dataType:"json",
success(data){
/* dataType指定为"json",jQuery直接将响应结果处理为JSON */
console.log(data.key1);
console.log(data.key2);
...
},
...
});
JSON
字符串形式
服务器端发送数据:
/* 设置响应内容为普通文本并设置编码格式,
"application"指定响应内容为应用类型,不在页面上直接展示 */
response.setContentType("application/text;charset=UTF-8");
/* JSON字符串必须使用"{}"包括,且key必须使用""包括 */
String json = "{\"key1\":value,\"key2\":value...}";
/* 发送响应 */
response.getOutputStream().write(json.getBytes("UTF-8"));
客户端接收数据:
$.ajax({
...
dataType:"text",
success(data){
/* dataType设置为"text",jQuery将原样接收响应字符串,需要将字符串转换为JSON */
var json = JSON.parse(data);
console.log(json.key1);
console.log(json.key2);
...
},
...
});
示例
login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>login</title>
<!-- 远程jQuery -->
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.js"></script>
<script type="text/javascript">
$(function(){
var usernameInput = $("input[name='username']");
var passwordInput = $("input[name='password']");
var usernameInputFinished = false;
var passwordInputFinished = false;
usernameInput.on("focusin",function(){usernameInputFinished = false;});
passwordInput.on("focusin",function(){passwordInputFinished = false;});
/* 提交结果 */
function submit(username,password) {
$.ajax({
type:"POST",
url:"http://localhost:8080/ServletDemo/login",
async:true,
/* 要求响应格式为JSON,如果响应格式不正确,将跳至error回调 */
dataType:"json",
data:{
username:username,
password:password
},
success:function(data){
console.log("request succeed");
if(data.message == "login success") {
alert("login success");
}
else if(data.message == "user not exists") {
alert("please check username");
}
else if(data.message == "password wrong") {
alert("please check password");
}
},
error:function(){
alert("please check the internet connection");
}
});
}
usernameInput.on("focusout",function(){
usernameInputFinished = true;
/* 检查username非空 */
if(!usernameInput.val()) {
usernameInputFinished = false;
alert("user name cannot be null");
}
/* 检查是否同时输入完成了password */
else if(passwordInputFinished) {
console.log("user finished input");
/* 提交数据 */
submit(usernameInput.val(),passwordInput.val());
}
});
passwordInput.on("focusout",function(){
passwordInputFinished = true;
/* 检查password非空 */
if(!passwordInput.val()) {
passwordInputFinished = false;
alert("password cannot be null");
}
/* 检查是否同时输入完成了username */
else if(usernameInputFinished) {
console.log("user finished input");
/* 提交数据 */
submit(usernameInput.val(),passwordInput.val());
}
});
});
</script>
<style type="text/css">
*{
font-family: "Youyuan";
}
</style>
</head>
<body>
<h1>请登录</h1><br/>
账户<input name="username" type="text"/><br/>
密码<input name="password" type="password"/>
</body>
</html>
LoginServlet.java
@WebServlet(value="/login")
public class LoginServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request,HttpServletResponse response)
throws ServletException,IOException {
/* 设置请求编码格式为UTF-8 */
request.setCharacterEncoding("UTF-8");
/* 设置响应内容为JSON及编码格式为UTF-8 */
response.setContentType("application/json;charset=UTF-8");
System.out.println("request in");
/* 获取数据 */
String username = request.getParameter("username");
String password = request.getParameter("password");
System.out.println("username : " + username + " , " + "password : " + password + ".");
if(username.equals("Lishaoyin")) {
if(password.equals("123456789")) {
/* 服务器端发送JSON字符串必须严格符合{"key1":value,"key2":value}格式,
"{}"必须加上,且双引号使用"\""转义 */
response.getOutputStream().write(
"{\"message\":\"login success\"}".getBytes("UTF-8"));
System.out.println("login success");
} else {
response.getOutputStream().write(
"{\"message\":\"password wrong\"}".getBytes("UTF-8"));
System.out.println("password wrong");
}
} else {
response.getOutputStream().write(
"{\"message\":\"user not exists\"}".getBytes("UTF-8"));
System.out.println("user not exists");
}
}
}