Servlet

Servlet

概述

随着Web应用业务需求的增多,动态Web资源的开发变得越来越重要。目前,很多公司都提供了开发动态Web资源的相关技术,其中,比较常见的有ASP、PHP、JSP和Servlet等。基于Java的动态Web资源开发,Sun公司提供了Servlet和JSP两种技术。

Servlet: server applet: 运行在服务器端的小程序。Java提供了Servlet接口,此接口的实现类可以运行在服务器上。Servlet 主要用于处理客户端传来的 HTTP 请求,并返回一个响应。
Servlet由Servlet容器提供,所谓的Servlet容器是指提供了Servlet 功能的服务器,Servlet容器将Servlet动态地加载到服务器上。与HTTP 协议相关的Servlet使用HTTP请求和HTTP响应与客户端进行交互。因此,Servlet容器支持所有HTTP协议的请求和响应。Servlet应用程序的体系结构如图所示。
Servlet
Servlet技术特点:

  • 方便:Servlet提供了大量的实用工具,如处理很难完成的HTML表单数据、读取和设置HTTP头,以及处理Cookie和跟踪会话等。
  • 跨平台:Servlet用Java类编写,可以在不同操作系统平台和不同应用服务器平台下运行。
  • 灵活性和可扩展性:采用Servlet开发的Web应用程序,由于Java类的继承性及构造函数等特点,使得应用灵活,可随意扩展。

Servlet

入门

  1. 创建JavaEE项目

  2. 定义一个类,实现Servlet接口。

  3. 在web.xml中配置:

    <servlet>
    <servlet-name>demo1</servlet-name>
    <servlet-class>ccom.example.tomcat.ServletDemo1</servlet-class>
    </serblet>
    
    <servlet-mapping>
    	<servlet-name>demo1</servlet-name>
        <url-pattern>/demo1</url-pattern>
    </servlet-mapping>
    

执行原理:

  1. 当服务器接受到客户端浏览器的请求后,会解析请求URL路径,获取访问的Servlet的资源路径
  2. 查找web.xml文件,是否有对应的<url-pattern>标签体内容。
  3. 如果有,则在找到对应的<servlet-class>全类名
  4. tomcat会将字节码文件加载进内存,并且创建其对象
  5. 调用其方法

Servlet常用方法

Servlet

  1. 被创建时:执行init方法,只执行一次。
    默认情况下,第一次被访问时,创建Servlet。
    可以修改Servlet的创建时机,在<Servlet>里修改<load-on-startup>的值。

    <servlet>
            <servlet-name>demo2</servlet-name>
            <servlet-class>com.example.tomcat.ServletDemo2</servlet-class>
    <!--        指定Servlet的创建时机
                1.第一次被访问时创建
                <load-on-startup>的值为负数
                2.在服务器启动时创建
                <load-on-startup>0~9999</load-on-startup>数字小的先
    -->
            <load-on-startup>5</load-on-startup>
    </servlet>
       
    

    Servlet的init方法只执行一次,说明一个Servlet在内存中只存在一个对象,Servlet是单例的。

    • 多个用户同时访问时,可能存在线程安全问题。
    • 解决:尽量不要再Servlet中定义成员变量。即使定义了成员变量,也不要对其修改。
  2. 提供服务:执行service方法。
    每次访问Servlet时,都执行一次service。

  3. 被销毁:destroy方法
    在服务器正常关闭时,Servlet被销毁,才会执行destroy方法。

Servlet3.0:支持注解配置,不需要写麻烦的web.xml了

  1. 创建JavaEE项目,Servlet版本选3.0以上,可以不创建web.xml

  2. 定义一个类,实现Servlet接口。

  3. 在类上使用@WebServlet注解,如下配置
    @WebServlet("资源路径")

    urlpartten:访问该Servlet的虚拟路径

    • 一个Servlet可以定义多个访问路径:@WebServlet({"/demo1","/d1","/dm1"})
    • 路径定义规则:
      1. /xxx
      2. xxx/yyy 多层目录
      3. *.do 扩展名匹配

    WebServlet的源码

     public @interface WebServlet {
     	String name() default ""; //相当于<Servlet-name>
     	String[] value() default {};//代表urlPattern
     	String[] urlPatterns() default {};//同上
     	int loadOnStartup() default -1;//相当于<load-on-sta
     	WebInitParam[] initParams() default {};
     	boolean asyncSupported() default false;
     	String smallIcon() default "";
     	String largeIcon() default "";
     	String description() default "";
     	String displayName() default "";
     }
    

Servlet体系结构

接口Servlet-->抽象类GenericServlet-->抽象类HttpServlet
Servlet接口的实现类要重写5个方法,其实一般只需要使用service方法,如果重写5个方法就很麻烦。Java提供了两个抽象类方便我们的使用。

  • GenericServlet:将Servlet接口中其他的方法做了默认空实现,只将service()方法作为抽象。此类没有实现http请求处理。
  • HttpServlet:http协议的一种封装,简化操作。定义类继承HttpServlet并重写doGet/doPost方法,就能轻松的处理客户端的数据。也可以像基本的Servlet类一样重写service方法,不区分get和post方法,采用相同的程序来处理。

Servlet使用细节

生命周期

出生:请求第一次到达Servlet时,对象就创建出来,并且初始化成功。只出生一次,就放到内存中。

活着:服务器提供服务的整个过程中,该对象一直存在,每次只是执行service方法。

死亡:当服务停止时,或者服务器宕机时,对象消亡。

通过分析Servlet的生命周期我们发现,它的实例化和初始化只会在请求第一次到达Servlet时执行,而销毁只会在Tomcat服务器停止时执行,由此我们得出一个结论,Servlet对象只会创建一次,销毁一次。所以,Servlet对象只有一个实例。如果一个对象实例在应用中是唯一的存在,那么我们就说它是单实例的,即运用了单例模式。

Servlet

线程安全

因为Servlet是单例,单例对象的类成员只会随类实例化时初始化一次,之后的操作都是改变,而不会重新初始化。所以可能存在线程安全性问题。所以在Servlet中定义类成员要慎重。如果类成员是共用的,并且只会在初始化时赋值,其余时间都是获取的话,那么是没问题。如果类成员并非共用,或者每次使用都有可能对其赋值,那么就要考虑线程安全问题了,把它定义到doGet或者doPost方法里面去就可以了。

不用全局变量即可。

Servlet依赖

单独的Servlet还不能提供服务,还需要依赖几个变量:ServletRequest用来封装客户端请求,ServletResponse用来向客户端发送响应,ServletConfig保存Servlet的配置信息,ServletContext保存整个web项目的信息。

Servlet

ServletConfig

每个Servlet都一个自己的ServletConfig,保存了此Servlet的配置信息。它在初始化阶段读取了web.xml中为Servlet准备的初始化配置,并把配置信息传递给Servlet,所以生命周期与Servlet相同。

ServletContext

概述

ServletContext对象,它是应用上下文对象。每一个应用有且只有一个ServletContext对象。它可以实现让应用中所有Servlet间的数据共享。 通常网站下方的 copyright 和备案号都是通过ServletContext对象保存的。

生命周期

出生: 应用一加载,该对象就被创建出来了。一个应用只有一个实例对象。(Servlet和ServletContext都是单例的)

活着:只要应用一直提供服务,该对象就一直存在。

死亡:应用被卸载(或者服务器挂了),该对象消亡。

域对象

域对象的概念,它指的是对象有作用域。域对象可以实现数据共享。不同作用范围的域对象,共享数据的能力不一样。

在Servlet规范中,一共有4个域对象。ServletContext是web应用中最大的作用域,叫application域。每个应用只有一个application域。它可以实现整个应用间的数据共享功能。
Session会话域
request请求域
pageContext页面域:jsp特有

ServletContext的使用

ServletContext 是应用上下文对象。每一个应用中只有一个 ServletContext 对象。
作用:可以获得应用的全局初始化参数和达到 Servlet 之间的数据共享。
生命周期:应用一加载则创建,应用被停止则销毁。

Servlet

ServletContext配置

ServletContext既然被称之为应用上下文对象,所以它的配置是针对整个应用的配置,而非某个特定Servlet的配置。它的配置被称为应用的初始化参数配置。

配置的方式,需要在<web-app>标签中使用<context-param>来配置初始化参数。具体代码如下:

<!--配置应用初始化参数-->
<context-param>
    <!--用于获取初始化参数的key-->
    <param-name>servletContextInfo</param-name>
    <!--初始化参数的值-->
    <param-value>This is application scope</param-value>
</context-param>
<!--每个应用初始化参数都需要用到context-param标签-->
<context-param>
    <param-name>globalEncoding</param-name>
    <param-value>UTF-8</param-value>
</context-param>

常用方法

public class ServletContextDemo extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        /**
        获取ServletContext对象
        1. 通过request对象获取:request.getServletContext();
		2. 通过Servlet对象获取:this.getServletContext();
        */
        ServletContext context = getServletContext();

        //获取全局配置的globalEncoding
        String value = context.getInitParameter("globalEncoding");
        System.out.println(value);

        //获取应用的访问虚拟目录
        String contextPath = context.getContextPath();
        System.out.println(contextPath);

        //文件有两个路径,一个是idea的工作空间路径,一个是部署在服务器上的路径
        //根据虚拟目录获取应用部署的磁盘绝对路径
        //获取webapp/b.txt文件的绝对路径
        String b = context.getRealPath("/b.txt");
        System.out.println(b);

        //获取webapp/WEB-INF/c.txt文件的绝对路径
        String c = context.getRealPath("/WEB-INF/c.txt");
        System.out.println(c);

        //获取java类目录下的a.txt文件的绝对路径
        String a = context.getRealPath("/WEB-INF/classes/a.txt");
        System.out.println(a);


        //向域对象中存储数据
        context.setAttribute("username","zhangsan");

        //移除域对象中username的数据
        //context.removeAttribute("username");
    }

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

ServletRequest

Servlet最主要的作用就是处理客户端请求,并向客户端做出响应。为此,针对Servlet的每次请求,Web服务器在调用service()方法之前,都会创建两个对象,分别是HttpServletRequest和 HttpServletResponse。其中,HttpServletRequest 用于封装 HTTP 请求消息,简称 request对象。HttpServletResponse 用于封装 HTTP 响应消息,简称 response 对象。

  1. request对象和response对象的原理

    1. request和response对象是由服务器创建的。我们来使用它们
    2. request对象是来获取请求消息,response对象是来设置响应消息
    3. Servlet
  2. request对象继承体系结构
    ServletRequest(interface) —>HttpServletRequest(interface)—>org.apache.catalina.connector.RequestFacade(class from tomcat)

  3. request功能:

    1. 获取请求消息数据

      1. 获取请求行数据
        GET /day14/demo1?name=zhangsan HTTP/1.1
        方法:

        1. 获取请求方式 :String getMethod() GET

        2. 获取虚拟目录String getContextPath() /day14。ServletContext也有这个方法。

        3. 获取Servlet路径:String getServletPath() /demo1

        4. 获取get方式请求参数:String getQueryString() name=zhangsan

        5. 获取请求URIString getRequestURI(): /day14/demo1
          StringBuffer getRequestURL(): http://localhost/day14/demo1
          URL:统一资源定位符
          URI:统一资源标识符

        6. 获取协议及版本:String getProtocol() HTTP/1.1

        7. 获取客户机的IP地址:String getRemoteAddr()

        8. 获取ServletContext:ServletContext getServletContext()

        9. @WebServlet("/RequestDemo1")
          public class RequesDemo1 extends HttpServlet {
              @Override
              protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
                  
                  //1. 获取请求方式 String getMethod()
                  System.out.println(req.getMethod());
                  // 2. 获取虚拟目录:  String getContextPath()
                  System.out.println(req.getContextPath());
                  //3. 获取Servlet路径: String getServletPath()
                  System.out.println(req.getServletPath());
                  //4. 获取get方式请求参数  String getQueryString()
                  System.out.println(req.getQueryString());
                  //5. 获取请求URI:/day14/demo1
                  //  String getRequestURI():	/day14/demo1
                  // StringBuffer getRequestURL():  http://localhost/day14/demo1
                  System.out.println(req.getRequestURI());
                  System.out.println(req.getRequestURL());
                  //6. 获取协议及版本: String getProtocol()
                  System.out.println(req.getProtocol());
                  //7. 获取客户机的IP地址: String getRemoteAddr()
                  System.out.println(req.getRemoteAddr());
              }
          }
          
      2. 获取请求头数据

        Servlet
        String getHeader(String name):通过请求头的名称获取请求头的值
        常用的头:user-agent; referer

        @WebServlet("/rd3")
        public class RequestDemo3 extends HttpServlet {
            @Override
            protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
                String ua = req.getHeader("user-agent");
                if(ua.contains("Chrome")){
                    System.out.println("谷歌来了..");
                }else if(ua.contains("Firefox")){
                    System.out.println("火狐来了..");
                }
            }
        }
        

        Enumeration<String> getHeaderNames():获取所有的请求头名称

        @WebServlet("/rd2")
        public class RequestDemo2 extends HttpServlet {
            @Override
            protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
                //1.获取所有请求头名称
                Enumeration<String> headerNames = req.getHeaderNames();
                //2.遍历
                while(headerNames.hasMoreElements()){//Enumeration接口见jdk文档
                    //根据名称获取请求头的值
                    String name = headerNames.nextElement();
                    String value = req.getHeader(name);
                    System.out.println(name+"------"+value);
                }
            }
        }
        
      3. 获取请求参数

        1. 如果是post请求方式,可以使用流对象获取请求体。步骤:

          1. 获取流对象
            BufferedReader getReader() 获取字符输入流,只能获取文本流行
            ServletInputStream getInputStream() 获取字节输入流,可以操作所有类型数据,用于文件上传

          2. 再从流对象中拿数据

            @WebServlet("/rd5")
            public class RequestDemo5 extends HttpServlet {
                @Override
                protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
                    //获取请求体
                    //1.获取字符流
                    BufferedReader reader = req.getReader();
                    //2.读取数据
                    String line = null;
                    while ((line = reader.readLine())!=null){
                        System.out.println(line);
                    }
                }
            }
            
        2. 通用方式:不论post还是get都能用
          String getParameter(String name): 根据参数名称获取参数值
          String[] getParametersValues(String name): 根据参数名称获取参数值的数组,一般用于复选框
          Enumeration<String> getParameterNames(): 获取所有请求的参数名称
          Map<String,String[]> getParameterMap():获取所有参数名和值的map集合
          上面的name和value其实就是html标签体的name属性值和value属性值
          中文乱码问题:
          get方式:tomcat8已经解决中文乱码方式
          post方式:会乱码,在获取参数前加一个代码:req.setCharacterEncoding("utf-8")

请求转发

请求转发:一个Servlet接收请求然后发送给另一个Servlet处理

当一个Web资源收到客户端的请求后,如果希望服务器通知另外一个资源去处理请求,可以通过RequestDispatcher接口的实例对象来实现。在ServletRequest接口中定义了一个获取RequestDispatcher对象的方法。

Servlet

获取了RequestDispatcher对象后,可以通过两个相关的方法通知其他的Servlet处理请求。

Servlet
请求转发步骤

  1. 通过request对象获取请求转发器对象:RequestDispatcher getRequestDispatcher(String path);

  2. 使用RequestDispatcher对象来转发:forward(ServeletRequest req, ServletResponse resp);

    @WebServlet("/rd7")
    public class RequestDemo7 extends HttpServlet {
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            System.out.println("demo7被访问了。。。");
            //获取转发对象并转发
    //        RequestDispatcher requestDispatcher = req.getRequestDispatcher("/rd8");
    //        requestDispatcher.forward(req,resp);
            req.getRequestDispatcher("/rd8").forward(req,resp);
        }
    }
    

特点

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

请求包含

指的是使用include()方法将Servlet请求转发给其他Web资源进行处理,与请求转发不同的是,在请求包含返回的响应消息中,既包含了当前Servlet的响应消息,也包含了其他Web资源所作出的响应消息。

/**
 * 请求包含
 * 它是把两个Servlet的响应内容合并输出。
 * 注意:这种包含是动态包含。
 * 动态包含的特点:各编译各的,只是最后合并输出。
 */
public class RequestDemo8 extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        response.getWriter().write("I am request demo8 ");
        //1.拿到请求调度对象
        RequestDispatcher rd = request.getRequestDispatcher("/RequestDemo9");
        //2.实现包含的操作
        rd.include(request, response);
    }

    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doGet(request, response);
    }
}
/**
 * 被包含者
 */
public class RequestDemo9 extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        response.getWriter().write("include request demo 9 ");
    }

    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doGet(request, response);
    }

}

ServletResponse

功能:设置响应消息

  1. 设置响应行:
    设置状态码:setStatus(int sc) 。正常情况下,Web 服务器会默认产生一个状态码为200的状态行。
    错误信息状态码:setError(int sc)。用于发送表示错误信息的状态码,例如,404状态码表示找不到客户端请求的资源。
    sendError(int code, String message)。此重载方法除了发送状态码外,还可以增加一条用于提示说明的文本信息,该文本信息将出现在发送给客户端的正文内容中。

  2. 设置响应头:
    Servlet
    addHeader()、setHeader()、addInt Header()、setIntHeader()方法都是用于设置各种头字段的,而setContentType()、setLocale()和setCharacterEncoding()方法用于设置字符编码,这些设置字符编码的方法可以有效解决乱码问题。

    resp.setContentType("text/html");
    resp.getWriter().print("<h1>hello</h1>");//会解析标签
    
    resp.setContentType("text/plain");
    resp.getWriter().print("<h1>hello</h1>");//纯文本,不会解析标签
    
    //MIME类型:text/plain 纯文本、text/html HTML文档、text/xml
    //application/x-msdownload 需要下载的资源
    //image/jpeg image/gif ... 图片资源
    
  3. 设置响应体
    由于在HTTP响应消息中,大量的数据都是通过响应消息体传递的,因此,ServletResponse遵循以IO流传递大量数据的设计理念。在发送响应消息体时,定义了两个与输出流相关的方法

    1. 第一步获取输出流
      字符输出流:PrintWriter getWriter()
      字节输出流:ServletOutputStream getOutputStream()
    2. 第二步使用输出流,将数据输出到客户端浏览器
      注意:response获得的字符流和字节流只能二选一,不可同时使用,否则会发生IllegalStateException异常。response获得的流不用关闭,服务器会自己关闭。
      可以将想要向客户端输出的内容放在HTML标签中,浏览器会自动解析。

案例一:完成重定向

Servlet
代码实现

  1. 设置状态码302:resp.setStatus(302);
  2. 设置响应头location: resp.setHeader(“location”,”/respDemo2”);
  3. 两步合一步的写法:resp.sendRedirect(“/respDemo2”);

重定向的特点:redirect

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

转发的特点:forword

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

路径写法:

  1. 绝对路径:通过绝对路径可以确定唯一资源
    http://localhost/day15/responseDemo2 以/开头的路径
  2. 相对路径:通过相对路径不可以确定唯一资源,关键是找到当前资源和目标资源之间的相对位置关系
    ./index.html 不以/开头,以.开头路径
    ./代表当前目录,./可以省略 ../:后退一级目录

路径需不需要加虚拟目录

  1. 给客户端浏览器使用:需要加虚拟目录(项目的访问路径),比如 HTML 页面中的元素 <a> <form action="/day15/servlet1"> 里的路径
    建议虚拟目录动态获取:request.getContextPath(),最好不要写/day15/这种,防止以后改了虚拟目录
  2. 给服务器使用:不需要加虚拟目录,比如转发路径

案例二:服务器向客户端输出字符

@WebServlet("/respDemo1")
public class RespDemo1 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html;charset=utf-8");
        PrintWriter pw = resp.getWriter();
        pw.println("<h1>hello world</h1>");
    }

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

中文乱码问题:

  1. 看一下浏览器使用的字符集是什么,比如是GBK
  2. 字符输出流的默认编码是ISO-8859-1,如果和浏览器的字符集不一样就需要更改
  3. 在获取流的代码前加一句:resp.setCharSetEncoding(“GBK”);
  4. 但上面的方法不保险,不同的电脑有不一样的编码,可以告诉浏览器按照服务器使用的字符集来解析:resp.setContentType("text/html;charset=utf-8");
    这样就万无一失了。

案例三:网页定时刷新并跳转

在 HTTP 协议中,定义了一个Refresh头字段,它可以通知浏览器在指定的时间内自动刷新并跳转到其他页面。

@WebServlet("/refreshDemo")
public class RefreshDemo extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //2秒后刷新并跳转百度
        resp.setHeader("refresh","2;https://www.baidu.com");
    }
@WebServlet("/refreshDemo")
public class RefreshDemo extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //每3秒刷新一次本页面,常见于购物网站显示库存
        resp.setHeader("refresh","3");
    }

案例四:生成验证码

public class ResponseDemo3 extends HttpServlet {
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        int width = 200;
        int height = 35;
        /**
         * 实现步骤:
         * 	1.创建图像内存对象
         *  2.拿到画笔
         *  3.设置颜色,画矩形边框
         *  4.设置颜色,填充矩形
         *  5.设置颜色,画干扰线
         *  6.设置颜色,画验证码
         *  7.把内存图像输出到浏览器上
         */
        //创建内存图像
        BufferedImage image = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);//参数:宽度,高度 (指的都是像素),使用的格式(RGB)
        Graphics g = image.getGraphics();//画笔就一根

        //设置颜色
        g.setColor(Color.BLUE);
        //画边框
        g.drawRect(0, 0, width, height);

        //设置颜色
        g.setColor(Color.GRAY);
        //填充矩形
        g.fillRect(1, 1, width-2, height-2);

        //设置颜色
        g.setColor(Color.WHITE);
        //拿随机数对象
        Random r = new Random();
        //画干扰线 10条
        for(int i=0;i<10;i++){
            g.drawLine(r.nextInt(width), r.nextInt(height),r.nextInt(width), r.nextInt(height));
        }

        //设置颜色
        g.setColor(Color.RED);
        //改变字体大小
        Font font = new Font("宋体", Font.BOLD,30);//参数:1字体名称。2.字体样式 3.字体大小
        g.setFont(font);//设置字体
        //画验证码	4个
        int x = 35;//第一个数的横坐标是35像素
        for(int i=0;i<4;i++){
            //r.nextInt(10)+""这种写法效率是十分低的
            g.drawString(String.valueOf(r.nextInt(10)), x, 25);
            x+=35;
        }

        //输出到浏览器上
        //参数: 1.内存对象。2.输出的图片格式。3.使用的输出流
        ImageIO.write(image, "jpg", response.getOutputStream());
    }

    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doGet(request, response);
    }

}

案例五:设置缓存时间

/**
 * 设置缓存时间
 * 	使用缓存的一般都是静态资源
 *  动态资源一般不能缓存。
 *  我们现在目前只掌握了Servlet,所以用Servlet做演示
 * @author 黑马程序员
 * @Company http://www.itheima.com
 *
 */
public class ResponseDemo4 extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        String str = "设置缓存时间";
        /*
         * 设置缓存时间,其实就是设置响应消息头:Expires 但是值是一个毫秒数。
         * 使用的是
         * 	response.setDateHeader();
         *
         * 缓存1小时,是在当前时间的毫秒数上加上1小时之后的毫秒值
         */
        response.setDateHeader("Expires",System.currentTimeMillis()+1*60*60*1000);
        response.setContentType("text/html;charset=UTF-8");
        response.getOutputStream().write(str.getBytes());
    }

    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doGet(request, response);
    }

}

案例六:文件下载

public class ResponseDemo8 extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        /*
         * 文件下载的思路:
         * 		1.获取文件路径
         * 		2.把文件读到字节输入流中
         * 		3.告知浏览器,以下载的方式打开(告知浏览器下载文件的MIME类型)
         * 		4.使用响应对象的字节输出流输出到浏览器上
         */
        //1.获取文件路径(绝对路径)
        ServletContext context = this.getServletContext();
        String filePath = context.getRealPath("/uploads/6.jpg");//通过文件的虚拟路径,获取文件的绝对路径
        //2.通过文件路径构建一个字节输入流
        InputStream in  = new FileInputStream(filePath);
        //3.设置响应消息头
        response.setHeader("Content-Type", "application/octet-stream");//注意下载的时候,设置响应正文的MIME类型,用application/octet-stream
        response.setHeader("Content-Disposition", "attachment;filename=1.jpg");//告知浏览器以下载的方式打开
        //4.使用响应对象的字节输出流输出
        OutputStream out = response.getOutputStream();
        int len = 0;
        byte[] by = new byte[1024];
        while((len = in.read(by)) != -1){
            out.write(by, 0, len);
        }
        in.close();
    }

    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doGet(request, response);
    }

}

中文乱码问题

html页面设置:<meta charset="UTF-8">

get方式不用设置,tomcat8自动解决,post需要设置。request设置:req.setCharacterEncoding("UTF-8");

response设置:resp.setContentType("text/html;charset=UTF-8");

在jsp页面中也要设置:<%@page ContentType="text/html;charset=UTF-8"

web.xml常用配置

web.xml除了可以配置Servlet,还有下面的常用配置

  1. 修改web应用默认首页

    <welcome-file-list>
            <welcome-file>index.html</welcome-file>
            <welcome-file>index.htm</welcome-file>
            <welcome-file>index.jsp</welcome-file>
            <welcome-file>login.html</welcome-file>
            <welcome-file>login.htm</welcome-file>
            <welcome-file>login.jsp</welcome-file>
            <welcome-file>error.html</welcome-file>
    </welcome-file-list>
    <!-- 可以设置多个默认首页,第一个不能访问就找第二个-->
    
  2. Servlet通配符映射

    <servlet>
            <servlet-name>patternServlet</servlet-name>
            <servlet-class>com.example.servlet1.patternServlet</servlet-class>
    </servlet>
    <servlet-mapping>
            <servlet-name>patternServlet</servlet-name>
            <url-pattern>/pattern/*</url-pattern>
    </servlet-mapping>
    <!-- 通过上面的设置,只要在浏览器输入 虚拟目录/pattern/任意字符 就可以访问了。比如1号员工可以输入/pattern/1 来访问。-->
    
  3. 设置全局参数

    <!-- 所谓的全局参数其实就是 ServletContext 中保存的信息 -->
    <context-param>
            <param-name>copyright</param-name>
            <param-value>2004-2020 sogou.com 京ICP备11001839号-1</param-value>
    </context-param>
    <context-param>
            <param-name>title</param-name>
            <param-value>搜狗搜索</param-value>
    </context-param>
    
  4. 设置404、500状态码的默认页面
    tomcat默认的错误页面不友好,我们需要手动写几个错误页面,然后在web.xml中配置。这样当发生了错误时就会跳转到我们写好的错误页面。
    404错误页面

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
    </head>
    <body>
        404 NOT FOUND 请检查你的网址
    </body>
    </html>
    

    web.xml配置

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

项目正式发布

可以通过 idea 的 export 将项目导出成一个 war 包,然后将此 war 包复制到 tomcat 的 webapp 目录下。然后双击 startup.bat 启动 tomcat 服务器,该项目就会自动部署。

Servlet

上一篇:343.整数拆分


下一篇:03-变量