# Servlet入门

# 环境部署

常见java相关的web服务器软件有:

  • webLogic:oracle公司,大型的JavaEE服务器,支持所有的JavaEE规范,收费的。
  • webSphere:IBM公司,大型的JavaEE服务器,支持所有的JavaEE规范,收费的。
  • JBOSS:JBOSS公司的,大型的JavaEE服务器,支持所有的JavaEE规范,收费的。
  • Tomcat:Apache基金组织,中小型的JavaEE服务器,仅仅支持少量的JavaEE规范servlet/jsp。开源的,免费的。

这里使用Tomcat作为Java服务器。

# 基本操作

这里操作都是windows下的:

  1. 启动tomcat,运行bin目录下的startup.bat。
  2. 关闭tomcat,运行bin目录下的shutdown.bat,也可在命令行直接ctrl c。

# 目录结构

# java项目

java项目的目录结构: -- 项目的根目录
    -- WEB-INF目录(WEB-INF目录下的资源不能被浏览器直接访问):
        -- web.xml:web项目的核心配置文件
        -- classes目录:放置字节码文件的目录
        -- lib目录:放置依赖的jar包

# Tomcat目录结构

Tomcat的目录结构:

  1. bin:主要用来存放tomcat的命令。
  2. conf:主要用来存放tomcat的一些配置文件。
    1. server.xml可以设置端口号、设置域名或IP、默认加载的项目、请求编码
    2. web.xml可以设置tomcat支持的文件类型
    3. context.xml可以用来配置数据源之类的
    4. tomcat-users.xml用来配置管理tomcat的用户与权限
    5. 在Catalina目录下可以设置默认加载的项目
  3. lib:lib目录主要用来存放tomcat运行需要加载的jar包。
  4. logs:logs目录用来存放tomcat在运行过程中产生的日志文件。
    1. windows中是catalina.xxxx-xx-xx.log。
    2. linux中是catalina.out。
  5. temp:存放tomcat在运行过程中产生的临时文件。
  6. webapps:webapps目录用来存放应用程序,当tomcat启动时会去加载webapps目录下的应用程序。可以以文件夹、war包、jar包的形式发布应用。
  7. work:work目录用来存放tomcat在运行时的编译后文件,例如JSP编译后的文件。清空该目录然后重启tomcat,可达到清除缓存作用。

# 启动问题

  1. 启动中控制台乱码,找到conf下的logging.properties文件,将其中的encoding = UTF-8的部分全部修改为encoding = GBK。
  2. 运行时没有JAVA_HOME环境变量。
  3. 端口被占用。windows下需要找到对应端口并关闭:
    1. netstat -ano|findstr "端口号"
    2. tasklist|findstr "PID"
    3. 在windows任务管理器中关掉对应的进程

# 部署项目

Tomcat中部署项目有三种方式:

  1. 直接将项目放到webapps目录下即可。也可将项目打成一个war包,再将war包放置到webapps目录下,war包会自动解压缩,删除war包会删除该war包对应的项目。
  2. 配置conf/server.xml文件,在<Host>标签体中配置<Context docBase="D:\test" path="/testPath" />
    • docBase:项目存放的路径
    • path:虚拟目录
  3. 在conf\Catalina\localhost创建任意名称的xml文件,在该文件中写:<Context docBase="D:\test" />,此种方式配置的虚拟目录是xml文件的名称。此种方式是热部署的方式,改变xml能不用重启服务器即可生效。

# IntelliJ配置Tomcat

  1. Idea会为每一个Tomcat部署的项目单独建立一份配置文件,其所在位置在CATALINA_BASE环境变量中。
  2. Tomcat真正访问的是它自己部署的web项目,它对应着工作空间中web目录下的所有资源。

# Eclipse配置Tomcat

  1. 在Window->Preferences->Server->Runtime Environments配置。
  2. 配置好后在项目右键->Properties->JavaBuildPath->Add Library->Server Runtime->Apache Tomcat v9.0。
  3. 在Window->Show View->Servers->新增服务器。

注意:

  1. 如果需要在项目中添加web.xml,可以右击项目,找到Java EE Tools,然后Genertate Deployment Descriptor Stub。
  2. 在JavaEE项目中添加包依赖,需要添加到/WebContent/WebContent/WEB-INF/lib中,添加到其他目录,即使加到项目属性的JavaBuildPath->Libraries->Classpath中,在Tomcat中运行的时候也会出现NoClassDefFoundError,Tomcat中运行的程序寻找的依赖是JavaBuildPath->Libraries->Classpath->WebAppLibraries中的包,而不是Classpath中的包。

# 常见问题

  1. 使用Tomcat10遇到cannot be cast to class jakarta.servlet.Servlet。后来在Apache Tomcat Versions (opens new window)查了下Tomcat版本与servlet兼容性发现Tomcat版本差很多。对于Tomcat 10,官方是这样写的:

    Tomcat 10是服务于Jakarta EE9,使用Tomcat 10之前的人需要注意,作为JavaEE向JakartaEE的迁移结果,所有实现API的主要包都从javax.变为jakarta.,当从Tomcat 9 迁移到Tomcat 10时需要代码的改变。

  2. Tomcat启动会在控制台显示乱码问题。在Tomcat配置文件logging.properties中把java.util.logging.ConsoleHandler.encoding = UTF-8改成java.util.logging.ConsoleHandler.encoding = GBK即可。

# Servlet

它是运行在服务器端的小程序,可被看成是一个接口,定义了Java类被浏览器访问到(tomcat识别)的规则。注意它有两个重要实现:

  • GenericServlet:将Servlet接口中其他的方法做了默认空实现,只将service()方法作为抽象。
  • HttpServlet:对http协议的一种封装,简化操作。

我们后面使用它可自定义一个类,实现Servlet接口,复写方法,或者直接继承上面的实现类,下面是一个简单的示例,它有如下操作步骤:

  1. 定义一个servlet。
  2. 配置servlet。

MyServlet.java文件内容:

import java.io.IOException;

import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public class MyServlet implements Servlet{

    @Override
    public void destroy() {
        // TODO Auto-generated method stub
        System.out.println("destroy server");
        
    }

    @Override
    public ServletConfig getServletConfig() {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public String getServletInfo() {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public void init(ServletConfig arg0) throws ServletException {
        // TODO Auto-generated method stub
        System.out.println("init service");
        
    }

    @Override
    public void service(ServletRequest arg0, ServletResponse arg1) throws ServletException, IOException {
        // TODO Auto-generated method stub
        System.out.println("hello world!123456");
        
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44

web.xml文件内容:

<?xml version="1.0" encoding="UTF-8" ?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0">
    <display-name>JavaWebLearn</display-name>
    <welcome-file-list>
        <welcome-file>index.html</welcome-file>
        <welcome-file>index.htm</welcome-file>
        <welcome-file>index.jsp</welcome-file>
        <welcome-file>default.html</welcome-file>
        <welcome-file>default.htm</welcome-file>
        <welcome-file>default.jsp</welcome-file>
    </welcome-file-list>
    <servlet>
        <servlet-name>demo1</servlet-name>
        <servlet-class>MyServlet</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>demo1</servlet-name>
        <url-pattern>/testweb</url-pattern>
    </servlet-mapping>
</web-app>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 执行原理

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

# Servlet生命周期

Servlet中有几个方法比较重要:

  1. init:创建servlet时执行,只执行一次。
  2. service:每次访问servlet方法时执行一次。
  3. destroy:销毁servlet时执行,只执行一次。

注意:

  1. Servlet会在两种情况下被创建,创建时执行init方法,可在web.xml中的<servlet>标签中配置。
    1. 在servlet第一次被访问时创建。<load-on-startup>的值为负数。
    2. 在服务器启动时创建。<load-on-startup>的值为0或正整数。
  2. Servlet的init方法,只执行一次,说明一个Servlet在内存中只存在一个对象,Servlet是单例的。
    • 多个用户同时访问时,可能存在线程安全问题。
    • 尽量不要在Servlet中定义成员变量,即使定义了成员变量,尽量只定义只读成员变量。
  3. destroy是Servlet被销毁时执行,一般服务器关闭时,Servlet被销毁。
    • 只有服务器正常关闭时,才会执行destroy方法。
    • destroy方法在Servlet被销毁之前执行,一般用于释放资源。

# 注解

在Servlet 3.0时,可以不创建web.xml,使用WebServlet注解。它的定义如下:

public abstract @interface javax.servlet.annotation.WebServlet extends java.lang.annotation.Annotation {
  
  // Method descriptor #5 ()Ljava/lang/String;
  public abstract java.lang.String name() default "";
  
  // Method descriptor #9 ()[Ljava/lang/String;
  public abstract java.lang.String[] value() default {};
  
  // Method descriptor #9 ()[Ljava/lang/String;
  public abstract java.lang.String[] urlPatterns() default {};
  
  // Method descriptor #12 ()I
  public abstract int loadOnStartup() default (int) -1;
  
  // Method descriptor #15 ()[Ljavax/servlet/annotation/WebInitParam;
  public abstract javax.servlet.annotation.WebInitParam[] initParams() default {};
  
  // Method descriptor #17 ()Z
  public abstract boolean asyncSupported() default false;
  
  // Method descriptor #5 ()Ljava/lang/String;
  public abstract java.lang.String smallIcon() default "";
  
  // Method descriptor #5 ()Ljava/lang/String;
  public abstract java.lang.String largeIcon() default "";
  
  // Method descriptor #5 ()Ljava/lang/String;
  public abstract java.lang.String description() default "";
  
  // Method descriptor #5 ()Ljava/lang/String;
  public abstract java.lang.String displayName() default "";

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

注意:

  • 上面中的value代表urlPatterns,urlPatterns相当于web.xml中的<url-pattern>。loadOnStartup相当于web.xml中的<load-on-startup>。
  • 一个Servlet可以定义多个访问路径:@WebServlet({"/hehe","/hello/world","/*.do"})。

# Request对象

request和response对象是由服务器创建的。request对象是来获取请求消息,response对象是来设置响应消息。

# Request继承结构

request对象继承体系结构:

  1. ServletRequest(接口)
  2. HttpServletRequest(接口)
  3. org.apache.catalina.connector.RequestFacade(实现类)

# Request基本功能

  1. 获取请求行数据

    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("/requestDemo1")
    public class RequestDemo1 extends HttpServlet {
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    
        }
    
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    
            //1. 获取请求方式 :GET
            String method = request.getMethod();
            System.out.println(method);
            //2.(*)获取虚拟目录:
            String contextPath = request.getContextPath();
            System.out.println(contextPath);
            //3. 获取Servlet路径: 
            String servletPath = request.getServletPath();
            System.out.println(servletPath);
            //4. 获取get方式请求参数:
            String queryString = request.getQueryString();
            System.out.println(queryString);
            //5.(*)获取请求URI:
            String requestURI = request.getRequestURI();
            StringBuffer requestURL = request.getRequestURL();
            System.out.println(requestURI);
            System.out.println(requestURL);
            //6. 获取协议及版本:
            String protocol = request.getProtocol();
            System.out.println(protocol);
            //7. 获取客户机的IP地址:
            String remoteAddr = request.getRemoteAddr();
            System.out.println(remoteAddr);
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
  2. 获取请求头数据

    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("/requestDemo2")
    public class RequestDemo2 extends HttpServlet {
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    
        }
    
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            //演示获取请求头数据
    
            //获取所有请求头名称
            Enumeration<String> headerNames = request.getHeaderNames();
            //遍历输出这些头信息
            while(headerNames.hasMoreElements()){
                String name = headerNames.nextElement();
                //根据名称获取请求头的值
                String value = request.getHeader(name);
                System.out.println(name+"---"+value);
            }
    
            //获取请求头数据:user-agent
            String agent = request.getHeader("user-agent");
            //判断agent的浏览器版本
            if(agent.contains("Chrome")){
                //谷歌
                System.out.println("谷歌来了...");
            }else if(agent.contains("Firefox")){
                //火狐
                System.out.println("火狐来了...");
            }
    
            //获取请求头数据:referer
            String referer = request.getHeader("referer");
            System.out.println(referer);//http://localhost/day14/login.html
    
            //防盗链
            if(referer != null ){
                if(referer.contains("/day14")){
                    //正常访问
                   // System.out.println("播放电影....");
                    response.setContentType("text/html;charset=utf-8");
                    response.getWriter().write("播放电影....");
                }else{
                    //盗链
                    //System.out.println("想看电影吗?来优酷吧...");
                    response.setContentType("text/html;charset=utf-8");
                    response.getWriter().write("想看电影吗?来优酷吧...");
                }
            }
    
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
  3. 获取请求体数据,注意只有POST请求方式才有请求体,在请求体中封装了POST请求的请求参数

    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;
    
    @WebServlet("/requestDemo3")
    public class RequestDemo3 extends HttpServlet {
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            //获取请求消息体--请求参数
    
            //1.获取流对象
                //BufferedReader getReader():获取字符输入流,只能操作字符数据
                //ServletInputStream getInputStream():获取字节输入流,可以操作所有类型数据
            //2.再从流对象中拿数据
    
            BufferedReader br = request.getReader();
            String line = null;
            while((line = br.readLine()) != null){
                System.out.println(line);
            }
    
            //post 获取请求参数
    
            //根据参数名称获取参数值
            String username = request.getParameter("username");
    
           //根据参数名称获取参数值的数组
            String[] hobbies = request.getParameterValues("hobby");
            for (String hobby : hobbies) {
                System.out.println(hobby);
            }
    
            //获取所有请求的参数名称
    
            Enumeration<String> parameterNames = request.getParameterNames();
            while(parameterNames.hasMoreElements()){
                String name = parameterNames.nextElement();
                System.out.println(name);
                String value = request.getParameter(name);
                System.out.println(value);
                System.out.println("----------------");
            }
    
            // 获取所有参数的map集合
            Map<String, String[]> parameterMap = request.getParameterMap();
            Set<String> keyset = parameterMap.keySet();
            for (String name : keyset) {
                //获取键获取值
                String[] values = parameterMap.get(name);
                System.out.println(name);
                for (String value : values) {
                    System.out.println(value);
                }
    
                System.out.println("-----------------");
            }
    
            //获取中文输入参数
            //get方式:tomcat 8 已经将get方式乱码问题解决了
            //post方式:会乱码,解决办法是在获取参数前,设置request的编码
            //1.设置流的编码
            request.setCharacterEncoding("utf-8");
            //获取请求参数
            String username = request.getParameter("username");
            System.out.println(username);
    
        }
    
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74

# Request扩展功能

  1. 请求转发:一种在服务器内部的资源跳转方式

    1. 步骤:
      1. 通过request对象获取请求转发器对象:RequestDispatcher getRequestDispatcher(String path)。
      2. 使用RequestDispatcher对象来进行转发:forward(ServletRequest request, ServletResponse response)。
    2. 特点:
      1. 浏览器地址栏路径不发生变化。
      2. 只能转发到当前服务器内部资源中。
      3. 转发是一次请求。
  2. 共享数据时就需要使用域对象,它是一个有作用范围的对象,可以在范围内共享数据

    • request:代表一次请求的范围,一般用于请求转发的多个资源中共享数据。方法:
      • void setAttribute(String name,Object obj):存储数据
      • Object getAttitude(String name):通过键获取值
      • void removeAttribute(String name):通过键移除键值对
    • ServletContext:服务器会为每一个工程创建一个对象,这个对象就是ServletContext对象。这个对象全局唯一,而且工程内部的所有servlet都共享这个对象,所以叫全局应用程序共享对象。
      • 获取方式:通过request或者在doPost和doGet内部直接调用getServletContext()。
  3. 示例:

    请求转发页面:

    
    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("/requestDemo4")
    public class RequestDemo4 extends HttpServlet {
       protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
           System.out.println("demo8888被访问了。。。");
           //转发到demo9资源
           /*
           RequestDispatcher requestDispatcher = request.getRequestDispatcher("/requestDemo9");
           requestDispatcher.forward(request,response);
           */
    
           //存储数据到request域中
           request.setAttribute("msg","hello");
    
           request.getRequestDispatcher("/requestDemo9").forward(request,response);
           //request.getRequestDispatcher("http://www.itcast.cn").forward(request,response);
    
       }
    
       protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    
           this.doPost(request,response);
       }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32

    被转发的页面:

    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("/requestDemo5")
    public class RequestDemo9 extends HttpServlet {
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    
            //获取数据
            Object msg = request.getAttribute("msg");
            System.out.println(msg);
    
            System.out.println("被访问了。。。");
            ServletContext servletContext = request.getServletContext();
        }
    
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            this.doPost(request,response);
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23

# Response对象

设置响应消息

  1. 设置响应行
    1. 格式:HTTP/1.1 200 ok
    2. 设置状态码:setStatus(int sc)
  2. 设置响应头:setHeader(String name, String value)
  3. 设置响应体,使用步骤:
    1. 获取输出流
      • 字符输出流:PrintWriter getWriter()
      • 字节输出流:ServletOutputStream getOutputStream()
    2. 使用输出流,将数据输出到客户端浏览器

下面是几个案例。

# 重定向

重定向(Redirect)就是通过各种方法将各种网络请求重新定个方向转到其它位置(如:网页重定向、域名的重定向、路由选择的变化也是对数据报文经由路径的一种重定向)。

重定向和转发的区别是:

  • 重定向的特点:redirect
    1. 地址栏发生变化
    2. 重定向可以访问其他站点(服务器)的资源
    3. 重定向是两次请求。不能使用request对象来共享数据
  • 转发的特点:forward
    1. 转发地址栏路径不变
    2. 转发只能访问当前服务器下的资源
    3. 转发是一次请求,可以使用request对象来共享数据

如果只使用html进行重定向:

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <!-- 方法一:-->
        <!-- <meta http-equiv="refresh" content="2;url=http://www.baidu.com"> -->
        <title>Title</title>
        <script type="text/javascript">
            window.onload = function() {
                var i = 2;
                var tim = document.getElementById("timers");
                var timer = setInterval(function() {
                    if (i == 0) {
                        //方法二:
                        window.location.href="http://www.baidu.com";
                        console.log("清除定时!");
                        clearInterval(timer);
                    } else {
                        tim.innerHTML = i;
                        --i;
                    }
                }, 1000);

            }
        </script>
    </head>
    <body>
        <p>您将在 <span class="timeShow" id="timers">3</span> 秒内被重定向到新的地址。</p>

        <p>如果超过 3 秒后您仍未跳转,请点击<a href="http://www.baidu.com">百度首页</a></p>
    </body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

如果使用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("/responseDemo1")
public class ResponseDemo1 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        System.out.println("demo1........");



        //访问/responseDemo1,会自动跳转到/responseDemo2资源
       /* //1. 设置状态码为302
        response.setStatus(302);
        //2.设置响应头location
        response.setHeader("location","/day15/responseDemo2");*/

        request.setAttribute("msg","response");

        //动态获取虚拟目录
        String contextPath = request.getContextPath();

        //简单的重定向方法
        response.sendRedirect(contextPath+"/responseDemo2");
        //response.sendRedirect("http://www.baidu.com");

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request,response);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

# 服务器返回数据

  1. 服务器输出字符数据到浏览器,如果输出中文字符乱码,说明服务器的编码和浏览器的解码不一致,此时需要:

    1. 设置该流的默认编码
    2. 告诉浏览器响应体使用的编码
    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;
    
    @WebServlet("/responseDemo2")
    public class ResponseDemo2 extends HttpServlet {
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    
            //获取流对象之前,设置流的默认编码:ISO-8859-1 设置为:GBK
           // response.setCharacterEncoding("utf-8");
    
            //告诉浏览器,服务器发送的消息体数据的编码。建议浏览器使用该编码解码
            //response.setHeader("content-type","text/html;charset=utf-8");
    
            //简单的形式,设置编码
            response.setContentType("text/html;charset=utf-8");
    
            //1.获取字符输出流
            PrintWriter pw = response.getWriter();
            //2.输出数据
            //pw.write("<h1>hello response</h1>");
            pw.write("你好啊啊啊 response");
        }
    
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            this.doPost(request,response);
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
  2. 服务器输出字节数据到浏览器:

    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;
    
    @WebServlet("/responseDemo3")
    public class ResponseDemo3 extends HttpServlet {
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            response.setContentType("text/html;charset=utf-8");
    
            //1.获取字节输出流
            ServletOutputStream sos = response.getOutputStream();
            //2.输出数据
            sos.write("你好".getBytes("utf-8"));
        }
    
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            this.doPost(request,response);
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23

# 下载文件示例

超链接指向的资源如果能够被浏览器解析,则在浏览器中展示,如果不能解析,则弹出下载提示框。我们希望任何资源都必须弹出下载提示框,此时需要使用响应头设置资源的打开方式:

content-disposition:attachment;filename=xxx

下面是一个下载文件的示例:

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("/downloadServlet")
public class DownloadServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1.获取请求参数,文件名称
        String filename = request.getParameter("filename");
        //2.使用字节输入流加载文件进内存
        //2.1找到文件服务器路径
        ServletContext servletContext = this.getServletContext();
        String realPath = servletContext.getRealPath("/img/" + filename);
        //2.2用字节流关联
        FileInputStream fis = new FileInputStream(realPath);

        //3.设置response的响应头
        //3.1设置响应头类型:content-type
        String mimeType = servletContext.getMimeType(filename);//获取文件的mime类型
        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();


    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request,response);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52

其中DownloadUtils.java为:

import sun.misc.BASE64Encoder;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;


public class DownLoadUtils {

    //1. 获取客户端使用的浏览器版本信息
    //2. 根据不同的版本信息,设置filename的编码方式不同
    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;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

# ServletContext对象

该对象代表整个web应用,可用来:

  1. 获取MIME类型,MIME类型是在互联网通信过程中定义的一种文件数据类型,它的格式:大类型/小类型,比如text/html image/jpeg。要查看Tomcat的MINE类型,可在conf/web.xml查看。
  2. 可用来共享数据,服务器会为每一个工程创建一个对象,这个对象就是ServletContext对象,该对象全局唯一,而且工程内部的所有servlet都共享这个对象。
  3. 获取文件的真实(服务器)路径。

示例如下:

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("/servletContextDemo1")
public class ServletContextDemo1 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //获取
        ServletContext context = this.getServletContext();
        //ServletContext context1 = request.getServletContext();
        //基本使用
        String filename = "a.jpg";//image/jpeg
        String mimeType = context.getMimeType(filename);
        System.out.println(mimeType);
        //设置数据
        context.setAttribute("info","hello");
        // 获取文件的服务器路径
        String b = context.getRealPath("/b.txt");//web目录下资源访问
        System.out.println(b);
        String c = context.getRealPath("/WEB-INF/c.txt");//WEB-INF目录下的资源访问
        System.out.println(c);
        String a = context.getRealPath("/WEB-INF/classes/a.txt");//src目录下的资源访问
        System.out.println(a);

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request,response);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

# 域对象

Servlet三大域对象的应用 request、session、application(ServletContext)。

  • request,一个用户可有多个;
  • session,一个用户一个;
  • servletContext,所有用户共用一个。ServletContext是一个全局的储存信息的空间,服务器开始就存在,服务器关闭才释放,为了节省空间,提高效率,ServletContext中要放必须的、重要的、所有用户需要共享的一些信息。