• Javaweb:Servlet


    servlet简介

    Servlet(Server Applet)是 Java Servlet 的简称,是使用 Java 语言编写的运行在服务器端的程序。具有独立于平台和协议的特性,主要功能在于交互式地浏览和生成数据,生成动态Web内容。通常来说,Servlet 是指所有实现了 Servlet 接口的类。Servlet的主要工作流程如下图:

    Servlet 的请求首先会被 HTTP 服务器(如 Apache)接收,HTTP 服务器只负责静态 HTML 页面的解析,而 Servlet 的请求会转交给 Servlet 容器,Servlet 容器会根据 web.xml 文件中的映射关系,调用相应的 Servlet,Servlet 再将处理的结果返回给 Servlet 容器,并通过 HTTP 服务器将响应传输给客户端。

    Servlet 主要用于处理客户端传来的 HTTP 请求,并返回一个响应,它能够处理的请求有 doGet() 和 doPost() 等。
    Servlet 由 Servlet 容器提供,Servlet 容器是指提供了 Servlet 功能的服务器(如 Tomcat)。
    Servlet 容器会将 Servlet 动态加载到服务器上,然后通过 HTTP 请求和 HTTP 应与客户端进行交互。

    总结

    • servlet 是 JavaEE 规范之一,规范就是接口
    • servlet 是 JavaWeb三大组件之一。其余两个组件是Filter过滤器,Listener监听器
    • Servlet 是运行在服务器上的一个Java小程序。
    • 介于浏览器和web服务器之间的中间层,servlet可以获取浏览器的数据将其交给服务器,也可以将服务器端的数据库中信息交给浏览器。
    • Servlet 可以使用 javax.servlet 和 javax.servlet.http 包创建

    Tomcat与Servlet的关系

    Tomcat 是Web应用服务器,是一个Servlet/JSP容器. Tomcat 作为Servlet容器,负责处理客户请求,把请求传送给Servlet,并将Servlet的响应传送回给客户.而Servlet是一种运行在支持Java语言的服务器上的组件。
    Servlet最常见的用途是扩展Java Web服务器功能,提供非常安全的,可移植的,易于使用的CGI替代品。
    从http协议中的请求和响应可以得知,浏览器发出的请求是一个请求文本,而浏览器接收到的也应该是一个响应文本。

    tomcat 服务器文件的含义

    • bin:二进制执行文件。里面最常用的文件是startup.bat,如果是 Linux 或 Mac 系统启动文件为 startup.sh。
    • conf:配置目录。里面最核心的文件是server.xml。可以在里面改端口号等。默认端口号是8080,也就是说,此端口号不能被其他应用程序占用。
    • lib:库文件。tomcat运行时需要的jar包所在的目录
    • logs:日志
    • temp:临时产生的文件,即缓存
    • webapps:web的应用程序。web应用放置到此目录下浏览器可以直接访问
    • work:编译以后的class文件。

    Servlet 涉及的类和接口

    在了解Servlet 涉及的类和接口之前,我们先对servlet规范核心类有个大致的映像:

    接下来我们要说的是Servlet的四个类:

    ServletConfig对象,ServletContext对象。以及request对象,response对象(另一个专题再讲)

    Servlet类的继承体系

    Servlet 接口----->  GenericServlet 类 ------> HttpServlet 类 -----> HttpServlet的实现类
    
    • Servlet 接口 定义了Servlet的行为规范
    • GenericServlet 类声明了 Servlet接口,做了很多空实现,并持有一个ServletConfig类的引用
    • HttpServlet 类实现了Service方法,对浏览器的GET和POST请求做了分发处理
    • HttpServlet的实现类 根据业务需求重写doGet和doPost方法即可

    ServletConfig类

    在使用 声明Servlet接口 这个方法创建Servlet实例时会出现ServletConfig类。这个类定义了一些可以返回关于Servlet信息的方法。但是在除init方法外也可以调用 getServletConfig() 来获取该Servlet的ServletConfig类对象。
    一个Servlet对象有对应的一个ServletConfig对象。

    参数:

    • getServletName() :获取servlet的名称,也就是我们在web.xml中配置的servlet-name
    • getServletContext():获取ServletContext对象,该对象的作用看下面讲解
    • getInitParameter(String):获取在servlet中初始化参数的值。这里注意与全局初始化参数的区分。这个获取的只是在该servlet下的初始化参数
    • getInitParameterNames():获取在Servlet中所有初始化参数的名字,也就是key值,可以通过key值,来找到各个初始化参数的value值。注意返回的是枚举类型

    获取ServletConfig对象:通过继承父类(HttpServlet或GenericServlet)的方法得到一个ServletConfig对象

    ServletConfig config = this.getServletConfig();
    

    作用:

    • 可以获取 Servlet程序的别名,即web.xml中 Servlet-name 标签中的值。
    • 获取初始化参数 init-param。
    • 获取 ServletContext 对象。

    示例:

    public class HelloServlet implements Servlet {
    
        @Override
        public void init(ServletConfig servletConfig) throws ServletException {
            String initParameter = servletConfig.getInitParameter("abc");	//1234
            String servletName = servletConfig.getServletName();	//HelloServlet
            ServletContext servletContext = servletConfig.getServletContext();
            //org.apache.catalina.core.ApplicationContextFacade@515de15
        }
        @Override
        public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
            ServletConfig servletConfig = getServletConfig();	
        }
        ...
    }
    

    web.xml中内容

    <servlet>
        <servlet-name>HelloServlet</servlet-name>
        <servlet-class>com.hsh.test.HelloServlet</servlet-class>
        <init-param>				<!--初始化参数必须定义在servlet标签内,且只有当前servlet可以使用该参数 -->
            <param-name>abc</param-name>
            <param-value>1234</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>HelloServlet</servlet-name>
        <url-pattern>/hello</url-pattern>
    </servlet-mapping>
    

    ServletContext 接口

    tomcat为每个web项目都创建一个ServletContext实例,tomcat在启动时创建,服务器关闭时销毁,在一个web项目中共享数据,管理web项目资源,为整个web配置公共信息等,通俗点讲,就是一个web项目,就存在一个ServletContext实例,每个Servlet读可以访问到它。

    概述:

    • 一个接口,表示Servlet的上下文对象。
    • 一个Web工程对应一个ServletContext对象。
    • ServletContext是一个域对象,这个域就是整个web工程。
    • ServletContext对象在web工程启动时创建,关闭时销毁。

    域对象:像map一样存取数据的对象,这里的域指的是存取数据的操作范围。域对象存取数据一般为setAttribute(),getAttribute()。

    参数:

    • setAttribute(String name, Object obj) 在web项目范围内存放内容,以便让在web项目中所有的servlet读能访问到
    • getAttribute(String name) 通过指定名称获得内容
    • removeAttribute(String name) 通过指定名称移除内容
    • getInitPatameter(String name)通过指定名称获取初始化值
    • getInitParameterNames()  获得枚举类型
    • String getRealPath(String path)根据资源名称得到资源的绝对路径

    代码如下:

        ServletContext servletContext = this.getServletContext();
        String path = servletContext.getRealPath("/WEB-INF/web.xml");
        System.out.println(path);
    
    F:myworkspaceServletTestoutartifactsServletTest_war_explodedWEB-INFweb.xml
    
    • getResourceAsStream(java.lang.String path)获取web项目下指定资源的内容,返回的是字节输入流。InputStream

    代码如下:

    InputStream in  = servletContext.getResourceAsStream("/WEB-INF/web.xml");
            InputStreamReader isr = new InputStreamReader(in,"UTF-8");
            BufferedReader br = new BufferedReader(isr);
            String s = null;
            while((s =br.readLine())!=null){
                System.out.println(s);
            }
    
    • getResourcePaths(java.lang.String path) 指定路径下的所有内容。

    代码如下:

    Set set = servletContext.getResourcePaths("/WEB-INF");
    Iterator iterator = set.iterator();
    while(iterator.hasNext()){
        System.out.println(iterator.next());
    }
    
    • RequestDispatcher getRequestDispatcher(String path) 参数表示要跳转到哪去

    代码如下:

    RequestDispatcher rd = servletContext.getRequestDispatcher("/Servlet1");
    rd.forward(req,resp);
    

    作用:

    • 获取web.xml中配置的上下文参数 context-param
    • 获取当前工程路径,格式: /工程路径
    • 获取工程部署到服务器硬盘上的绝对路径
    • 向map一样存取数据

    示例:

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletContext context = getServletContext();
        String a = context.getInitParameter("a");
        String b = context.getInitParameter("b");
        String contextPath = context.getContextPath();
        String realPath = context.getRealPath("/resources");
        System.out.println("初始化参数:"+a+","+b);
        System.out.println("当前工程路径:"+contextPath); //输出的是当前module中web目录的地址
        System.out.println("工程部署路径:"+realPath);		//输出的是web目录下resources被部署在服务器端磁盘上的位置
        /**
        初始化参数:apple,banana
    	当前工程路径:/servletTest_war_exploded
    	工程部署路径:H:ideaworkpathworkspace01outartifactsservletTest_war_exploded
    esources
        */
        context.setAttribute("key1","value1");
        Object key1 = context.getAttribute("key1");
        System.out.println(key1);		//输出value1
    }
    

    web.xml中内容

    <!--ServletContext 对象只能获取到context-param标签下的参数,不能获取某个Servlet标签下的init-param标签的内容 -->
    <context-param>
        <param-name>a</param-name>
        <param-value>apple</param-value>
    </context-param>
    <context-param>
        <param-name>b</param-name>
        <param-value>banana</param-value>
    </context-param>
    

    工程结构图

    HttpServletRequest类

    每次有http请求发送给tomcat,tomcat就会把http协议信息解析好封装在Request对象中,然后传递到Service方法中供我们使用,我们用HttpServletRequest可以获取到所有的请求的信息。

    常用方法

    • getRequestURI():获取请求资源路径
    • getRequestURL():获取请求的统一资源路径(绝对路径)
    • getRemoteHost():获取客户端IP地址
    • getHeader(paraName):获取请求头中某个参数的值
    • getParameter(paraName):获取参数的值
    • getParameterValue():获取多个参数的值
    • getMethod():获取请求的方式(GET/POST)
    • setAttribute(key,value):设置域数据。 这个域数据的生命周期取决于当前的request
    • getAttribute(key):返回域数据
    • getRequestDispatcher():获取请求转发对象
    • req.setCharacterEncoding("UTF-8") : 解决中文乱码问题,要放在获取参数之前

    HttpServletResponse类

    每次有http请求发送给tomcat,tomcat都会创建一个response对象传递给Servlet使用,然后传递到Service方法中供我们使用,我们用HttpServletRequest可以获取到所有的请求的信息。

    getWriter() 和 getOutputStream() 两种输出字符流只能同时使用一个。

    • response.setCharacterEncoding("UTF-8") 设置服务器的字符集为UTF-8
    • response.setHeader("Content-Type","text/html;charset=UTF-8") 通过响应头设置浏览器也使用UTF-8字符集
    • response.setContentType("text/html;charset=UTF-8") 同时设置服务器,浏览器,响应头都使用UTF-8

    Servlet的生命周期

    Servlet生命周期可分为5个步骤

    • 加载Servlet。当Tomcat第一次访问Servlet的时候,Tomcat会负责创建Servlet的实例
    • 初始化。当Servlet被实例化后,Tomcat会调用init()方法初始化这个对象
      • 补充:当客户端向 Servlet 容器发出 HTTP 请求要求访问 Servlet 时,Servlet 容器首先会解析请求,检查内存中是否已经有了该 Servlet 对象,如果有,则直接使用该 Servlet 对象,如果没有,则创建 Servlet 实例对象,然后通过调用 init() 方法实现 Servlet 的初始化工作。需要注意的是,在 Servlet 的整个生命周期内,它的 init() 方法只能被调用一次。
    • 处理服务。当浏览器访问Servlet的时候,Servlet 会调用service()方法处理请求
      • 补充:这是 Servlet 生命周期中最重要的阶段,在这个阶段中,Servlet 容器会为这个请求创建代表 HTTP 请求的 ServletRequest 对象和代表 HTTP 响应的 ServletResponse 对象,然后将它们作为参数传递给 Servlet 的 service() 方法。
      • service() 方法从 ServletRequest 对象中获得客户请求信息并处理该请求,通过 ServletResponse 对象生成响应结果。
      • 在 Servlet 的整个生命周期内,对于 Servlet 的每一次访问请求,Servlet 容器都会调用一次 Servlet 的 service() 方法,并且创建新的 ServletRequest 和 ServletResponse 对象,也就是说,service() 方法在 Servlet 的整个生命周期中会被调用多次。
    • 销毁。当Tomcat关闭时或者检测到Servlet要从Tomcat删除的时候会自动调用destroy()方法,让该实例释放掉所占的资源。一个Servlet如果长时间不被使用的话,也会被Tomcat自动销毁
    • 卸载。当Servlet调用完destroy()方法后,等待垃圾回收。如果有需要再次使用这个Servlet,会重新调用init()方法进行初始化操作。
      • 补充:当服务器关闭或 Web 应用被移除出容器时,Servlet 随着 Web 应用的关闭而销毁。在销毁 Servlet 之前,Servlet 容器会调用 Servlet 的 destroy() 方法,以便让 Servlet 对象释放它所占用的资源。在 Servlet 的整个生命周期中,destroy() 方法也只能被调用一次。
      • 在这里,Servlet 对象一旦创建就会驻留在内存中等待客户端的访问,直到服务器关闭或 Web 应用被移除出容器时,Servlet 对象才会销毁。

    简单总结:只要访问Servlet,service()就会被调用。init()只有第一次访问Servlet的时候才会被调用。destroy()只有在Tomcat关闭的时候才会被调用。

    Servlet工作流程

    Servlet 的每次请求,Web 服务器在调用 service() 方法之前,都会创建 HttpServletRequest 和 HttpServletResponse 对象。其中,HttpServletRequest 对象用于封装 HTTP 请求消息,简称 request 对象。HttpServletResponse 对象用于封装 HTTP 响应消息,简称 response 对象

    在浏览器的地址栏输入:http://ip:port/appNames/servlet

    1)通过浏览器和ip:port和这个服务器建立连接。
    2) 浏览器会生成一个请求数据包(路径appNames/servlet)向服务器发送请求。
    3) 服务器收到请求数据包,分析请求资源路径做精准定位,通过请求的appName查找webapps文件下面的appName做匹配,匹配上了需要获取web.xml中的servlet(mapping)。 
    4) 服务器创建两个对象:

    • 第一个对象:请求对象,该对象实现了HttpServletRequest接口,服务器会将请求数据包中的数据解析出来,存储在该对象里。这样做的好处是没有必要理解http协议,只需要读取request。
    • 第二个对象:响应对象,实现了HttpServletResponse接口,作用是servlet处理完成后的结果可以存放到该对象上,然后服务器依据该对象的数据生成响应数据包。

    5) servlet在执行servlet()方法时,可以通过request获取请求数据,也可以将处理结果存放到response上。然后服务器与响应对象直接形成一个默契,生成一个响应数据包给浏览器。
    6)浏览器解析服务器返回的响应数据包,生成响应的结果。

    简单总结:Servlet访问的过程:

    Http请求---->web.xml--------> url -pattern----->servlet-name----->servlet-class-----> QuickStratServlet(对应的Class文件)

    下图有助于理解工作流程

    根据图中可见,当浏览器中发生了请求事件,

    • 会向 Web 服务器发送了一个 HTTP 请求;
    • Web 服务器根据收到的请求,会先创建一个 HttpServletRequest 和 HttpServletResponse 对象,然后再调用相应的 Servlet 程序。
    • 在 Servlet 程序运行时,它首先会从 HttpServletRequest 对象中读取数据信息,然后通过 service() 方法处理请求消息,并将处理后的响应数据写入到 HttpServletResponse 对象中。最后,Web 服务器会从 HttpServletResponse 对象中读取到响应数据,并发送给浏览器。

    注:在 Web 服务器运行阶段,每个 Servlet 都只会创建一个实例对象,针对每次 HTTP 请求,Web 服务器都会调用所请求 Servlet 实例的 service(HttpServletRequest request,HttpServletResponse response)方法,并重新创建一个 request 对象和一个 response 对象。

    servlet 表单数据

    浏览器使用两种方法可将信息传递到 Web 服务器,分别为 GET 方法和 POST 方法。

    前端的HTML页面中只有form表单用POST方式发送请求时使用的是http协议的POST方式,其余都为GET方式

    GET方法

    • 浏览器使用的默认方法,该方法会生成一个很长的字符串来包含浏览器中的信息,所以不适用于传输密码等重要信息。而且,GET方法有大小限制,最多传输1024个字符
    • 服务端用doGet() 方法来处理 GET请求

    POST方法

    -浏览器向后台传输数据的可靠方法,它会将信息包装成一个单独的消息,以标准的形式发送到服务端。服务端用doPost() 方法来处理POST请求

    使用servlet 读取表单数据

    Servlet 处理表单数据,这些数据会根据不同的情况使用不同的方法自动解析:

    • getParameter(): 调用 request.getParameter() 方法来获取表单参数的值。
    • getParameterValues():如果参数出现一次以上,则调用该方法,并返回多个值,例如复选框。
    • getParameterNames():如果想要得到当前请求中的所有参数的完整列表,则调用该方法。

    以GET方法为例:

    @WebServlet("/HelloForm")
    public class HelloForm extends HttpServlet {
       ... //省略构造方法
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            // 设置响应内容类型
            response.setContentType("text/html;charset=UTF-8");
         
            PrintWriter out = response.getWriter();
            String title = "使用 GET 方法读取表单数据";
            // 处理中文
            String name =new String(request.getParameter("name").getBytes("ISO-8859-1"),"UTF-8");
            String docType = "<!DOCTYPE html> 
    ";
            out.println(docType +
                "<html>
    " +
                "<head><title>" + title + "</title></head>
    " +
                "<body bgcolor="#f0f0f0">
    " +
                "<h1 align="center">" + title + "</h1>
    " +
                "<ul>
    " +
                "  <li><b>站点名</b>:"
                + name + "
    " +
                "  <li><b>网址</b>:"
                + request.getParameter("url") + "
    " +
                "</ul>
    " +
                "</body></html>");
        }
             
        // 处理 POST 方法请求的方法
        public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            doGet(request, response);
        }
    }
    

    在浏览器中输入URL:http://localhost:8080/TomcatTest/HelloForm?name=嘻嘻哈哈&url=www.xxhh.com,就能在页面中获取到name和 url 的显示

    这里不多介绍servlet相关参数和例子了,本篇主要还是介绍什么是servlet,让大家有个入门概念。之后在request、response的专题中再详细介绍,也会在javaweb例子中复习servlet的相关方法和知识。

    Servlet的url匹配顺序

    当一个请求发送到servlet容器的时候,容器先会将请求的url减去当前应用上下文的路径作为servlet的映射url,比如我访问的是http://localhost/atomy/user/aaa.html,我的应用上下文是atomy,容器会将http://localhost/atomy去掉,剩下的/user/aaa.html部分拿来做servlet的映射匹配。这个映射匹配过程是有顺序的,而且当有一个servlet匹配成功以后,就不会去理会剩下的servlet了。其匹配规则和顺序如下:

    • 精确路径匹配。例子:比如servletA 的url-pattern为 /test,servletB的url-pattern为 /* ,这个时候,如果我访问的url为http://localhost/test ,这个时候容器就会先进行精确路径匹配,发现/test正好被servletA精确匹配,那么就去调用servletA,也不会去理会其他的servlet了。
    • 最长路径匹配。例子:servletA的url-pattern为/test/,而servletB的url-pattern为/test/a/,此时访问http://localhost/test/a时,容器会选择路径最长的servlet来匹配,也就是这里的servletB。
    • 扩展匹配。 如果url最后一段包含扩展,容器将会根据扩展选择合适的servlet。例子:servletA的url-pattern:*.action
    • 最后, 如果前面三条规则都没有找到一个servlet,容器会根据url选择对应的请求资源。如果应用定义了一个default servlet,则容器会将请求丢给default servlet

    Servlet是单例的吗

    单例模式的定义:一个类有且仅有一个实例,并且自行实例化向整个系统提供。如果按照Java中单例的定义,那么当Servlet没有实现SingleThreadModel接口时,它确实是单例的。

    其实不然!

    Servlet并不是单例,只是容器让它只实例化一次,变现出来的是单例的效果而已!

    init()->service()->destroy()
    

    在Servlet规范中,对于Servlet单例与多例定义如下:

    “Deployment Descriptor”, controls how the servlet container provides instances of the servlet.For a servlet not hosted in a distributed environment (the default), the servlet container must use only one instance per servlet declaration. However, for a servlet implementing the SingleThreadModel interface, the servlet container may instantiate multiple instances to handle a heavy request load and serialize requests to a particular instance.
    

    上面规范提到: 如果一个Servlet没有被部署在分布式的环境中,一般web.xml中声明的一个Servlet只对应一个实例。 而如果一个Servlet实现了SingleThreadModel接口,就会被初始化多个实例。默认20个.

    Servlet的load-on-startup标签

    在servlet的配置当中,

    <load-on-startup>1</load-on-startup>
    

    含义是:标记容器是否在启动的时候就加载这个servlet。当值为0或者大于0时,表示容器在应用启动时就加载这个servlet;当是一个负数时或者没有指定时,则指示容器在该servlet被选择时才加载。正数的值越小,启动该servlet的优先级越高。

    配置load-on-startup后,servlet在startup后立即加载,但只是调用servlet的init()方法,用以初始化该servlet相关的资源。初始化成功后,该servlet可响应web请求;如未配置load-on-startup,容器一般在第一次响应web请求时,会先检测该servlet是否初始化,如未初始化,则调用servlet的init()先初始化,初始化成功后,再响应请求。

  • 相关阅读:
    中阶 d04.1 xml解析
    中阶 d04 xml 概念及使用
    中阶 d03.5 (正篇)完整的Dao 操作数据库
    中阶d03.4 JDBC_DAO
    中阶d03.3 JDBC_CURD_Util --- 使用 junit执行单元测试(增删改查)
    单元测试 junit
    idle中上传jar包并使用的方法
    intelij idea 和 eclipse 使用上的区别
    中阶d03.2 JDBC联合properties使用,通过读取本地配置文件为代码传递参数
    swift init 初始化
  • 原文地址:https://www.cnblogs.com/kylinxxx/p/14230741.html
Copyright © 2020-2023  润新知