JavaWeb——tomcat 与 servlet初识(1)

一、JavaEE

  • 是 java 企业级的开发技术,能够开发动态资源,也能够打造大规模的应用程序。它也是一个技术体系,建立在 JavaSE 基础之上,提供了实现企业级开发的基础 API。现在大多数的 java 应用程序都是基于 JavaEE 开发的。

二、JavaWeb

  1. web 称为互联网以及应用于网络上的各设备、协议、规范及技术。JaveWeb 就是基于 java 语言来开发符合 JavaEE 规范要求的网络应用程序(B/S,也包括C/S)。
  2. web 包含两种资源,一种是静态资源,不会发生变化,html 页面,css,js 的文件,图片,音视频文件等都是静态资源。它们都是存放在服务器中,由服务器进行管理,这类的服务器主要用来处理静态资源,一般称为应用服务器,比如,APACHE,NGINX。另一种就是动态资源,它的内容是由后台程序执行的结果来生成的,它也要由服务器来管理,这部分服务器一般称为 web 服务器,比如 tomcat,jetty,weblogic,websphere,jboss,lls 等。web 服务器也可以处理静态资源,只不过功能没有应用服务器强大。

三、tomcat 服务器

  1. 它是开源的、免费的服务器产品。
  2. 它对动态资源的处理效率很高。
  3. 它本身就是 java 应用程序,它与 javaweb 应用程序是天然的组合,它主要是对 javaweb 程序进行发布、管理及运行。
  4. 它本身并不大,不会占用太多的系统资源。
  5. 它的设计目标,是能够很好的执行中小型的应用程序,现在随着分布式开发技术的广为使用,它依然是 javaweb 应用的主流服务器。一些互联网大厂,就是使用 tomcat 来执行自己的各种应用。
  6. 它在实际的生产环境中,往往会安装很多套,每一套负责管理和发布一个模块,多个服务器之间可以实现互相通讯,这个通讯目前也有一些优秀的产品或框架来支持。

四、tomcat 应用程序的目录结构

  • bin:可执行的程序,包括启动,停止 tomcat,注册服务;
  • conf:配置目录,一些主要的配置文件比如 server.xml,web.xml 等都在下面;
  • lib:程序目录,tomcat 的字节码文件以 jar 的形式放在该目录下;
  • logs:日志目录,是 tomcat 启动,运行和注销整个过程中所发生的日志信息,它的主要内容是提供给系统监控人员来观察并分析问题;
  • temps:临时目录,存放运行期生成的一些临时文件,注销后这些文件会被删除;
  • work:工作目录,它是 tomcat 的工作场所,主要用途一是存放 session,二是保存编译后的 jsp 的源码及字节码;
  • webapps:应用发布目录,设计者开发的项目可以目录或 war 的形式放在该目录下,它们会由 tomcat 管理、发布和执行。

五、tomcat 应用程序的构成及启动

  • 由各种组件构成,结构比较复杂,可以了解一下它的基本结构及各组件的作用。

  • server.xml 中的配置内容的结构与 tomcat 的组件结构保持一致,相关的激活配置项目与 tomcat 的运行紧密相关,不可随意修改。

  • tomcat 的设计模式:观察者模式;外观模式;命令模式;模板模式。

  • tomcat 程序的基本结构:

    • public interface Lifecycle 接口,它是所有组件都要实现的根接口,它里面有三个主要方法:init(),start(),destroy(),它们在各个组件中都需要实现,即使哪个组件未实现该方法,它的上级类就必须要实现。
    • public final class Bootstrap 是启动类,其中包含了 main 方法,启动类本身先初始化,然后再从上到下启动各个组件进行初始化,各组件初始化后,再调用各组件的 start() 方法来启动组件。
    • tomcat 提供了自己的类加载器。
    1. 保证 tomcat 自身与管理项目具有隔离性,在 jvm 中,判断一块字节码的唯一性的标志是:类加载器.包名.类名。
    2. 各项目之间要实现隔离,不同的项目可能会使用到相同的第三方的 jar 包,但是版本又不同。
    3. jsp 页面修改后需要重新编译成字节码,那么新的字节码需要具有热部署的功能。

六、HTTP 协议

  • 超文本传输协议,它属于应用层的协议,它关注于传输信息的格式,不关心底层的数据如何传输。现在多数使用的是 http1.1 版本,它主要提供了长连接的方式,客户与服务器建立连接后,可以进行多次数据的相互传输,也就是在一个连接的范围内可以进行多次的请求与响应。
  1. HTTP 协议的工作模型

    • 应用层:应用程序,http、dns
    • 传输层:操作系统,tcp、udp
    • 网络层:路由器,ip、icmp
    • 物理链路层:网卡,线路,arp
  2. 什么是 http 协议的请求

    • 请求就是客户向服务器发送的请求某种资源的内容。请求的资源一般包括得到数据、向服务器添加数据、修改数据、删除数据等。
  3. http 请求包含的内容

    (1)请求行:请求方式(GET/POST),请求的 uri(资源路径),使用的协议及版本。

    (2)请求头:多个键值对,是客户需要告诉服务器的相关内容。

    (3)请求正文(不是必须的)。

  4. 请求方式:http1.1 支持八种请求方式

    • get:需要从服务器得到资源,请求部分的数据最多 1 k,请求参数格式: url?参数=值&参数=值……。属于 get 请求的几种方式:输入浏览器地址栏的 RUL ;点击网页上的超链接;表单的 method 属性取值为 get。此种请求没有请求正文。
    • post:向服务器提交数据包括表单数据和上传的文件。请求部分的数据不受限制。post 请求发送的参数、发送的文件都是通过请求正文来传送的。因此相较于 get 来看,post 具有保密性,get 请求的参数在地址栏上可见。表单的 method 属性取值为 post 则属于 post 请求。
    • put:向服务器提交需要覆盖的数据。通过发送 put 请求的技术是 ajax。
    • delete:要求服务器删除数据。只能通过 ajax 来发送。
    • connect
    • head
    • options
    • trace
  5. http 响应包含的内容

    (1)响应状态行:协议名;状态码;状态短语

    (2)响应消息头

    (3)响应正文

    说明
    200 OK //客户端请求成功,即处理成功,这是我们最希望看到的结果
    303 Other //我把你redirect到其它的页面,目标的URL通过响应报文头的Location告诉你
    304 Not Modified //告诉客户端,你请求的这个资源至你上次取得后,并没有更改,你直接用你本地的缓存吧,我很忙哦,你能不能少来烦我啊!
    400 Bad Request //客户端请求有语法错误,不能被服务器所理解
    401 Unauthorized //请求未经授权,这个状态代码必须和WWW-Authenticate报头域一起使用
    403 Forbidden //服务器收到请求,但是拒绝提供服务
    404 Not Found //请求资源不存在,eg:输入了错误的URL,找不到页面/页面已经被网站删除了
    500 Internal Server Error //服务器发生不可预期的错误,此时应该查查服务端的日志了,肯定抛出了一堆异常
    503 Server Unavailable //服务器当前不能处理客户端的请求,一段时间后可能恢复正常
    状态码 说明 通俗解释
    1xx 响应中——表示请求已经接受,继续处理 消息:一般是告诉客户端请求已经收到了,正在处理,别急
    2xx 成功——表示请求已经被成功接收、理解、接受。 处理成功:一般表示请求收悉、我明白你要的、请求已受理、已经处理完成等信息
    3xx 重定向——要完成请求必须进行更进一步的操作 重定向到其它地方:它让客户端再发起一个请求以完成整个处理
    4xx 客户端错误——请求有语法错误或请求无法实现 处理发生错误,责任在客户端:如客户端的请求一个不存在的资源,客户端未被授权,禁止访问等
    5xx 服务器端错误——服务器未能实现合法的请求 "处理发生错误,责任在服务端:如服务端抛出异常,路由出错,HTTP版本不支持等

七、系统化认识 tomcat 服务器

  1. tomcat 是基于组件化的结构,由多个组件组成整个 server。包括:

    • server(表示服务器),
    • service(一个服务,可以有多个),
    • container(servlet 容器),
    • connector(tcp 协议的连接组件),
    • engine(与连接组件都属于容器),engine 又分为 host(主机,对应于域名),同时对应于 tomcat 中的 webapps 目录。
    • context(上下文)对应 webapps 目录下的各个目录(应用程序)。
    • wrapper(servlet 的包装器),对应于应用中的动态资源,也包括 jsp 等。
  2. 以上提到的所有部分都属于组件,tomcaat 为了统一的有序的管理和监听这些组件的生命周期,它们都具有相似的层级结构。

    • 所有组件的根接口是 Lifecycle,该接口规定了组件的生命周期过程。
    • LifecycleBase 抽象类实现了 Lifecycle 接口,它通过模板设计模式(完成某种任务有固定的步骤,通过抽象类的方法来明确步骤的执行顺序,通过子类来实现各个步骤)规定了组件的生命周期的执行步骤。
    • LifecycleMBeanBase,它提供了组件生命周期中的一些公共的实现。
    • 最底层就是组件的实现类。
  3. 结合源码了解各组件的启动过程

    • class Bootstrap 是 tomcat 的启动类,包含 main() 方法。bootstrap.init();对自己进行初始化。

    • startupInstance = startupClass.getConstructor().newInstance();类型是 org.apache.catalina.startup.Catalina,用成员变量 catalinaDaemon 引用。

    • main 方法中调用:daemon.load(args);daemon.start();这两个方法就是所有组件的初始化和启动的方法。

    • bootstrap#aeqnon.load(args);
      catalinaDaemon.getClass().getMethod( "load”, paramTypes);

    • catalina 的 load() 方法:protected String configFile = “conf/server.xml”;//解析配置文件

      getServer().init();//得到 server 组件,进行初始化

    • server.init();//初始化服务器,在 init() 方法内部调用的是 initInternal() 方法。

    • service.init();

      executor.init();//初始化线程池

      connector.init();//初始化连接组件

      ……所有的组件按照层次关系,由上级调用下级的初始化方法完成初始化。

    • daemon.start();

      getServer().start();//启动server

      ……所有的组件按照层次关系,由上级调用下级的 start() 方法进行启动。

  4. tomcat 是如何工作的:主要是接收请求,处理后发出响应。

    (1)protocolHandler.start(); //启动对8080端口的请求监听,serverSocked. accept);
    (2)当请求到达,解析http连接的内容.
    (3)创建请求对象Request

    public Request createRequest () {
            
        Request request = new Request ();
    	request. setConnector (this); return (request);
    }
    

    (4)创建响应对象

   public Response oreateResponse () {
     
   	Response response = new Response();
   	response.setConnector (this);
   	return (response)}

(5)把请求和响应对象发送给 engine,按照顺序向后传递,每个节点都会做出一些处理,一直到 wrapper,把请求和响应对象交给 servlet,由 servlet 来使用。此过程采用的设计模式就是责任链模式。

  1. java 设计模式之责任链模式(responsibility):属于行为型模式

    • 解决的问题:当请求产生时,如果需要对请求进行多种处理,就可以让各种处理的对象按照指定的顺序进行执行。
    • 好处:实现了请求与处理部分的解耦,也实现了处理中各部分的解耦。
    • 请求——处理一——处理二——……处理n
    • 责任链模式的例子:
    package com.zhong.test_13;
    
    /**
     * @author 华韵流风
     */
    public interface Process {
           //表示处理的接口
    
        void execute(Request request, ProcessChain processChain);
    }
    
    package com.zhong.test_13;
    
    /**
     * @author 华韵流风
     */
    public class Request {
           
        private String content;//请求的内容
    
        public Request(String content) {
           
            this.content = content;
        }
    
        @Override
        public String toString() {
           
            return "Request{" +
                    "请求内容='" + content + '\'' +
                    '}';
        }
    }
    
package com.zhong.test_13;

import java.util.ArrayList;
import java.util.List;

/**
 * @author 华韵流风
 * 包含所有的责任链上的处理对象,以及处理的顺序
 */
public class ProcessChain {
     
    private int index;//处理的顺序
    private List<Process> processList = new ArrayList<>();//包含所有的处理对象

    //添加处理对象的方法
    public ProcessChain addProcess(Process process) {
     
        processList.add(process);
        return this;
    }

    //启动责任链的处理方法,所有的处理都能够按顺序完成
    public void doProcess(Request request) {
     
        if (index == processList.size()) {
     
            return;
        }
        processList.get(index).execute(request, this);
        index++;
        doProcess(request);
    }
}
package com.zhong.test_13;

/**
 * @author 华韵流风
 */
public class ParseRequestHeader implements Process {
     
    @Override
    public void execute(Request request, ProcessChain processChain) {
     
        System.out.println("解析" + request + "的请求头");
    }
}
package com.zhong.test_13;

/**
 * @author 华韵流风
 */
public class ParseRequestLine implements Process {
     
    @Override
    public void execute(Request request, ProcessChain processChain) {
     
        System.out.println("解析" + request + "的请求行");
    }
}
package com.zhong.test_13;

/**
 * @author 华韵流风
 */
public class ResponsibilityDemo {
     
    public static void main(String[] args) {
     
        Request request = new Request("查询用户");
        Process lines = new ParseRequestLine();
        Process header = new ParseRequestHeader();

        ProcessChain chain = new ProcessChain();
        //加入处理对象
        chain.addProcess(lines).addProcess(header);
        //执行整个责任链
        chain.doProcess(request);
        /*
        *结果
        *解析Request{请求内容='查询用户'}的请求行
		*解析Request{请求内容='查询用户'}的请求头
        */
    }
}

八、JavaWeb 项目的结构

  • webapps
    • docs
    • exapmles
      • jsp
      • META-TNF
      • servlest
      • WEB-INF(安全目录,里面的内容不能被外部访问)
        • classes(存放编译后的字节码文件)
        • jsp
        • jsp2
        • lib(存放项目要使用的第三方的 jar 包)
        • tags
        • web.xml(项目的核心配置文件)
      • websocket
      • index.html
    • host-manager
    • manager
    • ROOT
  • 提示,最简单的项目只有一个根目录,目录下可以没有任何内容。
  • 一般要求,在根目录下有 WEB-INF 目录,WEB-INF 目录下面要有 web.xml 文件。

九、手工创建一个项目

  • 有利于了解 JavaWeb 项目最基本的工作方式。
  1. 在 webapps 下面创建一个项目目录名称为 myproject,在根目录下放置安全目录,在安全目录下添加 classes 目录和 web.xml 配置文件。

  2. 创建 Servlet

    (1)创建一个 java 工程,把 servlet.api.jar 引入到本工程中。

    (2)创建一个类实现 Servlet 接口,实现所有的方法。

    (3)在以上类的 service 方法中向客户端输出一句话。

    (4)编译以上 Servlet 类。

    (5)把 Servlet 的字节码文件按照包名在 classes 下创建相应的目录,复制文件到该目录下。

    (6)在 web.xml 文件中,对刚创建的 Servlet 进行配置。

    (7)启动 tomcat,本项目就被发布,外部就可以访问它。

    (8)通过浏览器地址栏发出 get 请求,请求的资源就是上面的 Servlet。

    (9)Servlet 执行完毕,向浏览器发回响应消息。

  3. 由 servlet 处理请求的工作过程

    (1)发出的 URL :Request URL: http://localhost:8080/myproject/myservleturl。

    (2)http 表示使用 http 协议,localhost 表示访问本机,8080表示访问本机工作在8080端口的进程(tomcat)。

    (3)tomcat 接收到请求后解析请求行,myservlet 表示当前请求要访问的项目名称,myservleturl 表示当前请求需要哪个 servlet 进行处理。

    (4)在配置文件中,servlet 的 servlet-mapping 下面的 url-pattern 的内容是 myservleturl,它对应 servlet-name。

    (5)容器就依据以上的配置信息,根据 servlet-name 得到 servlet-class 的字节码文件名,首先加载 servlet 的字节码,再通过反射创建 servlet 的对象,把请求和响应对象通过 service 方法传递给 servlet,在 servlet 方法中就可以使用以上两个对象。

    (6)通过字节码对象调用 service 方法,service 就执行了。它需要利用响应对象得到字节输出流对象,利用字节流对象发送信息。

    (7)service 方法执行完成后就向客户的浏览器发回响应消息。

  4. 什么是 servlet

    • Servlet 是 JavaWeb 应用程序最基础的 API 之一。如果一个类实现了 Servlet 接口。它由两个单词组成,server 和 applet,字面含义是运行在服务器中的 java 小程序。 servlet 可以干任何 java 可以干的事情。任何 JavaWeb 应用程序,都要使用 servlet 来完成各种任务,包括使用框架,底层依然是使用的 servlet 的功能。
  5. 注意,在使用 Servlet 之前,要针对它在 web.xml 中进行配置。配置是因为 web 程序是没有 main() 方法的。所以 servlet 的对象要交给容器进行加载并实例化。配置信息明确的告诉了容器 servlet 的字节码是谁,交给哪个请求进行使用。

    
    <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"
      metadata-complete="true">
    
      
      <servlet>
      	<servlet-name>myservletservlet-name>
      	<servlet-class>com.zhong.MyServletservlet-class>
      servlet>
    
      <servlet-mapping>
      	<servlet-name>myservletservlet-name>
          
      	<url-pattern>/myservleturlurl-pattern>
      servlet-mapping>
    
    web-app>
    
    
    
  6. 通过 idea 平台来创建 JavaWeb 应用程序。(创建步骤)

十、HttpServlet

  • 这是一个抽象类,我们创建的 servlet 继承该类,好处在于 HttpServlet 添加了一些非接口的新的方法,这些方法让 Servlet 的设计变得更加简单,功能更加强大。
  • HttpServlet 继承自 GenericServlet,它实现了 Servlet 接口,我们创建的 Servlet 也就实现了 Servlet。
  1. Servlet 接口:

    • //
      // Source code recreated from a .class file by IntelliJ IDEA
      // (powered by Fernflower decompiler)
      //
      
      package javax.servlet;
      
      import java.io.IOException;
      
      public interface Servlet {
               
          void init(ServletConfig var1) throws ServletException;
      
          ServletConfig getServletConfig();
      
          void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
      
          String getServletInfo();
      
          void destroy();
      }
      
      

      其中的三个 void 方法是生命周期方法,在 Servlet 的创建,使用及销毁过程会被调用。其中 service() 方法就是处理请求的方法。其中的请求和响应对象的类型,属于底层的对象类型。

  2. GenericServlet 实现了 Servlet 接口,也实现了 ServletConfig 接口,在实现以上接口的方法的同时,它还添加了自己的方法。

    • 实现了 Servlet 的 init() 方法,该方法中传入了一个对象 ServletConfig,就是 Servlet 的配置对象,它封装了 Servlet 的配置信息,可以取出来使用。该方法中还调用了本类自己的 init() 无参的方法,因此在子类中对 Servlet 进行初始化需要重写该方法。
    • public ServletConfig getServletConfig(),调用此方法可以得到 ServletConfig 的对象。
    • public ServletContext getServletContext(),ServletContext 是本项目中共用的具有共享性的唯一的,在所有 Servlet 对象中都可以使用的全局性应用对象。
  3. HttpServlet 的主要方法的作用。

    • public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
               
          HttpServletRequest request;
          HttpServletResponse response;
          try {
               
              request = (HttpServletRequest)req;
              response = (HttpServletResponse)res;
          } catch (ClassCastException var6) {
               
              throw new ServletException(lStrings.getString("http.non_http"));
          }
          this.service(request, response);
      }
      
      

      该方法把底层的请求和响应对象强转为 HttpServletRequest 和 HttpServletResponse 对象,ServletRequest 和 HttpServletRequest 都是接口,后者的功能更加强大,它新增了一些与 http 协议相关的功能。protected void service(HttpServletRequest req, HttpServletResponse resp) 方法是本类自己的方法,该方法依据不同的请求方式,分别调用该类所添加的针对不同请求方式的处理方法。

  4. 自定义的 Servlet 方法

    • 依据上面的分析,在自定义的Servlet中只需要根据当前的请求方式去重写相应的处理请求的方法,比如重写 doGet(),doPost()……

十一、Servlet 的生命周期

  • Servlet 也属于组件之一,同样有自己的生命周期,理解它的生命对象特征对于它的使用是非常重要的。在配置中可以添加配置项,让 servlet 在容器启动的时候被加载并初始化。1,取值可以是1-n,数字越小越先加载。建议采用这种方法。
  1. 体会生命周期的过程
    • 当第一次请求 servlet 时,该 servlet 会被加载和实例化,调用它的 init() 方法完成初始化。
    • 当请求到达 Servlet,请求会被处理。
    • 只要容器在持续期,该 Servlet 的实例不会消失,而且该实例是单例的,只有一份。无论有多少个请求向该 Servlet 进行发送,都是由同一个 Servlet 对象进行处理。
    • Servlet 对象只有一个,多个请求可能会同时使用它,因此 servlet 是多个请求的共享资源,就可能发生线程并发的情况。另外,对每个请求,容器都会从线程池中拿出一个线程去进行处理,多个请求就对应多个线程。因此,就存在多线程共享资源的问题。在早期版本中,servlet 是多例的,但是会造成内存的急剧膨胀,还有就是让 servlet 去实现 SingleThreadModel 接口,servlet 才是单例的,现在的版本该接口已经过时。因此新版本中,servlet 只能是单例的,使用者自己解决并发问题。最简单的方案是不要在自定义的 servlet 中添加具有共享性的成员变量而且该变量会被不同的请求进行修改。
    • 当容器关闭时,会调用 Servlet 的 destroy() 方法,销毁 servlet。

你可能感兴趣的:(java编程,javaweb,tomcat,java,tomcat,http,servlet)