天天看点

Servlet体系结构

一. 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);​

特点 :

  1. 浏览器地址栏路径不发生变化
  2. 只能转发到当前服务器内部资源中
  3. 转发是一次请求

2.6. 共享数据

  • 域对象 : 一个有作用范围的对象, 可以再范围内共享数据
  • request域 : 代表一次请求的范围, 一般用于请求转发的多个资源中共享数据
  • 方法 :
void setAttribute(String var1, Object var2);      
Object getAttribute(String var1);      
void removeAttribute(String var1);      
  • 获取ServletContext
  • ServletContext getServletContext();

2.7 注意事项

  1. login.html中form表单的action路径的写法
  • 虚拟目录 + Servlet的资源路径
  1. BeanUtils工具类, 简化数据封装

    用于封装JavaBean的, 导包时为 : ​

    ​import org.apache.commons.beanutils.BeanUtils;​

  1. 要求 :
  1. 类必须被public修饰
必须提供空参的构造器
  1. 成员变量必须使用private修饰
  2. 提供公共的setter和getter方法
  1. 功能 : 封装数据
  2. 成员变量 :
  3. 方法 :
  1. ​setProperty(Object bean, String name, Object value)​

  2. ​getProperty(Object bean, String name)​

  3. populate(Object obj, Map map) : 将map集合的键值对信息封装到对应的JavaBean对象中

登录案例思路

Servlet体系结构

三. Response对象

功能 : 设置响应消息

  1. 设置响应行
  1. 格式 : HTTP/1.1 200 ok
  2. 设置状态码 : setStatus(int sc)
  1. 设置响应头

    setHeader(String name, String value)

  2. 设置响应体
  • 使用步骤 :
  1. 获取输出流
  • 字符输出流 : PrintWriter getWriter()
  • 字节输出流 : ServletOutputStream getOutputStream()
  1. 使用输出流, 将数据输出到客户端浏览器

3.1 转发和重定向的区别

重定向特点 : redirect

  1. 地址栏发生变化
  2. 重定向可以访问其他站点(服务器)的资源(需要写虚拟目录)
  3. 重定向是两次请求. 不能使用request对象来共享数据

转发特点 : forward

  1. 转发地址路径不变
  2. 转发只能访问当前服务器下的资源(不用写虚拟目录)
  3. 转发是一次请求, 可以使用request对象来共享数据

3.2 路径写法

  1. 相对路径

    通过相对路径不可以确定唯一资源

    如 : ./index.html

    不以 / 开头, 以 . 开头

    规则 : 找到当前资源和目标资源之间的相对位置关系

  • ./ : 当前目录 (不写代表./)
  • …/ : 后退一级目录
  1. 绝对路径

    通过绝对路径可以确定唯一资源

    如 : http://localhost/test/responServlet2 -> /test/responServlet2

    以 / 开头的路径

    规则 : 判断定义的路径给谁用?

  • 给客户端浏览器使用 : 需要加虚拟目录(项目的访问路径)
  • 虚拟目录可能会发生改变, 因此需要动态获取
// 动态获取虚拟目录
String contextPath = request.getContextPath();
// 简单重定向
response.sendRedirect(contextPath + "/responServlet2");      
Servlet体系结构

虚拟目录为 ​

​/​

  • 给服务器使用 : 不需要加虚拟目录

3.3 服务器输出字符数据到浏览器

步骤 :

  1. 获取字符输出流

    ​​

    ​PrintWriter pw = response.getWriter();​

  2. 输出数据

    ​​

    ​pw.write("xxx");​

注意 :

  • 乱码问题 :
  1. ​PrintWriter pw = respon.getWriter();​

    ​ 获取的流的默认编码是ISO-8859-1
  2. 设置该流的默认编码
  3. 告诉浏览器响应体使用的编码

// 简单形式, 设置编码, 是在获取流之前设置

​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 获取方式

  1. 通过request对象获取

    ​​

    ​request.getServletContext();​

  2. 通过HttpServlet获取

    ​​

    ​this.getServletContext();​

4.2 功能

4.2.1 获取MIME类型

  • MIME类型 : 在互联网通信过程中定义的一种文件数据类型
  • 格式 : 大类型/小类型 text/html image/jpeg
  • 获取 : ​

    ​String mimeType = servletContext.getMimeType(fileName);​

4.2.2 域对象 : 共享数据

  1. ​servletContext1.setAttribute("msg", "servletContextTransmit");​

    ​ // 设置数据
  2. ​Object msg = servletContext1.getAttribute("msg");​

    ​// 获取数据
  3. ​removeAttribute(String name)​

    ​ // 删除数据
  • ServletContext对象范围 : 所有用户所有请求的数据

4.2.3 获取文件的真实(服务器)路径

存放文件的位置 :

  1. src目录下 : ​

    ​String realPath1 = context.getRealPath("/WEB-INF/clases/a.txt");​

  2. WEB-INF下 : ​

    ​String realPath2 = context.getRealPath("/WEB-INF/c.txt");​

  3. web目录下 : ​

    ​String realPath = context.getRealPath("b.txt");​

原理 :

Servlet体系结构

图片中的路径中打开后,继续打开 conf -> Catalina -> localhost, 里面有ROOT.xml文件

Servlet体系结构

该文件实际上就是Tomcat设置的虚拟目录的名字, 该项目设置虚拟目录为 ​

​/​

​, 因此该文件名称为ROOT

打开该文件后, 显示如下内容

<?xml version="1.0"?>
<Context docBase="C:\Document\Daily_Java\out\artifacts\JavaWebLearn_war_exploded" path=""/>      

该路径则为项目的部署位置

Servlet体系结构
Servlet体系结构
Servlet体系结构

这三张图进行对比可以发现,

文件中的文件显示为 -> web目录中的文件

项目中其他文件夹 -> 存放在WEB-INF文件夹内

WEB-INF文件中的内容 -> 与其他文件夹存放在一起, 都在WEB-INF文件夹中

因此, 在获取真实路径时, 需要看文件到底存放在什么位置, 若是在web目录下,则直接​

​"/xx.xx"​

​​即可, 否则, 需要添加它的额外路径, 如 ​

​/WEB-INF/xx.xx​

​​ 或者 ​

​/WEB-INF/classes/xx.xx​

案例: 下载图片

文件下载需求 :

  1. 页面显示超链接
  2. 点击超链接后弹出下载提示框
  3. 完成图片文件下载

分析 :

  1. 超链接指向的资源如果能够被浏览器解析, 浏览器中展示, 如果不能解析, 则弹出下载提示框, 不满足需求
  2. 任何资源都必须弹出下载提示框
  3. 使用响应头设置资源的打开方式 :
  • content-disposition: attachment;filename=xxx
  1. 定义页面, 编辑超链接href属性, 指向servlet, 传递资源名称filename
  2. 定义servlet
  1. 获取文件名称
  2. 使用字节输入流加载文件进内存
  3. 指定response的响应头 : content-disposition:attachment;filename=xxx
  4. 将数据写出到response输出流
  • 中文文件问题
  • 解决 :
  1. 获取客户端使用的浏览器版本信息
  2. 根据不同的版本信息, 设置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>      

继续阅读