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 

猜你喜欢

转载自www.cnblogs.com/alimayun/p/12354704.html