zoukankan      html  css  js  c++  java
  • JavaWeb(二):Servlet

    一、本教程使用的Web容器——Tomcat

    Tomcat是提供一个支持Servlet和JSP运行的容器。Servlet和JSP能根据实时需要,产生动态网页内容。而对于Web服务器来说, Apache仅仅支持静态网页,对于支持动态网页就会显得无能为力;Tomcat则既能为动态网页服务,同时也能为静态网页提供支持。尽管它没有通常的Web服务器快、功能也不如Web服务器丰富,但是Tomcat逐渐为支持静态内容不断扩充。大多数的Web服务器都是用底层语言编写如C,利用了相应平台的特征,因此用纯Java编写的Tomcat执行速度不可能与它们相提并论。一般来说,大的站点都是将Tomcat与Apache的结合,Apache负责接受所有来自客户端的HTTP请求,然后将Servlets和JSP的请求转发给Tomcat来处理。Tomcat完成处理后,将响应传回给Apache,最后Apache将响应返回给客户端。

    1.1 Tomcat访问静态资源

    tomcat访问所有的资源,都是用Servlet来实现的。

    在Tomcat看来,资源分3种:

    • 静态资源,如css,html,js,jpg,png等     
    • Servlet   
    • JSP

    运行应用程序的Web容器将会有一个或多个内建的Servlet,这些Servlet用于处理JavaServer Pages、显示目录列表(如果启用了该功能的话)和访问静态资源,例如HTML页面和图片。

    JspServlet用于处理JSP页面,DefaultServlet是处理静态资源的Servlet,在tomcat的conf目录下web.xml:

    <servlet>
            <servlet-name>default</servlet-name>
            <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
            <init-param>
                <param-name>debug</param-name>
                <param-value>0</param-value>
            </init-param>
            <init-param>
                <param-name>listings</param-name>
                <param-value>false</param-value>
            </init-param>
            <load-on-startup>1</load-on-startup>
        </servlet>
    
       <servlet>
            <servlet-name>jsp</servlet-name>
            <servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
            <init-param>
                <param-name>fork</param-name>
                <param-value>false</param-value>
            </init-param>
            <init-param>
                <param-name>xpoweredBy</param-name>
                <param-value>false</param-value>
            </init-param>
            <load-on-startup>3</load-on-startup>
        </servlet>
    
        <!-- The mapping for the default servlet -->
        <servlet-mapping>
            <servlet-name>default</servlet-name>
            <url-pattern>/</url-pattern>
        </servlet-mapping>
    
        <!-- The mappings for the JSP servlet -->
        <servlet-mapping>
            <servlet-name>jsp</servlet-name>
            <url-pattern>*.jsp</url-pattern>
            <url-pattern>*.jspx</url-pattern>
        </servlet-mapping>

    1.2 安装tomcat

    配置jdk环境变量

    下载,解压,目录结构如下:

    bin目录 可执行文件,如启动tomcat

    conf目录 配置文件

    lib目录 第三方依赖的jar包

    logs目录 日志

    temp目录 临时文件

    webapps目录 部属的web应用

    work目录 jsp翻译成servlet再翻译成class的文件

    双击bin中的startup.bat可以启动tomcat

    若已经启动一个tomcat应用,再启动同一个tomcat应用,会抛出端口占用异常

    用bin中的shutdown.bat可以关闭tomcat

    修改端口号

    server.xml

    <Connector connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443"/>

    1.3 Tomcat中JavaWeb目录结构

    1.3.1 目录结构

    创建的Web项目,必须有一个根目录,根目录下:

    • *.html、*.jsp等外部可以公开访问的文件,看成公开目录
    • WEB-INF/web.xml,必须要有, 部属描述符
    • WEB-INF/classes,编译后的Java类文件,可选
    • WEB-INF/lib,Java类库文件

    WEB-INF/目录中的文件是受到保护的,不能通过URL访问。

    1.3.2 手工创建一个web项目

    用eclipse创建一个普通java工程

    创建com.aidata包,并创建Person类

    复制代码
    package com.aidata;
    
    public class Person {
    
        public String getPersonInfo() {
            return "person info...";
        }
    }
    复制代码

    下面手工创建tomcat项目目录

    创建WebContent根目录

    创建WEB-INF目录

    创建目录WEB-INF/classes,创建com文件夹,com下创建aidata文件夹,将eclipse的bin目录下生成的Person.class拷贝过来

    创建目录WEB-INF/lib

    从tomcat拷贝一个web.xml到WEB-INF目录下

    在根目录下创建一个jsp页面

    复制代码
    <%@page import="com.aidata.Person"%>
    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title>Insert title here</title>
    </head>
    <body>
    
        <%
          Person person = new Person();
          System.out.println(person.getPersonInfo());
        %>
        
    </body>
    </html>
    复制代码

    将根目录拷贝到tomcat的webapps目录下

    启动tomcat

    浏览器输入http://localhost:8080/WebContent/hello.jsp,命令行窗口会打印

    Tomcat8 IDEA 乱码

    IDEA控制台乱码:找到IDEA的 idea64.exe.vmoptions 文件,添加  -Dfile.encoding=UTF-8 

    浏览器乱码:确保html或jsp文件保存的编码格式为UTF-8,可以notepad++转为utf-8编码格式

     二、了解Servlet

    Java Web项目的构成:

    • 需要自己的代码和它依赖的第三方库
      • Servlet、JSP、过滤器、监听器......
      • HTML、JS、CSS......
    • 部属描述符,其中包含了部属和启动应用程序的指令
    • 可以添加ClassLoader用于将自己的应用程序与同一台服务器上的其他Web应用程序隔离
    • 通过某种方式将应用程序打包,生成WAR和EAR文

    Servlet是JavaEE规范的一种,主要是为了扩展Java作为Web服务的功能。Servlet就是一群人来制定java应用中使用web时的各种规范,统一接口,其他内部实现由厂商自己实现,tomcat、jetty、jboss等等应运而生,面向接口编程。我们讲Servlet,默认是说Servlet实现,Servlet的实现是一个运行在Web服务器中的Java小程序,接收和响应来自Web客户端的请求,使用HTTP进行通信。

    可以这么理解,JCP指定规范,提供接口,Web容器厂商实现了Web接收、响应等功能,我们实现具体的业务逻辑功能。

    2.1 Servlet容器与Servlet

    Servlet容器为Java Web应用提供运行时环境,它负责管理Servlet和JSP的生命周期,以及管理它们的共享数据,从下图可知,客户Web浏览器只与Web容器打交道。

    Servlet生命周期相关方法,这些方法都是Servlet容器负责调用:

    • 构造器:第一次请求Servlet时,创建Servlet实例,调用构造器,只被调用一次,单例
    • init方法:只被调用一次,在创建好实例后立即被调用,用于初始化当前Servlet
    • service:被多次调用,每次请求都会调用,实际用于响应请求
    • destroy:只被调用一次,在当前Servlet所在的WEB应用被卸载前调用,用于释放当前Servlet所占用的资源

     

     Servlet容器响应客户请求:

     2.2 Servlet的API

    API包含了两个软件包,十二个接口和九个类。

    软件包:javax.servlet

       所包含的接口:RequestDispatcher;Servlet;ServletConfig;ServletContext;ServletRequest;ServletResponse;SingleThreadModel。

       所包含的类:GenericServlet;ServletInputStream;ServletOutputStream;ServletException;UnavailableException。

    软件包:javax.servlet.http

           所包含的接口:HttpServletRequest;HttpServletResponse;HttpSession;HttpSessionBindingListener;HttpSessionContext。

           所包含的类:Cookie;HttpServlet;HttpSessionBindingEvent;HttpUtils。

     

    三、创建第一个Servlet——只实现Servlet接口的最简单Servlet

     步骤:

    • 新建web项目
    • 在src中新建包,包下新建类,该类实现Servlet接口,重写要实现的方法
    • 配置web.xml

     3.1 创建Web项目

    3.1.1 Eclipse

    不使用Mavenhttps://www.cnblogs.com/greenteaone/p/7929908.html

    使用Mavenhttps://blog.csdn.net/huijiahekele/article/details/78589680

    3.1.2 IDEA

     不使用Mavenhttps://www.cnblogs.com/cangqinglang/p/10027199.html

    使用Maven

    创建maven项目

     配置tomcat

    选project structure

     选tomcat

     

     去 Apache网站 http://tomcat.apache.org/whichversion.html 查看tomcat的servlet版本

    Servlet SpecJSP SpecEL SpecWebSocket SpecAuthentication (JASIC) SpecApache Tomcat VersionLatest Released VersionSupported Java Versions
    5.0 3.0 4.0 2.0 2.0 10.0.x 10.0.0-M5 (alpha) 8 and later
    4.0 2.3 3.0 1.1 1.1 9.0.x 9.0.35 8 and later
    3.1 2.3 3.0 1.1 1.1 8.5.x 8.5.55 7 and later
    3.1 2.3 3.0 1.1 N/A 8.0.x (superseded) 8.0.53 (superseded) 7 and later
    3.0 2.2 2.2 1.1 N/A 7.0.x 7.0.104 6 and later
    (7 and later for WebSocket)
    2.5 2.1 2.1 N/A N/A 6.0.x (archived) 6.0.53 (archived) 5 and later
    2.4 2.0 N/A N/A N/A 5.5.x (archived) 5.5.36 (archived) 1.4 and later
    2.3 1.2 N/A N/A N/A 4.1.x (archived) 4.1.40 (archived) 1.3 and later
    2.2 1.1 N/A N/A N/A 3.3.x (archived) 3.3.2 (archived) 1.1 and later

     根据servlet版本配置 web.xml

    3.0

    <?xml version="1.0" encoding="UTF-8"?>  
    <web-app
            version="3.0"  
            xmlns="http://java.sun.com/xml/ns/javaee"  
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
            xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">  
       
    </web-app>

    4.0

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
      version="4.0">
    
    
    </web-app>

     pom.xml中加入jstl和standard依赖

        <dependency>
          <groupId>javax.servlet</groupId>
          <artifactId>jstl</artifactId>
          <version>1.2</version>
        </dependency>
    
        <dependency>
          <groupId>taglibs</groupId>
          <artifactId>standard</artifactId>
          <version>1.1.2</version>
        </dependency>

    在JSP页面引入核心标签库的代码为:<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

    开启EL表达式:<%@page isELIgnored="false"%>

     3.2 实现Servlet接口

    需要实现的方法:

    • public void init(ServletConfig servletConfig)  初始化,只会调用一次
    • public ServletConfig getServletConfig()  返回一个 ServletConfig 对象,该对象用来返回初始化参数和 ServletContext。ServletContext 接口提供有关 servlet 的环境信息。
    • public void service(ServletRequest servletRequest, ServletResponse servletResponse)  核心方法
    • public String getServletInfo()  可选,它提供有关 servlet 的信息,如作者、版本、版权。
    • public void destroy()  Servlet卸载前调用
    • 可以编写构造方法
    package com.aidata.javawe;
    
    import javax.servlet.*;
    import java.io.IOException;
    
    public class HelloServlet implements Servlet {
        @Override
        public void init(ServletConfig servletConfig) throws ServletException {
            System.out.println("init");
        }
    
        @Override
        public ServletConfig getServletConfig() {
    
            return null;
        }
    
        @Override
        public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
            System.out.println("service");
        }
    
        @Override
        public String getServletInfo() {
            System.out.println("getServletInfo");
            return null;
        }
    
        @Override
        public void destroy() {
            System.out.println("destroy......");
        }
    
        // 构造器
        public HelloServlet(){
            System.out.println("HelloServlet's constructor");
        }
    }

     

    init(ServletConfig servletConfig)方法

    第一个请求到达之时Servlet启动,init方法在Servlet构造完成之后调用。显然,如果init方法要完成大量工作,当第一个请求达到时,init方法执行会花大量时间。

    ServletConfig:封装了Servle的t配置信息,并且可以获取ServletContext对象

    ServletContext接口:

    一个Web应用程序中的所有Servlet都共享同一个ServletContext对象,所以,ServletContext对象被称之为 application 对象(Web应用程序对象)。 
    Servlet引擎为每个Web应用程序都创建一个对应的ServletContext对象,ServletContext对象被包含在ServletConfig对象中,调用ServletConfig.getServletContext方法可以返回ServletContext对象的引用。
    功能:
    可以认为是当前Web的一个大管家,可以获得当前Web应用各个方面的信息

    • ·获取WEB应用程序的初始化参数
    • ·记录日志
    • ·application域范围的属性
    • ·访问资源文件
    • ·获取虚拟路径所映射的本地路径
    • ·Web应用程序之间的访问
    • ·ServletContext的其他方法

    获取当前web应用的某一个文件在服务器上的绝对路径,而不是部属前的路径:
    getRealPath(String path)

    String realPath = servletContext.getRealPath("/index.jsp");
    System.out.println(realPath);

    结果

    C:UsersJieZIdeaProjectsWebDemooutartifactsfirstweb_war_explodedindex.jsp
    注意是在out路径里

    获取当前WEB应用的名称

    String contextPath = servletContext.getContextPath();
    System.out.println(contextPath);

    获取当前WEB应用的某一个文件对应的输入流
    getResourceAsStream(String path): path的/为当前的根目录

     try {
         ClassLoader classLoader = getClass().getClassLoader();
         InputStream is = classLoader.getResourceAsStream("jdbc.properties");
         System.out.println("1." + is);
     }catch (Exception e){
         e.printStackTrace();
     }
    
    try {
        InputStream is2 = servletContext.getResourceAsStream("/WEB-INF/classes/jdbc.properties");
        System.out.println("2." + is2);
    }catch (Exception e){
        e.printStackTrace();
    }

    结果

    1.java.io.BufferedInputStream@400c929d
    2.java.io.FileInputStream@7d42e4bb

    3.3 在web.xml中配置Servlet

    Servlet程序必须通过Servlet容器来启动运行,并且储存目录有特殊要求,通需要存储在<WEB应用程序目录>WEB-INFclasses目录中。 Servlet程序必须在WEB应用程序的web.xml文件中进行注册和映射其访问路径,才可以被Servlet引擎加载和被外界访问。

    web.xml中重要的两个标签:

    • <servlet>元素用于注册一个Servlet,它包含有两个主要的子元素:<servlet-name><servlet-class>,分别用于设置Servlet的注册名称和Servlet的完整类名。
    • <servlet-mapping>元素用于映射一个已注册的Servlet的一个对外访问路径,它包含有两个子元素:<servlet-name><url-pattern>,分别用于指定Servlet的注册名称和Servlet的对外访问路径。
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
             version="4.0">
    
        <!-- 配置和映射Servlet -->
        <servlet>
            <servlet-name>helloServlet</servlet-name>
            <servlet-class>com.aidata.javawe.HelloServlet</servlet-class>
            
        </servlet>
        <servlet-mapping>
            <servlet-name>helloServlet</servlet-name>
            <url-pattern>/hello</url-pattern>
        </servlet-mapping>
    </web-app>

    同一个Servlet可以被映射到多个URL上,即多个<servlet-mapping>元素的<servlet-name>子元素的设置值可以是同一个Servlet的注册名。
    在Servlet映射到的URL中也可以使用*通配符,但是只能有两种固定的格式:一种格式是“*.扩展名”,另一种格式是以正斜杠(/)开头并以“/*”结尾。

    配置步骤

    • 向描述符中添加Servlet
    • 将Servlet映射到URL

    load-on-startup参数

    前面提到过init方法如果有大量工作,当一个请求达到时才开始执行,会大大影响请求回应的效率

    可以指定Servlet被创建的时机,若为负数,则在第一次请求时被创建,若为0或正数,则在当前WEB应用被Servlet容器加载时创建实例。当多个Servlet配置都包含该标签,它们将按照标签内的值的大小顺序启动,且数值越小越早被创建。

    <servlet>
        <servlet-name>helloServlet</servlet-name>
        <servlet-class>com.aidata.javawe.HelloServlet</servlet-class>
        <load-on-startup>2</load-on-startup>
    </servlet>

    Servlet的初始化参数

    可以在web.xml中的servlet标签的init-param子标签内配置Servlet的初始化参数

    <servlet>
        <servlet-name>helloServlet</servlet-name>
        <servlet-class>com.aidata.javawe.HelloServlet</servlet-class>
    
        <!--配置Servlet的初始化参数,且该节点必须在load-on-startup的前面-->
        <init-param>
            <!--参数名-->
            <param-name>user</param-name>
            <!--参数值-->
            <param-value>root</param-value>
        </init-param>
        <init-param>
            <param-name>password</param-name>
            <param-value>1230</param-value>
        </init-param>
        <load-on-startup>-1</load-on-startup>
    </servlet>

    获取Servlet初始化参数

    servletConfig.getInitParameter(String name):获取指定参数名的初始化参数
    servletConfig.getInitParameterNames():获取参数名组成的Enumeration对象

    public class HelloServlet implements Servlet {
        @Override
        public void init(ServletConfig servletConfig) throws ServletException {
            System.out.println("init");
            String user = servletConfig.getInitParameter("user");
            System.out.println("user:"+user);
    
            Enumeration<String> names = servletConfig.getInitParameterNames();
            while (names.hasMoreElements()){
                String name = names.nextElement();
                String value = servletConfig.getInitParameter(name);
                System.out.println("^^"+name+":"+value);
            }
        }

    结果

    init
    user:root
    ^^password:1230
    ^^user:root

    Web应用的初始化参数

    可以在web.xml中配置整个应用的初始化参数

    使用标签:

    <context-param>
            <param-name>xxx</param-name>
            <param-value>xxx</param-value>
    </context-param>
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
             version="4.0">
    
        <!--配置当前Web应用的初始化参数-->
        <context-param>
            <param-name>driver</param-name>
            <param-value>com.mysql.jdbc.Driver</param-value>
        </context-param>
        <context-param>
            <param-name>jdbcUrl</param-name>
            <param-value>jdbc:mysql:///aidata</param-value>
        </context-param>
    
        <!-- 配置和映射Servlet -->
        <servlet>
            <servlet-name>helloServlet</servlet-name>
            <servlet-class>com.aidata.javawe.HelloServlet</servlet-class>
    
            <!--配置Servlet的初始化参数,且该节点必须在load-on-startup的前面-->
            <init-param>
                <!--参数名-->
                <param-name>user</param-name>
                <!--参数值-->
                <param-value>root</param-value>
            </init-param>
            <init-param>
                <param-name>password</param-name>
                <param-value>1230</param-value>
            </init-param>
            <load-on-startup>-1</load-on-startup>
        </servlet>
        <servlet-mapping>
            <servlet-name>helloServlet</servlet-name>
            <url-pattern>/hello</url-pattern>
        </servlet-mapping>
    </web-app>

    获取整个应用的初始化参数则使用:

    servletContext.getInitParameter(String name):获取指定参数名的初始化参数
    servletContext.getInitParameterNames():获取参数名组成的Enumeration对象

    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
        System.out.println("init");
     
        ServletContext servletContext = servletConfig.getServletContext();
        String driver = servletContext.getInitParameter("driver");
        System.out.println("driver:"+driver);
    
        Enumeration<String> name2 = servletContext.getInitParameterNames();
        while (name2.hasMoreElements()){
            String name = name2.nextElement();
            System.out.println("--->"+name);
        }
    }

    这里设置的初始化参数可以被所有的Servlet所获取,而Servlet的初始化参数只有那个Sevlet可以获取

    四、HTTP、ServletRequest和ServletResponse

     4.1 HTTP协议、Get方法和Post方法

    Web浏览器与Web服务器之间的一问一答的交互过程必须遵循一定的规则,这个规则就是HTTP协议,HTTP是超文本传输协议的简写,它是TCP/IP协议集中的一个应用层协议,用于定义Web浏览器与Web服务器之间交换数据的过程以及数据本身的格式。
    HTTP协议的版本HTTP/1.0、HTTP/1.1、HTTP-NG

     请求数据包

     响应数据包

     响应状态码

     HTTP的会话方式

     浏览器访问多图页面的过程,每个图片都是一对单独的请求和响应:

     Get和Post

    GET请求把请求参数附在url的后面,用?连接,是一个个键值对。在浏览器地址栏中输入某个URL地址或单击网页上的一个超链接时,浏览器发出的HTTP请求消息的请求方式为GET,使用GET方式传递的数据量一般限制在1KB以下。

     Post主要用于传递表单信息,传递的信息要大的多。

     4.2 ServletRequest和ServletResponse

     我们知道了浏览器会像服务端发送HTTP请求,那么我们的Servlet是如何处理请求的呢?

    两个接口:

    ServletRequest:封装了请求信息,可以从中获取到任何的请求信息。官方文档http://tomcat.apache.org/tomcat-5.5-doc/servletapi/javax/servlet/ServletRequest.html
    ServletResponse:封装了响应信息,如果想给给用户什么响应,具体可以使用接口的方法实现。官方文档http://tomcat.apache.org/tomcat-5.5-doc/servletapi/javax/servlet/ServletResponse.html
    这两个接口的实现类,都是服务器给予实现的,并在服务器调用service方法时传入。

    ServletRequest

    封装了请求信息. 可以从中获取到任何的请求信息。

    获取请求参数

    String getParameter(String name)

    根据请求参数的名字, 返回参数值,
    该方法只能获取到第一个提交的值,若请求参数有多个值(例如 checkbox), 要用下一个方法

     HTML

    <form action="loginServlet" method="post">
        user: <input type="text" name="user"/>
        password: <input type="text" name="password">
        <input type="submit" value="Submit"/>
    </form>

    Servlet

    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        System.out.println("请求来了...");
        System.out.println(servletRequest);
        String user = servletRequest.getParameter("user");
        String password = servletRequest.getParameter("password");
        System.out.println(user + "," + password);
    }

    结果

    "请求来了..."
    org.apache.catalina.connector.RequestFacade@44fe4cd9                        
    john,
    123

    String[] getParameterValues(String name)

    根据请求参数的名字, 返回请求参数对应的字符串数组。

    需要一组参数,用多选框。

     HTML

    <form action="loginServlet" method="post">
        user: <input type="text" name="user"/>
        password: <input type="text" name="password">
        <br><br>
        interesting:
        <input type="checkbox" name="interesting" value="reading"/>Reading
        <input type="checkbox" name="interesting" value="game"/>Game
        <input type="checkbox" name="interesting" value="party"/>Party
        <input type="checkbox" name="interesting" value="sport"/>Sport
        <input type="checkbox" name="interesting" value="tv"/>TV
        <input type="submit" value="Submit"/>
    </form>

    Servlet

    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        System.out.println("请求来了...");
    
        String interesting = servletRequest.getParameter("interesting");
        System.out.println(interesting);
        String[] interestings = servletRequest.getParameterValues("interesting");
        for (String interest: interestings){
            System.out.println("-->" + interest);
        }
    }

    结果

    "请求来了..."
    reading  
    -->reading
    -->game
    -->party
    -->sport    

    Enumeration getParameterNames()

    返回参数名对应的 Enumeration 对象,
    类似于 ServletConfig(或 ServletContext) 的 getInitParameterNames() 方法。


    Map getParameterMap()

    返回请求参数的键值对,

    key:参数名, value: 参数值, String 数组类型。

     Servlet

    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        System.out.println("请求来了...");
       
        String[] interestings = servletRequest.getParameterValues("interesting");
        for (String interest: interestings){
            System.out.println("-->" + interest);
        }
        Enumeration<String> names = servletRequest.getParameterNames();
        while (names.hasMoreElements()){
            String name = names.nextElement();
            String val = servletRequest.getParameter(name);
            System.out.println("^^"+name + ": " + val);
        }
        Map<String, String[]> map = servletRequest.getParameterMap();
        for (Map.Entry<String, String[]> entry: map.entrySet()){
            System.out.println("**" + entry.getKey() + ":" + Arrays.asList(entry.getValue()));
        }
    }

    结果

    -->reading
    -->game
    -->party
    -->sport
    ^^user: zhaojie
    ^^password: 123
    ^^interesting: reading 
    **user:[zj]
    **password:[123]
    **interesting:[reading, game, party, sport]

    HttpServletRequest

    是 SerlvetRequest 的子接口,针对于 HTTP 请求所定义,里边包含了大量获取 HTTP 请求相关的方法。 

    获取请求的 URI

    获取请求方式

     httpServletRequest.getMethod()  

    HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
    String requestURI = httpServletRequest.getRequestURI();
    System.out.println(requestURI);
    
    String method = httpServletRequest.getMethod();
    System.out.println(method);

    结果

    /firstweb/loginServlet
    POST

    若是一个 GET 请求, 获取请求参数对应的那个字符串, 即?后的那个字符串

    String queryString = httpServletRequest.getQueryString();
    System.out.println(queryString); 

    结果

    user=aidata&password=123456&interesting=game&interesting=party&interesting=shopping

    获取请求的 Serlvet 的映射路径

    String servletPath = httpServletRequest.getServletPath();
    System.out.println(servletPath); 

    结果

    /loginServlet

    attribute 相关的几个方法

    ServletResponse

    封装了响应信息, 如果想给用户什么响应, 具体可以使用该接口的方法实现。

    getWriter()

    返回 PrintWriter 对象. 调用该对象的 print() 方法, 将把 print() 中的参数直接打印到客户的浏览器上。

    设置响应的内容类型

    response.setContentType("application/msword");
    指定为word类型,点击提交会下载word文档

    servletResponse.setContentType("application/msword"); //word类型
    PrintWriter out = servletResponse.getWriter();
    out.println("helloworld...");

    void sendRedirect(String location)

    请求的重定向. (此方法为 HttpServletResponse 中定义,需要强转)

    五、从自定义GenericServlet到HttpServlet

     5.1 编写一个Servlet,发现问题

    在 web.xml 文件中设置两个Web应用的初始化参数user和password。定义一个 login.html, 里边定义两个请求字段: user, password. 发送请求到 loginServlet。创建一个 LoginServlet, 在其中获取请求的user和password,比对其和 web.xml 文件中定义的请求参数是否一致。若一致, 响应 Hello:xxx, 若不一致, 响应 Sorry: xxx xxx 为 user。

    web.xml

    <!-- 配置当前WEB应用初始化参数-->
    <context-param>
        <param-name>user</param-name>
        <param-value>atguigu</param-value>
    </context-param>
    <context-param>
        <param-name>password</param-name>
        <param-value>123567</param-value>
    </context-param>
    <servlet>
        <servlet-name>loginServlet</servlet-name>
        <servlet-class>com.atguigu.javaweb.LoginServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>loginServlet</servlet-name>
        <url-pattern>/loginServlet</url-pattern>
    </servlet-mapping>

    HTML

    <form action="loginServlet" method="post">
        user: <input type="text" name="username"/>
        password: <input type="password" name="password">
        <input type="submit" value="Submit"/>
    </form>

    <form action="loginServlet" ...>中的loginServlet是url不是Servlet的名称

     Servlet

    private ServletConfig servletConfig;
    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
        this.servletConfig = servletConfig; // 为了在service方法中使用servletConfig
    }
    
    @Override
    public ServletConfig getServletConfig() {
        return null;
    }
    
        @Override
    public String getServletInfo() {
          // TODO Auto-generated method stub
          return null;
    }
    
     @Override
        public void destroy() {
            // TODO Auto-generated method stub       
    }
    
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
         //1.获取请求参数:username,password
         String username = servletRequest.getParameter("username");
         String password = servletRequest.getParameter("password");
         //2.获取WEB应用的初始化参数:user,password
         ServletContext servletContext = servletConfig.getServletContext();
         String initUser = servletContext.getInitParameter("user");
         String initPassword = servletContext.getInitParameter("password");
    
        PrintWriter out = servletResponse.getWriter();
         //3.比对
        //4.打印响应字符串
         if (initUser.equals(username) && initPassword.equals(password)){
             out.println("Hello" + username);
         }else {
             out.println("sorry" + username);
         }
    }

    我们发现当实现Servlet接口的时候,有很多方法如getServletInfo()根本用不到,但是还是不得不实现,导致了大量的空方法。

    为了在service()中使用servletConfig,还定义了一个属性。

    其实,有一个抽象类GenericServlet可以简化我们的工作。

    官方文档http://tomcat.apache.org/tomcat-5.5-doc/servletapi/javax/servlet/GenericServlet.html

    5.2 编写自己的GenericServlet

    方便开发,编写如下抽象类,实现Serclet和ServletConfig接口

    import javax.servlet.*;
    import java.io.IOException;
    import java.util.Enumeration;
    
    /**
     * 自定义的一个Servlet接口的一个实现类,目标是让开发的任何Servlet都继承,以简化开发
     */
    public abstract class MyGenericServlet  implements Servlet, ServletConfig {
        /**
         * Servlet的方法
         */
        private ServletConfig servletConfig;
        @Override
        public void init(ServletConfig servletConfig) throws ServletException {
             this.servletConfig = servletConfig;
        }
    
        @Override
        public ServletConfig getServletConfig() {
            return null;
        }
    
        @Override
        public abstract void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException;
    
        @Override
        public String getServletInfo() {
            return null;
        }
    
        @Override
        public void destroy() {}
    
    
    
        /**
         * ServletConfig接口的方法
         */
    
        @Override
        public String getServletName() {
            return servletConfig.getServletName();
        }
    
        @Override
        public ServletContext getServletContext() {
            return servletConfig.getServletContext();
        }
    
        @Override
        public String getInitParameter(String s) {
            return servletConfig.getInitParameter(s);
        }
    
        @Override
        public Enumeration<String> getInitParameterNames() {
            return servletConfig.getInitParameterNames();
        }
    
    }

    GenericServlet是Servlet接口和ServletConfig接口的实现类,但是一个抽象类,其中的service方法为抽象方法
    如果新建的Servlet程序直接继承GenericServlet会使开发更简洁
    官方具体实现:

    • 在GenericServlet中声明了一个ServletConfig类的成员变量,在init(ServletConfig)方法中对器进行初始化
    • 利用ServletConfig成员变量的方法实现了ServletConfig接口的方法
    • 定义了一个init()方法,在init(ServletConfig)方法中对其进行调用,子类可直接覆盖init()在其中实现对Servlet的初始化
    • 不建议直接覆盖init(ServletConfig),因为如果忘记编写super(ServletConfig),而还是用了ServletConfig接口的方法,则会出现空指针异常
    • 新建的init(){}并非Servlet的生命周期方法,而init(ServletConfig)是生命周期相关的方法

     5.3 编写自己的HttpServlet

    GenericServlet是一个不依赖具体协议的Servlet,它只包含了一个抽象的service方法和几个辅助方法。缺乏对HTTP的支持,比如,当ServletRequest想要获取请求方式是GET还是POST,需要调用HttpServlet,需要强转,还是麻烦。

    编写一个支持HTTP的Servlet

    /**
     * 针对HTTP协议定义的一个Servlet类
     */
    public class MyHttpServlet extends MyGenericServlet{
        @Override
        public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
            if (servletRequest instanceof HttpServletRequest){
                HttpServletRequest request = (HttpServletRequest) servletRequest;
    
            if (servletResponse instanceof HttpServletResponse){
                HttpServletResponse response = (HttpServletResponse)servletResponse;
                service(request, response);
            }}
        }
    
        private void service(HttpServletRequest servletRequest, HttpServletResponse servletResponse) throws ServletException, IOException{
            //1.获取请求方式
            String method = servletRequest.getMethod();
            //2.根据请求方式再创建对应的处理方法
            if ("GET".equalsIgnoreCase(method)){
                doGet(servletRequest, servletResponse);
            }
            if ("POST".equalsIgnoreCase(method)){
                doPost(servletRequest, servletResponse);
            }
    
        }
        public void doGet(HttpServletRequest servletRequest, HttpServletResponse servletResponse) throws ServletException, IOException{
    
        }
        public void doPost(HttpServletRequest servletRequest, HttpServletResponse servletResponse) throws ServletException, IOException {
    
        }
    }

    于是,只要继承上面的类就可以大大简化开发

    public class LoginServlet2 extends MyHttpServlet {
        @Override
        public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
           String method = req.getMethod(); //不再用强转
            System.out.println(method);
    
            //1.获取请求参数:username,password
            String username = req.getParameter("username");
            String password = req.getParameter("password");
            System.out.println("--------------" + username);
            //2.获取WEB应用的初始化参数:user,password
    
            String initUser = getServletContext().getInitParameter("user");
            String initPassword = getServletContext().getInitParameter("password");
    
            PrintWriter out = resp.getWriter();
            //3.比对
            //4.打印响应字符串
            if (initUser.equals(username) && initPassword.equals(password)){
                out.println("Hello" + username);
            }else {
                out.println("sorry" + username);
            }
        }
    }

    上面的定义的类基本实现了一般开发中常用的HttpServlet
    HttpServlet继承自GenericServlet,针对于HTTP协议定制
    在service()方法中,直接把ServletRequest和ServletResponse转为HttpServletRequest和HttpServletResponse,并调用了重载的service(HttpServletRequest, HttpServletResponse)
    在service(HttpServletRequest , HttpServletResponse)获取了请求方式:reque.getMethod(),根据请求方式又创建了doXxx()方法(Xxx为具体的请求方式,比如doGet、doPost)。

    实际开发中,直接继承HttpServlet,并根据请求方式复写doXxx()方法接口
    好处:直接有针对性覆盖doXxx()方法,直接使用HttpServletRequest和HttpServletResponse,不再需要强转

    一个字:

    棒!

  • 相关阅读:
    Spring之InstantiationAwareBeanPostProcessor接口介绍
    Spring之BeanPostProcessor(后置处理器)介绍
    JVM中垃圾收集算法总结
    JVM中对象的回收过程
    zookeeper实现动态感知服务器上下线
    Spring事务的传播行为
    java工厂模式
    Spring加载流程源码分析03【refresh】
    Redis客户端操作之Jedis
    微服务设计的四个原则
  • 原文地址:https://www.cnblogs.com/aidata/p/11974648.html
Copyright © 2011-2022 走看看