JavaWeb2

1. web服务器软件:Tomcat

1.1 概述

  • 服务器:安装了服务器软件的计算机
  • 服务器软件:接受用户的请求,处理请求,做出响应
  • web服务器软件:接受用户的请求,处理请求,做出响应
    • 在web服务器软件中,可以部署web项目,让用户通过浏览器来访问这些项目
  • 常见的Java相关的web服务器软件
    • webLogic:Oracle公司,大型的JavaEE服务器,支持所有的JavaEE规范,收费的
    • webSphere:IBM公司,大型的JavaEE服务器,支持所有的JavaEE规范,收费的
    • JBOSS:JBOSS公司的,大型的JavaEE服务器,支持所有的JavaEE规范,收费的
    • Tomcat:Apache基金阻阻止,中小型的JavaEE服务器,仅仅支持少量的JavaEE规范。开源的,免费的。
  • JavaEE:Java语言在企业级开发中使用的技术规范的总和,一共规定了13项大的规范

1.2 Tomcat安装、卸载、启动

image

  • 启动

    • bin/starp.bat,双击运行即可

    • 访问:在浏览器输入 http://localhost:8080 回车访问自己

      http://别人的ip:8080 回车访问别人

    • 可能遇到的问题:

      • 运行后的黑窗口一闪而过:

        • 原因:没有正确配置JAVA_HOME环境变量
        • 解决方案:正确配置JAVA_HOME环境变量
      • 启动报错:

        • 暴力解决方案:找到占用8080对应的进程,杀死该进程

          • cmd:netstat -ano
          • 找到8080对应的PID(进程ID)、
          • 任务管理器找到该进程并结束
        • 温柔解决方案:修改自身的端口号(不建议)

          • 打开conf/server.xml文件

          • 修改默认端口号

                <Connector port="80" protocol="HTTP/1.1"
                           connectionTimeout="20000"
                           redirectPort="8443" />
            
    • 一般会将Tomcat的默认端口号修改为80。80端口号是http协议的默认端口号。

      • 好处:访问时不用输入端口号
  • 关闭

    • 正常关闭:
      1. 执行bin/shutdown.bat批处理文件
      2. 或者在启动窗口按Ctrl+C
    • 强制关闭:(不推荐)
      • 点击启动窗口的×
  • 配置

    • 部署项目的方式:

      • 直接将项目放在webapps文件夹下
        • 访问时输入:localhost/hello/hello.html即可访问
        • /hello:项目的访问路径--->虚拟目录 一般和资源名称相同
        • hello.html:资源名称
        • 简化:将项目文件夹打包成hello.war文件,将其放在webapps文件夹下,会自动解压缩
      • 配置conf/server.xml文件(不推荐,很不安全)
        • 在<Host>标签体中配置<Context docBase="D:\hello" path="/hehe"/>
        • 其中docbase:项目存放的路径 path:虚拟路径
      • 在conf\Catalina\localhost下创建任意名称的xml文件。在文件中编写
        • <Context docBase="D:\hello" />
        • 虚拟目录即xml文件的名称
    • 静态项目和动态项目区别

      • 目录结构不同

        • java动态项目的目录结构:

          • 项目的根目录

            • WEB-INF目录:
              • web.xml:web项目的核心配置文件
              • classes目录:放置字节码文件的目录
              • lib目录:放置依赖的jar包

            image

1.3 Tomcat与IDEA集成&创建项目

将Tomcat继承到IDEA中,并且创建JavaEE的项目,部署项目(本人使用IDEA 2021 3.3 版本)

IDEA集成Tomcat:

  • Run--->Edit Configurations
  • 左上角加号--->Tomcat Sever--->Local
  • 选择JRE--->OK

创建JavaEE项目

  • new Module--->Java Module--->Next-->起模块名为myTomcat--->Finish--->ok
  • 右键创建的Module--->add Framework Support
  • 选择 JavaEE Version为Java EE 7
  • 勾选web Application,JavaEE 7版本默认为3.1版本,勾选Create web.xml--->ok
    • 一般默认会在Tomcat的Deployment添加该项目,若没有则执行下列步骤
  • Run--->Edit Configurations--->Tomcat Sever--->Deployment
  • 左上角加号--->Artifact--->myTomcat:war exploded--->Application context:默认为/(即虚拟目录)
  • OK

2. Servlet入门

2.1 概述

Servlet即 server applet,运行在服务器端的小程序

image

  • servlet就是一个接口,定义了Java类被浏览器访问到(tomcat识别)的规则。
  • 将来我们自定义一个类,实现Servlet接口,复写方法

2.2 快速入门

  • 创建JavaEE项目

  • 定义一个类,实现Servlet接口

    • 关于手写类实现Servlet不会导包的问题解决办法:
      • File--->Project Structure--->myTomcat-->Dependencies
      • 左上角加号--->Library--->Application Server Libraries--->Tomcat 8.5.81
      • Add Selected--->Apply--->OK
  • 实现接口中的抽象方法

    public class ServletDemo1 implements Servlet {
        @Override
        public void init(ServletConfig servletConfig) throws ServletException {
    
        }
    
        @Override
        public ServletConfig getServletConfig() {
            return null;
        }
    
        //提供服务的方法
        @Override
        public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
            System.out.println("Hello Servlet");
        }
    
        @Override
        public String getServletInfo() {
            return null;
        }
    
        @Override
        public void destroy() {
    
        }
    }
    
    
  • 配置Servlet

    	<!--配置Servlet-->
        <servlet>
            <servlet-name>demo1</servlet-name>
            <servlet-class>com.mark.web.servlet.ServletDemo1</servlet-class>
        </servlet>
    
        <!--映射-->
        <servlet-mapping>
            <servlet-name>demo1</servlet-name>
            <url-pattern>/demo1</url-pattern>
        </servlet-mapping>
    
  • 在浏览器中输入http://localhost:8080/demo1 会发现IDEA控制台上输出Hello Servlet

2.3 执行原理

image

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

2.4 Servlet中的方法

public class ServletDemo2 implements Servlet {

    /**
     * 初始化方法
     * 在Servlet被创建时执行,只执行一次
     * @param servletConfig
     * @throws ServletException
     */
    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
        System.out.println("init...");
    }

    /**
     * 获取ServletConfig对象
     * ServletConfig是Servlet的配置对象
     * @return
     */
    @Override
    public ServletConfig getServletConfig() {
        return null;
    }

    /**
     * 提供服务的方法
     * 每一次Servlet被访问时执行,执行多次
     * @param servletRequest
     * @param servletResponse
     * @throws ServletException
     * @throws IOException
     */
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        System.out.println("servlet...");
    }


    /**
     * 获取Servlet的一些信息:版本、作者等等
     * @return
     */
    @Override
    public String getServletInfo() {
        return null;
    }

    /**
     * 销毁方法
     * 在Servlet被杀死时执行(在服务器正常关闭时执行),只执行一次
     */
    @Override
    public void destroy() {
        System.out.println("destroy...");
    }
}

  • Servlet的生命周期方法:

    • 被创建:执行init方法,只执行一次

      • 默认情况下,第一次被访问时,Servlet被创建

      • 可以指定Servlet的创建时机:修改web.xml配置文件

        <servlet>
                <servlet-name>demo2</servlet-name>
                <servlet-class>com.mark.web.servlet.ServletDemo2</servlet-class>
                <!--指定Servlet的创建时机
                    1.第一次被访问时创建
                        <load-on-startup>的值为负数(默认值为-1)
                    2.在服务器启动时创建
                        <load-on-startup>的值为0或正整数(一般配0-10)
                -->
                <load-on-startup>5</load-on-startup><!--当服务器启动时加载-->
            </servlet>
        
      • init方法,只执行一次,说明一个Servlet在内存中只存在一个对象,Servlet是单例的

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

      • 每次访问Servlet时,Service方法都会被调用一次
    • 被销毁:执行destory方法,只执行一次

      • Servlet被销毁时执行。服务器关闭时,Servlet被销毁。
      • 只有服务器正常关闭时,才会执行destory方法
      • destory方法在Servlet被销毁前执行,一般用于释放资源

2.5 注解配置

  • Servlet 3.0
    • 好处:支持注解配置。可以不需要web.xml
    • 步骤:
      • 创建JavaEE项目,选择Servlet的版本3.0以上,可以不创建web.xml
      • 定义一个类,实现Servlet接口
      • 复写方法
      • 在类上使用@WebServlet注解,进行配置 @WebServlet(urlPatterns = "/demo")
        • 还可以直接写@WebServlet("/demo") 引号中直接写资源路径

2.6 IDEA与Tomcat的相关配置

  • IDEA会为每一个Tomcat部署的项目单独建立一份配置文件
    • 查看控制台的log:Using CATALINA_BASE: "C:\Users\Mark\AppData\Local\JetBrains\IntelliJIdea2021.3\tomcat\2cbcd2b9-3ae3-45b3-b582-c86f8e7e95e4"
  • 工作空间项目 和 tomcat部署的web项目
    • tomcat真正访问的是"tomcat部署的web项目","tomcat部署的web项目"对应着”工作空间项目“的web目录下的所有资源。
    • WEB-INF目录下的资源不能被浏览器直接访问
  • 断点调试:加断点后Debug(小虫子)

3. 深入了解Servlet

3.1 Servlet体系结构

Interface Servlet

​ -----GenericServlet抽象类 实现了Servlet接口

​ -----HttpServlet抽象类 继承于GenericServlet抽象类

  • GenericServlet:将Servlet接口中的其他方法做了默认空实现,只将service()作为抽象

    • 将来定义Servlet类时,可以继承GenericServlet,实现service()即可

      @WebServlet("/demo2")
      public class ServletDemo2 extends GenericServlet {
          @Override
          public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
              System.out.println("Demo2...");
          }
      }
      
  • HttpServlet:对HTTP协议的一种封装,可以简化操作。接收数据不再需要在service()中写if-else判断HTTP的方式为get还是post

    • 步骤:

      • 定义类继承HttpServlet

      • 复写doGet()或者doPost()方法

        @WebServlet("/demo3")
        public class ServletDemo3 extends HttpServlet {
            @Override
            protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
                System.out.println("doGet...");//默认为get方式
            }
        
            @Override
            protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
                System.out.println("doPost...");//可以通过表单方式改用post
            }
        }
        
        
        <!DOCTYPE html>
        <html lang="en">
        <head>
            <meta charset="UTF-8">
            <title>Title</title>
        </head>
        <body>
            <form action="demo3" method="post">
              <!--如果配置中Deployment-Application context为/则可以写为/demo3
        		但是如果为/路径 则只能写demo3,不能加/
        		-->
                <input name="username">
                <input type="submit" value="提交">
            </form>
        </body>
        </html>
        

3.2 Servlet的相关配置

  • urlpartten:Servlet的访问路径

    • 一个Servlet可以定义多个访问路径,浏览器可以通过任意一个访问路径访问

      @WebServlet({"/d4","/dd4","/demo4"})
      public class ServletDemo4 extends HttpServlet {
          @Override
          protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
              System.out.println("demo4...");
          }
      }
      
    • 路径定义规则:

      • /xxx

      • /xxx/xxx:多层路径,目录结构

        @WebServlet("/user/demo4")
        public class ServletDemo4 extends HttpServlet {
            @Override
            protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
                System.out.println("demo4...");
            }
        }
        
        @WebServlet("/user/*")//访问时可以通过任意路径访问,/*是访问优先级最低的
        public class ServletDemo4 extends HttpServlet {
            @Override
            protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
                System.out.println("demo4...");
            }
        }
        
      • *.do

        @WebServlet("*.do")//※不要加/     访问时通过xxx.do访问  xxx为任意路径
        public class ServletDemo4 extends HttpServlet {
            @Override
            protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
                System.out.println("demo4...");
            }
        }
        

4. HTTP协议

4.1 概述

  • 概念:HTTP即Hyper Text Transfer Protocol,超文本传输协议
    • 传输协议:定义了客户端和服务器端通信时,发送数据的格式
  • 特点:
    • 基于TCP/IP的高级协议
    • HTTP的默认端口号:80
    • 基于请求/响应模型的 一次请求对应一次响应
    • 是无状态的协议:每次请求之间相互独立,不能交互数据
  • 历史版本:
    • 1.0 每一次请求响应都会建立新的连接
    • 1.1 服用连接

4.2 请求消息数据格式

  • 请求行

    ​ 请求方式 请求url 请求协议/版本

    ​ Get /myServlet/login.html HTTP/1.1

    • HTTP协议有7中请求方式,常用的有两种

      • GET

        • 请求参数在请求行中(在URL后):

          http://localhost:8080/myServlet/demo3?username=aaa

        • 请求的url长度有限制

        • 不太安全

      • POST

        • 请求参数在请求体中
        • 请求的url长度没有限制的
        • 相对安全
  • 请求头:浏览器告诉服务器浏览器自身的一些信息

    ​ 请求头名称:请求头值

    • 常见的请求头:
      • User-Agent:浏览器告诉服务器,浏览器访问服务器使用的浏览器版本信息
        • 可以以在服务器端获取该头的信息,解决兼容性问题
      • Referer:告诉服务器,当前请求从哪里来
        • 作用:
          • 防盗链:
          • 统计工作:
  • 请求空行:就是用于分割POST请求的请求头和请求体的

    • 空行
  • 请求体(正文):封装POST请求消息的请求体

  • 字符串格式:

    GET /myServlet/login.html HTTP/1.1
    Host: localhost:8080
    User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:103.0) Gecko/20100101 Firefox/103.0
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
    Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
    Accept-Encoding: gzip, deflate, br
    Referer: http://localhost:8080/myServlet/login.html
    Connection: keep-alive
    Cookie: JSESSIONID=7AAA4903DCD0183D7F0A8ECD445090E1
    Upgrade-Insecure-Requests: 1
      User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36
    

5. Request

5.1 原理

image

request和response对象的原理

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

5.2 Request获取请求消息

5.2.1 Request继承的体系结构

  • ServletRequest --- 接口

    • HttpServletRequest --- 接口(继承于ServletRequest)
    • org.apache.catalina.connector.RequestFacade --- 实现类(tomcat编写)

5.2.2 Request获取请求消息

  • 获取请求行数据

    Get /myServlet/demo1?name=zhangsan HTTP/1.1

    • 方法:

      • 获取请求方式:GET

        String getMethod()

      • ⭐获取虚拟目录:/myServlet

        String getContextPath()

      • 获取Servlet路径:/demo1

        String getServletPath()

      • 获取get方式的请求参数:?name=zhangsan

        String getQueryString()

      • ⭐获取请求URI:/myServlet/demo1

        String getRequestURI()

        String getRequestURL(): 返回 http://myServlet/demo1

      • 获取协议以及版本:HTTP/1.1

        String getProtocol()

      • 获取客户机的IP地址

        String getRemoteAddr()

      public class RequestDemo1 extends HttpServlet {
          @Override
          protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
              //获取请求方式:GET
              String method = request.getMethod();
              System.out.println(method);
              //⭐获取虚拟目录:/myServlet
              String contextPath = request.getContextPath();
              System.out.println(contextPath);
              //获取Servlet路径:/demo1
              String servletPath = request.getServletPath();
              System.out.println(servletPath);
              //获取get方式的请求参数:?name=zhangsan
              String queryString = request.getQueryString();
              System.out.println(queryString);
              //⭐获取请求URI:/myServlet/demo1
              String requestURI = request.getRequestURI();
              System.out.println(requestURI);
              StringBuffer requestURL = request.getRequestURL();
              System.out.println(requestURL);
              //获取协议以及版本:HTTP/1.1
              String protocol = request.getProtocol();
              System.out.println(protocol);
              //获取客户机的IP地址
              String remoteAddr = request.getRemoteAddr();
              System.out.println(remoteAddr);
          }
      }
      
  • 获取请求头数据

    • 方法:

      • String getHeader(String name):通过请求头的名称获取请求头的值
      • Enumeration getHeaderNames():获取所有的请求头名称
      @WebServlet("/RequestDemo2")
      public class RequestDemo2 extends HttpServlet {
          @Override
          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);
              }
          }
      }
      
      host--localhost:8080
      user-agent--Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:103.0) Gecko/20100101 Firefox/103.0
      accept--text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/
      *;q=0.8
      accept-language--zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
      accept-encoding--gzip, deflate, br
      connection--keep-alive
      cookie--JSESSIONID=324F172A257FA6B8DF4869314F01E64B; Webstorm-ec1c0726=62f9d3ca-a2fa-4d36-aef6-9086650fec79
      upgrade-insecure-requests--1
      sec-fetch-dest--document
      sec-fetch-mode--navigate
      sec-fetch-site--none
      sec-fetch-user--?1
      

      @WebServlet("/RequestDemo3")
      public class RequestDemo3 extends HttpServlet {
          @Override
          protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
              //获取请求头数据:user-agent
              String agent = request.getHeader("user-agent");
              //判断agent的浏览器版本
              if (agent.contains("Chrome")) {
                  System.out.println("谷歌");
              } else if (agent.contains("Firefox")) {
                  System.out.println("火狐");
              }
          }
      }
      
      @WebServlet("/RequestDemo4")
      public class RequestDemo4 extends HttpServlet {
          @Override
          protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
              //获取请求头数据:referer
              String referer = request.getHeader("referer");
              System.out.println(referer);//http://localhost:8080/myServlet/login.html
      
              //防盗链
              if (referer != null) {
                  if (referer.contains("/myServlet")) {
      //                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("你进不去哦");
                  }
              }
          }
      }
      
  • 获取请求体数据

    • 请求体:只有POST请求方式,才有请求体,在请求体中封装了POST请求的请求参数

    • 步骤:

      • 获取流对象
        • BufferReader getReader():获取字符输入流,只能操作字符数据
        • ServletInputStream getInputStream():获取字节输入流,可以操作所有类型数据
      • 再从流对象中拿数据
      @Override
      protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
          //获取请求消息体
      
          //1. 获取字符流
          BufferedReader br = request.getReader();
          String line = null;
          while ((line = br.readLine()) != null) {
              System.out.println(line);
          }
      }
      
      username=aaa&password=ddd
      

5.2.3 Request其他功能

  • 获取请求参数通用方式

    • String getParameter(String name):根据参数名称来获取参数值 username=aaa&password=ddd

      @WebServlet("/RequestDemo6")
      public class RequestDemo6 extends HttpServlet {
          @Override
          protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
              String username = request.getParameter("username");
              System.out.println("get");
              System.out.println(username);
          }
      
          @Override
          protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
      //        String username = request.getParameter("username");
      //        System.out.println("post");
      //        System.out.println(username);
              this.doGet(request,response);//执行doGet的代码逻辑,节省代码重复
          }
      }
      
      <form action="/myServlet/RequestDemo6" method="post">
          <input type="text" name="username" placeholder="username"><br>
          <input type="text" name="password" placeholder="password"><br>
          <input type="submit" value="注册">
      </form>
      
    • String[] getParameterValues(String name):根据参数名称来获取参数值的数组 hobby=xx&hobby=game

      @WebServlet("/RequestDemo6")
      public class RequestDemo6 extends HttpServlet {
          @Override
          protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
              String[] hobbies = request.getParameterValues("hobby");
              for (String hobby : hobbies) {
                  System.out.println(hobby);
              }
          }
      }
      
      <form action="/myServlet/RequestDemo6" method="get">
          <input type="text" name="username" placeholder="username"><br>
          <input type="text" name="password" placeholder="password"><br>
          <input type="checkbox" name="hobby" value="game">游戏
          <input type="checkbox" name="hobby" value="study">学习<br>
          <input type="submit" value="注册">
      </form>
      
    • Enumeration getParameterNames():获取所有请求参数的名称

      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<String,String[]> getParameterMap():获取所有参数的map集合

      Map<String, String[]> parameterMap = request.getParameterMap();
      Set<String> keyset = parameterMap.keySet();
      for (String name : keyset) {
          //根据key键获取值
          String[] values = parameterMap.get(name);
          System.out.println(name);
          for (String value : values) {
              System.out.println(value);
          }
          System.out.println("---------");
      }
      
    • 中文乱码问题

      • get方式:tomcat8已经将get方式乱码问题解决

      • post方式:会乱码

        • 解决方式:获取参数前设置request编码

          request.setCharacterEncoding("utf-8");

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

image

  • 步骤:

    • 通过request对象来获取请求转发器对象:RequestDispatcher getRequestDispatcher(String path)

    • 使用RequestDispatcher对象来进行转发:forward(ServletRequest request, ServletResponse response)

      @Override
          protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
              System.out.println("demo8被访问");
              //转发到demo9资源
      //        RequestDispatcher requestDispatcher = request.getRequestDispatcher("/RequestDemo9");
      //        requestDispatcher.forward(request,response);
              request.getRequestDispatcher("/RequestDemo9").forward(request,response);
          }
      
  • 特点:

    • 浏览器地址栏路径不发生变化
    • 只能转发到当前服务器内部资源中
    • 转发是一次请求,多个资源使用同一次请求
  • 共享数据

    • 域对象:一个有作用范围的对象,可以在范围内共享数据

    • request域:代表一次请求的范围,一般用于请求转发的多个资源中共享数据

    • 方法:

      • void setAttribute(String name,Object obj):存储数据
      • Object getAttribyte(String name):通过键获取值
      • removeAttribute(String name):通过键移除键值对
      protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
          System.out.println("demo8被访问");
          //存储数据
          request.setAttribute("msg","hello");
          //转发到demo9资源
          request.getRequestDispatcher("/RequestDemo9").forward(request,response);
      }
      
      protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
          System.out.println("demo9被访问");
          //获取数据
          Object msg = request.getAttribute("msg");
          System.out.println(msg);
      }
      
  • 获取ServletContext动态获取虚拟目录

    • ServletContext getServletContext()

      @Override
      protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
          ServletContext servletContext = request.getServletContext();
          System.out.println(servletContext);
          //org.apache.catalina.core.ApplicationContextFacade@5e3dfad6
      }
      

5.3 用户登陆

5.3.1 需求

1.编写login.html登录页面

​ username & password 两个输入框

2.使用Druid数据库连接池技术,操作mysql,day14数据库中user表
3.使用JdbcTemplate技术封装JDBC
4.登录成功跳转到SuccessServlet展示:登录成功!用户名,欢迎您
5.登录失败跳转到FailServlet展示:登录失败,用户名或密码错误

5.3.2 分析

image

5.3.3 开发步骤

  • 创建项目,导入HTML页面,配置文件、jar包

  • 创建数据库环境

    CREATE DATABASE Login;
    
    USE Login;
    
    CREATE TABLE USER(
    	id INT PRIMARY KEY auto_increment,
    	username VARCHAR(32) UNIQUE NOT NULL,
    	password VARCHAR(32) NOT NULL
    );
    
  • 创建包com.mark.domain,创建类User

    package com.mark.domain;
    
      /**
    - @ClassName User
    - @Description TODO 用户的JavaBean(实体类)
    - @Author Mark
    - @Date 2022/7/31 20:44
    - @Version 1.0
      */
    public class User {
      private int id;
      private String username;
      private String password;
    
      public int getId() {
          return id;
      }
    
      public void setId(int id) {
          this.id = id;
      }
    
      public String getUsername() {
          return username;
      }
    
      public void setUsername(String username) {
          this.username = username;
      }
    
      public String getPassword() {
          return password;
      }
    
      public void setPassword(String password) {
          this.password = password;
      }
    
      @Override
      public String toString() {
          return "User{" +
                  "id=" + id +
                  ", username='" + username + '\'' +
                  ", password='" + password + '\'' +
                  '}';
      }
      }
    
  • 创建包com.mark.dao,创建类UserDao,提供login()方法

    package com.mark.dao;
    
    import com.mark.domain.User;
    import com.mark.util.JDBCUtils;
    import org.springframework.dao.DataAccessException;
    import org.springframework.jdbc.core.BeanPropertyRowMapper;
    import org.springframework.jdbc.core.JdbcTemplate;
    
    /**
     * @ClassName UserDao
     * @Description TODO 操纵数据库中User表的类
     * @Author Mark
     * @Date 2022/7/31 20:48
     * @Version 1.0
     */
    public class UserDao {
    
        //声明JDBCTemplate对象共用
        private JdbcTemplate template = new JdbcTemplate(JDBCUtils.getDataSource());
    
        /**
         * 登陆方法
         * @param loginUser 只有用户名和密码
         * @return User包含用户全部数据, 没有查询到,返回null
         */
        public User login(User loginUser) {
            try {
                String sql = "select * from user where username=? and password = ?";
                User user = template.queryForObject(sql,
                        new BeanPropertyRowMapper<User>(User.class),
                        loginUser.getUsername(), loginUser.getPassword());
                return user;
            } catch (DataAccessException e) {
                e.printStackTrace();//记录日志
                return null;
            }
        }
    }
    
    
  • 编写com.mark.web.servlet.LoginServlet类

    • login.html中form表单的action路径的写法:
      • 虚拟目录+Servlet的资源路径
    package com.mark.web.servlet;
    
    import com.mark.dao.UserDao;
    import com.mark.domain.User;
    
    import javax.servlet.*;
    import javax.servlet.http.*;
    import javax.servlet.annotation.*;
    import java.io.IOException;
    
    @WebServlet("/LoginServlet")
    public class LoginServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            request.setCharacterEncoding("utf-8");
            String username = request.getParameter("username");
            String password = request.getParameter("password");
            User loginUser = new User();
            loginUser.setUsername(username);
            loginUser.setPassword(password);
            UserDao dao = new UserDao();
            User user = dao.login(loginUser);
            if (user == null) {
                //登陆失败
                request.getRequestDispatcher("/FailServlet").forward(request, response);
            } else {
                //登陆成功
                request.setAttribute("user", user);
                request.getRequestDispatcher("/SuccessServlet").forward(request, response);
            }
        }
    
        @Override
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            this.doGet(request, response);
        }
    }
    
    package com.mark.web.servlet;
    
    import javax.servlet.*;
    import javax.servlet.http.*;
    import javax.servlet.annotation.*;
    import java.io.IOException;
    
    @WebServlet("/FailServlet")
    public class FailServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            //给页面写一句话
            response.setContentType("text/html;charset=utf-8");
            response.getWriter().write("登录失败,用户名或密码错误");
        }
    
        @Override
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            this.doGet(request,response);
        }
    }
    
    
    package com.mark.web.servlet;
    
    import com.mark.domain.User;
    
    import javax.servlet.*;
    import javax.servlet.http.*;
    import javax.servlet.annotation.*;
    import java.io.IOException;
    
    @WebServlet("/SuccessServlet")
    public class SuccessServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            //获取request域中共享的user对象
            User user = (User) request.getAttribute("user");
    
            if (user != null) {
                //给页面写一句话
                response.setContentType("text/html;charset=utf-8");
                response.getWriter().write("登录成功!" + user.getUsername() + ",欢迎您");
            }
        }
    
        @Override
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            this.doGet(request, response);
        }
    }
    
    

5.3.4 BeanUtils工具类

完成(简化)数据的封装,用于封装JavaBean

		request.setCharacterEncoding("utf-8");
//        String username = request.getParameter("username");
//        String password = request.getParameter("password");
//        User loginUser = new User();
//        loginUser.setUsername(username);
//        loginUser.setPassword(password);
        //获取所有请求参数
        Map<String, String[]> parameterMap = request.getParameterMap();
        //创建User对象
        User loginUser= new User();
        //使用BeanUtils封装
        try {
            BeanUtils.populate(loginUser,parameterMap);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
  • JavaBean:标准的Java类,一般放在domain包里

    • 要求:
      • 类必须被public修饰
      • 必须提供空参的构造器
      • 成员遍历必须使用private修饰
      • 提供公共的getter和setter方法
    • 功能:封装数据
  • 概念:

    • 成员变量

    • 属性:setter和getter方法截取后的产物。

      • 例如:getUsername() ---> Username --->username

      • 大多数时候和成员变量一样,少数情况不一样

        private String gender;
        
        public void setHehe(String gender) {
            this.gender = gender;
        }
        
  • 方法:

    • setProperty():设置属性

      @Test
      public void test(){
          User user = new User();
      
          try {
              BeanUtils.setProperty(user,"username","张三");
          } catch (IllegalAccessException e) {
              e.printStackTrace();
          } catch (InvocationTargetException e) {
              e.printStackTrace();
          }
      
          System.out.println(user);
      }
      
    • getProperty():获取属性

      String username = null;
      try {
          username = BeanUtils.getProperty(user, "username");
      } catch (IllegalAccessException e) {
          e.printStackTrace();
      } catch (InvocationTargetException e) {
          e.printStackTrace();
      } catch (NoSuchMethodException e) {
          e.printStackTrace();
      }
      
    • ⭐populate():封装

6. HTTP协议:响应消息

  • 数据格式:

    • 响应行
      • 组成:协议/版本 响应状态码 状态码 描述
      • 响应状态码:服务器告诉客户浏览器本次请求和响应的一个状态
        • 状态码都是3位数字
        • 分类:
          • 1xx:服务器接收客户端消息但没有接收完成,等待一段时间后,发送1xx状态码
          • 2xx:成功
          • 3xx:重定向。代表:302(重定向)、304(访问缓存)
          • 4xx:客户端错误。
            • 代表:
              • 404(请求路径没有对应资源)
              • 405(请求方式没有doxxx方法)
          • 5xx:服务器端错误。代表:500(服务器内部出现异常)
    • 响应头
      • 格式:头名称 : 值
      • 常见的响应头::
        • Content-Type:服务器告诉客户端本次响应体数据格式以及编码格式
        • Content-disposition:服务器告诉客户端以什么格式打开响应体数据
          • 值:
            • in-line:默认值,在当前页面内打开
            • attachment:以附件形式打开响应体。常用于文件下载:attachment;filename=xxx
    • 响应空行
    • 响应体:传输的数据
  • 响应字符串格式

    HTTP/1.1 200 OK
    Bdpagetype: 1
    Connection: keep-alive
    Content-Type: text/html; charset=utf-8
    Date: Mon, 01 Aug 2022 04:36:04 GMT
    Server: BWS/1.1
    
    
    <html>
    <head>
        <title>$Title$</title>
    </head>
    <body>
        hello,Response
    </body>
    </html>
    

7. Response对象

7.1 Response功能介绍

  • 设置响应消息
    • 设置响应行
      • 设置状态码:setStatus(int sc)
    • 设置响应头
      • setHeader(String name, String value)
    • 设置响应体
      • 使用步骤:
        • 获取输出流
          • 字符输出流:PrintWriter getWriter()
          • 字节输出流:ServletOutputStream getOutputStream()
        • 使用输出流,将数据输出到客户端浏览器

7.2 Response案例

7.2.1 完成重定向

image

  • 重定向:资源跳转的方式

    @WebServlet("/ResponseDemo1")
    public class ResponseDemo1 extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            System.out.println("Demo1...");
            //访问ResponseDemo1会自动跳转到ResponseDemo2
    
            /*
            //1.设置状态码为302
            response.setStatus(302);
            //设置响应头location
            response.setHeader("location","/myServlet/ResponseDemo2");
             */
    
            //简单的重定向
            response.sendRedirect("/myServlet/ResponseDemo2");
        }
    
        @Override
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            this.doGet(request, response);
        }
    }
    
    
  • 重定向特点:redirect

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

    • 路径的分类:

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

        • 如./index.html
        • 不以/开头,以.开头路径

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

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

        规则:判断定义的路径是给谁用的?判断请求将来从哪发出

        • 给客户端浏览器使用:需要加虚拟目录(项目的访问路径)
          • 建议虚拟目录动态获取:request.getContextPath()
        • 给服务器使用:不需要加虚拟目录

        动态获取虚拟目录:

        //动态获取虚拟目录
        String contextPath = request.getContextPath();
        
        //简单的重定向
        response.sendRedirect(contextPath+"/ResponseDemo2");
        

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

  • 步骤:

    • 获取字符输出流

    • 输出数据

      protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
          //获取字符输出流
          PrintWriter pw = response.getWriter();
          //输出数据
          //pw.write("hello response");
          pw.write("<h1>hello response</h1>");
      }
      
  • 中文乱码问题:编解码使用的字符集不一致。客户端浏览器于电脑一致:使用GBK2312,而tomcat使用ISO-8859-1

    • request.setCharacterEncoding("GBK");获取流对象之前设置流的默认编码。

    • 告诉浏览器,服务器发送的消息体数据的编码,建议浏览器使用该编码解码

      response.setHeader("content-type", "text/html;charset=utf-8");

      简单形式:response.setContentType("text/html;charset=utf-8");

7.2.3 服务器输出字节数据到浏览器

@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    response.setHeader("content-type", "text/html;charset=utf-8");
    //获取字节输出流
    ServletOutputStream sos = response.getOutputStream();
    //输出数据
    sos.write("hello".getBytes());
}

7.2.4 验证码案例

  • 本质:图片

  • 目的:防止恶意表单注册

    package com.mark.web.response;
    
    import javax.imageio.ImageIO;
    import javax.servlet.*;
    import javax.servlet.http.*;
    import javax.servlet.annotation.*;
    import java.awt.*;
    import java.awt.image.BufferedImage;
    import java.io.IOException;
    import java.util.Random;
    
    @WebServlet("/CheckCodeServlet")
    public class CheckCodeServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            //创建一个对象,在内存中代表图片(验证码图片对象)
            int width = 100;
            int height = 50;
            BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_BGR);
            //美化图片
            //填充背景色
            Graphics g = image.getGraphics();//画笔对象
            g.setColor(Color.PINK);//设置画笔颜色
            g.fillRect(0, 0, width, height);//填充颜色
            //画边框
            g.setColor(Color.BLUE);
            g.drawRect(0, 0, width - 1, height - 1);
            //写验证码
            String str = "QWERTYUIOPASDGFHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm0123456789";
            //生成随机角标
            Random random = new Random();
            for (int i = 1; i <= 4; i++) {
                int index = random.nextInt(str.length());
                //获取字符
                char ch = str.charAt(index);
                g.drawString(ch + "", width / 5 * i, height / 2);
            }
    
            //干扰线
            g.setColor(Color.GREEN);
            for (int i = 1; i < 10; i++) {
                //随机生成坐标点
                int x1 = random.nextInt(width);
                int x2 = random.nextInt(width);
                int y1 = random.nextInt(height);
                int y2 = random.nextInt(height);
                g.drawLine(x1, y1, x2, y2);
            }
    
            //将图片输出到页面展示
            ImageIO.write(image, "jpg", response.getOutputStream());
        }
    
        @Override
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            this.doGet(request, response);
        }
    }
    
    
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    
        <script>
            /*  点击超链接或者图片,换一张
                1.给超链接和图片绑定单击事件
                2.重新设置src的属性值
             */
            window.onload=function () {
                //获取图片对象
                var img = document.getElementById("checkCode");
                //绑定单击事件
                img.onclick=function () {
                    //加时间戳
                    var date = new Date().getTime();
                    img.src="/myServlet/CheckCodeServlet?"+date;
                }
            }
        </script>
    
    </head>
    <body>
    
    <img id="checkCode" src="/myServlet/CheckCodeServlet"/>
    <a id="change" href="">看不清,换一张</a>
    
    </body>
    </html>
    

8. ServletContext对象

8.1 概述

  • 概念:代表整个web应用,可以和程序的容器(服务器)来通信
  • 功能:
    • 获取MIME类型
    • 域对象:共享数据
    • 获取文件的真实(服务器)路径

8.2 获取

  • 通过request对象获取

    • request.getServletContext();
    • 通过HttpServlet获取:this.getServletContext();
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1.通过request对象获取
        ServletContext context1 = request.getServletContext();
        //2.通过HttpServlet获取
        ServletContext context2 = this.getServletContext();
    
        System.out.println(context1);
        System.out.println(context2);
    
        System.out.println(context1==context2);
        /*
        org.apache.catalina.core.ApplicationContextFacade@483e3ce3
        org.apache.catalina.core.ApplicationContextFacade@483e3ce3
        true
        */
    
    }
    

8.3 功能

  • 获取MIME类型:

    • MIME类型:在互联网通信过程中定义的一种文件数据类型标准

      • 格式:大类型/小类型 eg: text/html image/jpeg
    • 获取:String getMimeType(String file)

      @Override
      protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
          ServletContext context = this.getServletContext();
          //定义文件名称
          String filename="a.jpg";
          //获取MIME类型
          String mimeType = context.getMimeType(filename);
          System.out.println(mimeType);//image/jpeg
      }
      
  • 域对象:共享数据

    • setAttribute(String name,Object value)

    • getAttribute(String name)

    • removeAttribute(String name)

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

      @WebServlet("/ServletContextDemo3")
      public class ServletContextDemo3 extends HttpServlet {
          @Override
          protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
              ServletContext context = this.getServletContext();
              context.setAttribute("msg","haha");
          }
      
          @Override
          protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
              this.doGet(request,response);
          }
      }
      
      @Override
      protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
          ServletContext context = this.getServletContext();
          Object msg = context.getAttribute("msg");
          System.out.println(msg);
      }
      
  • 获取文件真实(服务器)路径

    • 方法:String getRealPath(String path)
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        ServletContext context = this.getServletContext();
        //获取文件的服务器路径
        String realPath = context.getRealPath("/b.txt");//web目录下资源访问
        //File file = new File(realPath);
        System.out.println(realPath);
        //D:\Code\IJavaMyJavaWeb\out\artifacts\myServlet_war_exploded\b.txt
        String realPath1 = context.getRealPath("/WEB-INF/c.txt");//WEB-INF目录下的资源访问
        System.out.println(realPath1);
        //D:\Code\IJavaMyJavaWeb\out\artifacts\myServlet_war_exploded\WEB-INF\c.txt
        String realPath2 = context.getRealPath("/WEB-INF/clsses/a.txt");//src目录下的资源路径
        System.out.println(realPath2);
        //D:\Code\IJavaMyJavaWeb\out\artifacts\myServlet_war_exploded\WEB-INF\clsses\a.txt
    }
    

8.4 案例:文件下载

  • 文件下载需求:

    • 页面显示超链接
    • 点击超链接后弹出下载提示框
    • 完成图片文件下载
  • 分析:

    • 超链接指向的资源如果能够被浏览器解析,则在浏览器中展示,如果不能解析,则弹出下载提示框。不满足需求
    • 任何资源都必须弹出下载提示框
    • 使用响应头设置资源的打开方式:
      • content-disposition:attachment;filename=xx;
  • 步骤:

    • 定义页面,编辑超链接href属性,指向Servlet,传递资源名称filename

    • 定义Servlet

      • 获取文件名称
      • 使用字节输入流加载文件进内存
      • 指定response的响应头content-diposition:attachment;filename=xx;
      • 将数据写出到response输出流
      <!DOCTYPE html>
      <html lang="en">
      <head>
          <meta charset="UTF-8">
          <title></title>
      </head>
      <body>
          <a href="/myServlet/img/1.jpg">图片1</a>
      
          <a href="/myServlet/img/1.mp4">视频1</a>
          <hr>
      
          <a href="/myServlet/DownloadServlet?filename=1.jpg">图片2</a>
      
          <a href="/myServlet/DownloadServlet?filename=1.mp4">视频2</a>
      </body>
      </html>
      
      @Override
      protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
          //1.获取请求参数、文件名称
          String filename = request.getParameter("filename");
          //使用字节输入流加载文件进内存
          //找到文件服务器路径
          ServletContext servletContext = this.getServletContext();
          String realPath = servletContext.getRealPath("/img/" + filename);
          //用字节流关联
          FileInputStream fis = new FileInputStream(realPath);
      
          //设置response的响应头
          //设置响应头类型content-type
          String mimeType = servletContext.getMimeType(filename);//获取文件的mime类型
          response.setHeader("content-type", mimeType);
          //设置响应头打开方式content-disposition
          response.setHeader("content-disposition", "attachment;filename" + filename);
          //将输入流的数据写出到输出流中
          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();
      }
      
  • 中文文件名问题:

    • 解决思路:
      • 获取客户端浏览器使用的版本信息
      • 根据不同的版本信息,设置filename的编码方式不同
    //解决中文文件名问题
    //获取user-agent请求头
    String agent = request.getHeader("user-agent");
    //使用工具类方法编码文件名
    filename = DownloadUtils.getFileName(agent,filename);
    
posted @   风吹头蛋凉OvO  阅读(23)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)
点击右上角即可分享
微信分享提示