文章目录
第十三章 Servlet
13.1 前期知识储备
- Tomcat
- http协议
- XML标记语言
13.2 什么是servlet
概论:servlet是javaweb中最核心的对象,那想必大家都知道什么是javaweb了吧? java+web =javaweb 是两个完全不同的东西组合而来的。java是一种编程语言,web是一种网络传输协议,万维网、http、https应该知道吧,这个东西,只要遵循了它的协议,可以用任何语言写程序,都可以被web认可,完全可以被运行。比如,你有Python写了一个程序,并且遵循了web的协议规范,就可以被使用。 那web里面有什么东西呢?为什么可以运行所有的程序呢? 其实,web只是一个协议,没有任何的实现,至于实现呢?要交给自己去实现。
这里说一下java的实现,这个java是怎么实现web的呢? 有一个组织,非常的牛,叫阿帕奇基金会,他们就开发了一个遵循web规范的一个技术。叫容器,什么是容器呢? 容器就是一个装东西的啊,所以,遵循了web协议开发了一个web容器,这个协议叫web容器不太好吧,总的有一个自己的名字吧,不然,万一,其他人也实现了这个web协议,他叫啥呢。
考虑到了这个问题。所以 阿帕奇基金会就给这个容器起了一个名字,叫 Tomcat 。这个Tomcat大家不陌生吧,他就是一个web容器,这个东西可强大了,是他发明出了servlet技术。所以,做java开发的工程师,要想自己写web应用发布到web(万维网)上,让别人能看到自己的网站,就必须得用它的技术啊,得遵循它的规范啊。
所以,以后,我们做网站开发,就要用Tomcat的servlet技术做开发,那到这里可能就有一个疑问了,我们写java程序的时候,都要有一个主程序类,这个主程序类的main方法是整个程序的入口。那到了servlet这里,它咋不要我们写主程序入口啊。这就感到很奇怪了吧,不过,也不怪咱啊,咱也是第一次接触这个玩意啊,哎,可是,仔细想想,这个servlet是Tomcat的技术啊,而Tomcat是啥,是一个容器,web容器,所以,我们根据servlet的技术写完了代码,把它打包放到Tomcat的容器里面,容器运行了发现是遵循了自家的servlet技术写的web程序,咱认识啊,所以,容器就给咱们把这个web程序给跑起来了。然后我们就可以在世界的任何一个使用互联网的地方对它进行访问了。
13.3 第一个servlet程序
1 选择idea中的file 菜单,选择 new project 项目
2 选择 java Enterprise(java企业项目)
3.选择jdk版本,如果没有可以选择 download下载,或者添加自己已经安装好的idea路径
4 Project template 选择 web 应用模板,灰色的名称表示会默认生成该文件
5 Application server 选择Tomcat容器(服务器)如果没有,可以选择New...
,添加已经下载好的Tomcat目录(请全选择Tomcat server)
6 Build tool 选择maven构建工具,选择maven能大大简化我们对jar包的管理,不需要创建lib目录
7 Test runner 选择测试类型,选择Junit 单元测试
8 languages 选择 我们的java语言
9 到最下面, 选择next 到达下一步
10 name 表示项目名称
11 location 选择项目所存储的路径
12 Artifact Coordinates maven项目管理jar的方法
Group 表示组织,填入格下方有灰色文字提示,表示可以填公式的域名,一般倒着写,比如 www.baidu,com写为
com.biadu.www
Artifact: 表示项目名称
Version 表示版本号
13 如果全部填完表示可以通过 域名+项目名+版本号查询到该项目。即maven则是通过这种方式完成对jar包的管理。
14 点击 Finsh
创建成功的项目
启动Tomcat服务器运行该项目
点击 Add Configuration
添加配置
点击右上角的+
号 添加模板,选择Tomcat server 在选择它的Local 表示本地。
name : 表示服务器名称
Application server 表示服务器的配置文件,点击 Configure 可以编辑该信息
URl: 表示在游览器访问的路径
Http port 表示 http协议的端口号
如果有如下提示则点击 红色 !
感叹号来修复,他表示还没有任何项目被部署。
选择第二类型进行部署即可。
修改Application context 上下文信息,只留下一个根路径 /
确定之后,点击绿色的三角形运行服务器
之后游览器会自动打开,进入项目的首页
到此,第一个javaweb项目完成。
13.4 servlet 分析
13.4.1 servlet 接口方法
pom文件
<packaging>war</packaging>
表示打包方式为war ,运行打包插件之后,把该文件放入到Tomcat 的webapps中即可通过项目名访问项目。
<groupId>javax.servlet</groupId>
是servlet的核心jar包
其余两个分别是Junit5册jar包
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>1.0-SNAPSHOT</version>
<name>demo</name>
<packaging>war</packaging>
<properties>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.source>1.8</maven.compiler.source>
<junit.version>5.7.0</junit.version>
</properties>
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.3.0</version>
</plugin>
</plugins>
</build>
</project>
创建一个MyServlet类实现Servlet接口方法
package com.example.demo;
import javax.servlet.*;
import java.io.IOException;
public class MyServlet implements Servlet {
/**
* 初始化
* @param servletConfig
* @throws ServletException
*/
@Override
public void init(ServletConfig servletConfig) throws ServletException {
}
/**
* 获取Servlet配置
* @return
*/
@Override
public ServletConfig getServletConfig() {
return null;
}
/**
* 进行服务处理
* @param servletRequest
* @param servletResponse
* @throws ServletException
* @throws IOException
*/
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
}
/**
* 获取Servlet的信息
* @return
*/
@Override
public String getServletInfo() {
return null;
}
/**
* 销毁方法
*/
@Override
public void destroy() {
}
}
点击Servlet进去查看。
一个有五个方法
init()
方法表示初始化,可以对servlet的一些配置信息提前进行预处理。
getServletConfig()
表示可以获取Servlet的一些配置信息,如servlet的名称,servlet上下文,init的参数等。
service()
服务的方法,该方法用于处理请求,游览器发送的请求会经过该方法进行处理。
getServletInfo()
获取servlet的信息,如,作者,版权 版本等。
destory()
销毁方法,servlet完成一次请求之后,就调用该方法进行处理。
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package javax.servlet;
import java.io.IOException;
public interface Servlet {
void init(ServletConfig var1) throws ServletException;
ServletConfig getServletConfig();
void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
String getServletInfo();
void destroy();
}
ServletConfig 接口
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package javax.servlet;
import java.util.Enumeration;
public interface ServletConfig {
String getServletName();
ServletContext getServletContext();
String getInitParameter(String var1);
Enumeration<String> getInitParameterNames();
}
创建一个类实现该接口
package com.example.demo;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import java.util.Enumeration;
/**
* @Author: lyf
* @CreateTime: 2022-11-30
* @description:
*/
public class MyServletConfig implements ServletConfig {
/**
* 获取Servlet名称
* @return
*/
@Override
public String getServletName() {
return null;
}
/**
* 获取ServletContext上下文
* @return
*/
@Override
public ServletContext getServletContext() {
return null;
}
/**
* 获取初始化时的参数名称
* @param s
* @return
*/
@Override
public String getInitParameter(String s) {
return null;
}
/**
* 获取所有的初始化参数名称,返回一个集合
* @return
*/
@Override
public Enumeration<String> getInitParameterNames() {
return null;
}
}
ServletContext接口
实现该接口的方法就可以获取到上下文的信息。
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package javax.servlet;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Enumeration;
import java.util.EventListener;
import java.util.Map;
import java.util.Set;
import javax.servlet.ServletRegistration.Dynamic;
import javax.servlet.descriptor.JspConfigDescriptor;
public interface ServletContext {
String TEMPDIR = "javax.servlet.context.tempdir";
String ORDERED_LIBS = "javax.servlet.context.orderedLibs";
String getContextPath();
ServletContext getContext(String var1);
int getMajorVersion();
int getMinorVersion();
int getEffectiveMajorVersion();
int getEffectiveMinorVersion();
String getMimeType(String var1);
Set<String> getResourcePaths(String var1);
URL getResource(String var1) throws MalformedURLException;
InputStream getResourceAsStream(String var1);
RequestDispatcher getRequestDispatcher(String var1);
RequestDispatcher getNamedDispatcher(String var1);
/** @deprecated */
@Deprecated
Servlet getServlet(String var1) throws ServletException;
/** @deprecated */
@Deprecated
Enumeration<Servlet> getServlets();
/** @deprecated */
@Deprecated
Enumeration<String> getServletNames();
void log(String var1);
/** @deprecated */
@Deprecated
void log(Exception var1, String var2);
void log(String var1, Throwable var2);
String getRealPath(String var1);
String getServerInfo();
String getInitParameter(String var1);
Enumeration<String> getInitParameterNames();
boolean setInitParameter(String var1, String var2);
Object getAttribute(String var1);
Enumeration<String> getAttributeNames();
void setAttribute(String var1, Object var2);
void removeAttribute(String var1);
String getServletContextName();
Dynamic addServlet(String var1, String var2);
Dynamic addServlet(String var1, Servlet var2);
Dynamic addServlet(String var1, Class<? extends Servlet> var2);
Dynamic addJspFile(String var1, String var2);
<T extends Servlet> T createServlet(Class<T> var1) throws ServletException;
ServletRegistration getServletRegistration(String var1);
Map<String, ? extends ServletRegistration> getServletRegistrations();
javax.servlet.FilterRegistration.Dynamic addFilter(String var1, String var2);
javax.servlet.FilterRegistration.Dynamic addFilter(String var1, Filter var2);
javax.servlet.FilterRegistration.Dynamic addFilter(String var1, Class<? extends Filter> var2);
<T extends Filter> T createFilter(Class<T> var1) throws ServletException;
FilterRegistration getFilterRegistration(String var1);
Map<String, ? extends FilterRegistration> getFilterRegistrations();
SessionCookieConfig getSessionCookieConfig();
void setSessionTrackingModes(Set<SessionTrackingMode> var1);
Set<SessionTrackingMode> getDefaultSessionTrackingModes();
Set<SessionTrackingMode> getEffectiveSessionTrackingModes();
void addListener(String var1);
<T extends EventListener> void addListener(T var1);
void addListener(Class<? extends EventListener> var1);
<T extends EventListener> T createListener(Class<T> var1) throws ServletException;
JspConfigDescriptor getJspConfigDescriptor();
ClassLoader getClassLoader();
void declareRoles(String... var1);
String getVirtualServerName();
int getSessionTimeout();
void setSessionTimeout(int var1);
String getRequestCharacterEncoding();
void setRequestCharacterEncoding(String var1);
String getResponseCharacterEncoding();
void setResponseCharacterEncoding(String var1);
}
测试 servlet方法,在对应的方法中加入对应的代码。
package com.example.demo;
import javax.servlet.*;
import java.io.IOException;
public class MyServlet implements Servlet {
/**
* 初始化
* @param servletConfig
* @throws ServletException
*/
@Override
public void init(ServletConfig servletConfig) throws ServletException {
String servletName = servletConfig.getServletName();
System.out.println("init方法:"+servletName);
}
/**
* 获取Servlet配置
* @return
*/
@Override
public ServletConfig getServletConfig() {
MyServletConfig myServletConfig = new MyServletConfig();
String servletName = myServletConfig.getServletName();
System.out.println(servletName);
return myServletConfig;
}
/**
* 进行服务处理
* @param servletRequest
* @param servletResponse
* @throws ServletException
* @throws IOException
*/
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("service 方法正在处理 ...");
}
/**
* 获取Servlet的信息
* @return
*/
@Override
public String getServletInfo() {
return null;
}
/**
* 销毁方法
*/
@Override
public void destroy() {
System.out.println("destroy ...");
}
}
在web.xml 中配置servlet的请求映射。
``xml
<servlet>
<servlet-name>myServlet</servlet-name>
<servlet-class>com.example.demo.MyServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>myServlet</servlet-name>
<url-pattern>/myServlet</url-pattern>
</servlet-mapping>
```
启动Tomcat服务器
打开游览器访问配置的地址
http://localhost:8080/myServlet
运行结果如下 输出了Servlet中的init方法,与service方法。
现在关闭服务器 destroy方法被调用
如果控制台出现乱码,修改配置Tomcat的配置,加入如下参数 -Dfile.encoding=UTF-8
总结
通过上面的几个类中的方法,我们知道,servlet内部规定了这么多方法,非常的大而全,很是nice,但是呢,没创建一个Servlet我们就要实现接口里面的所有方法,是不是有点太复杂了。而且里面的一些方法,有些我们自己也用不到啊。有没有一个好的解决方案呢,
答案是,当然有,而且帮我们省略了好多复杂的逻辑,它就是HttpServlet类。
13.4.2 HttpServlet
HttpServlet是Servlet的子类(小技巧提示:如果一个类是接口或者是抽象类,那么它的子类的命名规范则是在它的名称上加了它自己的特色。如:Servlet,则它的子类:XxxServlet)
点击HttpServlet类的关系分析,继承关系如下。
GenericServlet
类
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package javax.servlet;
import java.io.IOException;
import java.io.Serializable;
import java.util.Enumeration;
import java.util.ResourceBundle;
public abstract class GenericServlet implements Servlet, ServletConfig, Serializable {
private static final String LSTRING_FILE = "javax.servlet.LocalStrings";
private static ResourceBundle lStrings = ResourceBundle.getBundle("javax.servlet.LocalStrings");
private transient ServletConfig config;
public GenericServlet() {
}
public void destroy() {
}
public String getInitParameter(String name) {
ServletConfig sc = this.getServletConfig();
if (sc == null) {
throw new IllegalStateException(lStrings.getString("err.servlet_config_not_initialized"));
} else {
return sc.getInitParameter(name);
}
}
public Enumeration<String> getInitParameterNames() {
ServletConfig sc = this.getServletConfig();
if (sc == null) {
throw new IllegalStateException(lStrings.getString("err.servlet_config_not_initialized"));
} else {
return sc.getInitParameterNames();
}
}
public ServletConfig getServletConfig() {
return this.config;
}
public ServletContext getServletContext() {
ServletConfig sc = this.getServletConfig();
if (sc == null) {
throw new IllegalStateException(lStrings.getString("err.servlet_config_not_initialized"));
} else {
return sc.getServletContext();
}
}
public String getServletInfo() {
return "";
}
public void init(ServletConfig config) throws ServletException {
this.config = config;
this.init();
}
public void init() throws ServletException {
}
public void log(String msg) {
this.getServletContext().log(this.getServletName() + ": " + msg);
}
public void log(String message, Throwable t) {
this.getServletContext().log(this.getServletName() + ": " + message, t);
}
public abstract void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
public String getServletName() {
ServletConfig sc = this.getServletConfig();
if (sc == null) {
throw new IllegalStateException(lStrings.getString("err.servlet_config_not_initialized"));
} else {
return sc.getServletName();
}
}
}
通过类中的方法发现,该类帮我实现了Servlet与ServletConfig类中的大部分方法。
ServletConfig中的getInitParameter()
public String getInitParameter(String name) {
ServletConfig sc = this.getServletConfig();
if (sc == null) {
throw new IllegalStateException(lStrings.getString("err.servlet_config_not_initialized"));
} else {
return sc.getInitParameter(name);
}
}
ServletConfig中的getServletContext()
public ServletContext getServletContext() {
ServletConfig sc = this.getServletConfig();
if (sc == null) {
throw new IllegalStateException(lStrings.getString("err.servlet_config_not_initialized"));
} else {
return sc.getServletContext();
}
}
接下来分析HttpServlet子类看看,它都帮我们做了些什么。
HttpServlet
该类中定义了http中常用的请求方式以及调用对应的方法。
部分代码如下
private static final String METHOD_DELETE = "DELETE";
private static final String METHOD_HEAD = "HEAD";
private static final String METHOD_GET = "GET";
private static final String METHOD_OPTIONS = "OPTIONS";
private static final String METHOD_POST = "POST";
private static final String METHOD_PUT = "PUT";
private static final String METHOD_TRACE = "TRACE";
private static final String HEADER_IFMODSINCE = "If-Modified-Since";
private static final String HEADER_LASTMOD = "Last-Modified";
private static final String LSTRING_FILE = "javax.servlet.http.LocalStrings";
private static ResourceBundle lStrings = ResourceBundle.getBundle("javax.servlet.http.LocalStrings");
public HttpServlet() {
}
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String method = req.getMethod();
long lastModified;
if (method.equals("GET")) {
lastModified = this.getLastModified(req);
if (lastModified == -1L) {
this.doGet(req, resp);
} else {
long ifModifiedSince = req.getDateHeader("If-Modified-Since");
if (ifModifiedSince < lastModified) {
this.maybeSetLastModified(resp, lastModified);
this.doGet(req, resp);
} else {
resp.setStatus(304);
}
}
} else if (method.equals("HEAD")) {
lastModified = this.getLastModified(req);
this.maybeSetLastModified(resp, lastModified);
this.doHead(req, resp);
} else if (method.equals("POST")) {
this.doPost(req, resp);
} else if (method.equals("PUT")) {
this.doPut(req, resp);
} else if (method.equals("DELETE")) {
this.doDelete(req, resp);
} else if (method.equals("OPTIONS")) {
this.doOptions(req, resp);
} else if (method.equals("TRACE")) {
this.doTrace(req, resp);
} else {
String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[]{method};
errMsg = MessageFormat.format(errMsg, errArgs);
resp.sendError(501, errMsg);
}
}
测试HttpServlet
重写doGet与doPost方法
public class MyHttpServlet extends HttpServlet {
private static final long serialVersionUID = 160282082403086532L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("doGet 方法被执行");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("doPost 方法被执行");
}
}
在web.xml中加入对应的servlet映射。
<servlet>
<servlet-name>httpServlet</servlet-name>
<servlet-class>com.example.demo.MyHttpServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>httpServlet</servlet-name>
<url-pattern>/httpServlet</url-pattern>
</servlet-mapping>
在webapp文件夹下创建一个HttpServlet.jsp 文件
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head>
<title>httpServlet</title>
</head>
<body>
<a href="httpServlet">doGet 方法请求</a>
<br>
<form method="post" action="httpServlet">
<button type="submit">doPost 方法请求</button>
</form>
</body>
</html>
运行Tomcat服务器
访问链接
http://localhost:8080/httpServlet.jsp
点击对应的方法,查看控制台输出消息
结果如下。
13.4.3 执行过程
1 启动Tomcat成功之后,访问项目路径
2 根据项目路径在web.xml 中查找对应的servlet-mapping映射,在根据映射中的值查找对应的Servlet文件。
3 根据请求的类型(post get)完成响应
4 返回处理的数据
13.4.4 生命周期
因为servlet是运行在Tomcat容器中,所以它的生命周期有容器来管理。
1 servlet的类加载与实例化
2 init() 方法完成初始化
3 service() 方法完成请求处理
4 destroy() 方法完成servlet的销毁
13.5 ServletContext 上下文
每一个web项目都有一个ServletContext上下文,那他是用来干嘛的呢?有没有想过一个问题,就是在这么多的servlet当中,如果每一个servlet都要获取一个配置信息,那他会怎么做呢? 是每一个servlet都自己写一个方法去获取这些配置信息吗, 答案显示不是,可以把它存在一个全局变量当中。如果有servlet来获取这个信息,就直接从这个全局变量中获取即可,而这个全局变量就相当于servletcontext上下文。
通过查看servletContext接口中的方法,则知道该上下文拥有了哪些功能,有设置全局属性方法,有获取请求路径方法,有动态添加过滤器方法等等,更多的功能,请看源码。
常用的servletContext功能。
1 获取全局配置参数
2 获取web中的项目资源
3 获取 servlet之间的共享数据
13.5.1 获取全局配置参数
在web.xml 文件中定义两个全局参数
<!-- 全局配置参数-->
<context-param>
<param-name>username</param-name>
<param-value>wangwu</param-value>
</context-param>
<context-param>
<param-name>password</param-name>
<param-value>admin</param-value>
</context-param>
修改MyHttpServlet类,在doGet中加入如下代码
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("doGet 方法被执行");
Enumeration<String> initParameterNames = getServletContext().getInitParameterNames();
while (initParameterNames.hasMoreElements()){
String element = initParameterNames.nextElement();
String values = getServletContext().getInitParameter(element);
System.out.println(element+":"+values);
}
}
修改Tomcat服务器的部署选项功能,标记的地方表示ServletContext上下文。如果只输入/
表示该项目的上下文为跟目录(相当于是空值一样)。
上下文还表示项目的访问路径,如果上下文的值为/
则它的访问路径为:
http://localhost:8080/*
*
表示servlet的映射路径 如 http://localhost:8080/httpServlet.jsp
如果项目有上下文 且值为 /javaweb
,则它的访问路径为
http://localhost:8080/javaweb/*
*
表示servlet的映射路径 如 http://localhost:8080/javaweb/httpServlet.jsp
启动Tomcat服务,访问路径 http://localhost:8080/httpServlet.jsp
点击doGet方法链接
控制台输出如下信息
13.5.2 获取web中的资源
在webapp文件创建一个aa.txt 资源文件。
在MyHttpServlet中增加如下代码
package com.example.demo;
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.Enumeration;
import java.util.Set;
/**
* @Author: lyf
* @CreateTime: 2022-11-30
* @description:
*/
public class MyHttpServlet extends HttpServlet {
private static final long serialVersionUID = 160282082403086532L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("doGet 方法被执行");
// 获取所有的初始化参数
Enumeration<String> initParameterNames = getServletContext().getInitParameterNames();
while (initParameterNames.hasMoreElements()){
String element = initParameterNames.nextElement();
String values = getServletContext().getInitParameter(element);
System.out.println(element+":"+values);
}
//获取文件所在路径
String realPath = getServletContext().getRealPath("aa.txt");
System.out.println("aa.txt文件所在路径"+realPath);
// 获取文件的流对象
InputStream resourceAsStream = getServletContext().getResourceAsStream("aa.txt");
System.out.println("文件流对象"+resourceAsStream);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("doPost 方法被执行");
}
}
运行Tomcat服务器
结果如下
13.5.3 共享数据域中的值
Servlet 之间的数据共享,对登录次数做统计实现共享数据。
创建一个LoginServlet 对象
public class LoginServlet extends HttpServlet {
private static final long serialVersionUID = -1799694677898262115L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//从上下文中获取全局参数 count
Integer count = (Integer) getServletContext().getAttribute("count");
if (count ==null){
// 如果等于 null 则设置该值为 1
getServletContext().setAttribute("count",1);
}else{
// 不等于null,表示在累加
getServletContext().setAttribute("count",count+1);
}
}
}
添加请求LoginServlet的请求映射
<servlet>
<servlet-name>loginServlet</servlet-name>
<servlet-class>com.example.demo.LoginServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>loginServlet</servlet-name>
<url-pattern>/login</url-pattern>
</servlet-mapping>
修改MyHttpServlet中的doGet方法。
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Integer count = (Integer) getServletContext().getAttribute("count");
if (count == null){
resp.getWriter().write("no login");
}
resp.getWriter().write("count:"+ count);
}
重启Tomcat 访问 http://localhost:8080/login
在访问 http://localhost:8080/httpServlet
即可查看到count中的值。
总结
通过案例,发现了上下文设置的全局属性,在所有的Servlet中是共享的,获取到的数据也是全局共享的。上下文就像是整个web项目的公共空间,所有的servlet都可以在里面放东西,也可以在里面取东西,上下文的生命周期随着web项目的初始化而创建,随着web项目的关闭而销毁。
13.6 ServletConfig
获取Servlet的配置信息,那设置这些配置信息在哪里呢? 在web.xml 里面。
创建一个DataServlet
public class DataServlet extends HttpServlet {
private static final long serialVersionUID = 2763986695002987487L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
}
在web.xml 中配置该servlet的信息。 < init-param >表示初始化的参数。
<servlet>
<servlet-name>dataServlet</servlet-name>
<servlet-class>com.example.demo.DataServlet</servlet-class>
<init-param>
<param-name>Driver</param-name>
<param-value>com.mysql.jdbc.Driver</param-value>
</init-param>
<init-param>
<param-name>url</param-name>
<param-value>jdbc:mysql://localhost:3306/test</param-value>
</init-param>
<init-param>
<param-name>username</param-name>
<param-value>root</param-value>
</init-param>
<init-param>
<param-name>password</param-name>
<param-value>root</param-value>
</init-param>
</servlet>
在DataServlet中获取web中的dataServlet的初始化参数
public class DataServlet extends HttpServlet {
private static final long serialVersionUID = 2763986695002987487L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Enumeration<String> initParameterNames = getServletConfig().getInitParameterNames();
while (initParameterNames.hasMoreElements()){
String element = initParameterNames.nextElement();
String value = getServletConfig().getInitParameter(element);
System.out.println(element+":"+value);
}
}
}
重启Tomcat 访问路径 http://localhost:8080/data
结果如下
13.7 HttpServletRequest
HttpServletRequest对象封装了从游览器发出的所有请求信息,比如 请求的参数,请求的类型,请求的远程地址与主机等等,如果一个方法中含有HttpServletRequest对象,就可以获取到该对象的所有信息。
HttpServletRequest的继承关系
查看ServletRequest中的方法
这里列举几个常用的方法
//获取上下文中的全局属性
public Object getAttribute(String name);
//返回所有的上下文属性
public Enumeration<String> getAttributeNames();
//获取请求的编码格式
public String getCharacterEncoding();
//请求的设置编码
public void setCharacterEncoding(String env) throws UnsupportedEncodingException;
// 获取上下文的长度
public int getContentLength();
// 获取请求的参数
public String getParameter(String name);
//获取所有的请求参数
public Enumeration<String> getParameterNames();
//获取所有的参数一map的方式
public Map<String, String[]> getParameterMap();
HttpServletRequest
HttpServletRequest中常用的方法
//获取客户端的cookies
public Cookie[] getCookies();
// 根据请求头的名称获取请求头的值
public String getHeader(String name);
//根据请求头的名称获取请求头的值,该请求用于返回多个值的情况,如 Accept-Language
public Enumeration<String> getHeaders(String name);
//返回所有的请求头名称的值
public Enumeration<String> getHeaderNames();
//获取请求的方法名称
public String getMethod();
//获取路径信息
public String getPathInfo();
//获取上下文路径
public String getContextPath();
//获取请求路径URL
public String getRequestURI();
//获取session 参数为true 没有就创建一个
public HttpSession getSession(boolean create);
//获取session
public HttpSession getSession();
以上两种接口的实现方法类
ServletRequestWrapper对应RequestServlet的实现类
HttpServletRequest对应HttpServletRequestWrapper实现类
这两个类实现了各自所对应的接口方法,我们直接调用方法.
获取HttpServletRequest中的信息。
下面,通过HttpServletRequest获取用户提交的信息。
修改httpServlet.jsp 文件,修改信息如下。
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head>
<title>httpServlet</title>
</head>
<body>
<br>
<form method="post" action="login">
用户名: <input type="text" name="username"><br>
密码: <input type="password" name="password"> <br>
是否记住密码:<input type="radio" name="true"><br>
<button type="submit" name="register">注册</button>
<button type="submit">登录</button>
</form>
</body>
</html>
LoginServlet
package com.example.demo;
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.Arrays;
import java.util.Enumeration;
import java.util.Map;
import java.util.Set;
/**
* @Author: lyf
* @CreateTime: 2022-11-30
* @description:
*/
public class LoginServlet extends HttpServlet {
private static final long serialVersionUID = -1799694677898262115L;
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String username = req.getParameter("username");
String password = req.getParameter("password");
String aTrue = req.getParameter("true");
System.out.println("username:"+username
+"password:"+password
+"remember me:"+aTrue);
doGet(req,resp);
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取请求的头部信息
System.out.println("获取请求头信息");
Enumeration<String> headerNames = req.getHeaderNames();
while (headerNames.hasMoreElements()){
String element = headerNames.nextElement();
String value = req.getHeader(element);
System.out.println(element+":"+value);
}
System.out.println("------------------");
System.out.println("获取请求体信息");
Enumeration<String> parameterNames = req.getParameterNames();
while (parameterNames.hasMoreElements()){
String element = parameterNames.nextElement();
String value = req.getParameter(element);
System.out.println(element+":"+value);
}
System.out.println("---------------------");
System.out.println("以Map方式获取客户端发送过来的参数信息");
Map<String, String[]> parameterMap = req.getParameterMap();
Set<String> key= parameterMap.keySet();
for (String name: key) {
String[] values = parameterMap.get(name);
System.out.println(name+":"+ Arrays.toString(values));
}
System.out.println("----------------");
System.out.println("获取一些地址与其他的信息");
System.out.println("getRequestURL"+req.getRequestURL());
System.out.println("getRequestURI"+req.getRequestURI());
System.out.println("getAuthType"+req.getAuthType());
System.out.println("changeSessionId"+req.changeSessionId());
System.out.println("getHeaderNames"+req.getHeaderNames());
System.out.println("getContextPath"+req.getContextPath());
System.out.println("getMethod"+req.getMethod());
System.out.println("getPathInfo"+req.getPathInfo());
System.out.println("getRemoteUser"+req.getRemoteUser());
System.out.println("getSession"+req.getSession());
}
}
重启服务器,访问 http://localhost:8080/httpServlet.jsp
界面如下
输入值之后,点击登录
查看控制台输出如下内容
如何解决请求数据中文乱码问题?
这行设置一定要写在getParameter之前。
request.setCharacterEncoding("UTF-8");
13.8 HttpServletResponse
HttpServletResponse 封装了返回数据给客户端的对象。当请求进来处理完数据之后,需要返回数据给客户端,则以HttpServletResponse对象进行返回。
ServletResponse
ServletResponse 常用方法
//获取响应客户端的编码格式
public String getCharacterEncoding();
//设置响应客户端的编码格式
public void setCharacterEncoding(String charset);
//获取返回数据到客户端的格式 如 text/html;charset=utf-8
public String getContentType();
//设置返回数据到客户端的格式 如 text/html;charset=utf-8
public void setContentType(String type);
// 以字符流的形式向客户端写数据
public PrintWriter getWriter() throws IOException;
HttpServletResponse
HttpServletResponse 常用方法
// 返回一个Cookies到客户端
public void addCookie(Cookie cookie);
//对URL进行编码
public String encodeURL(String url);
//重定向
public void sendRedirect(String location) throws IOException;
//设置响应到客户端的响应头
public void setHeader(String name, String value);
//添加响应头 名称必须是给定的名称
public void addHeader(String name, String value);
//设置响应状态
public void setStatus(int sc);
//获取响应状态
public int getStatus();
//获取响应到客户端的头信息
public String getHeader(String name);
//通过名称获取值集合
public Collection<String> getHeaders(String name);
//获取值集合
public Collection<String> getHeaderNames();
ResponseServlet的实现类
输出HttpResponseServlet中的方法信息
创建一个MyResponseServlet类
public class MyResponseServlet extends HttpServlet {
private static final long serialVersionUID = -7172678549095820207L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setCharacterEncoding("UTF-8");
resp.setContentType("text/html;charset=utf-8");
Cookie cookie = new Cookie("username", "zhangsan");
resp.addCookie(cookie);
resp.setHeader("connection","keep-alive");
resp.addHeader("accept-encoding","gzip, deflate");
resp.getWriter().write("hello,World");
resp.setStatus(200);
resp.sendRedirect("https.//www.baidu.com");
}
}
在web.xml中加入映射
<servlet>
<servlet-name>myResponseServlet</servlet-name>
<servlet-class>com.example.demo.MyResponseServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>myResponseServlet</servlet-name>
<url-pattern>/myResponse</url-pattern>
</servlet-mapping>
出去Tomcat服务器 访问 http://localhost:8080/myResponse
结果如下
13.9 请求转发与重定向
请求转发
什么是请求转发,当游览器向服务器发送请求,服务器中的一个servlet接收到请求之后,发现自己处理不了这个请求,于是,把这个请求转发给另一个servlet帮忙处理,处理好之后,返回结果给请求帮忙的servlet,而这个servlet等到处理好的结果之后,就把所有的数据全部发送到客户端。
案例
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.getRequestDispatcher("index.jsp").forward(req,resp);
}
登录成功之后跳转到该页面
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head>
<title>JSP - Hello World</title>
</head>
<body>
<h1><%= "Hello World!" %></h1>
<br/>
<a href="hello-servlet">Hello Servlet</a>
<h3>登录成功</h3>
</body>
</html>
重启Tomcat服务器,访问 http://localhost:8080/httpServlet.jsp
跳转到index页面
请求转发的特点
// 使用示例:
request.getRequestDispatcher("login_success.html").forward(request, response);
1. 地址上显示的是请求第一个servlet的地址,返回200ok。
2. 请求次数只有一次,因为是服务器内部帮客户端执行了后续的工作。
3. 只能跳转自己项目的资源路径。
4. 效率上稍微高一点,因为只执行一次请求。
5. 可以使用上一次的request对象。
重定向
当服务器接收到客户端的请求时,如果不想处理该请求,则返回一个路径给客户端,让客户端重新去请求该路径。
案例
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.sendRedirect("index.jsp");
}
重启Tomcat服务器 访问 http://localhost:8080/httpServlet.jsp
运行结果如下
总结
// 第一种:使用示例
response.setStatus(302);
response.setHeader("Location", "login_success.html");*/
// 第二种:使用示例
response.sendRedirect("login_success.html");
1. 地址上显示的是最后的那个资源的路径地址。
2. 请求次数最少有两次,服务器在第一次请求后,会返回302以及一个地址,浏览器在根据这个地址,执行第二次访问。
3. 可以跳转到任意路径,不是自己的工程也可以跳。
4. 效率稍微低一点,执行两次请求。
5. 后续的请求,没法使用上一次的request存储的数据,或者没法使用上一次的request对象,因为这是两次不同的请求。
13.10 Cookie
什么是Cookie呢?由于游览器是无状态连接的,如果关闭游览器,之前的通信信息就不存在的,所以就发展出了一个cookie, cookie是游览器特有的技术,相当于一个存储空间,可以存储服务器发送给客户端的数据。
案例
package com.example.demo;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class CookieServlet extends HttpServlet {
private static final long serialVersionUID = -6463133145963830709L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取cookie
Cookie[] cookies = req.getCookies();
if (cookies.length ==1){
System.out.println("没有其它Cookie存在,只有JSESSIONID这个Cookie存在!");
//创建一个cookie
Cookie cookie = new Cookie("username","wangwu");
//设置cookie的备注信息
cookie.setComment("用户名信息");
/**
* 设置cookie的存活时间
* 正值 :表示过了这个值就失效
* 负值 :表示关闭游览器就失效
* cookie没有删除的方法,如果想删除,就设置该cookie的值为 0
*/
cookie.setMaxAge(60*60*24*7);
//只有访问域名是localhost才会带上这个cookie
cookie.setDomain("localhost");
// 设置访问路径,只有是该路径才会带上这个cookie
cookie.setPath("/cookie");
// 重新给cookie赋值
cookie.setValue("lisi");
// 添加cookie到响应对象中 并返回给客户端
resp.addCookie(cookie);
}else {
// 不等于 1 则输出cookie
for (Cookie cookie:cookies){
if ("username".equals(cookie.getName())){
resp.getWriter().write(cookie.getName()+":"+cookie.getValue());
}
}
}
}
}
在web.xml 中配置映射路径
<servlet>
<servlet-name>cookieServlet</servlet-name>
<servlet-class>com.example.demo.CookieServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>cookieServlet</servlet-name>
<url-pattern>/cookie</url-pattern>
</servlet-mapping>
重启Tomcat服务器 访问路径 http://localhost:8080/cookie
访问其他路径,不会携带该cookie
总结
cookie可以保存服务器发送过来的数据,避免了关闭游览器数据丢失的麻烦。cookie可以应用在记住密码功能,当用户勾选了cookie,服务器就把用户名与密码的信息包装在cookie当中,返回给客户端。
13.11 Session
由于cookie是存在客户端的,需要通过传输才能到达客户端,存在安全隐患,而且还有数据大小与个数限制等。所以,为了解决这个问题,发展出了数据保存在服务器的Session技术,session是一种会话技术,可以保存上一次通信的会话记录,客户端第一次请求的时候,如果需要保存此次会话记录,则创建一个session对象,并通过cookie返回该session对象的id,下次客户端请求,则带上这个sessionId获取上次的会话记录。
案例
package com.example.demo;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.xml.crypto.Data;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;
public class SessionServlet extends HttpServlet {
private static final long serialVersionUID = 1664657940310831116L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//设置响应的编码格式
resp.setCharacterEncoding("UTF-8");
resp.setContentType("text/html;charset=utf-8");
// 获取输出的游览器的对象
PrintWriter writer = resp.getWriter();
// 获取session() 参数为true,表示没有则创建一个session,false 表示没有则返回空。
HttpSession session = req.getSession(false);
String id = null;
// 判断是否为空,如果为空,创建一个session对象
if (session == null) {
HttpSession newSession = req.getSession(true);
id = newSession.getId();
// 输出创建sessionId的值
writer.write("JSESSIONID:" + id);
// 设置session的属性值
newSession.setAttribute("username", "lisi");
// 获取属性值
String value = (String) newSession.getAttribute("username");
// 输出属性值
writer.write("username: "+value);
return;
}
// 第一次session请求为空, 第二次请求不为空,则执行一下代码
// 获取第一次session设置的值
String value = (String) session.getAttribute("username");
id = session.getId();
writer.write("第二次请求的session值:"+value);
writer.write("第二次sessionId:"+id);
long creationTime = session.getCreationTime();
writer.write("第二次访问session创建的时间:"+creationTime);
long lastAccessedTime = session.getLastAccessedTime();
writer.write("第二次访问session 最后访问的时间: "+lastAccessedTime);
// 设置session失效
session.invalidate();
}
}
web.xml 配置映射
<servlet>
<servlet-name>sessionServlet</servlet-name>
<servlet-class>com.example.demo.SessionServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>sessionServlet</servlet-name>
<url-pattern>/session</url-pattern>
</servlet-mapping>
重启Tomcat,访问路径: http://localhost:8080/session
查看游览器如下,第一次访问 控制台为 set-Cookie值
再一次刷新,发现请求头已经带上sessionID。
13.12 Listener
Listener 简称 监听器, 用于监听某一事件的变化,如当创建与销毁对象时,被监听到,则执行相应的处理,执行过程是通过回调函数进行处理。
13.12.1 三种监听对象
1 ServletContextListener: 对ServletContext 上下文对象及其属性进行监听。
2 HttpServletListener : 对HttpServlet对象及其属性操作进行监听。
3 ServletRequestListener : 对 ServletRequest对象及其属性进行监听。
13.12.1.1 ServletContextListener
ServletContextListener用于监听ServletContext对象作用域创建和销毁,利用它可以完成自己想要的初始化工作。
生命周期:
servletcontext创建:
1、启动服务器的时候
servletContext销毁:
1、关闭服务器
2、从服务器移除项目
如何创建
public class MyContextListener implements ServletContextListener{
/**
* 上下文创建是调用该方法
* @param sce
*/
@Override
public void contextInitialized(ServletContextEvent sce) {
ServletContext servletContext = sce.getServletContext();
String contextPath = servletContext.getContextPath();
System.out.println("contextPath: "+contextPath);
}
/**
* 上下文销毁是调用该方法
* @param sce
*/
@Override
public void contextDestroyed(ServletContextEvent sce) {
Object source = sce.getSource();
String s = (String) source;
System.out.println("资源: "+s);
}
}
在web.xml 中配置listener
<listener>
<listener-class>com.example.demo.listener.MyContextListener</listener-class>
</listener>
13.12.1.2 ServletRequestListener
ServletRequestListener用于监听ServletRequest对象作用域创建和销毁,利用它可以判断当前受否存在请求。
生命周期
request创建:
1、访问服务器上的任意资源都会有请求出现。
访问 html :会
访问 jsp :会
访问 servlet :会
request销毁:
1、服务器已经对这次请求作出了响应。
如何配置
public class MyServletRequestListener implements ServletRequestListener {
@Override
public void requestInitialized(ServletRequestEvent sre) {
System.out.println("requestInitialized被执行");
}
@Override
public void requestDestroyed(ServletRequestEvent sre) {
System.out.println("requestDestroyed被执行...");
}
}
<listener>
<listener-class>com.example.demo.listener.MyServletRequestListener</listener-class>
</listener>
13.12.1.3 HttpSessionListener
监听对象
HttpSessionListener用于监听HttpSession对象作用域创建和销毁,利用它可以统计在线人数。
生命周期:
session的创建:
1、只要调用getSession()方法。
html :不会
jsp :会
servlet :会
session的销毁:
1、会话超时30分钟。
2、非正常关闭服务器。
3、正常关闭服务器(序列化)
如何创建
public class MyHttpServletListener implements HttpSessionListener {
@Override
public void sessionCreated(HttpSessionEvent se) {
System.out.println("sessionCreated被执行。。。");
}
@Override
public void sessionDestroyed(HttpSessionEvent se) {
System.out.println("sessionDestroyed被执行。。。");
}
}
<listener>
<listener-class>com.example.demo.listener.MyHttpServletListener</listener-class>
</listener>
13.12.2 监听三个作用域属性状态变化
ServletContextAttributeListener
: 监听ServletContext存值状态变更。
ServletRequestAttributeListener
监听ServletRequest存值状态变更。
HttpSessionAttributeListener
监听HttpSession存值状态变更。
如何创建
这里实现三个作用域属性监听的接口
package com.example.demo.listener;
import javax.servlet.*;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;
public class MyAllListener implements ServletContextAttributeListener, ServletRequestAttributeListener, HttpSessionAttributeListener {
@Override
public void attributeAdded(ServletContextAttributeEvent event) {
System.out.println("监听到上下文添加属性...");
}
@Override
public void attributeRemoved(ServletContextAttributeEvent event) {
System.out.println("监听到上下移除加属性...");
}
@Override
public void attributeReplaced(ServletContextAttributeEvent event) {
System.out.println("监听到上下文替换属性...");
}
@Override
public void attributeAdded(ServletRequestAttributeEvent srae) {
System.out.println("监听到request添加属性...");
}
@Override
public void attributeRemoved(ServletRequestAttributeEvent srae) {
System.out.println("监听到request移除属性...");
}
@Override
public void attributeReplaced(ServletRequestAttributeEvent srae) {
System.out.println("监听到request替换属性...");
}
@Override
public void attributeAdded(HttpSessionBindingEvent event) {
System.out.println("监听到session添加属性...");
}
@Override
public void attributeRemoved(HttpSessionBindingEvent event) {
System.out.println("监听到session移除属性...");
}
@Override
public void attributeReplaced(HttpSessionBindingEvent event) {
System.out.println("监听到session替换属性...");
}
}
<listener>
<listener-class>com.example.demo.listener.MyAllListener</listener-class>
</listener>
创建测试
package com.example.demo.listener;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
/**
* @Author: lyf
* @CreateTime: 2022-12-01
* @description:
*/
public class ListenerTest extends HttpServlet {
private static final long serialVersionUID = 687535293301909982L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取上下文对象
ServletContext servletContext = getServletContext();
// 设置上下文对象属性
servletContext.setAttribute("contextAttr","上下文属性");
// 替换属性值
servletContext.setAttribute("contextAttr","被替换掉的上下文属性");
// 移除属性
servletContext.removeAttribute("contextAttr");
// req对象
// 设置req属性值
req.setAttribute("reqAttr","reqAttr属性");
// reqAttr 属性替换
req.setAttribute("reqAttr","reqAttr属性");
//移除属性
req.removeAttribute("reqAttr");
//获取session对象
HttpSession session = req.getSession();
//设置属性
session.setAttribute("sessionAttr","session属性");
// 替换相同属性名称的值
session.setAttribute("sessionAttr","session属性");
// 移除属性
session.removeAttribute("sessionAttr");
}
}
配置映射关系
<servlet>
<servlet-name>listenerTest</servlet-name>
<servlet-class>com.example.demo.listener.ListenerTest</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>listenerTest</servlet-name>
<url-pattern>/listener</url-pattern>
</servlet-mapping>
出去tomcat服务器,访问路径 http://localhost:8080/listener
控制台输出如下
13.12.3 监听HttpSession存储状态变更
HttpSessionBindingListener
主要作用
监听对象与session绑定和解除绑定的动作。
如何创建
public class MyHttpSessionBindingListener implements HttpSessionBindingListener {
@Override
public void valueBound(HttpSessionBindingEvent event) {
System.out.println("Session valueBound 已绑定");
}
@Override
public void valueUnbound(HttpSessionBindingEvent event) {
System.out.println("Session valueUnBound 解除绑定");
}
}
如何配置
这类监听器不需要配置
HttpSessionActivationListener
作用:
用于监听现在session的值是钝化(序列化)还是活化(反序列化)的动作。
钝化(序列化) :把内存中的数据存储到硬盘上。
活化(反序列化):把硬盘中的数据读取到内存中。
如何钝化
1. 在tomcat的 conf/context.xml 里面配置
对所有的运行在这个服务器的项目生效。
2. 在tomcat的 conf/Catalina/localhost/context.xml 里面配置
对localhost生效。
3. 在自己的web工程项目中的 META-INF/context.xml 里面配置
只对当前的工程生效。
具体配置信息如下:
maxIdleSwap : 1分钟不用就钝化。
directory : 钝化后的那个文件存放的目录位置。
<Context>
<Manager className="org.apache.catalina.session.PersistentManager" maxIdleSwap="1">
<Store className="org.apache.catalina.session.FileStore" directory="D:/Passivate"/>
</Manager>
</Context>
如何创建
public class MyHttpSessionActivationListener implements HttpSessionActivationListener {
@Override
public void sessionDidActivate(HttpSessionEvent se) {
System.out.println("sessionDidActivate 活化");
}
@Override
public void sessionWillPassivate(HttpSessionEvent se) {
System.out.println("sessionWillPassivate 钝化");
}
}
如何配置
这里监听器不用配置
测试
在target目录中新建context.xml
demo\target\demo-1.0-SNAPSHOT\META-INF
重启tomcat 访问http://localhost:8080/session
查看指定的持久化文件夹。
13.13 Filter
Filter 简称 过滤器,负责对客户端发送过来的请求进行过滤,如果满足条件,则放行到servlet进行处理,如果又多个条件,满足第一个条件之后继续执行下一个过滤器,直到所有的过滤器都执行完成,才到servlet处理,如果中途有一个不满足条件,则后面的所有过滤器都不会执行。
Filter也叫拦截器,过滤也叫拦截,可以拦截 一些特殊的请求 如敏感词 统一编码格式 实现自动登录功能等等。
13.13.1 Filter 的生命周期
创建: 在服务启动的时候创建
销毁:在服务关闭的时候销毁
13.13.2 Filter 实现
public class MyFilter implements Filter {
/**
* 过滤器初始化
* @param filterConfig
* @throws ServletException
*/
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("init ...");
}
/**
* 过滤
* @param request
* @param response
* @param chain
* @throws IOException
* @throws ServletException
*/
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
//进行过滤 如果不满足条件则直接return
// return;
//满足条件放行,进行下一步过滤
chain.doFilter(request,response);
}
/**
* 过滤器销毁
*/
@Override
public void destroy() {
System.out.println("过滤器销毁");
}
}
在web.xml 中配置过滤器
<filter>
<filter-name>myFilter</filter-name>
<filter-class>com.example.demo.filter.MyFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>myFilter</filter-name>
<url-pattern>/filter</url-pattern>
<!-- 可以配置多个拦截类型-->
<dispatcher>REQUEST</dispatcher>
</filter-mapping>
13.13.3 Filter 执行顺序
1 客户端发出请求,先经过过滤器,如果过滤器放行,那么才能到servlet。
2 服务器根据注册在web文件中的filter-mapper 顺序来执行过滤器,如果使用注解,则根据过滤器名称的字母顺序来执行过滤器。
3 如果有多个过滤器,那么他们会按照注册的映射顺序来排队。只要有一个过滤器不放行,那么后面排队的过滤器以及咱们的servlet都不会收到请求。如果全部放行了,那么回来的时候将会是反向执行,比如以下顺序:
Filter1
FIlter2
Filter3
.....(Servlet)
FIlter3
Fitler2
Filter1
注意:init方法的参数 FilterConfig , 可以用于获取Filter在注册的名字以及初始化参数,其实这里的设计的初衷与ServletConfig是一样的。
FilterConfig 类见名知义, 它是 Filter 过滤器的配置文件类。
Tomcat 每次创建 Filter 的时候, 也会同时创建一个 FilterConfig 类, 这里包含了 Filter 配置文件的配置信息。
FilterConfig 类的作用是获取 filter 过滤器的配置内容
1、 获取 Filter 的名称 filter-name 的内容
2、 获取在 Filter 中配置的 init-param 初始化参数
3、 获取 ServletContext 对象
13.13.4 Filter 匹配规则
1 全路径匹配:/a
2 前半段匹配:/a/b/c/*
3 后缀名匹配:*.action
4 通配符,拦截所有web资源。/*
13.13.5 Filter 拦截类型
注意:针对 的是web配置文件中的过滤器mapper的dispatcher 设置的选项。
REQUEST : 只要是请求过来都拦截,默认就是REQUEST。
FORWARD : 只要是转发过来都拦截。
ERROR : 页面出错发生跳转。
INCLUDE : 包含页面的时候就拦截。
13.13.6 统一编码格式
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
// 对ServletRequest对象转换为HttpServlet对象
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
// 设置请求的编码格式
req.setCharacterEncoding("UTF-8");
//设置响应的编码格式
resp.setCharacterEncoding("UTF-8");
resp.setContentType("text/html;charset=utf-8");
//放行执行下一个过滤器
chain.doFilter(req,resp);
}
13.14 Servlet3.0
Servlet 3.0 作为 Java EE 6 规范体系中一员,随着 Java EE 6 规范一起发布。该版本在前一版本(Servlet 2.5)的基础上提供了若干新特性用于简化 Web 应用的开发和部署。其中有几项特性的引入让开发者感到非常兴奋,同时也获得了 Java 社区的一片赞誉之声:
新增的注解支持
:该版本新增了若干注解,用于简化 Servlet、过滤器(Filter)和监听器(Listener)的声明,这使得 web.xml 部署描述文件从该版本开始不再是必选的了。
文件上传API简化:从该版本开始,极大地简化了文件上传的操作。
异步处理支持
:有了该特性,Servlet 线程不再需要一直阻塞,直到业务处理完毕才能再输出响应,最后才结束该 Servlet 线程。在接收到请求之后,Servlet 线程可以将耗时的操作委派给另一个线程来完成,自己在不生成响应的情况下返回至容器。针对业务处理较耗时的情况,这将大大减少服务器资源的占用,并且提高并发处理速度。
动态注册组件
:在初始化ServletContext容器的时候,可以支持动态注册三大组件。
可插性支持:如果说 3.0 版本新增的注解支持是为了简化 Servlet/ 过滤器 / 监听器的声明,从而使得 web.xml 变为可选配置, 那么新增的可插性 (pluggability) 支持则将 Servlet 配置的灵活性提升到了新的高度。熟悉 Struts2 的开发者都知道,Struts2 通过插件的形式提供了对包括 Spring 在内的各种开发框架的支持,开发者甚至可以自己为 Struts2 开发插件,而 Servlet 的可插性支持正是基于这样的理念而产生的。使用该特性,现在我们可以在不修改已有 Web 应用的前提下,只需将按照一定格式打成的 JAR 包放到 WEB-INF/lib 目录下,即可实现新功能的扩充,不需要额外的配置。
13.14.1 注解开发
Servlet
创建
package com.example.demo.annotation;
@WebServlet(
name = "annotation", // servlet 的名称
value = {"/annotation"}, // servlet的映射路径。可以有多个,value与urlPatterns 一样 两个不能同时实现。
loadOnStartup = 2,// 初始化的顺序
initParams = {@WebInitParam(name = "username",value = "admin")},//初始化参数
asyncSupported = false // 是否支持异步
)
public class AnnotationServlet extends HttpServlet {
private static final long serialVersionUID = -1765814380553232447L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
PrintWriter writer = resp.getWriter();
writer.write("doGet 方法执行。。。");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
}
@WebServlet
|
属性名 | 类型 | 描述 |
---|---|---|
filterName | String | 指定 Servlet 的 name 属性,如果没有显式指定,则该 Servlet 的取值即为类的全限定名 |
value | String[] | 该属性等价于 urlPatterns 属性,两个属性不能同时使用 |
urlPatterns | String[] | 指定一组 Servlet 的 URL 匹配模式 |
loadOnStartup | int | 指定servlet的加载顺序 |
initParams | WebInitParam[] | 指定一组 Servlet 初始化参数 |
asyncSupported | boolean | 声明 Servlet 是否支持异步操作模式 |
description | String | 描述该servlet的信息 |
displayName | String | 该 Servlet 的显示名,通常配合工具使用 |
测试,运行tomcat服务器,访问 http://localhost:8080/annotation
Listener
创建
@WebListener() //标记该类是监听器 默认有value属性,表示监听器的名称
public class AnnotationListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("contextInitialized ing ...");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("contextDestroyed ing...");
}
}
Filter
创建
@WebFilter(
filterName = "annotationFilter",
value = {"/*"}, //表示过滤的路径,可以配置多个,value与urlPatterns 一样,两个不能同时出现
dispatcherTypes = {DispatcherType.REQUEST} ,// 表示拦截的类型(对什么请求进行过滤)可以配置多个
initParams = {@WebInitParam(name = "username",value = "admin")} ,// FilterConfig初始化参数 可以配置多个
asyncSupported = false, // 异步支持
servletNames = {"annotation"} // 代表指定拦截那几个servlet对象
)
public class AnnotationFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("do Filter ing ...");
chain.doFilter(request,response);
}
@Override
public void destroy() {
}
}
属性列表
|
属性名 | 类型 | 描述 |
---|---|---|
filterName | String | 指定过滤器的 name 属性 |
value | String | 指定映射路径,不能与urlPatterns 一起使用 |
servletNames | String[] | 指定过滤器将应用于哪些 Servlet |
dispatcherTypes | DispatcherType | 指定过滤器的转发模式 具体取值包括: ASYNC、ERROR、FORWARD、INCLUDE、REQUEST |
initParams | WebInitParam[] | 指定一组过滤器初始化参数 |
asyncSupported | boolean | 声明过滤器是否支持异步操作模式 |
description | String | 该过滤器的描述信息 |
displayName | String | 该过滤器的显示名,通常配合工具使用 |
测试
注释掉web.xml 中的配置信息,只留<web-app 中的内容
访问 http://localhost:8080/annotation
13.14.2 web.xml与注解配置文件同时存在
对于servlet来说:
若两种配置方式的url-pattern值相同,则应用无法启动。
若两种配置方式的url-pattern值相同,那么相当该servlet具有两个映射url-pattern。
对于filter来说:
无论两种配置方式的url-pattern值是否相同,其都是作为独立的filter出现的。
对于listener来说:
如果两种配置方式都进行了同一个listener注册,那么也只能算一个listener。
13.14.3 禁用注解
如果只想要使用web.xml中的配置而忽略注解注册的组件,只需要在web.xml跟标签添加一个属性即可。
<?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>
metadata-complete="false"
:表示web.xml配置和注解配置同时生效,默认是false。
metadata-complete="true"
:表示web.xml配置有效,而注解配置则被忽略。
13.15 文件上传
<form action="upload" method="post" enctype="multipart/form-data">
选择文件:<input type="file" name="myfile" /> <br />
上传文件:<input type="submit" value="上传" />
</form>
package com.example.demo;
import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
import java.io.IOException;
/**
* @Author: lyf
* @CreateTime: 2022-12-01
* @description:
*/
@WebServlet(value = "/upload")
@MultipartConfig//上传文件配置注解
public class UploadServlet extends HttpServlet {
private static final long serialVersionUID = 3344953656853001L;
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取文件名称
Part part = req.getPart("myfile");
// 写入文件到磁盘
part.write("D:/aa.txt");
// 设置响应类型
resp.setHeader("Content-Type", "text/html;charset=UTF-8");
resp.getWriter().write("上传成功");
}
}
@MultipartConfig注解
属性名 | 类型 | 描述 |
---|---|---|
fileSizeThreshold | int | 当数据量大于该值时,内容将被写入文件 |
location | String | 存放生成的文件地址 |
maxFileSize | long | 允许上传的文件最大值。默认值为-1,表示没有限制 |
maxRequestSize | long | 针对该multipart/form-data请求的最大数量,默认值为-1,表示没有限制 |
重启tomcat 访问
13.16 异步支持
注意问题
如果你的servlet开启了异步支持,那么你的filter也必须开启异步支持,否则会报错!
经典场景
在用户注册的时候,通常会发送一封注册通知邮件,这里就是用到了异步处理的技术。
如何实现
1 开启之前的AnnotationFilter类 支持异步
2 创建RegisterServlet
@WebServlet(value = "/register",asyncSupported = true) // 开启异步支持
public class RegisterServlet extends HttpServlet {
private static final long serialVersionUID = 9172291075084892119L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取异步对象
AsyncContext asyncContext = req.startAsync();
// 设置异步超时时间
asyncContext.setTimeout(6000);
// 开启异步线程
asyncContext.start(() -> {
try {
Thread.sleep(30000);
}catch (InterruptedException e){
System.out.println(e.getMessage());
}
System.out.println("邮件已经发送,请注意查收");
});
//设置响应的编码方式
resp.setHeader("Content-Type","text/html;charset=utf-8");
resp.getWriter().write("恭喜你注册成功,请查看邮件确认注册信息");
}
}
重启Tomcat ,访问映射路径。
结果如下
设置线程超时时间是6秒
6秒之后就向页面输出注册成功
子线程需要睡眠60000秒之后,发送邮件成功。
13.17 动态注册
在上下文中注册组件有两种,一种是web.xml 形式配置,一种是注解配置,但两种都达不到动态注册的效果,所以 在上下文监听器中增加了对三大组件进行动态注册 Servlet Filter Listener。
具体实现
在AnnotationLIstener中进行三大注解动态注册
package com.example.demo.annotation;
import com.example.demo.listener.MyAllListener;
import javax.servlet.*;
import javax.servlet.annotation.WebListener;
import java.util.EnumSet;
/**
* @Author: lyf
* @CreateTime: 2022-12-01
* @description:
*/
@WebListener()
public class AnnotationListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("contextInitialized ing ...");
ServletContext servletContext = sce.getServletContext();
// 动态注册servlet
ServletRegistration.Dynamic annotation = servletContext.addServlet("annotation", AnnotationServlet.class);
annotation.addMapping("/anno");
annotation.setLoadOnStartup(2);
annotation.setInitParameter("username","admin");
//动态注册 filter
FilterRegistration.Dynamic annotationFilter = servletContext.addFilter("annotationFilter", AnnotationFilter.class);
//注意:addMappingForServletNames的第二个参数为true代表,在以前的过滤之后过滤,为false,代表在以前的过滤之前过滤。
annotationFilter.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST),true,"/a");
// 动态注册 Listener
servletContext.addListener(MyAllListener.class);
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("contextDestroyed ing...");
}
}
13.18 总结
Javaweb 的核心是三大组件,分别为 Servlet、Listener、Filter,三大组件支撑整个web应用。
Serlvet : servlet中的servletConfig是整个servlet的核心配置文件,其中,有servletContext负责整个程序的全局变量参数与配置。
Listener: 监听器,用于监听ServletContext、ServletRequest HttpSession 三大作用域的对象创建与属性配置。
Filter: 过滤器,用于对客户端发送过来的请求进行过滤。
Servlet 2.5版本基于web.xml 文件实现配置。在Servlet与FIlter当中,没有属性,属性全部放在了配置文件中进行设置,这样就实现了单例模式,不会存在数据的污染。
Servlet3.0版本 加入了注解支持,可以放弃web.xml 配置文件,加入文件上传注解,简化文件上传繁杂功能,动态组件注册,在上下文创建时注入三大组件。