Tomcat专题(二)-----Tomcat源码、嵌入式Tomcat

Tomcat顶层结构

Server:服务器的意思,代表整个tomcat服务 器,一个tomcat只有一个Server;

Service:Server中的一个逻辑功能层, 一个 Server可以包含多个Service;

Connector:称作连接器,是Service的核心组 件之一,一个Service可以有多个Connector, 主要是连接客户端请求;

Container:Service的另一个核心组件,按照 层级有Engine,Host,Context,Wrapper四种, 一个Service只有一个Engine,其主要作用是执 行业务逻辑;

Jasper:JSP引擎;

Session:会话管理;

Server

Server是Tomcat最顶层的容器,代表着整个服务器

Tomcat中其标准实现: org.apache.catalina.core.StandardServer类

StandardServer继承结构类图如下图: 除了实现Server接口还需要继承Lifecycle

这里要强调一点,整个Tomcat中的设计方式,我们讲的都是一个抽象,抽象在源码中都是接口,具体的实现一般都是StandardXXXX之类的。

Lifecycle这个概念重点讲下:像Tomcat这么大的系统,必要要对生命周期进行统一的管理,所以基本上大部分的组件都会去继承这个接口,Lifecycle把所有的启动、停止、关闭等都组织在了一起。
补充点:MBeanRegistration这个类是完成JMX的功能,就是我们俗称的监控管理功能,之前我们讲的使用jconsole查看Tomcat也就是通过JMX玩的。

好处:生命周期统一接口Lifecycle把所有的启 动、停止、关闭等都放在一起统一管理

Service

Service:一个Tomcat包含多个Service

Tomcat中其标准实现: org.apache.catalina.core.StandardServic类

StandardService继承结构类图如下图: 除了实现Service接口还需要继承Lifecycle

好处:生命周期统一接口Lifecycle把所有的启 动、停止、关闭等都放在一起统一管理

Service拓展出Tomcat原型

Service中请求监听和请求处理分开为两个模块: Connector负责处理请求监听; Container负责处理请求处理;

一个Service可以有多个Connector,但只能有 一个Container。

任何容器都有启动start()和关闭stop()方法。

Connector解析

Connector使用ProtocolHandler来处理请求的。

ProtocolHandler由包含了三个部件: Endpoint、Processor、Adapter。

Endpoint用来处理底层Socket的网络连接。 Processor用于将Endpoint接收到的Socket封装 成Request。 Adapter充当适配器,用于将Request转换为 ServletRequest交给Container进行具体的处理。

Container解析

Engine:引擎、只有一个 定义了一个名为Catalina的Engine

Host:站点、虚拟主机 一个Engine包含多个Host的设计,使得 一个服务器实例可以承担多个域名的服 务,是很灵活的设计

Context:一个应用 默认配置下webapps下的每个目录都是 一个应用

Wrapper:一个Servlet

在理解下面的4个容器之间的关系:

Tomcat启动流程

Tomcat启动两大流程:init流程和start流程

1、init流程:分别在Bootstrap、Catalina、StandardServer、StandardService 的init方法加入断点和输出日志

2、start流程:分别在Bootstrap、Catalina、StandardServer、StandardService 的start方法加入断点和输出日志

Lifecycle与模板方法模式

模板方法就是为多种类似业务提供一个算法执行的统一框 架,把这些业务中共同的部分抽取出来进行具体实现,而 某些业务中特定的需求推迟到子类中进行重写实现

案例:

Tomcat的启动过程中Catalina调用StandardService中的start() 方法,但是StandardService自身没有start()方法!

分析:

原来StandardService继承了抽象类LifecycleBase,它有start() 并且它在此方法中调用了一个未实现的抽象方法startInternal() ,Catalina调用StandardService中的start()最后会调用至 startInternal()

优点:

这种模式使得StandardService等这些类抽出一个共同的start() 在LifecycleBase中进行实现(方便统一生命周期管理)。如果它 需进行特殊的业务处理的话可以在startInternal()中处理

嵌入式Tomcat

嵌入式Tomcat: 非传统的部署方式,将Tomcat嵌入到主程序中进行运行。

优点: 灵活部署、任意指定位置、通过复杂的条件判断。

发展趋势: Springboot默认集成的是Tomcat容器。

Maven中Springboot引入Tomcat:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-tomcat</artifactId>
    <scope>provided</scope>
</dependency>    

Maven集成Tomcat插件

Tomcat 7 Maven插件:tomcat7-maven-plugin

<dependency>
    <groupId>org.apache.tomcat.maven</groupId>
    <artifactId>tomcat7-maven-plugin</artifactId>
    <version>2.2</version>
</dependency>

插件运行 选择pom.xml文件,击右键——>选择 Run As——> Maven build 在Goals框加加入以下命令: tomcat7:run

Maven集成Tomcat 插件启动Tomcat是常用的Tomcat嵌入式启动

pom.xml:

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>tomcat-maven</groupId>
  <artifactId>tomcat-maven</artifactId>
  <version>0.0.1-SNAPSHOT</version>

  <name>tomcat-maven</name>
  <!-- FIXME change it to the project‘s website -->
  <url>http://www.example.com</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.7</maven.compiler.source>
    <maven.compiler.target>1.7</maven.compiler.target>
  </properties>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
    <!-- 引入Maven的Tomcat -->
    <dependency>
        <groupId>org.apache.tomcat.maven</groupId>
        <artifactId>tomcat7-maven-plugin</artifactId>
        <version>2.2</version>
    </dependency>
  </dependencies>

  <build>
    <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
      <plugins>
      </plugins>
    </pluginManagement>
  </build>
</project>

Maven集成Tomcat插件启动分析

分析它的启动

Tomcat7RunnerCli是引导类

@SuppressWarnings( "static-access" )
public class Tomcat7RunnerCli
{
    public static void main( String[] args )
        throws Exception
    {
        .
        .
        .
        // here we go
        tomcat7Runner.run();
    }
}

进一步分析

Tomcat7RunnerCli主要依靠Tomcat7Runner。重要代码如下:

public class Tomcat7Runner
{
    public void run() throws Exception {
        // start with a server.xml
        if ( serverXmlPath != null || useServerXml() )
        {
            container = new Catalina();
            container.setUseNaming( this.enableNaming() );
            if ( serverXmlPath != null && new File( serverXmlPath ).exists() )
            {
                container.setConfig( serverXmlPath );
            }
            else
            {
                container.setConfig( new File( extractDirectory, "conf/server.xml" ).getAbsolutePath() );
            }
            container.start();
        }
        else
        {
            tomcat = new Tomcat()
            {
                public Context addWebapp( Host host, String url, String name, String path )
                {

                    Context ctx = new StandardContext();
                    ctx.setName( name );
                    ctx.setPath( url );
                    ctx.setDocBase( path );

                    ContextConfig ctxCfg = new ContextConfig();
                    ctx.addLifecycleListener( ctxCfg );

                    ctxCfg.setDefaultWebXml( new File( extractDirectory, "conf/web.xml" ).getAbsolutePath() );

                    if ( host == null )
                    {
                        getHost().addChild( ctx );
                    }
                    else
                    {
                        host.addChild( ctx );
                    }

                    return ctx;
                }
            };

            tomcat.getHost().setAppBase( new File( extractDirectory, "webapps" ).getAbsolutePath() );

            String connectorHttpProtocol = runtimeProperties.getProperty( HTTP_PROTOCOL_KEY );

            if ( httpProtocol != null && httpProtocol.trim().length() > 0 )
            {
                connectorHttpProtocol = httpProtocol;
            }

            debugMessage( "use connectorHttpProtocol:" + connectorHttpProtocol );

            if ( httpPort > 0 )
            {
                Connector connector = new Connector( connectorHttpProtocol );
                connector.setPort( httpPort );

                if ( httpsPort > 0 )
                {
                    connector.setRedirectPort( httpsPort );
                }
                connector.setURIEncoding( uriEncoding );

                tomcat.getService().addConnector( connector );

                tomcat.setConnector( connector );
            }

            // add a default acces log valve
            AccessLogValve alv = new AccessLogValve();
            alv.setDirectory( new File( extractDirectory, "logs" ).getAbsolutePath() );
            alv.setPattern( runtimeProperties.getProperty( Tomcat7Runner.ACCESS_LOG_VALVE_FORMAT_KEY ) );
            tomcat.getHost().getPipeline().addValve( alv );

            // create https connector
            if ( httpsPort > 0 )
            {
                Connector httpsConnector = new Connector( connectorHttpProtocol );
                httpsConnector.setPort( httpsPort );
                httpsConnector.setSecure( true );
                httpsConnector.setProperty( "SSLEnabled", "true" );
                httpsConnector.setProperty( "sslProtocol", "TLS" );
                httpsConnector.setURIEncoding( uriEncoding );
                .
                .
                .
                tomcat.getService().addConnector( httpsConnector );

                if ( httpPort <= 0 )
                {
                    tomcat.setConnector( httpsConnector );
                }
            }

            tomcat.start();

            Runtime.getRuntime().addShutdownHook( new TomcatShutdownHook() );
        }

        waitIndefinitely();
    }
}

分析结论

原来嵌入式启动就是调用了Tomcat的API来实现的

Tomcat API接口

实现嵌入式Tomcat的基础:

Tomcat本身提供了外部可以调用的API

Tomcat类:

1.位置:org.apache.catalina.startup.Tomcat

2.该类是public的。

3.该类有Server、Service、Engine、Connector、Host等属性。

4.该类有init()、start()、stop()、destroy()等方法。

分析结论: Tomcat类是外部调用的入口

手写Tomcat思路分析

分析

Tomcat单独启动时的流程

结论

使用Tomcat的API来实现:

1.新建一个Tomcat对象

2.设置Tomcat的端口号

3.设置Context目录

4.添加Servlet容器

5.调用Tomcat对象Start()

6.强制Tomcat等待

手写嵌入式Tomcat-----开发流程

1.准备好一个简单的Servlet项目

2.新建一个手写嵌入式Tomcat工程

3.Tomcat工程中使用一个类完成手写嵌入式Tomcat的功能

4.调用该类的main方法执行

5.效果演示和分析

代码:

1、代码结构

2、pom.xml

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>tomcat-maven</groupId>
  <artifactId>tomcat-maven</artifactId>
  <version>0.0.1-SNAPSHOT</version>

  <name>tomcat-maven</name>
  <!-- FIXME change it to the project‘s website -->
  <url>http://www.example.com</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.7</maven.compiler.source>
    <maven.compiler.target>1.7</maven.compiler.target>
  </properties>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
    <!-- 引入Maven的Tomcat -->
    <dependency>
        <groupId>org.apache.tomcat.maven</groupId>
        <artifactId>tomcat7-maven-plugin</artifactId>
        <version>2.2</version>
    </dependency>
  </dependencies>

  <build>
    <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
      <plugins>
      </plugins>
    </pluginManagement>
  </build>
</project>

3、servlet

public class DemoServlet extends HttpServlet {

    private static final long serialVersionUID = 1L;

    @Override
    public void doGet(HttpServletRequest request,
                      HttpServletResponse response)
        throws IOException, ServletException
    {
        response.setContentType("text/html");
        response.setCharacterEncoding("UTF-8");
        PrintWriter out = response.getWriter();

        out.println("<!DOCTYPE html><html>");
        out.println("<head>");
        out.println("<meta charset=\"UTF-8\" />");
        out.println("<title></title>");
        out.println("</head>");
        out.println("<body bgcolor=\"white\">");
        out.println("<h1>阿里-马云 V5!</h1>");
        out.println("</body>");
        out.println("</html>");
    }
}

4、EmbeddedTomcatServer-----服务器主类

public class EmbeddedTomcatServer {

    public static void main(String[] args) throws Exception{
        //把目录的绝对的路径获取到
        String classpath = System.getProperty("user.dir");
        System.out.println(classpath);
        //D:\workspace-tomcat\tomcat-maven
        //我们new一个Tomcat
         Tomcat tomcat = new Tomcat();

         //设置Tomcat的端口tomcat.setPort(9091)。两种写法都可以设置端口
         Connector connector = tomcat.getConnector();
         connector.setPort(9091);
         //设置Host
         Host host = tomcat.getHost();
         //我们会根据xml配置文件来
         host.setName("localhost");
         host.setAppBase("webapps");
         //前面的那个步骤只是把Tomcat起起来了,但是没啥东西
         //要把class加载进来,把启动的工程加入进来了
         Context context =tomcat.addContext(host, "/", classpath);

         if(context instanceof StandardContext){
             StandardContext standardContext = (StandardContext) context;
             standardContext.setDefaultContextXml("E:/utils/Tomcat/apache-tomcat-8.0.53/conf/web.xml");
             //我们要把Servlet设置进去
             Wrapper wrapper =  tomcat.addServlet("/", "DemoServlet", new DemoServlet());
             wrapper.addMapping("/king");
         }
         //Tomcat跑起来
         tomcat.start();
         //强制Tomcat server等待,避免main线程执行结束后关闭
         tomcat.getServer().await();
    }
}

5、运行主类,启动成功。

浏览器中访问:http://localhost:9091/king

原文地址:https://www.cnblogs.com/alimayun/p/12354704.html

时间: 2024-10-06 15:13:01

Tomcat专题(二)-----Tomcat源码、嵌入式Tomcat的相关文章

Tomcat专题二: JDK安装以及tomcat基本环境安装和使用

tomcat专题二:jdk安装以及tomcat基本安装和使用 书接tomcat专题一,在这一节将介绍一下java运行环境的安装以及tomcat的基本安装和使用.可能有人会问安装tomcat跟java运行环境有什么关系?正像专题一介绍的那样,tomcat本身只是一个业务框架,一个WEB容器,其底层还是基于jvm虚拟机来运行java程序的字节码文件.可以认为tomcat负责接受上层的应用请求,然后将请求的java程序交与jvm虚拟机执行并返回结果,这之间形成了一个调用关系,这在下面的tomcat启动

详解Tomcat系列(一)-从源码分析Tomcat的启动

在整个Tomcat系列文章讲解之前, 我想说的是虽然整个Tomcat体系比较复杂, 但是Tomcat中的代码并不难读, 只要认真花点功夫, 一定能啃下来. 由于篇幅的原因, 很难把Tomcat所有的知识点都放到同一篇文章中, 我将把Tomcat系列文章分为Tomcat的启动, Tomcat中各模块的介绍和Tomcat中的设计模式三部分, 欢迎阅读与关注. 一:通过idea搭建Tomcat源码阅读环境 首先我们到Tomcat的官网(http://tomcat.apache.org/)上下载Tomc

tomcat集群实现源码级别剖析

随着互联网快速发展,各种各样供外部访问的系统越来越多且访问量越来越大,以前Web容器可以包揽接收-逻辑处理-响应整个请求生命周期的工作,现在为了构建让更多用户访问更强大的系统,人们通过不断地业务解耦.架构解耦将web容器的逻辑处理抽离交由其他中间件处理,例如缓存中间件.消息队列中间件.数据存储中间件等等.Web容器负责的工作可能越来越少,但是它确实必不可少的部分,它负责接收用户请求并分别调用各个服务最后响应.可以说目前最受欢迎的web容器是用Java写的tomcat小猫,由于生产上的tomcat

二.jQuery源码解析之构建jQuery之构建函数jQuery的7种用法

一:$(selectorStr[,限制范围]),接受一个选择器(符合jQuery规范的字符串),返回一个jQuery对象;二:$(htmlStr[,文档对象]),$(html[,json对象])传入html字符串,创建一个新的dom元素 三:$(dom元素),$(dom元素集合)将dom元素转换成jQuery对象.四:$(自定义对象)封装普通对象为jQuery对象.五:$(回调函数)绑定ready事件监听函数,当Dom加载完成时执行.六:$(jQuery对象)接受一个jQuery对象,返回一个j

安卓图表引擎AChartEngine(二) - 示例源码概述和分析

首先看一下示例中类之间的关系: 1. ChartDemo这个类是整个应用程序的入口,运行之后的效果显示一个list. 2. IDemoChart接口,这个接口定义了三个方法, getName()返回值是listitem上显示的标题; getDesc()返回值是listitem上显示的描述内容. excute(context)返回值是一个Intent,当点击listitem后跳转到此Intent. 3. AbstractDemoChart类是一个抽象类,实现接口IDemoChart接口,这个类中封

十二.jQuery源码解析之.eq().first().last().slice()

eq(index):将集合中的索引为index的元素提取出来. first():返回集合中的第一个元素. .last():防护集合中的最后一个元素. .slice(start[,end]):返回集合中的给定区间段的元素. first()和last()调用eq(),eq()通过slice()实现,slice()通过 .pushStack()实现. 相关源码 285行:用法很奇特,通过一个"+"把可能为字符串的i转换成一个数值. 300~301:先借用数组方法slice()从当前jQuer

Spring 循环引用(二)源码分析

Spring 循环引用(二)源码分析 Spring 系列目录(https://www.cnblogs.com/binarylei/p/10117436.html) Spring 循环引用相关文章: <Spring 循环引用(一)一个循环依赖引发的 BUG>:https://www.cnblogs.com/binarylei/p/10325698.html <Spring 循环引用(二)源码分析>:https://www.cnblogs.com/binarylei/p/1032604

Spring IoC 依赖注入(二)源码分析

目录 Spring IoC 依赖注入(二)源码分析 1. 依赖注入口 - populateBean 1.1 doCreateBean 1.2 populateBean 2. 手动注入 2.1 相关的类说明 2.2 applyPropertyValues 2.3 BeanDefinitionValueResolver 2.4 依赖检查 2. 自动注入 2.1 那些字段会自动注入 2.2 名称注入 2.3 类型注入 Spring IoC 依赖注入(二)源码分析 本章主要分析 Spring IoC 依

【MyBatis】MyBatis Tomcat JNDI原理及源码分析

一. Tomcat JNDI JNDI(java nameing and drectory interface),是一组在Java应用中访问命名和服务的API,所谓命名服务,即将对象和名称联系起来,使得可以通过名称访问并获取对象. 简单原理介绍:点击访问 tomcat已经集成该服务(内置并默认使用DBCP连接池),简单来说就是键值对的mapping,而且在tomcat服务器启动的首页configuration中就已经有完成的示例代码.要想使用tomcat的JNDI服务,只需要导入相关的jar包,