一. Servlet体系结构
Servlet – 接口
GenericServlet – 抽象类
HttpServlet – 抽象类
- 由于Servlet接口中好多方法用不到, 因此GenericServlet对该接口的不常用方法做了默认空实现, 只将service()方法作为抽象方法等子类去实现.
- HTTPServlet : 对http协议的一种封装, 简化操作 — 可以定义类继承HTTPServlet类, 覆写doGet/doPost方法
二. Request对象
2.1 获取请求体数据
1.获取请求方式 : getMethod()
2.获取虚拟目录 : getContextPath()
3.获取Servlet路径 : getServletPath()
4.获取get方式请求参数 : getQueryString()
获取post方式请求参数 : getReader()
5.获取请求URI : getRequestURI() / getRequestURL()
6.获取协议及版本 : getProtocol()
7.获取客户端的IP地址 : getRemoteAddr()
2.2 获取请求头数据
1.获取所有请求头名称 : getHeaderName()
2.获取指定的请求头数据 : getHeader(String name)
2.3 获取请求参数通用方式
1.String getParameter(String name) : 根据参数名称获取参数值
2.String[] getParameterValues(String name) : 根据参数名称获取参数值的数组
3.Enumeration getParameterNames() : 获取所有请求的参数名称
4.Map<String, String[]) getParameterMap() : 获取所有参数的map集合
@WebServlet("/requestdemo6")
public class RequestDemo6 extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// post 获取请求参数
// 根据参数名称获取参数值
String username = request.getParameter("username");
System.out.println("post方式");
System.out.println(username);
// 根据参数名称获取参数值的数组
String[] hobbies = request.getParameterValues("hobby");
for (String hobby : hobbies) {
System.out.println(hobby);
}
// 获取所有请求的参数名称
Enumeration<String> parameterNames = request.getParameterNames();
while (parameterNames.hasMoreElements()) {
String key = parameterNames.nextElement();
String[] names = request.getParameterValues(key);
System.out.println(key);
for (String name : names) {
System.out.println(name);
}
System.out.println("--------------");
}
// 获取所有参数的map集合
Map<String, String[]> parameterMap = request.getParameterMap();
// 遍历
Set<String> keySet = parameterMap.keySet();
for (String key : keySet) {
// 根据键, 获取值
String[] values = parameterMap.get(key);
System.out.println(key);
for (String value : values) {
System.out.println(value);
}
System.out.println("--------------------");
}
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
2.4 中文乱码问题
get方式 : tomcat8 已经将get方式乱码问题解决了
post方式 : 会乱码
解决方法 :
request.setCharacterEncoding("utf-8");
2.5 请求转发
一种在服务器内部的资源跳转方式
方法 :
request.getRequestDispatcher("/Servlet路径").forward(request, response);
特点 :
- 浏览器地址栏路径不发生变化
- 只能转发到当前服务器内部资源中
- 转发是一次请求
2.6. 共享数据
- 域对象 : 一个有作用范围的对象, 可以再范围内共享数据
- request域 : 代表一次请求的范围, 一般用于请求转发的多个资源中共享数据
- 方法 :
void setAttribute(String var1, Object var2);
Object getAttribute(String var1);
void removeAttribute(String var1);
- 获取ServletContext
- ServletContext getServletContext();
2.7 注意事项
- login.html中form表单的action路径的写法
- 虚拟目录 + Servlet的资源路径
-
BeanUtils工具类, 简化数据封装
用于封装JavaBean的, 导包时为 :
import org.apache.commons.beanutils.BeanUtils;
- 要求 :
- 类必须被public修饰
必须提供空参的构造器
- 成员变量必须使用private修饰
- 提供公共的setter和getter方法
- 功能 : 封装数据
- 成员变量 :
- 方法 :
-
setProperty(Object bean, String name, Object value)
-
getProperty(Object bean, String name)
- populate(Object obj, Map map) : 将map集合的键值对信息封装到对应的JavaBean对象中
登录案例思路
三. Response对象
功能 : 设置响应消息
- 设置响应行
- 格式 : HTTP/1.1 200 ok
- 设置状态码 : setStatus(int sc)
-
设置响应头
setHeader(String name, String value)
- 设置响应体
- 使用步骤 :
- 获取输出流
- 字符输出流 : PrintWriter getWriter()
- 字节输出流 : ServletOutputStream getOutputStream()
- 使用输出流, 将数据输出到客户端浏览器
3.1 转发和重定向的区别
重定向特点 : redirect
- 地址栏发生变化
- 重定向可以访问其他站点(服务器)的资源(需要写虚拟目录)
- 重定向是两次请求. 不能使用request对象来共享数据
转发特点 : forward
- 转发地址路径不变
- 转发只能访问当前服务器下的资源(不用写虚拟目录)
- 转发是一次请求, 可以使用request对象来共享数据
3.2 路径写法
-
相对路径
通过相对路径不可以确定唯一资源
如 : ./index.html
不以 / 开头, 以 . 开头
规则 : 找到当前资源和目标资源之间的相对位置关系
- ./ : 当前目录 (不写代表./)
- …/ : 后退一级目录
-
绝对路径
通过绝对路径可以确定唯一资源
如 : http://localhost/test/responServlet2 -> /test/responServlet2
以 / 开头的路径
规则 : 判断定义的路径给谁用?
- 给客户端浏览器使用 : 需要加虚拟目录(项目的访问路径)
- 虚拟目录可能会发生改变, 因此需要动态获取
// 动态获取虚拟目录
String contextPath = request.getContextPath();
// 简单重定向
response.sendRedirect(contextPath + "/responServlet2");
虚拟目录为
/
- 给服务器使用 : 不需要加虚拟目录
3.3 服务器输出字符数据到浏览器
步骤 :
-
获取字符输出流
PrintWriter pw = response.getWriter();
-
输出数据
pw.write("xxx");
注意 :
- 乱码问题 :
-
获取的流的默认编码是ISO-8859-1PrintWriter pw = respon.getWriter();
- 设置该流的默认编码
- 告诉浏览器响应体使用的编码
// 简单形式, 设置编码, 是在获取流之前设置
response.setContentType("text/html;charset=utf-8");
3.4 服务器输出字节数据到浏览器
// 解决乱码问题
response.setContentType("text/html;charset=utf-8");
// 获取字节输出流
ServletOutputStream sos = response.getOutputStream();
// 输出数据
sos.write("你好".getBytes("utf-8"));
四. ServletContext对象
概念 : 代表整个web应用, 可以和程序的容器(服务器)来通信
4.1 获取方式
-
通过request对象获取
request.getServletContext();
-
通过HttpServlet获取
this.getServletContext();
4.2 功能
4.2.1 获取MIME类型
- MIME类型 : 在互联网通信过程中定义的一种文件数据类型
- 格式 : 大类型/小类型 text/html image/jpeg
- 获取 :
String mimeType = servletContext.getMimeType(fileName);
4.2.2 域对象 : 共享数据
-
// 设置数据servletContext1.setAttribute("msg", "servletContextTransmit");
-
// 获取数据Object msg = servletContext1.getAttribute("msg");
-
// 删除数据removeAttribute(String name)
- ServletContext对象范围 : 所有用户所有请求的数据
4.2.3 获取文件的真实(服务器)路径
存放文件的位置 :
- src目录下 :
String realPath1 = context.getRealPath("/WEB-INF/clases/a.txt");
- WEB-INF下 :
String realPath2 = context.getRealPath("/WEB-INF/c.txt");
- web目录下 :
String realPath = context.getRealPath("b.txt");
原理 :
图片中的路径中打开后,继续打开 conf -> Catalina -> localhost, 里面有ROOT.xml文件
该文件实际上就是Tomcat设置的虚拟目录的名字, 该项目设置虚拟目录为
/
, 因此该文件名称为ROOT
打开该文件后, 显示如下内容
<?xml version="1.0"?>
<Context docBase="C:\Document\Daily_Java\out\artifacts\JavaWebLearn_war_exploded" path=""/>
该路径则为项目的部署位置
这三张图进行对比可以发现,
文件中的文件显示为 -> web目录中的文件
项目中其他文件夹 -> 存放在WEB-INF文件夹内
WEB-INF文件中的内容 -> 与其他文件夹存放在一起, 都在WEB-INF文件夹中
因此, 在获取真实路径时, 需要看文件到底存放在什么位置, 若是在web目录下,则直接
"/xx.xx"
即可, 否则, 需要添加它的额外路径, 如
/WEB-INF/xx.xx
或者
/WEB-INF/classes/xx.xx
案例: 下载图片
文件下载需求 :
- 页面显示超链接
- 点击超链接后弹出下载提示框
- 完成图片文件下载
分析 :
- 超链接指向的资源如果能够被浏览器解析, 浏览器中展示, 如果不能解析, 则弹出下载提示框, 不满足需求
- 任何资源都必须弹出下载提示框
- 使用响应头设置资源的打开方式 :
- content-disposition: attachment;filename=xxx
- 定义页面, 编辑超链接href属性, 指向servlet, 传递资源名称filename
- 定义servlet
- 获取文件名称
- 使用字节输入流加载文件进内存
- 指定response的响应头 : content-disposition:attachment;filename=xxx
- 将数据写出到response输出流
- 中文文件问题
- 解决 :
- 获取客户端使用的浏览器版本信息
- 根据不同的版本信息, 设置filename的编码方式不同
import sun.misc.BASE64Encoder;
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")) {
// 火狐浏览器
BASE64Encoder base64Encoder = new BASE64Encoder();
filename = "=?utf-8?B?" + base64Encoder.encode(filename.getBytes("utf-8")) + "?=";
} else {
// 其它浏览器
filename = URLEncoder.encode(filename, "utf-8");
}
return filename;
}
}
@WebServlet("/downLoadServlet")
public class DownLoadServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1. 获取请求参数, 文件名称
String filename = request.getParameter("filename");
// 2. 使用字节输入流加载文件进内存
// 2.1 找到文件服务器路径
ServletContext context = this.getServletContext();
String realPath = context.getRealPath("/img/" + filename);
// 2.2 用字节流关联
FileInputStream fis = new FileInputStream(realPath);
// 3. 设置response的响应头
// 3.1 设置响应头类型 : content-type
// 获取文件的mime类型
String mimeType = context.getMimeType(filename);
response.setHeader("content-type", mimeType);
// 3.2 设置响应头打开方式 : content-disposition
// 解决中文文件名问题
// 1. 获取user-agent请求头
String agent = request.getHeader("user-agent");
// 2. 使用工具类方法编码文件名即可
filename = DownLoadUtils.getFileName(agent, filename);
response.setHeader("content-disposition", "attachment;filename=" + filename);
// 4. 将输入流的数据写出到输出流
ServletOutputStream sos = response.getOutputStream();
byte[] buff = new byte[1024 * 8];
int len = 0;
while ((len = fis.read(buff)) != -1) {
sos.write(buff, 0, len);
}
fis.close();
}
<body>
<!-- href的路径为: 虚拟路径 + 图片路径-->
<a href="/img/风景.jpg">图片1</a>
<hr>
<!--定义页面, 编辑超链接href属性, 指向servlet, 传递资源名称filename-->
<a href="/downLoadServlet?filename=风景.jpg">图片</a>
</body>