zoukankan      html  css  js  c++  java
  • Servlet

    Servlet

    Servlet 初体验

    1. 新建一个类,实现 Servlet 接口
    2. 重写 Servlet 的抽象方法,其中有一个 service 方法,用于处理浏览器请求和访问
      void service(ServletRequest servletRequest, ServletResponse servletResponse);
    3. web.xml 文件中配置 Servlet 程序一个访问地址
    4. 通过浏览器访问 Servlet 程序
    public class ServletTest implements Servlet {
    
        public ServletTest() {
            System.out.println("1. 执行构造器方法");
        }
    
        @Override
        public void init(ServletConfig servletConfig) throws ServletException {
            System.out.println("2. 执行 init 初始化方法");
        }
    
        @Override
        public ServletConfig getServletConfig() {
            return null;
        }
    
        @Override
        public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
            System.out.println("3. 执行 service 方法");
            System.out.println("Hello Servlet!");
        }
    
        @Override
        public String getServletInfo() {
            return null;
        }
    
        @Override
        public void destroy() {
            System.out.println("4. 停止 Tomcat 运行实例时调用");
        }
    }
    
    <!-- servlet 标签用来给 Tomcat 配置 Servlet 程序 -->
    <servlet>
        <!-- servlet-name 标签用于给 Servlet 程序起一个别名 -->
        <servlet-name>ServletTest</servlet-name>
        <!-- servlet-class 标签用于指定 Servlet 程序的全类名 -->
        <servlet-class>work.jkfx.ServletTest</servlet-class>
    </servlet>
    <!-- servlet-mapping 给 Servlet 程序配置访问路径 -->
    <servlet-mapping>
        <!-- servlet-name 标签用于指定给哪个 Servlet 程序配置访问地址 -->
        <servlet-name>ServletTest</servlet-name>
        <!-- url-pattern 标签指定 Servlet 的访问路径
            http://ip:port/web工程路径/hello -->
        <url-pattern>/hello</url-pattern>
    </servlet-mapping>
    

    JG5ri6.png

    Servlet 生命周期

    1. 执行 Servlet 构造器
    2. 执行 init 初始化方法
      前两步是在第一次访问的时候创建 Servlet 程序会调用
    3. 执行 service 方法
      每次访问的时候都会调用
    4. 执行 destroy 销毁方法
      停止 Tomcat 运行实例时调用

    GET 和 POST 请求分发

    当通过表单访问 Servlet 程序时,可以通过 service 方法中的 servletRequest 参数得到通过哪种方法提交的表单

    <!-- 俩个以不同方法提交表单按钮 访问同一个 Servlet 程序 -->
    <form action="/08_Servlet/hello" method="get">
        <input type="submit" value="GET 提交">
    </form>
    <form action="/08_Servlet/hello" method="post">
        <input type="submit" value="POST 提交">
    </form>
    
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        /*
        |---ServletRequest 接口
            |---HttpServletRequest 接口
                |---HttpServletRequestWrapper **实现类**
            |---ServletRequestWrapper 实现类
                |---HttpServletRequestWrapper 实现类
         */
        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
        /*
        通过 getMethod 可以获得表单提交时的方法
        String getMethod();
         */
        String method = httpServletRequest.getMethod();
        if("GET".equals(method)) {
            System.out.println("GET 请求");
            System.out.println("此处省略一万行处理 GET 请求的代码...");
        } else if("POST".equals(method)) {
            System.out.println("POST 请求");
            System.out.println("此处省略一万行处理 POST 请求的代码...");
        }
    }
    

    由于将处理 GET 和 POST 请求的代码都写在一个方法中,显得过于臃肿和不方便维护,所以一般将需要处理 GET 请求或 POST 请求的代码写到另一个方法中,在 service 方法中直接调用即可

    public void doGet() {
        System.out.println("GET 请求");
        System.out.println("此处省略一万行处理 GET 请求的代码...");
    }
    public void doPost() {
        System.out.println("POST 请求");
        System.out.println("此处省略一万行处理 POST 请求的代码...");
    }
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        String method = httpServletRequest.getMethod();
        if("GET".equals(method)) {
            doGet();
        } else if("POST".equals(method)) {
            doPost();
        }
    }
    

    继承 HttpServlet 实现 Servlet 程序

    一般在实际项目开发中,都是使用继承 HttpServlet 类的方式去实现 Servlet 程序。

    1. 编写一个类去继承 HttpServlet 类
    2. 根据业务需要重写 doGet 或 doPost 方法
    3. web.xml 中的配置 Servlet 程序的访问地址
    /*
    只需要重写 doGet 和 doPost 方法即可实现 GET 和 POST 分发处理
    在表单提交时访问此 Servlet 程序时会自动根据方法调用不同的方法
    */
    public class HttpServletTest extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            System.out.println("GET 请求");
            System.out.println("此处省略一万行处理 GET 请求的代码...");
        }
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            System.out.println("POST 请求");
            System.out.println("此处省略一万行处理 POST 请求的代码...");
        }
    }
    

    Servlet 类的继承体系

    |--- Interface Servlet              规范
        |--- Class GenericServlet       空实现
            |--- Class HttpServlet      请求分发
                |--- 自定义类            根据业务逻辑重写方法
    

    JGxO3j.png

    ServletConfig 类

    ServletConfig 类从类名上来看,就知道是 Servlet 程序的配置信息类。
    Servlet 程序和 ServletConfig 对象都是由 Tomcat 负责创建,我们负责使用。
    Servlet 程序默认是第一次访问的时候创建,ServletConfig 是每个 Servlet 程序创建时,就创建一个对应的 ServletConfig 对象。

    ServletConfig 类的三大作用

    1. 可以获取 Servlet 程序的别名 servlet-name 的值
    2. 获取初始化参数 init-param
    3. 获取 ServletContext 对象
    public void init(ServletConfig servletConfig) throws ServletException {
    //        1. 可以获取 Servlet 程序的别名 servlet-name 的值
        System.out.println(servletConfig.getServletName());
    //        2. 获取初始化参数 init-param
        String username = servletConfig.getInitParameter("username");
        String url = servletConfig.getInitParameter("url");
        System.out.println("username = " + username + ", url = " + url);
    //        3. 获取 ServletContext 对象
        ServletContext servletContext = servletConfig.getServletContext();
        System.out.println(servletConfig);
    }
    
    <!-- 在 servlet 标签下创建 init-param 标签
        可用来被 ServletConfig 对象获取得到配置信息 -->
    <init-param>
        <!-- 在 ServletConfig 对象中通过 param-name 值得到对应的 param-value 的值 -->
        <param-name>username</param-name>
        <param-value>root</param-value>
    </init-param>
    <!-- 一个 Servlet 标签可以有多个 init-param 标签 -->
    <init-param>
        <param-name>url</param-name>
        <param-value>jdbc:mysql://localhost:3306/test</param-value>
    </init-param>
    


    正常情况下,在除了在 init 方法中可以使用 ServletConfig 对象,在其它方法中可调用 getServletConfig 方法得到 ServletConfig对象。
    但是,如果是自己实现的 Servlet 接口或者继承的 HttpServlet 类重写了 init 方法,在调用 getServletConfig 方法时将得到 null
    因为在自己实现的 Servlet 类中并没有将 init 方法中的 servletConfig 参数赋值给成员变量供其它方法使用,然而在 GenericServlet 有一个 ServletConfig 的成员变量,在 GenericServlet 类的 init 方法中有将 ServletConfig 对象赋值给成员变量,若重写了 inti 方法便没有对成员变量进行赋值
    所以需要在继承 HttpServlet 类中重写 init 的方法中调用下父类 init 方法将 ServletConfig 对象让父类 init 方法赋值给成员变量以供其他方法使用
    super.init(config);

    ServletContext 类

    1. ServletContext 是一个接口,它表示 Servlet 上下文对象
    2. 一个 web 工程,只有一个 ServletContext 对象实例
    3. ServletContext 对象是一个域对象
    4. ServletContext 是在 web 工程部署启动的时候创建,在 web 工程停止的时候销毁

    域对象

    可以像 Map 一样存取数据的对象,叫域对象。这里的域指的是存取数据的操作范围,整个 web 工程。

    ServletContext 类的四个作用

    1. 获取 web.xml 中配置的上下文参数 context-param
    2. 获取当前的工程路径,格式: /工程路径
    3. 获取工程部署后在服务器硬盘上的绝对路径
    4. 像 Map 一样存取数据
    // 通过 ServletConfig 对象获得 ServletContext 对象
    ServletContext servletContext = getServletConfig().getServletContext();
    //        1、获取 web.xml 中配置的上下文参数 context-param
    String context_value1 = servletContext.getInitParameter("context_key1");
    System.out.println("context-param: context_key1 = " + context_value1);
    System.out.println("context-param: context_key2 = " + servletContext.getInitParameter("context_key2"));
    //        2、获取当前的工程路径,格式: /工程路径
    String contextPath = servletContext.getContextPath();
    System.out.println(contextPath);
    //        3、获取工程部署后在服务器硬盘上的绝对路径
    String realPath = servletContext.getRealPath("/");
    System.out.println(realPath);
    //        4、像 Map 一样存取数据
    servletContext.setAttribute("attribute_key1", "attribute_value1");
    servletContext.setAttribute("attribute_key2", "attribute_value2");
    
    <!-- 根标签下配置上下文参数
        可以被 ServletContext 对象访问得到 -->
    <context-param>
        <!-- key-value 键值对 -->
        <param-name>context_key1</param-name>
        <param-value>context_value1</param-value>
    </context-param>
    <!-- 可以配置多个 context-param 标签参数 -->
    <context-param>
        <param-name>context_key2</param-name>
        <param-value>context_value2</param-value>
    </context-param>
    
    // 可以直接通过 getServletContext 获取得到 ServletContext 对象
    ServletContext servletContext = getServletContext();
    // ServletContext 的生命周期是整个 web 工程的生命周期
    // 一个 web 工程对应一个 ServletContext 对象
    // 可以通过 SerletContext 对象获得其他 Servlet 程序 setAttribute 的值
    Object attribute_value1 = servletContext.getAttribute("attribute_key1");
    System.out.println("ServletContext: attribute_key1 = " + attribute_value1);
    System.out.println("ServletContext: attribute_key2 = " + servletContext.getAttribute("attribute_key2"));
    // 如果在 setAttribute 的 Servlet 程序之前执行 getAttribute 将得到 null
    

    HTTP 协议

    协议是指双方,或多方,相互约定好,大家都需要遵守的规则,叫协议。
    HTTP 协议,就是指,客户端和服务器之间通信时,发送的数据,需要遵守的规则,叫 HTTP 协议。
    HTTP 协议中的数据又叫报文。

    请求的 HTTP 协议格式

    客户端给服务器发送数据叫请求。
    服务器给客户端回传数据叫响应。
    请求又分为 GET 请求,和 POST 请求两种。

    GET 请求

    1. 请求行
      • 请求的方法 GET
      • 请求的资源路径 [?+请求参数]
      • 请求的协议版本 HTTP/1.1
    2. 请求头
      • key:value 键值对组成

    JtKY5D.png

    POST 请求

    1. 请求行
      • 请求的方法 GET
      • 请求的资源路径 [?+请求参数]
      • 请求的协议版本 HTTP/1.1
    2. 请求头
      • key:value 键值对组成
      • 空行
    3. 请求体:发送给服务器的数据

    Jt1o6J.png

    常用的请求头

    • Accept: 表示客户端可以接收的数据类型
    • Accept-Language: 表示客户端可以接收的语言类型
    • User-Agent: 表示客户端浏览器的信息
    • Host: 表示请求时的服务器 ip 和端口号

    JtsNvR.png

    JtsBVK.png

    JtsyPe.png

    Jts68H.png

    区分 GET 请求和 POST 请求

    GET 请求

    1. form 标签 method=get
    2. a 标签
    3. link 标签引入 css
    4. Script 标签引入 js 文件
    5. img 标签引入图片
    6. iframe 引入 html 页面
    7. 在浏览器地址栏中输入地址后敲回车

    POST 请求

    1. form 标签 method=post

    响应的HTTP 协议格式

    1. 响应行
      • 响应的协议和版本号
      • 响应状态码
      • 状态码描述符
    2. 响应头
      • key:value 键值对
      • 空行
    3. 响应体:回传给客户端的数据

    JtGtm9.png

    常用的响应码说明

    • 200:表示请求成功
    • 302:表示请求重定向
    • 404:表示请求服务器已经收到了,但是你要的数据不存在(请求地址错误)
    • 500:表示服务器已经收到请求,但是服务器内部错误(代码错误)

    MIME 类型说明

    MIME 是 HTTP 协议中数据类型。
    MIME 的英文全称是"Multipurpose Internet Mail Extensions" 多功能 Internet 邮件扩充服务。MIME 类型的格式是“大类型/小类型”,并与某一种文件的扩展名相对应。

    JtJxUI.png

    JtYC28.png

    HttpServletRequest 类

    每次只要有请求进入 Tomcat 服务器,Tomcat 服务器就会把请求过来的 HTTP 协议信息解析好封装到 Request 对象中。然后传递到 service 方法(doGet 和 doPost)中给我们使用。我们可以通过 HttpServletRequest 对象,获取到所有请求的信息。

    常用方法

    // String getRequestURI();  获取请求的资源路径
    System.out.println(req.getRequestURI());
    // StringBuffer getRequestURL();    获取请求的统一资源定位符
    System.out.println(req.getRequestURL());
    // String getRemoteUser();  获取客户端的 ip 地址
    System.out.println(req.getRemoteHost());
    // String getHeader(String name);   获取指定请求头信息
    System.out.println(req.getHeader("User-Agent"));
    // String getMethod();  获取请求方法
    System.out.println(req.getMethod());
    

    获取请求参数

    <form action="http://localhost:8080/08_Servlet/parameterTest">
        用户名:<input type="text" name="username"> <br>
        密码:<input type="password" name="password"> <br>
        兴趣爱好:<input type="checkbox" name="hobby" value="cpp">C++
        <input type="checkbox" name="hobby" value="java">Java
        <input type="checkbox" name="hobby" value="js">JavaScript <br>
        <input type="submit">
    </form>
    
    // String getParameter(String name);    获取得到表单项中的指定 name 的值
    String username = req.getParameter("username");
    String password = req.getParameter("password");
    // String[] getParameterValues(String name);    根据指定 name 属性得到值
    String[] hobbies = req.getParameterValues("hobby");
    
    System.out.println("username =>> " + username);
    System.out.println("password =>> " + password);
    System.out.println("hobbies =>> " + Arrays.asList(hobbies));
    

    解决在 POST 请求中得到的信息乱码

    // 设置请求体的字符集格式为 UTF-8
    // 此方法要在 getParameter 方法调用之前设置才有效
    req.setCharacterEncoding("UTF-8");
    

    请求转发

    1. 浏览器地址栏没有变化
    2. 一次请求
    3. 共享 Request 域中的数据
    4. 可以转发到 WEB-INF 目录下
    5. 不可以访问 web 工程外的资源

    JNXujA.png

    // Servlet1 和 Servlet2 共同完成某项业务
    // 先获取到请求参数
    String username = req.getParameter("username");
    // 检验请求参数 是否需要转发
    if("锋哥好帅".equals(username)) {
        req.setAttribute("servlet1_key", "servlet1_value");
        // RequestDispatcher getRequestDispatcher(String path); 设置转发的路径 只能是 web 工程下的资源路径
        RequestDispatcher requestDispatcher = req.getRequestDispatcher("/servlet2");
        // 转发到对应的资源路径
        requestDispatcher.forward(req, resp);
    
    // 检查从是否有 Servlet1 程序设置的数据
    Object servlet1_key = req.getAttribute("servlet1_key");
    if("servlet1_value".equals(servlet1_key)) {
        System.out.println("完成 Servlet2 程序的功能");
        System.out.println("此处省略一万行代码...");
    }
    

    base 标签的作用

    假设在 web 工程目录下有一个页面 /a/b/c.html ,在首页有一个 a 标签 /a/b/c.html 用于跳转到 c.html 页面,在 c.html 页面下有一个 a 标签 ../../index.html 用于跳转回首页。

    index.html 页面
    <body>
        <!-- 使用 a 标签跳转 -->
        <a href="/08_Servlet/a/b/c.html">/a/b/c.html</a>
        <!-- 通过 Servlet 程序跳转到 /a/b/c.html -->
        <a href="/08_Servlet/baseTest">baseServlet</a>
    </body>
    
    a/b/c.html
    <body>
        <!-- 所有相对路径的参照路径都是以浏览地址 -->
        <!-- 一般 base 标签写到 web 工程的路径 -->
        <a href="../../index.html">../../index.html</a>
        <!--若正常使用 a 标签跳转到 c.html 页面,通过此 a 标签可正常跳回 index.html 页面-->
    </body>
    

    若使用 Servlet 程序请求转发到 c.html 页面,此时浏览器地址栏的地址还是原来转发之前的地址,直接使用相对地址跳转,则将跳不到正确的页面。
    因为在页面中使用的相对路径,参照的地址是当时浏览器中的地址。
    解决方案是在 c.html 页面的 head 标签中添加一个 base 标签,此标签的作用是设置当前页面相对路径的参照地址。

    <head>
        <!-- 使用 base 标签设置当前页面参考的相对路径 -->
        <base href="http://localhost:8080/08_Servlet/a/b/">
    </head>
    <body>
        <!-- 所有相对路径的参照路径都是以浏览地址 -->
        <a href="../../index.html">../../index.html</a>
    </body>
    

    此时,通过 Servlet 程序转发到此页面,就解决了相对路径的问题。

    JavaWeb 中 / 的不同意义

    在 JavaWeb 中 / 是一种绝对路径。

    / 被浏览器解析,得到的地址是:http://ip:port/
    <a href="/">斜杠</a>

    / 被服务器解析,得到的地址是:http:ip:port/工程路径/

    1. /servlet
    2. servletContext.getRealPath("/");
    3. request.getRequestDispatcher("/");

    特殊情况:
    response.sendRedirect("/");将 / 发送给浏览器解析,得到的是:http://ip:port/

    HttpResponse 类

    HttpServletResponse 类和 HttpServletRequest 类一样。每次请求进来,Tomcat 服务器都会创建一个 Response 对象传递给 Servlet 程序去使用。HttpServletRequest 表示请求过来的信息,HttpServletResponse 表示所有响应的信息,如果需要设置返回给客户端的信息,都可以通过 HttpServletResponse 对象来进行设置

    两个输出流

    • PrintWriter getWriter();
      字符流,常用于回传字符串(常用)
    • ServletOutputStream getOutputStream()
      字节流,常用于下载(传递二进制数据)

    两个输出流只能使用一个,不可以同时使用。

    解决字符流回传中文乱码

    方式一

    // 设置服务器字符集
    resp.setCharacterEncoding("UTF-8");
    // 设置了服务器字符集回传数据还会出现乱码
    // 因为浏览器使用的编码格式是系统默认(一般为 gbk)还会出现编码格式不统一的问题
    // 还需要设置响应头 告诉浏览器回传数据格式为 UTF-8
    resp.setHeader("Content-Type", "text/html;charset=UTF-8");
    PrintWriter writer = resp.getWriter();
    // 默认情况下,直接回传中文字符将会在浏览器出现乱码
    writer.write("这是从 Servlet 程序传过来的字符");
    

    方式二

    // 此方法同时设置服务器和客户端都使用 UTF-8 字符集,还设置了响应头
    // 但是此方法只能在获取输出流之前设置
    // 如果获得了输出流,在调用此方法就不会有效果了
    resp.setContentType("text/html;charset=UTF-8");
    

    请求重定向

    请求重定向,是指客户端给服务器发请求,然后服务器告诉客户端说。我给你一些地址。你去新地址访问。叫请求重定向(因为之前的地址可能已经被废弃)。

    特点

    1. 浏览器地址栏发生变化
    2. 两次请求
    3. 不共享 Request 域中的数据
    4. 不能访问 WEB-INF 目录下的资源
    5. 可以重定向到 web 工程以外的资源

    JUAJeS.png

    方式一

    // 设置响应码为 302 表示重定向
    resp.setStatus(302);
    // 设置响应头 说明新地址 浏览器收到后再次对新地址发起请求
    resp.setHeader("Location", "http://localhost:8080/08_Servlet/response2");
    

    方式二

    // 直接使用此方法 一个方法搞定请求重定向
    resp.sendRedirect("http://localhost:8080/08_Servlet/response2");
    
    不一定每天 code well 但要每天 live well
  • 相关阅读:
    Vue插件之导出EXCEl
    vue.js--加载JSON文件的两种方式
    vue项目中axios的封装
    雪碧图布局
    开始学习算法
    Java中有关Null的9件事
    一个抓取知乎页面图片的简单爬虫
    浅析Java中的final关键字
    Java中String、StringBuilder以及StringBuffer
    把一个数组向右循环移动k位要求时间复杂度为O(n)
  • 原文地址:https://www.cnblogs.com/geekfx/p/12773948.html
Copyright © 2011-2022 走看看