Servlet
第一章:初识Servlet
1.1-什么是Servlet及作用
Servlet(Server Applet)是Java Servlet的简称,称为小服务程序或服务连接器,用Java编写的服务器端程序,具有独立于平台和协议的特性,主要功能在于交互式地浏览和生成数据,生成动态Web内容。
1.2-快速入门
步骤
- 创建JavaEE项目
- 定义一个类,实现Servlet接口中的方法
- 配置Servlet
演示
创建JavaEE项目
定义一个类ServletTest,实现Servlet接口方法
package cn.leilei.servletDemo;
import javax.servlet.*;
import java.io.IOException;
public class ServletTest implements Servlet {
@Override
public void init(ServletConfig servletConfig) throws ServletException {
System.out.println("开启服务器并访问时,Servlet对象创建了");
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("执行了ServletTest程序");
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
System.out.println("关闭服务器时,Servlet对象被销魂了");
}
}
配置Servlet,进入web目录中找到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_3_1.xsd"
version="3.1">
<!--配置Servlet -->
<servlet>
<!--关联的类名-->
<servlet-name>ServletTest</servlet-name>
<servlet-class>cn.leilei.servletDemo.ServletTest</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ServletTest</servlet-name>
<!--虚拟路径-->
<url-pattern>/test01</url-pattern>
</servlet-mapping>
</web-app>
启动服务器,并访问http://localhost/tomcatDemo/test01 可以在控制开看到输出的内容,多次刷新会多次执行service方法中的程序。
1.3-Servlet执行原理
- 当服务器接收到客户端浏览器的请求后,会解析请求URL路径,获取访问的Servlet的资源路径
- 查找web.xml文件,是否有对应的
<url-pattern>
标签体内容。 - 如果有,则在找到对应的
<servlet-class>
全类名 - tomcat会将字节码文件加载进内存,并且创建其对象
- 调用其方法
1.4-Servlet生命周期
- 被创建:执行init方法,只执行一次
默认情况下,第一次被访问时,Servlet被创建。
在在<servlet>
标签下,可以配置执行Servlet的创建时机。
- 第一次被访问时创建:值为负数
<load-on-startup>-1</load-on-startup>
- 在服务器启动时创建:值为0或正整数,
<load-on-startup>0</load-on-startup>
Servlet的init方法,只执行一次,说明一个Servlet在内存中只存在一个对象,Servlet是单例的
- 多个用户同时访问时,可能存在线程安全问题。
- 解决:尽量不要在Servlet中定义成员变量。即使定义了成员变量,也不要对修改值。
- 提供服务:执行service方法,执行多次
每次访问Servlet时,Service方法都会被调用一次。
- 被销毁:执行destroy方法,只执行一次
Servlet被销毁时执行。服务器关闭时,Servlet被销毁。
1.5-Servlet3.0的注解配置
之前版本存在的问题
在使用3.0之前的servlet版本实现动态生成web时,每提供一个访问服务类,都要在web.xml配置文件中进行相应的配置,若访问服务类较多时,web.xml会变得越来越臃肿且不易于维护。
新的方式:注解配置1
3.0及3.0之后,可以不需要web.xml配置文件,可以通过注解的方式实现配置。
在类上使用@WebServlet注解,进行配置,格式:@WebServlet("/资源路径")
代码如下:
package cn.leilei.servletDemo;
import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import java.io.IOException;
// 注解
@WebServlet("/test02")
public class ServletTest02 implements Servlet {
@Override
public void init(ServletConfig servletConfig) throws ServletException {
System.out.println("开启服务器并访问时,Servlet对象创建了");
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("执行了ServletTest程序");
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
System.out.println("关闭服务器时,Servlet对象被销魂了");
}
}
Servlet注解配置2
- 一个Servlet可以定义多个访问路径 : 格式为
@WebServlet({"/路径名称1","/路径名称2","/路径名称3"})
。 - 路径定义规则:
/xxx
/xxx/xxx
*
.do*
通配符,表示任意内容
package cn.leilei.ServletTest;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet({"/test","/test/test03","*.do"})
public class Test03 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("post...");
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("get...");
}
}
1.6-IDEA与tomcat的相关配置
- IDEA会为每一个tomcat部署的项目单独建立一份配置文件
- 服务器启动后查看控制台的log:Using CATALINA_BASE:
C:\Users\Bruce\.IntelliJIdea2018.3\system\tomcat\Tomcat_8_5_31_tomcatDemo
- 服务器启动后查看控制台的log:Using CATALINA_BASE:
- 工作空间项目 和 tomcat部署的web项目
- tomcat真正访问的是“tomcat部署的web项目”,"tomcat部署的web项目"对应着"工作空间项目" 的web目录下的所有资源
- WEB-INF目录下的资源不能被浏览器直接访问。
- 断点调试:使用"小虫子"启动 dubug 启动
第二章:Servlet体系结构
2.1-为什么要学习Servlet体系结构
在上一章节中,通过定义类实现Servlet接口动态处理Web时,我们可以发现实现类要实现所有Servlet方法,但是我们重点使用的是service方法,所以为了有选择的简化操作,我们需要学习Servlet相关的其他实现类及子类。
2.2-GenericServlet
GenericServlet
是一个抽象类,通过自定义类继承GenericServlet类时,仅仅只需要实现service方法即可。因为GenericServlet类实现了Servlet接口中的其他方法,并以抽象方法的形式声明了service方法。
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;
public abstract class GenericServlet implements Servlet, ServletConfig, Serializable {
private static final long serialVersionUID = 1L;
private transient ServletConfig config;
public GenericServlet() {
}
public void destroy() {
}
public String getInitParameter(String name) {
return this.getServletConfig().getInitParameter(name);
}
public Enumeration<String> getInitParameterNames() {
return this.getServletConfig().getInitParameterNames();
}
public ServletConfig getServletConfig() {
return this.config;
}
public ServletContext getServletContext() {
return this.getServletConfig().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() {
return this.config.getServletName();
}
}
自定义类实现GenericServlet
package cn.leilei.ServletTest;
import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;
import java.io.IOException;
@WebServlet("/test01")
public class Test01 extends GenericServlet {
// 重写sevice方法即可
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("test01");
}
}
2.3-HttpServlet
为什么要学习HttpServlet
在后端开发中,我们更多时候是在频繁地用技术处理客户端的请求,而客户端请求中,有两类请求是需要经常处理的,分别是get
和post
请求。
若我们通过继承Servlet体系结构中的GenericServlet类中的service方法处理客户端请求时,需要繁琐地解析客户端的请求种类(get或post),这样不便于提高程序的开发效率。
所以,Servlet体系结构中提供了HttpServlet
抽象类,HttpServlet封装了对http协议的操作,简化代码,在自定义类继承HttpServlet时,需要通过重写doGet/doPost方法来分别处理客户端的get或post请求。
另外需要注意的是,HttpServlet继承了GenericServlet。
代码演示
客户端html页面代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="test02" method="post">
<input type="text">
<input type="submit" value="post请求">
</form>
<form action="test02" method="get">
<input type="text">
<input type="submit" value="get请求">
</form>
</body>
</html>
服务端servlet代码:
package cn.leilei.ServletTest;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/test02")
public class Test02 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("get请求");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("post请求");
}
}
第三章:Http
3.1-什么是Http及作用
Http(Hyper Text Transfer Protocol) 超文本传输协议,架构在TCP协议上 。
作用:规定WWW服务器与浏览器之间信息传递规范 。
3.2-Http特点
- 基于TCP/IP的高级协议
- 默认端口号:80
- 基于请求/响应模型,一次请求对应一次响应
- Http是无状态的,即服务器不保留与客户交易时的任何状态。这就大大减轻了服务器记忆负担,从而保持较快的响应速度 。
3.3-Http版本
- 1.0版,每一次请求响应都会建立新的连接。
- 1.1版,复用连接。
3.4-请求报文格式
格式
- 请求行
- 请求头
- 请求空行
- 请求体
请求行
请求方式 /请求url 请求协议/协议版本
如:GET /mil HTTP/1.1
请求方式,有七种请求方式,常用的有两种:
get
- 请求参数在请求行中,在url后。
- 请求的url长度有限制
- 不太安全
post
- 请求参数在请求体中
- 请求的url参数没有限制
- 相对安全
请求头
客户端浏览器会通过请求头告诉服务器一些信息。
以下便是请求百度http://news.baidu.com/mil?user=admin
下的请求头信息
Host: news.baidu.com
Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.87 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Cookie: BAIDUID=E2956C50745049A430004D077D15B934:FG=1; BIDUPSID=E2956C50745049A430004D077D15B934; PSTM=1576459419; cflag=13%3A3; delPer=0; H_PS_PSSID=1450_21089_30211_30284_26350; LOCALGX=%u77F3%u5BB6%u5E84%7C%32%35%31%7C%u77F3%u5BB6%u5E84%7C%32%35%31; Hm_lvt_e9e114d958ea263de46e080563e254c4=1577168870; Hm_lpvt_e9e114d958ea263de46e080563e254c4=1577168880
Referer: http://localhost:63342/ServletDemo/web/login.html?_ijt=icoj0khmoej93qjero91n76b3m
由上可以发现,请求头中的信息以key:value
格式组织客户端信息。其中需要知道的是:
-
User-Agent
:浏览器告诉服务器,使用的浏览器版本信息。- 通过此信息可以在服务端处理浏览器的兼容问题。
-
Referer
:告诉服务器,我(当前请求)从哪里来。- 通过此信息可以实现防盗链技术
请求空行
空行,就是用于分割POST请求的请求头和请求体的。
请求体
post请求的参数在请求体中。
3.5-响应报文格式
格式
- 响应行
- 响应头
- 响应空行
- 响应体
响应行
组成:协议/版本 响应状态码 状态码描述
响应状态码:服务器告诉客户端浏览器本次请求和响应的一个状态。状态码是3位数字。分类如下:
- 1xx,服务器接收客户端消息,但没有接收完成,等待一段时间后,发送1xx多状态码。
- 2xx,成功。代表:200
- 3xx,重定向。代表:302(重定向),304(访问缓存)
- 4xx,客户端错误。
- 404(请求路径没有对应的资源)
- 405:请求方式没有对应的doXxx方法
- 5xx,服务器端错误。代表:500(服务器内部出现异常)
响应头
组织消息格式:key:value
常见的响应同消息:
- Content-Type:服务器告诉客户端本次响应体数据格式以及编码格式。
- Content-disposition:服务器告诉客户端以什么格式打开响应体数据。
- in-line:默认值,在当前页面内打开
- attachment;filename=xxx:以附件形式打开响应体。文件下载
响应体
服务端向客户端传输的数据
第四章:Request对象
4.1-概述
若我们在服务端实现处理客户端请求和处理响应时,需要学习两个对象,分别是:
- Request
- Response
request和response对象是由服务器创建的。
request对象是来获取请求消息,response对象是来设置响应消息。
本章先学习Request对象。
4.2-Request对象体系结构
4.3-获取请求消息数据
方法如下:
- 获取请求方式:
String getMethod()
- 获取虚拟目录:
String getContextPath()
- 获取Servlet路径:
String getServletPath()
- 获取get方式请求参数:
String getQueryString()
- 获取请求的URI:
String getRequestURI()
- 获取请求的URL:
StringBuffer getRequestURL()
- 获取协议版本:
String getProtocol()
- 获取客户机ip地址:
String getRemoteAddr()
代码演示
请求路径:http://localhost/requestdemo/test01?user=admin&pwd=abc
package it.leilei.RequtestTest;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/test01")
public class Test01 extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// - 获取请求方式:`String getMethod()`
System.out.println(req.getMethod()); // 结果:GET
// - 获取虚拟目录:**`String getContextPath()`**
System.out.println(req.getContextPath()); // 结果:/requestdemo
// - 获取Servlet路径:`String getServletPath()`
System.out.println(req.getServletPath()); // 结果:/test01
// - 获取get方式请求参数:`String getQueryString()`
System.out.println(req.getQueryString()); // 结果:user=admin&pwd=abc
// - 获取请求的URI:`String getRequestURI()`
// 统一资源标识符
System.out.println(req.getRequestURI()); // 结果:/requestdemo/test01
// - 获取请求的URL:`StringBuffer getRequestURL()`
// 统一资源定位符
System.out.println(req.getRequestURL()); // 结果:http://localhost/requestdemo/test01
// -获取协议及版本:String getProtocol()
System.out.println(req.getProtocol()); // 结果:HTTP/1.1
// -获取客户机ip地址
System.out.println(req.getRemoteAddr()); // 结果:0:0:0:0:0:0:0:1
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req,resp);
}
}
4.4-获取请求头数据
方法
- 根据请求头名称,获取对应的值:
String getHeader(String name)
- 获取所有的请求头名称:
Enumeration<String> getHeaderNames()
代码
package it.leilei.RequtestTest;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Enumeration;
@WebServlet("/test02")
public class Test02 extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Enumeration<String> headerNames = req.getHeaderNames();
// 循环遍历,并检测headerNames是否有更多的元素
while (headerNames.hasMoreElements()){
// 获取一个元素
String name = headerNames.nextElement();
// 根据请求头名称获取对应的值
String v = req.getHeader(name);
System.out.println(name + ":" + v);
System.out.println("--------------");
}
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req,resp);
}
}
4.5-获取请求体数据
方法
请求体:只有POST请求方式,才有请求体,在请求体中封装了POST请求的请求参数。
BufferedReader getReader()
:获取字符输入流,只能操作字符数据ServletInputStream getInputStream()
:获取字节输入流,可以操作所有类型数据
代码
html代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="test03" method="post">
<input type="text" name="username">
<input type="text" name="pwd">
<input type="submit" value="提交">
</form>
</body>
</html>
servlet代码
package it.leilei.RequtestTest;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.util.Enumeration;
@WebServlet( "/test03")
public class Test03 extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
BufferedReader reader = req.getReader();
String line = null;
while ((line=reader.readLine())!=null){
System.out.println(line);
}
// 结果:username=zhagnsan&pwd=aaa
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req,resp);
}
}
4.6-获取请求参数通用方式
不论get还是post请求方式都可以使用下列方法来获取请求参数
方法
String getParameter(String name)
:根据参数名称获取参数值String[] getParameterValues(String name)
:根据参数名称获取参数值的数组Enumeration<String> getParameterNames()
:获取所有请求的参数名称Map<String,String[]> getParameterMap()
:获取所有参数的map集合
关于获取参数值的乱码问题
- get方式:tomcat 8 已经将get方式乱码问题解决了
- post方式:会乱码
- 解决:在获取参数前,设置request的编码
request.setCharacterEncoding("utf-8")
;
- 解决:在获取参数前,设置request的编码
代码
html代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="test04" method="get">
<input type="text" name="user" placeholder="用户名"><br>
<input type="text" name="pwd" placeholder="密码"><br>
<input type="checkbox" name="hobby" value="足球">足球
<input type="checkbox" name="hobby" value="篮球">篮球
<input type="checkbox" name="hobby" value="拍球">拍球
<input type="checkbox" name="hobby" value="乒乓球">乒乓球
<br>
<input type="submit" value="提交">
</form>
</body>
</html>
servlet代码
package it.leilei.RequtestTest;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.Map;
import java.util.Set;
@WebServlet("/test04")
public class Test04 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//- `String getParameter(String name)`:根据参数名称获取参数值
System.out.println(request.getParameter("user"));
System.out.println("----------------------------");
//- `String[] getParameterValues(String name)`:根据参数名称获取参数值的数组
String[]values = request.getParameterValues("hobby");
for (String v : values) {
System.out.println(v);
}
System.out.println("----------------------------");
//- `Enumeration<String> getParameterNames()`:获取所有请求的参数名称
Enumeration<String> parameterNames = request.getParameterNames();
while (parameterNames.hasMoreElements()){
String name = parameterNames.nextElement();
System.out.println(request.getParameter(name));
}
System.out.println("----------------------------");
//- `Map<String,String[]> getParameterMap()`:获取所有参数的map集合
Map<String, String[]> parameterMap = request.getParameterMap();
Set<String> keys = parameterMap.keySet();
for (String key : keys) {
System.out.println(key + ":" + Arrays.toString(parameterMap.get(key)));
}
}
}
4.7-请求转发
什么是请求转发
一种在服务器内部的资源跳转方式。
步骤
- 通过request对象获取请求转发器对象:
RequestDispatcher getRequestDispatcher(String path)
- 使用RequestDispatcher对象来进行转发:
forward(ServletRequest request, ServletResponse response)
特点
- 浏览器地址栏路径不发生变化
- 只能转发到当前服务器内部资源中。
- 转发是一次请求
代码演示
Servlet代码-test05,访问test05并会跳入test06中
package it.leilei.RequtestTest;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/test05")
public class Test05 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("欢迎进入test05");
// 获取进入test06的转发器并跳转到test06
request.getRequestDispatcher("/test06").forward(request,response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request,response);
}
}
Servlet代码-test06
package it.leilei.RequtestTest;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/test06")
public class Test06 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("欢迎进入test06");
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request,response);
}
}
4.8-共享数据
域对象
域对象:一个有作用范围的对象,可以在范围内共享数据
request域:代表一次请求的范围,一般用于请求转发的多个资源中共享数据
方法
void setAttribute(String name,Object obj)
:存储数据Object getAttitude(String name)
:通过键获取值void removeAttribute(String name)
:通过键移除键值对
代码
请求路径:http://localhost/requestdemo/servletA?userName=zhangsan
servletA代码:
package it.leilei.RequtestTest;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/servletA")
public class ServletA extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.getRequestDispatcher("/servletB").forward(request,response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request,response);
}
}
ServletB代码:
package it.leilei.RequtestTest;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/servletB")
public class ServletB extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
System.out.println("欢迎进入ServletB");
System.out.println("您的名字是:" + request.getParameter("userName"));
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request,response);
}
}
第五章:登录案例
5.1-需求
5.2-步骤分析
5.3-开发步骤
01-创建项目,导入html页面、配置文件、jar包
02-数据准备
CREATE DATABASE db5;
CREATE TABLE USER(
id INT PRIMARY KEY AUTO_INCREMENT,
userName VARCHAR(32) NOT NULL,
pwd VARCHAR(32) NOT NULL
);
INSERT INTO USER VALUES(NULL,zhagnsan,123456),(NULL,lisi,abcdef);
03-创建cn.leilei.domain包中实体类User
package cn.leilei.domain;
/*用户实体类*/
public class User {
private int id;
private String userName;
private String pwd;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
}
04-创建cn.leilei.util包下的DruidUtils工具
package cn.leilei.util;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
/*
* 数据库连接池工具类
* */
public class DruidUtils {
private static DataSource ds;
private static InputStream is;
static {
Properties pro = new Properties();
try {
// 读取配置文件
is = DruidUtils.class.getClassLoader().getResourceAsStream("druid.properties");
pro.load(is);
// 创建数据库连接池对象
ds = DruidDataSourceFactory.createDataSource(pro);
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}finally {
if(is!=null){
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
// 获取Connection对象
public static Connection getConnection() throws SQLException {
return ds.getConnection();
}
// 释放资源
public static void close(Statement sta, Connection con, ResultSet rs) {
if(sta!=null){
try {
sta.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(con!=null){
try {
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(rs!=null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
public static void close(Statement sta, Connection con){
close(sta,con,null);
}
// 获取连接池对象
public static DataSource getDataSource(){
return ds;
}
}
05-创建cn.leilei.dao包下的UserDao类,提供login方法
package cn.leilei.dao;
import cn.leilei.domain.User;
import cn.leilei.util.DruidUtils;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
/*操作数据库user表的类*/
public class UserDao {
// 创建JdbcTemplate对象
JdbcTemplate jdbcTemplate = new JdbcTemplate(DruidUtils.getDataSource());
// 查询数据库中是否存在指定的用户
public User login(User loginUser){
try{
// 定义sql
String sql = "select * from user where userName=? and pwd=?";
// 执行
User user = jdbcTemplate.queryForObject(sql,new BeanPropertyRowMapper<User>(User.class),loginUser.getUserName(),loginUser.getPwd());
return user;
}catch (Exception e){
e.printStackTrace();
return null;
}
}
}
06-编写cn.leilei.web.servlet包中的LoginServlet类,处理登录请求
package cn.leilei.web.servlet;
import cn.leilei.dao.UserDao;
import cn.leilei.domain.User;
import org.apache.commons.beanutils.BeanUtils;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
User loginUser = new User();
Map<String, String[]> parameterMap = request.getParameterMap();
try {
BeanUtils.populate(loginUser,parameterMap);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
UserDao userdao = new UserDao();
User u = userdao.login(loginUser);
if(u==null){
request.getRequestDispatcher("/failed").forward(request,response);
}else {
request.setAttribute("user",u);
request.getRequestDispatcher("/success").forward(request,response);
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request,response);
}
}
07-编写cn.leilei.web.servlet包下的FiledServlet类处理登录失败时情况
package cn.leilei.web.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/failed")
public class FailedServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
response.getWriter().write("<h1>登录失败!</h1>");
response.getWriter().write("<h1>用户名或密码错误!</h1>");
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request,response);
}
}
08-编写cn.leilei.web.servlet包下的SuccessServlet类处理登录成功时情况
package cn.leilei.web.servlet;
import cn.leilei.domain.User;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/success")
public class SuccessServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
User user = (User)request.getAttribute("user");
response.setContentType("text/html;charset=utf-8");
response.getWriter().write("<h1>登录成功!</h1>");
response.getWriter().write("<h1>欢迎【"+user.getUserName()+"】!</h1>");
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request,response);
}
}
5.4-BeanUtils工具类
可以简化数据封装
org.apache.commons.beanutils.BeanUtils;
jar包下载
链接:https://pan.baidu.com/s/1skOTTH8xOBIXih7GZ_CHOw
提取码:fo7q
常用方法
setProperty(Object bean,String name,Object value)
getProperty(Object bean, String name)
populate(Object bean , Map map)
:将map集合的键值对信息,封装到对应的JavaBean对象中
第六章:Response对象
6.1-设置响应消息
设置状态码
- 方法:
setStatus(int sc)
设置响应头
- 方法:
setHeader(String name, String value)
设置响应体步骤
01-获取输出流
- 字符输出流:
PrintWriter getWriter()
; - 字节输出流:
ServletOutputStream getOutputStream()
;
02-使用输出流
代码演示
package cn.leilei.ServletTest;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
@WebServlet("/test07")
public class Test07 extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 设置状态码
resp.setStatus(200);
// 设置响应头,要在流之前设置
resp.setHeader("Content-type","text/html;charset=utf-8");
// 设置Content-type简化方式
// res.setContentType("text/html;charset=utf-8");
// 字符输出流
//PrintWriter writer = resp.getWriter();
//writer.write("<h1>Hello</h1>");
// 字节输出流;获取的流的默认编码是ISO-8859-1
ServletOutputStream outputStream = resp.getOutputStream();
outputStream.write("<h2>Servlet</h2>".getBytes());
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req,resp);
}
}
6.2-重定向
重定向的方式
方式1:response.setHeader("location", "跳转的资源路径");
方式2:response.sendRedirect("跳转的资源路径");
图解重定向
重定向和转发的特点
重定向(redirect)的特点:
- 客户端地址栏发生变化
- 重定向可以访问其他站点(服务器)的资源。
- 重定向是两次请求,不能使用request共享数据
转发(forward)的特点:
- 客户端地址了路径不发生变化
- 转发只能访问当前服务器下的资源
- 转发是一次请求,可以使用request共享数据
代码演示
Test04
package cn.leilei.ServletTest;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/test04")
public class Test04 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//【重定向方式1】
// 1. 设置状态码
response.setStatus(302);
// 2. 设置路径
// response.setHeader("location", "/servletDemo/test05");
// 【重定向方式2】
response.sendRedirect("/servletDemo/test05");
System.out.println("test04...");
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
Test05
package cn.leilei.ServletTest;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/test05")
public class Test05 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("test05...");
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request,response);
}
}
6.3-路径写法
相对路径
格式:相对路径无法确定唯一资源。
- 不是以
/
开头,而是以./
开头,如./index.html
规则:找到当前资源和目标资源之间的相对位置关系
./
当前目录../
上一层目录
绝对路径
格式:通过绝对路径可以确定唯一资源。
- 以
/
开头的路径,如http://localhost/servletDemo/register.html
→/servletDemo/register.html
规则:判断定义的路径是给谁用的?
- 给客户端使用,需要加虚拟目录(项目的访问路径)
- 建议虚拟目录动态获取:
request.getContextPath()
。
- 建议虚拟目录动态获取:
- 给服务器使用,不需要加虚拟目录(转发路径)
6.4-验证码案例
验证码的目的主要是为了防止表单的恶意操作(如注册)
需求
点击图片或超链接时切换验证码图片。
验证码图片上的内容和线条是随机的,由服务端提供。
实现步骤
- 构建html页面
- 加载页面时或点击图片或超链接时,图片资源需要向服务端请求
- 服务服务端绘制图片,然后以流的方式响应客户端。
实现代码
表单页面代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<p>
<span>验证码:</span>
<!--绝对路径-->
<img src="/servletDemo/checkCode" alt="">
<a href="#">看不清?换一张</a>
<script>
var imgNode = document.querySelector("img");
var aNode = document.querySelector("a");
aNode.onclick = imgNode.onclick = function () {
// 追加变化的参数,解决因图片缓存问题而不变化
imgNode.src = "/servletDemo/checkCode?" + new Date().getTime();
};
</script>
</p>
</body>
</html>
sevlet代码:
package cn.leilei.checkCodeDemo;
import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;
@WebServlet("/checkCode")
public class CheckCodeServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//服务器通知浏览器不要缓存
response.setHeader("pragma", "no-cache");
response.setHeader("cache-control", "no-cache");
response.setHeader("expires", "0");
//在内存中创建一个长80,宽30的图片,默认黑色背景
//参数一:长
//参数二:宽
//参数三:颜色
int width = 80;
int height = 30;
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
//获取画笔
Graphics g = image.getGraphics();
//设置画笔颜色为灰色
g.setColor(Color.GRAY);
//填充图片
g.fillRect(0, 0, width, height);
//产生4个随机验证码,12Ey
String checkCode = getCheckCode();
//将验证码放入HttpSession中
request.getSession().setAttribute("CHECKCODE_SERVER", checkCode);
//设置画笔颜色为黄色
g.setColor(Color.YELLOW);
//设置字体的小大
g.setFont(new Font("黑体", Font.BOLD, 24));
//向图片上写入验证码
g.drawString(checkCode, 15, 25);
//将内存中的图片输出到浏览器
//参数一:图片对象
//参数二:图片的格式,如PNG,JPG,GIF
//参数三:图片输出到哪里去
ImageIO.write(image, "PNG", response.getOutputStream());
}
/**
* 产生4位随机字符串
*/
private String getCheckCode() {
String base = "0123456789ABCDEFGabcdefg";
int size = base.length();
Random r = new Random();
StringBuffer sb = new StringBuffer();
for (int i = 1; i <= 4; i++) {
//产生0到size-1的随机值
int index = r.nextInt(size);
//在base字符串中获取下标为index的字符
char c = base.charAt(index);
//将c放入到StringBuffer中去
sb.append(c);
}
return sb.toString();
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
第七章:ServletContext对象
一个ServletContext对象代表整个web应用,可以和程序的容器(服务器)通信。
7.1-获取ServletContext对象
- 通过request对象获取
request.getServletContext()
- 通过HttpServlet对象获取
this.getServletContext()
7.2-获取MIME类型
MIME介绍
多用途互联网邮件扩展,它是一个互联网标准,在1992年最早应用于电子邮件系统,但后来也应用到浏览器。服务器会将它们发送的多媒体数据的类型告诉浏览器,而通知手段就是说明该多媒体数据的MIME类型,从而让浏览器知道接收到的信息哪些是MP3文件,哪些是Shockwave文件等等。服务器将MIME标志符放入传送的数据中来告诉浏览器使用哪种插件读取相关文件。
格式是:大类型/小类型;如:text/html、 image/jpeg
方法
String getMimeType(String file)
该方法会根据文件名,自动的去web.xml配置文件中获取对应的MIME标志符。
7.3-域对象
ServletContext对象范围:所有用户所有请求的数据。
方法:
- setAttribute(String name,Object value)
- getAttribute(String name)
- removeAttribute(String name)
7.4-获取文件在服务器上的真实路径
方法
String getRealPath(String var1)
;
代码
package cn.leilei.ServletContextDemo;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/serverContext02")
public class Test02Servlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
ServletContext servletContext = this.getServletContext();
// 获取web根目录下的资源真实路径
String str1 = servletContext.getRealPath("./a.txt");
//C:\Users\Bruce\Desktop\Gitee\JavaWeb\ServletDemo\out\artifacts\ServletDemo_war_exploded\a.txt
System.out.println(str1);
// 获取src目录下的资源路径
String str2 = servletContext.getRealPath("./WEB-INF/classes/a.txt");
System.out.println(str2);
// C:\Users\Bruce\Desktop\Gitee\JavaWeb\ServletDemo\out\artifacts\ServletDemo_war_exploded\WEB-INF\classes\a.txt
// 获取WEB-INFO目录下的资源路径
String str3 = servletContext.getRealPath("./WEB-INF/a.txt");
System.out.println(str3);
// C:\Users\Bruce\Desktop\Gitee\JavaWeb\ServletDemo\out\artifacts\ServletDemo_war_exploded\WEB-INF\a.txt
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request,response);
}
}
7.5-文件下载案例
需求
点击超链接下载任意类型的文件
步骤分析
- 定义html页面
- 定义处理文件名乱码工具类
- 定义Servlet处理请求程序
- 获取文件名参数值fileName
- 创建servletContent对象
- 获取文件的真实路径
- 创建字节输入流对象,根据文件真实路径获取服务器上的资源
- 根据文件名获取MIME类型的值
- 设置响应头,Content-type的MIME类型
- 通过文件名乱码处理类,处理文件名乱码问题
- 设置响应头,Content-disposition的值为
attachment;fileName=文件名
- 循环读取本地文件数据,边读取边通过响应输出流输出
html文件代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title><!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<a href="/photoLoad/loadP?fileName=itxia.png">下载图片-itxia</a>
<a href="/photoLoad/loadP?fileName=原力比特.png">下载图片-原力比特</a>
</body>
</html></title>
</head>
<body>
</body>
</html>
文件名乱码处理类
package cn.it.util;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
public class DownLoadUtils {
public static String getFileName(String agent, String filename) throws UnsupportedEncodingException {
if (agent.contains("MSIE")) {
// IE浏览器
filename = URLEncoder.encode(filename, "utf-8");
filename = filename.replace("+", " ");
} else if (agent.contains("Firefox")) {
// 火狐浏览器 JDK8支持BASE64Encoder
BASE64Encoder base64Encoder = new BASE64Encoder();
filename = "=?utf-8?B?" + base64Encoder.encode(filename.getBytes("utf-8")) + "?=";
} else {
// 其它浏览器
filename = URLEncoder.encode(filename, "utf-8");
}
return filename;
}
}
servlet类
import cn.it.util.DownLoadUtils;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.FileInputStream;
import java.io.IOException;
@WebServlet("/loadP")
public class loadP extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 获取文件名
String fileName = request.getParameter("fileName");
// 获取servletContext对象
ServletContext servletContext = this.getServletContext();
// 获取在服务器上的真实路径
String path = servletContext.getRealPath("./img/" + fileName);
// 获取响应输出流
ServletOutputStream os = response.getOutputStream();
// 获取对应的MIME类型
String mime = servletContext.getMimeType(fileName);
// 设置对应的MIME类型
response.setHeader("Content-type", mime);
// 解决中文名文件乱码问题
String agent = request.getHeader("user-agent");
fileName = DownLoadUtils.getFileName(agent, fileName);
// 设置响应头
response.setHeader("Content-disposition", "attachment;filename=" + fileName);
// 根据路径读取本地文件-Input流
FileInputStream fis = new FileInputStream(path);
byte[] bts = new byte[1024 * 8];
int len = 0;
while ((len = fis.read(bts)) != -1) {
os.write(bts, 0, len);
}
fis.close();
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
第八章:BaseServlet
问题:每实现一个功能,都要新建一个Servlet程序文件,这样随着功能的增加,Servlet程序文件会越来越多且难以管理。
解决方案:利用反射,抽取Servlet
实现方式:
- 约定客户端请求方式为:http://主机/类名/方法名
- 后端利用反射调用指定的方法
- 抽取BaseServlet中使用反射方式调用用户指定的类名下的方法
- 定义集多个功能的Servlet继承BaseServlet
需求
实现用户的登录、注册、退出功能
要求,在一个Servlet中集合登录、注册、退出功能
代码
BaseServlet.java
package web.servlet;
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.lang.reflect.Method;
public class BaseServlet extends HttpServlet {
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 获取请求路径
String servletPath = request.getRequestURI();
// 获取方法的名称
String methodName = servletPath.substring(servletPath.lastIndexOf("/")+1);
// 获取对应的方法
try {
Method method = this.getClass().getDeclaredMethod(methodName, HttpServletRequest.class, HttpServletResponse.class);
if(method!=null){
method.invoke(this,request,response);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
UserServlet.java
package web.servlet;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/user/*")
public class UserServlet extends BaseServlet {
//【登录方法】
public void login(HttpServletRequest request, HttpServletResponse response) throws IOException {
response.getWriter().write("login!");
}
//【注册方法】
public void register(HttpServletRequest request, HttpServletResponse response) throws IOException {
response.getWriter().write("register!");
}
//【退出方法】
public void exit(HttpServletRequest request, HttpServletResponse response) throws IOException {
response.getWriter().write("exit!");
}
}
客户端请求
- http://localhost/user/login 执行登录功能
- http://localhost/user/register 执行注册功能
- http://localhost/user/exit 执行退出功能