项目创建
1、在 idea 中创建 maven 项目
2、创建 web 目录:
在 main 目录下创建 webapp 文件夹,然后再 webapp 文件夹内创建 WEB-INF 文件夹
- webapp 目录就是 web 目录,里面存放动态、静态资源和配置文件,如 jsp、css、html等
- WEB-INF 目录下只有一个文件,就是web的配置文件:web.xml
3、idea 自动生成 web.xml:
【File】->【Project Structure】
【Modules】-> 选择当前项目下的 web 文件夹,然后点击右边的加号,选择 web.xml
调整 web.xml 文件的位置,然后依次点击 OK 即可
导入依赖项
在 pom.xml 文件中导入Servlet API:Servlet 有两个版本,一个是3.0版本之前,为 servlet-api;一个是3.0版本之后,为 javax.servlet-api,使用时根据需求进行导入
<!-- Servlet3.0之前的版本为servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<!-- Servlet3.0之后的版本为javax.servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
Servlet 创建
Servlet 的类结构如上,一般是继承 Servlet 的 HTTP 协议的实现类 —— HttpServlet,然后覆写其中相关方法。
Servlet 一般用来处理 HTTP 请求,而 HTTP 有多种请求方式,常见的有 GET、POST、PUT、DELETE 等,因此存在两种实现方式:
1、直接覆写 service() 方法,这样tomcat会根据请求方法自动调用不同的处理方法
public class OtherServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 处理请求的业务逻辑
}
}
2、针对每种不同的请求分别作处理:
public class OtherServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 一般会在doGet方法中进行业务逻辑,然后在其他请求方法中直接调用doGet()
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
为Servlet配置 url 映射:
1、注解方式,在相关类上添加 @WebServlet 注解
2、在 web.xml 文件中进行配置:
HttpServletRequest 的常用方法
1、获取请求行数据:
String getMethod() | 获取请求方式 |
---|---|
String getContextPath() | 获取虚拟目录[重要] |
String getServletPath() | 获取servlet路径 |
String getQueryString() | 获取get方式请求参数 |
String getRequestURI() | 获取请求URI[重要] |
StringBuffer getRequestURL() | 获取请求的URL |
String getProtocol() | 获取协议及版本 |
String getRemoteAddr() | 获取客户端的IP地址 |
String getRemoteHost() | 获取客户端的主机名 |
int getRemotePort() | 获取客户端的端口 |
- URI:Uniform Resource Identifier,统一资源标志符
- 访问资源的命名机制
- 存放资源的主机名
- 资源自身的名称
- URL:Uniform Resource Locator,统一资源定位符
2、获取请求头数据:
String getHeader(String name) | 通过请求头的名称获取请求头的值 |
---|---|
Enumeration |
获取所有的请求头名称 |
3、获取请求体数据:获取流对象
只有POST请求方式,才有请求体,在请求体中重新封装了POST的请求参数
BufferedReader getReader() | 获取字符输入流,只能操作字符数据 |
---|---|
ServletInputStream getInputStream() | 获取字节输入流,可以操作所有类型数据 |
4、获取请求参数:通用方式,不管GET或POST都可以使用该方法
String getParameter(String name) | 根据参数名称获取参数值 |
---|---|
String[] getParameterValues(String name) | 根据参数名称获取参数值的数组 |
Enumeration |
获取所有请求的参数名称 |
Map |
获取所有参数的map集合,key为参数名,value为参数值 |
请求乱码解决:由于 request 属于接受客户端的参数,所以存在其默认的语言编码(ISO-8859-1,此编码不支持中文),所以解析时会出现乱码,想要解决乱码问题,需要设置 request 中的编码方式,告诉服务器以何种方式解析数据,或接收到乱码数据中,再通过相应的编码格式还原
对于 get 请求:
// tomcat8之前
String str = new String(request.getParameter(name).getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
// tomcat8之后已经解决此问题,无需自行设置
对于 post 请求:
// 在获取参数前,设置request的编码与页面编码一致,一般写在filter中
req.setCharacterEncoding("utf-8");
三大域对象
域对象:一个有作用范围的对象,可以在范围内共享数据
Request 域
Request 域:即 javax.servlet.http.HttpServletRequest 对象,可以在一次请求中传递数据,主要用于请求转发的多个资源中共享数据
void setAttribute(String name,Object obj) | 存储数据到request域 |
---|---|
Object getAttribute(String name) | 通过键获取值 |
void removeAttribute(String name) | 通过键移除键值对 |
请求转发
请求转发:指服务器收到请求后,从一次资源跳转到另一个资源的操作叫做请求转发
请求转发属于服务器内部的资源跳转方式,无法跳转到外部的资源
// 通过request获取请求转发器对象并进行转发
req.getRequestDispatcher("views/index.jsp").forward(req, resp);
请求转发的特点:
- 浏览器地址栏路径不会发生变化
- 只能转发到服务器的内部资源(如 WEB-INF/ 下的静态资源,另一个 Servlet 等)
- 请求转发是一次请求,可以共享 Request 域中的数据
- 可以转发到 WEB-INF/ 目录下
注意:
- 从外部(浏览器)无法直接访问 WEB-INF 目录下的资源,但是可以通过请求转发进行访问
- 执行完跳转的 Servlet 的业务后,然后再回来继续执行原来的 servlet 的业务
测试一:测试一个 Servlet 存储属性,在另一个 Servlet 进行获取
DemoServletOne:
DemoServletTwo:
在浏览器中访问 http://127.0.0.1:8080/one,控制台输出如下:
测试二:在 servlet 中存储数据,在 jsp 页面获取
DemoServletThree:
WEB-INF/views/index.jsp:
在浏览器中访问 http://localhost:8080/three,在控制台输出:
HttpResponse 对象
HttpResponse对象的目的是为了响应浏览器发送给Web应用程序的HTTP请求,表示Web应用程序发送回浏览器的HTTP响应
常用方法:
1、获取响应流:
- 字符流:response.getWriter()
- 字节流:response.getOutputStream()
响应乱码问题:在响应中,如果响应的内容包含中文,则可能出现乱码,这是因为服务器响应的数据会经过编码成二进制进行传输,然后客户端再解码成相应的字符,当编码和解码用的不是同一字符集时则会出现乱码
字符流:getWriter() 获取的字符流,相应中文一定会出乱码,因为服务器端默认会使用 ISO-8859-1格式的编码,该编码不支持中文
// 设置服务器使用utf-8编码
response.setCharacterEncoding("utf-8");
// 指定客户端的编码方式
response.setHeader("content-type","text/html;charset=UTF-8");
// 同时指定两端编码方式,等效于上面两句
response.setContentType("text/html;charset=UTF-8");
字节流:getOutputStream() 同样需要指定两端的编码格式
// 指定客户端的编码方式
response.setHeader("content-type","text/html;charset=UTF-8");
ServletOutputStream os = response.getOutputStream();
// 服务端使用UTF-8编码
os.write("世界".getBytes(StandardCharsets.UTF_8));
2、响应给浏览器要发送的内容类型:response.setHeader(“Content-Type”,”text/html”);
3、设置响应信息的长度:response.setHeader(“Content-Length”,”31654”);
4、重定向:response.sendRedirect(“index.jsp”);
重定向:是服务器直到客户端的行为
当客户端发起第一个请求,被服务器接受处理后,服务器会进行响应,在响应的同时,会给客户端一个新的地址(下次请求的地址 response.sendRedirect(url)),客户端接受到响应后,会根据服务器给的新地址发起第二次请求,服务器接受请求并作出响应,重定向完成
重定向是客户端行为,具有两次请求,第一次响应时,会给出一个含有 location (再次请求的地址)的头信息,并且响应状态码为 301(永久重定向)、302(临时重定向)
重定向与请求转发:
请求转发 | 重定向 |
---|---|
request.getRequestDispatcher().forward() | response.sendRedirect() |
一次请求,数据在Request域中共享 | 两次请求,数据在Request域中不共享 |
服务端行为 | 客户端行为 |
地址栏不发生变化 | 地址栏发生变化 |
只能跳转到站内资源 | 可以跳转到任意地址 |
Session 域
Cookie:即 javax.servlet.http.Cookie,是浏览器提供的一种技术(需要浏览器的支持),通过服务器的程序能将一些只须保存在客户端、或者在客户端进行处理的数据,放在本地的计算机上,不需要通过网络传输
优点:提高网页处理的效率,减少服务器的负载
缺点:Cookie 保存在客户端,安全性较差
应用:常见的记住密码功能
格式:键值对用“=”连接,多个键值对间用“;”隔开
工作流程:第一次请求时服务端创建 Cookie 并返回给客户端,客户端保存该 Cookie,再此请求时会把该 Cookie 待带回服务器
Cookie 的创建与发送:
// javax.servlet.http.Cookie是java中专门用来处理Cookie的类
// 1.创建Cookie
Cookie cookie = new Cookie("username","Lily");
// 2.将Cookie添加到响应头
resp.addCookie(cookie);
Cookie 的获取:在服务端只提供了一个 getCookies() 的方法来获取客户端回传的所有 Cookie 组成的一个数组,如果需要获取单个 Cookie 则需要通过遍历,getName() 获取 Cookie 名称,getValue() 获取Cookie的值
// 1.获取Cookie数组
Cookie[] cookies = req.getCookies();
// 2.Cookie是否为空
if (Objects.nonNull(cookies)) {
// 遍历Cookie数组
for (Cookie cookie : cookies) {
// 获取Cookie名称
System.out.println(cookie.getName());
// 获取Cookie的值
System.out.println(cookie.getValue());
}
}
Cookie 的修改与删除:
- 修改:创建同名Cookie(该同名指Cookie除了value和maxAge之外,其他属性均相同),进行覆盖
- 删除:设置Cookie的 maxAge = 0
Cookie 的属性:
- String name:该Cookie的名称,一旦创建,不可更改
- Object value:该Cookie的值,值为Unicode字符时,需要为字符编码,值为二进制数据时,需要使用BASE64编码
- int maxAge:到期时间,指定该Cookie何时有效,通过 setMaxAge(int time) 进行设置,不同的到期时间设置影响Cookie的存活时间
- 负整数(默认 -1):不存储该Cookie,只在浏览器内存中存活,一旦关闭浏览器窗口,Cookie就会失效
- 正整数:表示存储的时间,单位为秒,该Cookie会被存储到硬盘中,哪怕关闭浏览器,该Cookie也会存活相应时间
- 0:表示删除该Cookie,当需要删除某个Cookie时,可以将该Cookie的到期时间设置为0
- boolean secure:该Cookie是否仅被安全协议传输,即SSL、https
- String path:该Cookie的使用路径
- 设置为 / ,则本域名下所有的程序都可以访问该 Cookie
- 设置为 /java/ ,则只有contextPath为 /java 的程序才可以访问该 Cookie
- 注意:该参数必须以字符 / 结尾
- String domain:可以访问该Cookie的域名
- 如果设置为 .google.com ,则所有以 google.com 结尾的域名均能访问该 Cookie
- 注意:该参数第一个字符必须为 . 或者完整域名如 www.baidu.com
- String comment:该Cookie的用途
- int version:该Cookie使用的版本号
- 0:表示该Cookie遵循Netscape的Cookie规范
- 1:表示遵循W3C的RFC2019规范
注意事项:
- Cookie保存在客户端
- 从客户端读取Cookie时,只能读取Cookie的name和value,其他属性都是不可读的,也不会被提交到服务端
Cookie 中存在中文时的处理:Chrome现在支持中文的Cookie值
/* 创建Cookie */
String name = "姓名";
String value = "李四";
// 通过URLEncoder.encode()来进行编码
name = URLEncoder.encode(name, "utf-8");
value = URLEncoder.encode(value, "utf-8");
// 创建Cookie对象
Cookie cookie = new Cookie(name, value);
// 发送Cookie对象
res.addCookie(cookie);
/* 获取Cookie */
URLDecoder.decode(cookie.getName(), "utf-8");
URLDecoder.decode(cookie.getValue(), "utf-8")
3、HttpSession:java.servlet.htto.HttpSession 对象,session 为HTTP协议的一部分,用于标识一次会话,或者说确认一个用户,并且在一次会话(一个用户的多次请求)期间共享数据。session 保存在服务端,客户端仅保留session的标识符 sessionID。
JSESSIONID:标识符,用于标识一次会话,会话过程如下,每当一次请求达到服务器,如果开启了会话(访问了 session),服务器第一步会查看是否从客户端回传了一个名为 JSESSIONID 的 cookie
- 如果没有则认为这是一个新的会话,则会创建一个新的session对象,并用唯一的 sessionId 为此次会话做一个标志
- 如果有 JSESSIONID 回传,服务器会根据该值去查看是否含有 id 为 JSESSIONID 值的 session 对象,如果没有,则会认为是一个新的会话,重新创建新的session对象,并标志此次会话;如果找到了相应的session对象,则认为是之前标志过的一次会话,返回该session对象,达到数据共享
JSESSIONID 本质是一个特殊的 Cookie ,当用户请求到达时,如果访问了 session,则服务器会创建一个名为 JSESSIONID,值为获取到的 session(不管是获取到还是新创建)的 sessionId 的 cookie 对象,并添加到 response 对象中,响应给客户端,有效时间为关闭浏览器。
Session 的使用依赖于客户端的支持,当客户端不支持 Cookie 或 Cookie 被禁用时,可以使用另一种解决方案:URL 重写 URL重写的原理:将该用户Session的id信息重写到URL地址中。服务器能够解析重写后的URL获取Session的id。这样即使客户端不支持Cookie,也可以使用Session来记录用户状态。HttpServletResponse 类提供了 encodeURL(Stringurl) 实现URL地址重写
session 域对象:session用来标识一次会话,在一次会话中数据是可以共享的,这是 session 作为域对象存在
// 获取Session对象
// 设置Session域对象
session.setAttribute("uname", "admin");
// 获取指定名称的session对象
String uname = (String) session.getAttribute("uname");
// 移除指定名称的session域对象
session.removeAttribute("uname");
session 对象的销毁:
默认到期时间:当客户端第一次请求servlet并且操作session时,session对象生成,tomcat中session的默认存活时间为30min,即客户端无操作的时间,一旦有操作,则重新计时
<!-- 在web.xml中进行配置 -->
<session-config>
<session-timeout>30</session-timeout>
</session-config>
在程序中自定义:
- 设置:session.setMaxInactiveInterval(int) 设置session的最大不活动时间,单位为 s
- 查看:session.getMaxInactiveInterval(int) 获取session的最大不活动时间,单位为 s
- 立即失效:session.invalidate() 让当前session立即失效
- 关闭浏览器:session只会存活在浏览器的内存中,一旦关闭浏览器,立即失效
ServletContext 域
每一个 web 应用有且只有一个 servletContext 对象,又称 Application 对象,从对象可知,该对象是与应用程序相关的。在 web 容器启动的时候,会为每一个 web 应用程序创建一个对象的 ServletContext 对象。
获取ServletContext对象的途径:有三种方式
// 通过request对象获取
ServletContext servletContext = req.getServletContext();
// 通过session对象获取
ServletContext servletContext = session.getServletContext();
// 通过servletConfig对象获取,在Servlet标准中提供了ServletConfig方法
ServletConfig servletConfig = getServletConfig();
ServletContext servletContext = servletConfig.getServletContext();
// 直接获取
ServletConfig servletConfig = getServletConfig();
ServletContext 的生命周期:在整个应用程序中有效,服务器关闭后失效
Servlet Context 对象有两大作用:
1、作为域对象用来共享数据,此时数据在整个应用程序中共享
ServletContext servletContext = getServletContext();
// 设置域对象
servletContext.setAttribute("name","value");
// 获取域对象
servletContext.getAttribute("name");
// 移除域对象
servletContext.removeAttribute("name");
2、该对象保存了应用程序相关信息
ServletContext servletContext = getServletContext();
// 获取当前服务器的版本信息
String serverInfo = servletContext.getServerInfo();
// 获取项目的真实路径(会获取该应用的根目录在所在操作系统中的真实目录)
String realPath = servletContext.getRealPath("/");
ServletContext 应用:
- 多个Servlet可以通过ServletContext对象来实现数据间的共享:
- Session 在同一客户端中共享数据
- ServletContext 在所有客户端中实现数据共享
实现Servlet的请求转发:两个转发效果是一样的
// request请求转发
request.getRequestDispatcher("/url").forward(request, response);
// servletContext请求转发
this.getServletContext().getRequestDispatcher("/url").forward(request, response);
获取Web应用的初始化参数:
整个web应用共享的初始化参数:
- 在web.xml中使用
标签为servlet配置初始化参数,然后使用 ServletContext.getInitParameter() 获取 ```xml name Lily
- 在Servlet中读取配置参数:
```java
// 在servlet中获取在web.xml中配置使用<context-param>标志配置的初始化参数
String name = this.getServletContext().getInitParameter("name");
String password = this.getServletContext().getInitParameter("password");
每个servlet独享的初始化参数:
- 在web.xml中使用
标签为servlet配置初始化参数,然后使用 servletConfig.getInitParameter() 获取 ```xml demo top.songfang.servlet.DemoServlet encoding utf-8
- 在该servlet中通过 servletConfig.getInitParameter() 获取
```java
String encoding = this.getServletContext().getInitParameter("encoding");
- 利用ServletContext读取资源文件(比如Properties文件)
- 文件在 webapp 文件夹下,即 WEB 应用的根目录,这时可以使用 ServletContext 来读取该资源文件
如果文件放在了 src 目录下,则需要使用类加载器去读取:
// 类加载器默认读取路径是 src 目录
InputStream resource = DemoServlet.class.getClassLoader().
getResourceAsStream("db.properties");
如果文件放在了src目录的某个包下,如com.example,则需要加上包的路径:
// 类加载器默认读取路径是 src 目录
InputStream resource = DemoServlet.class.getClassLoader().
getResourceAsStream("com/example/db.properties");
总结
- Request域对象:在一次请求中有效,请求转发有效,重定向失效
- Session域对象:在一次会话中有效,请求转发和重定向都有效,session销毁后失效
- ServletContext域对象:在整个应用程序中有效,服务器关闭后失效