Servlet

Http

概述

  • 超文本传输协议,是一种详细规定了浏览器和万维网服务器之间相互通信的规则,通过因特网传送万维网文档的数据传送协议,HTTP是一个应用层协议,由请求和响应沟通,是一个标准的客户端服务器模型

交互方式

http协议规定,默认情况下:

  1. 先由客户端(浏览器)向服务器(tomcat)发送请求(request)
  2. 服务器接收到请求后,会根据请求的资源路径,在服务器内部部署的项目中,查找是否有该资源
  3. 如果该资源存在,那么服务器就会将该资源返回(静态资源),或者把该资源的运行结果返回(动态资源)
  4. 如果该资源不存在,那么服务器就会返回一个状态码(404),表示本次访问的资源找不到
  5. 不管资源存在还是不存在,服务器都会针对本次请求,返回一个结果,这个结果就被称之为相应(response)
  6. 客户端(浏览器)接收到本次请求的相应内容之后,解析并且显示出来
  7. 此时,本次http协议下的访问结束,用户还可以再次发出请求,那么就重复上述过程

内容格式

请求

  • 客户端发送到服务端的请求信息,称之为请求(request),其实就是一个按照http协议的规则拼接而成的字符串
  • request请求消息包含三部分
    • 请求行----request line
    • 请求头(消息报头)----request head
    • 请求正文----request body
请求行
//格式:CRLF表示回车和换行
Method Request-URL HTTP-Version CRLF
  • Method表示请求方法;一般为GET或者POST
  • Request-URL是一个统一资源标识符,代表本次请求的资源路径
  • HTTP-Version表示请求的HTTP协议版本—HTTP/1.1

例如:GET /test.html HTTP/1.1

请求头(消息报头)

http请求报头的生成,主要有三种情况:

  • 浏览器自动生成的请求:
    • 绝大部分正常用户访问都是这类情况,只要是用户主动输入网址访问时发送的http请求,那这些头部字段都是浏览器自动生成的 ,比如host,cookie,user-agent,Accept-Encoding等等
  • 浏览器插件
    • javascript脚本增加或者修改的header,JS能够控制浏览器发起请求,也能在这里增加一些header,但是考虑到安全和性能的原因,对JS控制header的能力做了一些限制,比如host和cookie,user-agent等这些字段,JS是无法干预的
  • 中间代理:
    • 如果用户请求要经过一些中间代理(比如运营商或者公司网关),中间代理能够查看和修改用户的全部数据和任何头部字段,除非使用了HTTPS
请求正文

请求头和请求正文之间是一个空行,这个行非常很重要,它表示请求头已经结束,接下来的是请求正文,请求正文可以包含客户提交的字符串信息

POST /test.html HTTP/1.1 
Host: 127.0.0.1:9999 
User-Agent: Mozilla/5.0(Windows NT 6.1; WOW64; rv:47.0) Gecko/20100101 
Firefox/47.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,en;q=0.8,zh;q=0.5,en-US;q=0.3 
Accept-Encoding: gzip, deflate 
Connection: keep-alive 
id=1&name=tom&age=20
响应

服务器在接收和解析客户端的请求信息后,会返回给客户端一个HTTP响应信息(response),其实也是一个按照http协议的规则拼接而成的一个字符串

HTTP响应也是由三个部分组成:

  1. 响应状态行
  2. 消息报头(响应头)
  3. 响应正文
  • 响应状态行:
//格式如下:CRLF表示回车和换行
HTTP-Version Status-Code Reason-Phrase CRLF

HTTP/1.1 200 OK
  • HTTP-Version表示服务器和http协议的版本
  • Status-Code表示服务器发回的响应状态代码
  • Reason-Phrase表示状态码的文本描述

响应状态码的五种类型

分类分类描述
1xx信息,服务器收到请求,需要请求者继续执行操作
2xx成功,操作被成功将接收并处理
3xx重定向,需要进一步的操作以完成请求
4xx客户端错误,请求包含语法错误或者无法完成请求
5xx服务器错误,服务器在处理请求的过程中发生了错误

HTTP状态码列表

状态码状态码英文名中文描述
100Continue继续,客户端继续其请求
101Switching Protocols切换协议,服务器根据客户端的请求切换协议,只能切换到更高级的协议,例如,切换到HTTP的新版本协议
200OK请求成功,一般用户GET和POST请求
201Created已创建,成功请求并创建了新的资源
202Accepted已接受,已经接受请求,但未处理完成
203Non-Authoritative Information非授权信息,请求成功,但返回的meta信息不再原始的服务器,而是一个副本
204No Content无内容。服务器成功处理,但未返回内容。在未更新网页的情况下,可确保浏览器继续显示当前文档
205Reset Content重置内容。服务器处理成功,用户终端(例如:浏览器)应重置文档视图。可通过此返回码清除浏览器的表单域
206Partial Content部分内容。服务器成功处理了部分GET请求
300Multiple Choices多种选择。请求的资源可包括多个位置,相应可返回一个资源特征与地址的列表用于用户终端(例如:浏览器)选择
301Moved Permanently永久移动。请求的资源已被永久的移动到新URI,返回信息会包括新的URI,浏览器会自动定向到新URI。今后任何新的请求都应使用新的URI代替
302Found临时移动。与301类似。但资源只是临时被移动。客户端应继续使用原有URI
303See Other查看其它地址。与301类似。使用GET和POST请求查看
304Not Modified未修改。所请求的资源未修改,服务器返回此状态码时,不会返回任何资源。客户端通常会缓存访问过的资源,通过提供一个头信息指出客户端希望只返回在指定日期之后修改的资源
305Use Proxy使用代理。所请求的资源必须通过代理访问
306Unused已经被废弃的HTTP状态码
307Temporary Redirect临时重定向。与302类似。使用GET请求重定向
400Bad Request客户端请求的语法错误,服务器无法理解
401Unauthorized请求要求用户的身份认证
402Payment Required保留,将来使用
403Forbidden服务器理解请求客户端的请求,但是拒绝执行此请求
404Not Found服务器无法根据客户端的请求找到资源(网页)。通过此代码,网站设计人员可设置"您所请求的资源无法找到"的个性页面
405Method Not Allowed客户端请求中的方法被禁止
406Not Acceptable服务器无法根据客户端请求的内容特性完成请求
407Proxy Authentication Required请求要求代理的身份认证,与401类似,但请求者应当使用代理进行授权
408Request Time-out服务器等待客户端发送的请求时间过长,超时
409Conflict服务器完成客户端的PUT请求是可能返回此代码,服务器处理请求时发生了冲突
410Gone客户端请求的资源已经不存在。410不同于404,如果资源以前有现在被永久删除了可使用410代码,网站设计人员可通过301代码指定资源的新位置
411Length Required服务器无法处理客户端发送的不带Content-Length的请求信息
412Precondition Failed客户端请求信息的先决条件错误
413Request Entity Too Large由于请求的实体过大,服务器无法处理,因此拒绝请求。为防止客户端的连续请求,服务器可能会关闭连接。如果只是服务器暂时无法处理,则会包含一个Retry-After的响应信息
414Request-URI Too Large请求的URI过长(URI通常为网址),服务器无法处理
415Unsupported Media Type服务器无法处理请求附带的媒体格式
416Requested range not satisfiable客户端请求的范围无效
417Expectation Failed服务器无法满足Expect的请求头信息
500Internal Server Error服务器内部错误,无法完成请求
501Not Implemented服务器不支持请求的功能,无法完成请求
502Bad Gateway作为网关或者代理工作的服务器尝试执行请求时,从远程服务器接收到了一个无效的响应
503Service Unavailable由于超载或系统维护,服务器暂时的无法处理客户端的请求。延时的长度可包含在服务器的Retry-After头信息中
504Gateway Time-out充当网关或代理的服务器,未及时从远端服务器获取请求
505HTTP Version not supported服务器不支持请求的HTTP协议的版本,无法完成处理
  • 消息报头(响应头)

和请求报头中的描述一致

额外的,在响应的消息报头中,一般都会有Content-Type字段,用来告诉浏览器,本次响应回去的内容是什么类型的数据,浏览器可以根据这个对内容进行解析

  • html:Content-Type:text/html

  • xml:Content-Type:text/xml

  • 普通文本:Content-Type:text/plain

  • png图片:Content-Type:image/png

  • 响应正文:

响应正文就是服务器返回的资源的内容,响应正文和消息报头之间也需要有一个空行

实现方式

Servlet

javax.servlet.Servlet

它是Servlet技术的核心接口,所有的Servlet程序,都必须是该接口的实现类

  • init方法:初始化servlet对象的时候被调用
  • getServletConfig方法:返回ServletConfig对象,该对象封装了Servlet的配置信息
  • service方法:访问servlet对象的时候被调用(最重要的方法)
    • 当客户端浏览器通过制定地址访问该Servlet的时候,Tomcat会自动调用Servlet中的service方法,该方法的作用就是处理客户端浏览器发送的请求,并写回本次请求的相应内容
  • getServletInfo方法:返回servlet相关信息,比如作者,版本,版权等等
  • destory方法:销毁servlet对象的时候被调用
package com.briup.basic;

import javax.servlet.*;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * @Auther: pp's_husband
 * @Date: 2022/9/19---15:06
 * @Description:
 */
public class MyServlet implements Servlet {

    public MyServlet(){
        System.out.println("创建servlet...");
    }
    public void init(ServletConfig servletConfig) throws ServletException {
        System.out.println("com.briup.basic.MyServlet init ....");
    }

    public ServletConfig getServletConfig() {
        return null;
    }

    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        System.out.println("First servlet service...");
        //设置本次响应给客户端内容的类型,指定编码
        servletResponse.setContentType("text/html;charset=UTF-8");
        //通过响应对象获取输出流
        PrintWriter writer = servletResponse.getWriter();
        //给客户端写回内容
        writer.print("<html>");
        writer.print("<head><title>index</title></head>");
        writer.print("<body><h1>"+Math.random()+"</h1></body>");
        writer.print("</html>");
    }

    public String getServletInfo() {
        return null;
    }

    public void destroy() {

    }
}

web.xml中进行配置,目的是为了告诉tomcat,这个servlet对象的URL地址是什么,可以当做一种映射关系的配置,也可以用注解代替

1、@WebServlet(name=“…”,urlPattern=“/…”)

2、@WebServlet(value=“/…”)

<?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">

        <servlet>
            <servlet-name>my</servlet-name>
            <servlet-class>com.briup.basic.MyServlet</servlet-class>
        </servlet>
    <servlet-mapping>
        <servlet-name>my</servlet-name>
        <url-pattern>/MyServlet</url-pattern>
    </servlet-mapping>

GenericServlet

javax.servlet.GenericServlet

它是一个抽象类,它对Servlet接口中的方法进行默认实现,但是最重要的service方法并没有实现

package com.briup.basic;

import javax.jws.WebService;
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;
import java.io.PrintWriter;

/**
 * @Auther: pp's_husband
 * @Date: 2022/9/20---8:45
 * @Description:
 */
@WebServlet(name = "generic",urlPatterns = "/generic")
public class MyGenericServlet extends GenericServlet {

    private static final long serialVersionUID = 1L;
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        String name = servletRequest.getParameter("name");
        servletResponse.setContentType("text/plain;charset=utf-8");
        PrintWriter out = servletResponse.getWriter();
        out.println("hello"+name);
    }
}

注意:这种方法只要实现一个service方法即可,其他方法在父类中已经进行了默认实现

HttpServlet

javax.servlet.http.HttpServlet

它是一个抽象类,但是类中没有抽象方法,它继承了GenericServlet,对接收到的客户端请求,进行了细分,对应不同的请求类型,分别使用不同的方法进行处理,当前是GET请求的话,就会调用doGet方法进行处理,如果是POST请求,就会调用doPost方法进行处理

package com.briup.HttpServletRequestImpl;

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;

/**
 * @Auther: pp's_husband
 * @Date: 2022/9/20---14:33
 * @Description:
 */

@WebServlet("/Path")
public class PathServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("req.getContextPath() = " +req.getContextPath());
        System.out.println("req.getServletPath() = " +req.getServletPath());
        System.out.println("req.getRequestURL() = " +req.getRequestURL());
        System.out.println("req.getRequestURI() = " +req.getRequestURI());
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req,resp);
    }
}

注意

  1. HttpSerlvet是抽象类,但是里面没有抽象方法
  2. HttpServlet继承了GenericServlet,实现了service方法,并对service方法进行了重载
  3. 实现的service方法中,对请求和响应进行了强制类型装换,将普通的请求和响应对象,强制转换成了针对HTTP协议的请求和响应,点燃后调用重载的service方法,将转换的请求和响应传入
  4. 重载后的service方法中,根据当前请求的类型(get,post获取),再调用对应的doXXX方法进行处理
  5. doXXX方法中会有一些默认的实现,例如doGet方法中会默认返回http.method_delete_not_supported

GET和POST

HTTP协议中,定义了很多种请求的方式,但是浏览器默认的情况下,只能发出GET和POST方式请求

GET方式传参,参数会显示在地址栏中,并且参数的长度也会被地址栏的长度而限制

POST方式传参,参数不会显示在地址中,而是在请求体中,并且参数长度并没有直接限制

  • GET和POST的区别
    • url可见性:
      • get:url参数可见
      • post:url参数不可见
    • 数据传输上:
      • get:通过拼接url进行传递参数
      • post:通过body传递参数
    • 缓存性:
      • get请求是可以缓存的
      • post请求不可以缓存
    • 后退页面反应
      • get请求页面后退时,不产生影响
      • post请求页面后退时,会重新提交请求
    • 传输数据的大小
      • get一般传输数据大小不会超过2k-4k(根据浏览器不同,限制有差别)
      • post请求传输数据的大小根据php.ini配置文件设定,也可以无限大
    • 安全性:
      • 原则上post肯定要比get安全,毕竟传输参数时url不可见,但是也挡不住部分人闲着没有破解参数,但是对传递的参数进行加密,其实也差不多
    • 数据包:
      • get产生一个TCP数据包
        • 对于get方式的请求,浏览器会把http header和data一并发出去,服务器响应200(返回数据)
      • post产生两个TCP数据包
        • 对于post,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)
    • 网络环境好的情况下,发一次包的时间和发两次包的时间差别基本可以无视,而在网络环境差的情况下,两次包的TCP在验证数据包完整性上,有非常大的优点,并不是所有浏览器都会在post发送两次包,Firefox就只会发送一次

生命周期

  • 加载和实例化
    • 第一次被访问的时候创建(默认)—Tomcat启动的过程中创建(需要配置)
      • 配置中加入标签<load-on-startup>,标签值越小,越优先创建
    • Servlet容器负责加载和实例化Servlet,当Servlet容器启动时,或者在容器检测到需要这个Servlet来相应第一个请求时,创建Servlet实例,当Servlet容器
  • 初始化
    • 在Servlet实例化之后,容器将调用Servlet的init()方法初始化这个对象,初始化的目的是为了让Servlet对象在处理客户端请求前完成一些初始化的工作
    • init方法中客户通过ServletConfig对象来获取参数,在配置中,可以使用<init-param>进行初始化传参
  • 请求处理
    • Servlet容器调用Servlet的service方法对请求进行处理,要注意的是,在service方法调用之前,init方法必须成功执行,在service方法中,Servlet实例通过ServletRequest对象得到客户端的相关信息和请求信息,在对请求进行处理后,调用ServletRespose对象的方法设置响应信息,在service方法执行期间,日过发生错误,Servlet实例客户抛出异常,如果异常指示了改实例永久不可用,Servlet容器将调用实例的destory方法,释放该实例,此后对该实例的任何请求,都将受到容器发送的HTTP 404(请求的资源不可用)响应,如果是暂时不可用,就收到容器发送的HTTP 503 (服务器暂时忙,不能处理请求)响应
  • 服务终止
    • 当容器检测到一个Servlet实例应该从服务中被移除的时候,容器就会调用实例的destory方法,以便让该实例可以释放它所使用的资源,保存数据到持久存储设备中,当需要释放内存或者容器关闭时,容器就会调用Servlet实例的destory方法,在方法调用之后,容器就会释放这个Servlet实例,这个实例就会被Java的垃圾收集器回收

接收参数

  • String getParameter(String name):根据参数名称获取参数值
  • String[] getParameterValues(String name):根据参数名称获取参数值的数组
  • Enumeration getParameterName():获取所有请求的参数名称
  • Map<String,String[]> getParameterMap():获取所有参数的map集合

参数乱码

  • 如果是POST请求中携带的参数,出现乱码,可以调用setCharacterEncoding方法设置获取参数时使用的编码
  • Servlet中将中文写回给浏览器的时候,也可能会出现中文乱码,可以使用setContenType

跳转和重定向

跳转

  • 服务器内部跳转,其本质是,在servlet接收到一个请求的时候,可以将此请求转发给服务器内部的另一个资源中,如果这个资源是一个html页面,那么tomcat就会自动读取这个html页面并写回给浏览器

servlet跳转到html:

package com.briup.ServletWork;

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;
import java.io.PrintWriter;

/**
 * @Auther: pp's_husband
 * @Date: 2022/9/22---0:24
 * @Description:
 */

@WebServlet("*.do")
public class work2 extends HttpServlet {
    private static final long serialVersionUID = 1L;
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String name = req.getParameter("name");
        String password = req.getParameter("password");
        String zip = req.getParameter("zip");
        String address = req.getParameter("address");
        String telephone = req.getParameter("telephone");
        String email = req.getParameter("email");

        User user = new User();
        user.setUsername(name);
        user.setUsername(password);
        user.setUsername(zip);
        user.setUsername(address);
        user.setUsername(telephone);
        user.setUsername(email);

        if (name!=""&&password!=""&&zip!=""&&address!=""&&telephone!=""&&email!=""){
          RequestDispatcher dispatcher = req.getRequestDispatcher("/login.html");
          dispatcher.forward(req,resp);
         
        }else{
           RequestDispatcher dispatcher = req.getRequestDispatcher("register.html");
            dispatcher.forward(req,resp);
            
        }



        System.out.println("name = " + name);
        System.out.println("password = " + password);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }
}

servlet跳转到另一个servlet

package com.briup.ServletWork;

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;
import java.io.PrintWriter;

/**
 * @Auther: pp's_husband
 * @Date: 2022/9/22---0:24
 * @Description:
 */

@WebServlet("*.do")
public class work2 extends HttpServlet {
    private static final long serialVersionUID = 1L;
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String name = req.getParameter("name");
        String password = req.getParameter("password");
        String zip = req.getParameter("zip");
        String address = req.getParameter("address");
        String telephone = req.getParameter("telephone");
        String email = req.getParameter("email");

        User user = new User();
        user.setUsername(name);
        user.setUsername(password);
        user.setUsername(zip);
        user.setUsername(address);
        user.setUsername(telephone);
        user.setUsername(email);

        if (name!=""&&password!=""&&zip!=""&&address!=""&&telephone!=""&&email!=""){
           RequestDispatcher dispatcher = req.getRequestDispatcher("/login");
           dispatcher.forward(req,resp);
        }else{
            RequestDispatcher dispatcher = req.getRequestDispatcher("register");
           dispatcher.forward(req,resp);
            
        }



        System.out.println("name = " + name);
        System.out.println("password = " + password);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }
}

在这里插入图片描述

特点:

  • 需要使用request对象来完成
  • 服务器内部跳转期间,其实就是把请求request和响应response转发到下一个资源中,所以在整个过程中,使用的是同一个request和response
  • 服务器内部跳转,不会改变浏览器地址栏中的地址,因为从始至终浏览器只发出了一个请求

重定向

客户端重定向:

本质是,客户端浏览器发送第一次请求,访问servlet获取资源,但是服务器中,已经将资源转移到另一个位置了,所以需要通知客户端浏览器,重新发送第二次请求,访问一个新的资源位置,此时,服务器需要通知浏览器发送第二次请求,并且请求心底资源位置

  • 通过响应状态码(302),通知浏览器需要重定向
  • 通过响应头字段(Location:新地址),通知浏览器新的资源位置

在这里插入图片描述

特点:

  • 需要使用response对象来完成
  • 客户端重定向,会让浏览器发出第二次请求,那么在服务器内部会产生新的request对象和response对象
  • 客户端重定向,会改变浏览器地址栏中的地址,因为发出了第二次请求
package com.briup.ServletWork;

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;
import java.io.PrintWriter;

/**
 * @Auther: pp's_husband
 * @Date: 2022/9/22---0:24
 * @Description:
 */

@WebServlet("*.do")
public class work2 extends HttpServlet {
    private static final long serialVersionUID = 1L;
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String name = req.getParameter("name");
        String password = req.getParameter("password");
        String zip = req.getParameter("zip");
        String address = req.getParameter("address");
        String telephone = req.getParameter("telephone");
        String email = req.getParameter("email");

        User user = new User();
        user.setUsername(name);
        user.setUsername(password);
        user.setUsername(zip);
        user.setUsername(address);
        user.setUsername(telephone);
        user.setUsername(email);

       if(name!=""&&password!=""&&zip!=""&&address!=""&&telephone!=""&&email!=""){
            resp.sendRedirect("/maven_web_war/login.html");
        }else{

            resp.sendRedirect("/maven_web_war/register.html");
        }



        System.out.println("name = " + name);
        System.out.println("password = " + password);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }
}

项目路径

HttpServletRequest

javax.servlet.http.HttpServletRequest接口,表示http协议的请求

常用的和路径相关的方法:

  1. getContextPath(),获取项目的根路径(默认是项目名)
  2. getServletPath(),获取当前访问的servlet地址
  3. getRequestURL(),获取当前请求的URL地址
  4. getRequestURI(),获取当前请求的URI地址
package com.briup.HttpServletRequestImpl;

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;

/**
 * @Auther: pp's_husband
 * @Date: 2022/9/20---14:33
 * @Description:
 */

@WebServlet("/Path")
public class PathServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("req.getContextPath() = " +req.getContextPath());
        System.out.println("req.getServletPath() = " +req.getServletPath());
        System.out.println("req.getRequestURL() = " +req.getRequestURL());
        System.out.println("req.getRequestURI() = " +req.getRequestURI());
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req,resp);
    }
}

ServletContext

javax.servlet.ServletContext接口,表示Servlet上下文环境,代表了整个web项目,是非常重要的一个对象

常用的和路径相关的方法:

  1. getContextPath(),获取当前项目的根路径(默认就是项目名)
  2. getRealPath(String path),获取一个资源在服务器中的绝对路径
  3. getResourcePaths(String path),获取一个路径下面的所有路径
package com.briup.HttpServletRequestImpl;

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;
import java.util.Set;

/**
 * @Auther: pp's_husband
 * @Date: 2022/9/20---14:38
 * @Description:
 */
@WebServlet("/ServletContext")
public class PathServletContext extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //通过request对象获取ServletContext对象
        ServletContext sc = getServletContext();

        String cp = sc.getContextPath();
        System.out.println("获取当前项目的根路径 = " + cp);

        String rp = sc.getRealPath("/");
        System.out.println("获取一个资源在服务器中的绝对路径 = " + rp);

        Set<String> rsp = sc.getResourcePaths("/");
        System.out.println("获取一个路径下面的所有资源");

    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }
}

范围对象

在web项目中,如果有数据,需要进行传递,并且要求在某一个范围内进行传递,那么就可以在servlet中使用以下几个对象,在指定的范围内,进行数据传递

接口类型对象名描述
javax.servlet.http.HttpServletRequestrequest请求范围,数据存入后,在同一个请求内有效
javax.servlet.http.HttpSessionsession会话范围,数据存入后,在同一个会话中有效
javax.servlet.ServletContextapplication引用范围,数据存入后,在同一个项目中有效
  1. session对象
    • request.getSession()
  2. application对象
    • request.getServletContext()

声明周期:

1、request对象

客户端每次发送请求,服务器就会创建一个新的request对象,本次访问结束后,该request对象就会被销毁,所以每次请求都是一个全新的request对象,可以看出来,存入request中的数据,只能在一次请求之内有效

2、session对象

request.getSession()方法第一次被调用时,会创建session对象,session.invalidate()调用时,会销毁sesson,session如果自动超时的话,也会被销毁,默认时间为30分钟,指的是客户端不予服务器进行交互的时间,可以在web.xml中进行设置,单位是分钟<session-timeout>30</session-timeout>

3、application对象

启动tomcat服务器时,就会被创建,关闭tomcat服务器的时候就会被销毁,每个项目在运行期间,都会有且只有一个application对象,与这个项目对应,整个项目在运行期间,因为只有一个application对象,所以这个对象是所有用户共享的,大家都可以向这个对象里面存值,也可以再拿出来

会话追踪

无状态访问

http协议的访问是无状态的访问,当前访问是不会知道之前访问的猪肝太的,也就是说http协议的访问是不会帮我们保存访问记录/痕迹的,但是,有些时候我们需要记录之前的访问状态

Cookie

cookie是常用到的一种会话追踪技术,是浏览器中用来存储信息的一种本地文件,浏览器再向服务器发送请求的时候,可以根据服务器的地址,将对应的cookie中的信息,携带到请求头中,一起发送给服务器

注意,session是保存在服务器端的对象,而cookie是保存在客户端(浏览器)的一种本地文件

cookie和session

如果在一次请求的过程中,服务器端产生了session,那么这个session的id值,会写入到响应(response)中,然后由响应将session的id值,带回到浏览器中,浏览器收到后,再把这个id值写入到本地的cookie文件中,并且这个session的id值,在浏览器的cookie文件中国,会有一个默认的名字:JSESSIONID,之后,浏览器发送请求的时候,就会把之前保存在cookie文件中的JSESSIONID的值,传给服务器,服务器通过这个JSESSIONID的值,就能够知道服务器内存中是哪一个session对象,和当前这个客户端对应

这种情况下,就能达到一个效果,客户端的每一次访问,在服务器端都能够拿到相同的一个session对象,从而实现不同请求之间通过相同的session对象进行数据的共享

服务器写回cookie

使用response对象,可以把cookie带回到浏览器,然后浏览器把cookie对象中的内容保存到对应的一个cookie文件中

//创建cookie对象
Cookie c1 = new Cookie ("name","tom");
Cookie c2 = new Cookie ("msg","hello");

//设置cookie的有效时间
c1.setMaxAge(60*60*24*365);
c2.setMaxAge(60*60*24*365*10);

//把cookie放到response里面
response.addCookie(c1);
response.addCookie(c2);

注意,如果没有设置cookie生效的时间,那么这个cookie就是会话cookie,也就是当关闭浏览器的时候cookie就是失效了

服务器读取cookie

servlet中,客户使用request对象拿到从浏览器发送过来的cookie数据

//从request中拿到一个cookie数据
//如果没任何cookie数据则返回null
Cookie[] cookies = request.getCookies();
//遍历数组,拿出key和value
for(Cookie c :cookies){
    String key = c.getName;
    String value = c.getValue;
    System.out.println(key+":"+value);
}

URL重写

URL重写也可以实现会话追踪实现

当用户把浏览器的cookie功能禁用以后,浏览器在发请求的时候,响应(response)把cookie带回来,但是浏览器无法接收,这其中就包括最重要的JSESSIONID

浏览器cookie被禁用以后,可以用URL重写技术,也可以达到会话追中的效果,将需要进行会话追中的链接,使用指定方法进行处理后,得到新的链接地址:

String url = response.encodURL("url");

注意,重写后的url和原来的url比较,其实就是在url后面拼接上了JSESSIONID的值,以便点击链接的时候可以把JSESSIONID的值也传过来,从而达到会话追踪的效果

过滤器-Fileter

  • 在一个请求去访问某个志愿的时候,filter(过滤器)可以在这个请求访问到这个资源之前,把请求拦下,然后做出一系列的处理,例如编码转换,信息过滤,权限判断,登录验证等等,最后filter再决定,是否让这个请求去访问目标资源

编写

javax.Servlet.Filter是Servlet技术中提供的一个接口,实现该接口的类,并且完成对应的配置,那么这个类就是一个过滤器

package com.briup.filter;

import javax.servlet.*;
import java.io.IOException;
import java.util.Enumeration;

/**
 * @Auther: pp's_husband
 * @Date: 2022/9/22---14:30
 * @Description:
 */
public class FirstFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
       
    }
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
    }

    @Override
    public void destroy() {

    }
}

doFilter方法中有三个参数,前两个分别是请求和响应对象,第三个参数是比较重要的一个参数javax.servlet.FilterChain

同一个请求有可能要依次的通过两个或者多个过滤器,在servlet中,把这样多个过滤器看做一个过滤器链,这个过滤器链就用FilterChain接口类型来表示

chain.doFilter(req,resp),表示把当前的req和resp传给这个过滤器链中的下一个过滤器对象,然后对该请求进行处理,如果说链中已经没有下一个过滤器了,那么就把这次访问放行,去访问它的目标资源

编码过滤器:

package com.briup.filter;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

/**
 * @Auther: pp's_husband
 * @Date: 2022/9/21---0:08
 * @Description:
 */
public class EncodingFilter2 implements Filter {
    private String encoding;
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        //过滤器初始化的时候调用该方法
         encoding = filterConfig.getInitParameter("encoding");

         //默认值是UTF-8
        if (encoding == null){
            encoding = "UTF-8";
        }
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) servletRequest;
        System.out.println("encodingFilter = " + req.getRequestURI());

        //设置编码
        servletRequest.setCharacterEncoding(encoding);
        servletResponse.setCharacterEncoding(encoding);

        //将请求放行
        filterChain.doFilter(servletRequest,servletResponse);

    }

    @Override
    public void destroy() {

    }
}

配置

  • 和servlet程序一样,filter过滤器编写完成之后,并不能理科工作,还需要进行配置,制定这个过滤器会对哪些路径的访问生效

例如,在web.xml中配置编码过滤的filter对所有路径都生效

<filter>
    <filter-name>encodingFilter</filter-name>
    <filter-class>com.briup.filter.EncodingFilter</filter-class>
</filter>
<filter-mapping> 
    <filter-name>encodingFilter</filter-name>
    <url-pattern>/*</url-pattern> 
</filter-mapping>

其中<url-pattern></url-pattern>有四种配置

  • 精确匹配
    • 例如,<url-pattern>/test_servlet</url-pattern>表示此拦截器只会拦截/test_servlet这一个路径
  • 扩展名匹配
    • 例如,<url-pattern>*.html</url-pattern>表示此拦截器只会拦截后缀名是.html的路径
  • 路径匹配
    • 例如, <url-pattern>/test/*</url-pattern>表示此拦截器拦截/test路径下的所有资源
  • 匹配任意路径
    • 例如, <url-pattern>/*</url-pattern>表示此拦截器拦截项目下的所有资源

顺序

如果有多个过滤器,并且多个过滤器拦截的路径有相同的部分,就有一些路径被这多个过滤器共同拦截,那么过滤器的顺序是按照web.xml中配置的顺序从上到下执行的

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1"> 
    <filter> 
        <filter-name>encodingFilter</filter-name> 
        <filter-class>com.briup.filter.EncodingFilter</filter-class> </filter> 
    <filter-mapping>
        <filter-name>encodingFilter</filter-name> 
        <url-pattern>/*</url-pattern> 
        <dispatcher></dispatcher> 
    </filter-mapping>
    <filter>
        <filter-name>checkFilter</filter-name>
        <filter-class>com.briup.filter.CheckFilter</filter-class>
    </filter>
    <filter-mapping> 
        <filter-name>checkFilter</filter-name> 
        <url-pattern>/test</url-pattern>
    </filter-mapping>
</web-app>

当请求方法/test的时候,encodingFilter会先拦截,然后是checkFilter再拦截

生命周期

当服务器启动的时候,Filter会被创建并调用init方法初始化,服务器关闭的时候会被销毁并调用destory方法,重写init方法和destoru方法,启动服务器,访问资源被拦截,再关闭服务器,即可看到生命周期中的创建,初始化,访问,销毁的过程

拦截类型

在web.xml中进行配置的时候,还哦可以配置拦截器拦截的请求类型,默认是REQUEST,也就是只要是浏览器发送的请求,且满足路径要求,牛慧被拦截器拦截

例如,默认情况下,服务器内部跳转的请求,就无法被拦截,除非配置:

<filter>
    <filter-name>checkFilter</filter-name> 
    <filter-class>com.briup.filter.CheckFilter</filter-class>
</filter>
<filter-mapping> 
    <filter-name>checkFilter</filter-name> 
    <url-pattern>/test</url-pattern>
    <dispatcher>FORWARD</dispatcher> 
</filter-mapping>

这里默认的配置REQUEST类型的请求拦截,也就是浏览器直接发送请求,除此之外,还有其他的拦截类型,了解即可,重点关注的是REQUEST和FORWARD两种

注解

也可以使用注解来完成过滤器的配置工作,需要注意的是,使用注解@WebFilter的形式来配置的过滤的话,那么多个过滤器拦截同一个路径,过滤器的执行顺序是通过过滤器的类名首字母的顺序来执行的

package com.briup.filter; 

import java.io.IOException;
import javax.servlet.DispatcherType; 
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig; 
import javax.servlet.ServletException; 
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse; 
import javax.servlet.annotation.WebFilter;
import javax.servlet.annotation.WebInitParam; 
import javax.servlet.http.HttpServletRequest;
@WebFilter(value = "/*",initParams = {@WebInitParam(name = "encoding",value = "UTF-8")},dispatcherTypes = {DispatcherType.REQUEST})
public class EncodingFilter implements Filter{ 
    private String encoding; 
    @Override public void init(FilterConfig filterConfig) throws ServletException { //过滤器初始化的时候调用该方法
        encoding = filterConfig.getInitParameter("encoding"); System.out.println("encoding = "+encoding);
        if(encoding==null) { 
            encoding = "UTF-8"; 
        }
    }
    @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { 				HttpServletRequest req = (HttpServletRequest) request; 					System.out.println("encodingFilter:"+req.getRequestURI());
			//设置编码 
         request.setCharacterEncoding(encoding); response.setCharacterEncoding(encoding); 
//将请求放行 
chain.doFilter(request, response);
 }
    @Override public void destroy() {
        //过滤器销毁时候调用该方法 
    }

监听器-Listener

监听器(Listener)用于监听web程序中的事件,监听器主要用于Session,request,application进行监控,例如创建,修改,删除session,request,application等,并处罚响应的时间

分类

不同功能的监听器,需要实现不同的接口,可以分为以下几种监听器类型:

  • 监听request,session,application的创建和销毁,这些接口为:
    • ServletRequestListener
    • HttpSessionListener
    • ServletContextListener
  • 监听request,session,application三个对象中属性变化,这些接口为:
    • ServletRequestAttributeListener
    • HttpSessionAttributeListener
    • ServletContextAttributeListener
  • 监听Session对象里面存放着的其他对象,这些接口为:
    • HttpSessionBindingListener
    • HttpSessionActivationListener

使用:

1、监听request,session,application的创建和销毁

package com.briup.listener;

import javax.servlet.*;
import javax.servlet.annotation.WebListener;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

/**
 * @Auther: pp's_husband
 * @Date: 2022/9/21---0:16
 * @Description:
 */
@WebListener(value = "/work2")
public class ListenerTest implements HttpSessionListener, ServletContextListener, ServletRequestListener {
    //加载application
    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent) {
        ServletContext servletContext = servletContextEvent.getServletContext();
        System.out.println("即将启动:"+servletContext.getContextPath());
    }

    //销毁application
    @Override
    public void contextDestroyed(ServletContextEvent servletContextEvent) {
        ServletContext servletContext = servletContextEvent.getServletContext();
        System.out.println("即将关闭:"+servletContext.getContextPath());
    }

    //销毁request
    @Override
    public void requestDestroyed(ServletRequestEvent servletRequestEvent) {
        HttpServletRequest request = (HttpServletRequest) servletRequestEvent.getServletRequest();
        long time = System.currentTimeMillis() -(Long)request.getAttribute("dateCreated");
        System.out.println(request.getRemoteAddr()+"请求处理结束,用时:"+time+"毫秒");
    }

    //创建request
    @Override
    public void requestInitialized(ServletRequestEvent servletRequestEvent) {
        HttpServletRequest request = (HttpServletRequest) servletRequestEvent.getServletRequest();
        String uri = request.getRequestURI();
        uri = request.getQueryString() == null? uri : (uri+"?"+request.getQueryString());
        request.setAttribute("dateCreated",System.currentTimeMillis());
        System.out.println("IP"+request.getRemoteAddr()+"请求 "+uri);
    }

    //创建session
    @Override
    public void sessionCreated(HttpSessionEvent httpSessionEvent) {
        HttpSession session = httpSessionEvent.getSession();
        System.out.println("新创建一个session,ID为:" + session.getId());
    }

    //销毁session
    @Override
    public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
        HttpSession session = httpSessionEvent.getSession();
        System.out.println("销毁一个session,ID为:"+session.getId());
    }
}

2、监听request,session,application三个对象中属性变化

package com.briup.listener;

import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;

/**
 * @Auther: pp's_husband
 * @Date: 2022/9/21---8:51
 * @Description:
 */
public class SessionAttributeListenerTest implements HttpSessionAttributeListener {

    //添加属性
    @Override
    public void attributeAdded(HttpSessionBindingEvent se) {
        String name = se.getName();
        Object value = se.getValue();
        System.out.println("新建session属性:"+name+",值为:"+value);
    }

    //删除属性
    @Override
    public void attributeRemoved(HttpSessionBindingEvent se) {
        String name = se.getName();
        Object value = se.getValue();
        System.out.println("删除session属性:"+name+",值为:"+value);

    }

    //修改属性
    @Override
    public void attributeReplaced(HttpSessionBindingEvent se) {
        HttpSession session = se.getSession();
        String name = se.getName();
        Object oldValue = se.getValue();
        Object newValue = session.getAttribute(name);
        System.out.println("修改session属性:"+name+",原值:"+oldValue+",新值:"+newValue);
    }


}

3、监听session对象里面存放着的其他对象

package com.briup.listener;

import javax.servlet.http.*;
import java.io.Serializable;
import java.util.EventListener;

/**
 * @Auther: pp's_husband
 * @Date: 2022/9/21---8:56
 * @Description:
 */
public class User implements HttpSessionBindingListener, HttpSessionActivationListener, Serializable {
    private long id ;
    private String name;
    public User(){};

    public User(long id, String name) {
        this.id = id;
        this.name = name;
    }

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }

    //session即将被持久化到硬盘时
    @Override
    public void sessionWillPassivate(HttpSessionEvent se) {
        HttpSession session = se.getSession();
        System.out.println(this+"即将保存到硬盘-sessionId:"+session.getId());
    }

    //session从硬盘加载后
    @Override
    public void sessionDidActivate(HttpSessionEvent se) {
        HttpSession session = se.getSession();
        System.out.println(this+"已经成功从硬盘中加载-sessionId:"+session.getId());
    }

    //当前对象被放进session前
    @Override
    public void valueBound(HttpSessionBindingEvent event) {
        HttpSession session = event.getSession();
        String name = event.getName();
        System.out.println(this+"被绑定到session \""+session.getId()+"\"的"+name+"从属性上移除");

    }

    @Override
    public void valueUnbound(HttpSessionBindingEvent httpSessionBindingEvent) {

    }
}

error-page

在web.xml中,可以使用<error-page>来定制错误的返回页面

例如,如果出现404的情况,则跳转到/404.html的页面中

<error-page>
    <error-code>404</error-code> 
    <location>/404.html</location> 
</error-page>

也可以指定servlet中出现的异常情况,例如,当servlet程序的时候,如果出现了以下类型异常,则跳转到not_found.html页面

<error-page> 
    <error-type>java.io.FileNotFoundException</error-type> 					 <location>/not_found.html</location> 
</error-page>

WEB-INF

WEB-INF是Java中web应用中的安全目录

安全目录指的是客户端无法访问,只有服务端可以访问的目录

例如,如果将hello.html页面存放在WEB-INF下,那么浏览器中是无法通过以下地址直接访问到该页面的:

http://127.0.0.1:8989/servlet-test/WEB-INF/hello.html

但是,在servlet中是可以通过服务器内部跳转,来访问到WEB-INF目录下的资源的

因此,在浏览器可以访问servlet,让servlet再通过服务器内部跳转的方式,来访问WEB-INF下的hello.html,从而提高页面的安全性,因为在servlet中可以对本次请求进行验证,通过后跳转到WEB-INF下的页面,否则返回错误信息

文件上传

在servlet3.0之后上传文件需要的步骤

1、不需要导入额外的jar包,使用servlet提供的@MultipartConfig注解和javax.servlet.http.Part接口类型对象即可

2、页面表单一定要是post方式提交

3、表单编码类型设置为:enctypr=“multipart/form-data”,默认是application/x-www/form-urlencoded

4、上传成功的时候上传的文件可以在项目的目录,也可以在系统的目录

5、设置了表单的enctype=“multipart/form-data”,也可以通过HttpServletRequest对象的getParameter方法获取请求参数值

编写页面upload.html

<!DOCTYPE html>
<html>
    <head> 
        <meta charset="UTF-8">
        <title>上传</title> 
    </head>
    <body>
        <h1>hello world</h1> 
        <h2>上传</h2>
        <form action="/upload2" method="post" enctype="multipart/form-data"> 
            <input type="text" name="username" /><br> 
            <input type="file" name="file" /><br> 
            <input type="submit" value="上传" />
        </form> 
    </body>
</html>

2、编写servlet

package com.briup.listener;

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;
import java.io.InputStream;

/**
 * @Auther: pp's_husband
 * @Date: 2022/9/21---9:15
 * @Description:
 */
@WebServlet("/up.do")
@MultipartConfig(location = "D:\\upload")
public class UpLoadServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        

        //获取上传的文件
        Part part = req.getPart("file");
        String fileName = part.getSubmittedFileName();

        System.out.println(username+"上传了文件:"+fileName);
        System.out.println(password+"上传了文件:"+fileName);

        //避免上传的文件重复,文件名前面加上时间戳
        part.write(System.currentTimeMillis()+"-"+fileName);

    }
}

文件下载

servlet下载需要注意事项:

  • servlet其实就是用io流不同的去读哪个文件,然后再用输出流不停的写给客户端浏览器
  • 下载结束,也就是说输出流结束之后,下面是不能再写跳转的代码的,要不然会报错
  • 按照http协议的要求,设置下载的相应类型ContenType,以及设置响应头Content-Disposition来指定下载文件的名字

1、编写页面download.html

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>下载</title>
    </head>
    <body>
        <h1>hello world</h1>
        <h2>servlet下载</h2> 
        <a href="download?fileName=我的文档.txt">我的文档</a><br>
    </body> 
</html>

2、编写servlet

package com.briup.test; 
import java.io.File; 
import java.io.FileInputStream; 
import java.io.IOException; 
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;
@WebServlet("/download") 
public class DownLoadServlet extends HttpServlet { 
    private static final long serialVersionUID = 1L;
    private String path = "D:\\briup\\upload"; 
    @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 
        // 获得用户需要下载文件的名字 
        String fileName = request.getParameter("fileName"); 
        // 如果是下载文件的目录在项目中,可以通过该方法获得该下载文件目录的绝对路径 // String realPath = getServletContext().getRealPath(path);
        // 响应类型设置为下载:application/x-download
        response.setContentType("application/x-download"); 
        // 下载文件时显示文件的保存名称
        String fileDisplay = "briup_"+fileName;
        // 下载文件名的中文编码转换 因为写回去的时候是字节流 
        // 不设置会出现下载文件的中文名字乱码
fileDisplay = new String(fileDisplay.getBytes("utf-8"),"ISO-8859-1");
        // 设置响应头部信息(http协议中的固定设置方式) 
        response.addHeader("Content-Disposition", "attachment;filename="+fileDisplay); 
        FileInputStream in = null; try {
            // 获取servlet中是输出流 
            ServletOutputStream out = response.getOutputStream(); 
            // 使用IO流读取需要下载的文件 
            File file = new File(path,fileName);
            in = new FileInputStream(file); 
            // 读取文件后再写回给浏览器 
            byte[] b = new byte[1024];
            int len = -1;
            while ((len = in.read(b)) != -1) {
                out.write(b, 0, len);
            }
            in.close();
        } catch (Exception e) { 
            e.printStackTrace(); 
        }finally { 
            if(in!=null) {
                in.close();
            } 
        }
        //注意下载之后就不能使用request跳转了,因为response已经返回了
    }
}

三层架构

在web项目的代码中,经常会使用到三层架构

  • web层
  • service层
  • dao层

web层中代码的主要任务:

  • 接受客户端传过来的参数
  • 把参数封装成对象
  • 把封装好的对象/数据传给service
  • 根据service层的处理结果决定把那个页面/数据返回给客户端

service层中代码的主要任务:

  • 接受web层传过来的对象/数据(如果有的话)

  • 根据这些信息进行业务逻辑处理

例如,完成一个登陆功能够,web层接收到用户名和密码之后,把数据传给service层,service层就要根据这些信息来判断用户名是否存在,密码是否正确,用户是否有权限,用户状态当前是否可用,用户是否推送信息,是否给用户相关提示等等,这些都属于登录的业务逻辑处理

dao层中代码的主要任务:

  • 接受service传的参数(如果有的话)
  • 和数据库进行交互
  • 把交互结果返回给service层
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

潘潘她老公

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值