tomcat源码解析(一)--启动与Server.xml文件的解析

后台大部分功能是用java来实现的,因此想了解tomcat的底层实现。写下此文留做复习用吧。至于一些概念在一书《深入剖析tomcat》已经有很详细的分析了。 说明一下本文分析的tomcat版本为8.5.2。

开始:
首先找到org.apache.catalina.startup包下的Bootstrap类,它就是tomcat的启动类。找到main方法如下:

 public static void main(String args[]) {

        if (daemon == null) {
            Bootstrap bootstrap = new Bootstrap();
            ......
            bootstrap.init();
            ......
            daemon = bootstrap;
        } 
        ......

       if (command.equals("startd")) {
           args[args.length - 1] = "start";
           daemon.load(args);
           daemon.start();
        } 
        ......

    }

在这里先看 bootstrap.init()

  public void init() throws Exception {
        // 创建类的加载器
        initClassLoaders();
        ......

        Class<?> startupClass =
            catalinaLoader.loadClass
            ("org.apache.catalina.startup.Catalina");
        Object startupInstance = startupClass.newInstance();

        ......

        String methodName = "setParentClassLoader";
        Class<?> paramTypes[] = new Class[1];
        paramTypes[0] = Class.forName("java.lang.ClassLoader");
        Object paramValues[] = new Object[1];
        paramValues[0] = sharedLoader;
        Method method =
            startupInstance.getClass().getMethod(methodName, paramTypes);
        method.invoke(startupInstance, paramValues);
        catalinaDaemon = startupInstance;

    }

该方法通过反射的方法创建org.apache.catalina.startup.Catalina类的实例。

再回到main方法里面找到如下的代码,通过前面分析可知daemon就是在init方法里面创建的org.apache.catalina.startup.Catalina的实例。

if (command.equals("startd")) {
  args[args.length - 1] = "start";
  daemon.load(args);
  daemon.start();
 }

找到Catalina类的load方法

   public void load() {
        long t1 = System.nanoTime();
        ......

        Digester digester = createStartDigester(); // 设置了解析server.xml的规则

        InputSource inputSource = null;
        InputStream inputStream = null;
        File file = null;

        ......

        file = configFile(); // 这里打开的就是server.xml
        inputStream = new FileInputStream(file);
        inputSource = new InputSource(file.toURI().toURL().toString());

        ......
        // 解析server.xml文件并实例化对象
        inputSource.setByteStream(inputStream); // 用作XML解析的用的
        digester.push(this); // 压在栈顶,也就是为了设置server
        digester.parse(inputSource);

        ......
        getServer().init();

        ......

    }

先看createStartDigester()看它是如何解析文件的。

 protected Digester createStartDigester() {
        ......
        Digester digester = new Digester();
        ......
        digester.addObjectCreate("Server",  
                                "org.apache.catalina.core.StandardServer",
                                 "className");
        digester.addSetProperties("Server"); 
        digester.addSetNext("Server",
                            "setServer",
                            "org.apache.catalina.Server");
         ......

        digester.addRule("Server/Service/Connector",
                         new ConnectorCreateRule());
        digester.addRule("Server/Service/Connector",
                         new SetAllPropertiesRule(new String[]{"executor", "sslImplementationName"}));
        digester.addSetNext("Server/Service/Connector",
                            "addConnector",
                            "org.apache.catalina.connector.Connector");
       ......

        digester.addObjectCreate("Server/Service/Connector/Listener",
                                 null, // MUST be specified in the element
                                 "className");
        digester.addSetProperties("Server/Service/Connector/Listener");
        digester.addSetNext("Server/Service/Connector/Listener",
                            "addLifecycleListener",
                            "org.apache.catalina.LifecycleListener");

        ......
        return (digester);

    }

关于Digester的具体用法网上已经有很多了,在这里就不详细讲。只做一些简单的说明。

public void addObjectCreate(String pattern, String className,String attributeName)

第一个参数是匹配规则,如: <Server></Server>
第二个参数是创建默认对象的默认类
第三个参数是遇到匹配规则时用哪个属性来创建对象,如果没有则使用第二个参数传进去的默认类。
如createStartDigester里面的代码:

 digester.addObjectCreate("Server",  
                                 "org.apache.catalina.core.StandardServer",
                                 "className");

当遇到Server这个标签时,以className的属性来创建对象,如果该属性不存在,创建的对象则是org.apache.catalina.core.StandardServer。并把创建的对象放在Digester栈的栈顶。

public void addSetProperties(String pattern)

该方法是设置栈顶元素的属性 的。遇到指定的标签时,则把标签里面的属性设置到所处Digester栈,栈顶元素里面去。

public void addSetNext(String pattern, String methodName,
                           String paramType)

这个方法作用是当解析完该标签之后,则调用Digester栈里面比自己低一层的元素的methodName方法,将自己作为参数,并且把自己弹出栈。

回到org.apache.catalina.startup.Catalina的load方法。可以看到digester.push(this); Catalina类把自己压入digester栈中,看到createStartDigester解析server.xml的规则。

     digester.addObjectCreate("Server",  
                                 "org.apache.catalina.core.StandardServer",
                                 "className");
        digester.addSetProperties("Server"); 
        digester.addSetNext("Server",
                            "setServer",
                            "org.apache.catalina.Server");

在这里通过查看server.xml知道Server标签的className是不存在的,因此创建的是StandardServer的实例对象,然后在然后通过addSetNext来调用Catalina方法的setServer来把自己设置到Catalina类里面。
最后在 Catalina的load方法里面调用getServer().init()。也就是StandardServer的init方法。该方法后面再详细分析。

Catalina的load方法处理完之后就是调用该类的start方法了,代码如下:

  public void start() {
        ......
        getServer().start();
        ......
    }

可是该方法调用的是StandardServer的start方法。

从功能看到Catalina的load方法主要是解析server.xml文件并实例化里面的对象,而start方法主要是调用了StandardServer的start方法。

好了Catalina的启动过程就分析了,下面接着分析StandardServer类的方法。StandardServer的init方法是在父类LifecycleBase里面实现的,而该方法调用了StandardServer类里面的initInternal方法,带入如下:

protected void initInternal() throws LifecycleException {
        ......
        for (int i = 0; i < services.length; i++) {
            services[i].init();
        }
    }

在Catalina的createStartDigester方法

digester.addObjectCreate("Server/Service",
                                 "org.apache.catalina.core.StandardService",
                                 "className");

可以知道这里的services就是StandardService的实例对象。StandardService的init后面再分析。

接着分析StandardServer的start方法。该方法的实现也是在StandardServer的父类LifecycleBase里面实现的,该方法又调用了StandardServer的startInternal方法,代码如下:

   protected void startInternal() throws LifecycleException {
        ......
        synchronized (servicesLock) {
            for (int i = 0; i < services.length; i++) {
                services[i].start();
            }
        }
    }

由此可是该方法调用的是services的start方法。接着看到StandardService的init方法。该类的父类是LifecycleBase因此,直接看StandardService的initInternal方法即可,代码如下:

 protected void initInternal() throws LifecycleException {
       ......
        for (Executor executor : findExecutors()) {
            if (executor instanceof JmxEnabled) {
                ((JmxEnabled) executor).setDomain(getDomain());
            }
            executor.init();
        }

       ......
        synchronized (connectorsLock) {
            for (Connector connector : connectors) {
                ......
                connector.init();
                ......
            }
        }
    }

从代码可以看出该方法创建了初始化了线程池和Connector 。
接着看到StandardService的startInternal方法

    protected void startInternal() throws LifecycleException {

        ......

        synchronized (executors) {
            for (Executor executor: executors) {
                executor.start();
            }
        }
       ......
        synchronized (connectorsLock) {
            for (Connector connector: connectors) {
                ......
                if (connector.getState() != LifecycleState.FAILED) {
                     connector.start();
                }
                ......
            }
        }
    }

该方法主要是启动线程池和连接器。

  • 5
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值