千家信息网

Tomcat9如何加载server.xml

发表于:2025-02-03 作者:千家信息网编辑
千家信息网最后更新 2025年02月03日,小编给大家分享一下Tomcat9如何加载server.xml,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!1.Tomcat
千家信息网最后更新 2025年02月03日Tomcat9如何加载server.xml

小编给大家分享一下Tomcat9如何加载server.xml,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!

1.Tomcat启动

org.apache.catalina.startup.Bootstrap.main(String args[])
 public static void main(String args[]) {        synchronized (daemonLock) {            if (daemon == null) {                Bootstrap bootstrap = new Bootstrap();                try {                    bootstrap.init(); //初始化类加载器                } catch (Throwable t) {                    handleThrowable(t);                    t.printStackTrace();                    return;                }                daemon = bootstrap;            } else {                Thread.currentThread().setContextClassLoader(daemon.catalinaLoader);            }        }        //根据传入的不同指令,进行相应处理        try {            String command = "start";            if (args.length > 0) {                command = args[args.length - 1];            }            if (command.equals("startd")) {                args[args.length - 1] = "start";                daemon.load(args);                daemon.start();            } else if (command.equals("stopd")) {                args[args.length - 1] = "stop";                daemon.stop();            } else if (command.equals("start")) {                daemon.setAwait(true);                daemon.load(args);                daemon.start();                if (null == daemon.getServer()) {                    System.exit(1);                }            } else if (command.equals("stop")) {                daemon.stopServer(args);            } else if (command.equals("configtest")) {                daemon.load(args);                if (null == daemon.getServer()) {                    System.exit(1);                }                System.exit(0);            } else {                log.warn("Bootstrap: command \"" + command + "\" does not exist.");            }        } catch (Throwable t) {            if (t instanceof InvocationTargetException &&                    t.getCause() != null) {                t = t.getCause();            }            handleThrowable(t);            t.printStackTrace();            System.exit(1);        }    }

在main方法中主要为两部分逻辑:

  • 调用bootstrap.init()进行初始化

  • 根据传入不同的指令进行相应的处理,本文主要分析start指定,即服务启动。启动服务start主要调用了org.apache.catalina.startup.Catalina.load()和start()方法

org.apache.catalina.startup.Bootstrap.init()
 public void init() throws Exception {        initClassLoaders(); //初始化类加载        Thread.currentThread().setContextClassLoader(catalinaLoader); //设置当前线程的类加载器为catalinaLoader        SecurityClassLoad.securityClassLoad(catalinaLoader); //启用java安全管理的处理        //通过反射的方式实例化org.apache.catalina.startup.Catalina,并设置父类加载器为sharedLoader        if (log.isDebugEnabled())            log.debug("Loading startup class");        Class startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");        Object startupInstance = startupClass.getConstructor().newInstance();        if (log.isDebugEnabled())            log.debug("Setting startup class properties");        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;    }
  • 类加载器的初始化,创建commonLoader、catalinaLoader、sharedLoader,具体可参考上一篇 《Tomcat9源代码浅析-类加载体系》

  • 启用java安全管理的处理

  • 通过反射的方式实例化org.apache.catalina.startup.Catalina,并设置父类加载器为sharedLoader

org.apache.catalina.security.SecurityClassLoad
public final class SecurityClassLoad {    public static void securityClassLoad(ClassLoader loader) throws Exception {        securityClassLoad(loader, true);    }    static void securityClassLoad(ClassLoader loader, boolean requireSecurityManager) throws Exception {        if (requireSecurityManager && System.getSecurityManager() == null) {            return;        }        loadCorePackage(loader);        loadCoyotePackage(loader);        loadLoaderPackage(loader);        loadRealmPackage(loader);        loadServletsPackage(loader);        loadSessionPackage(loader);        loadUtilPackage(loader);        loadJavaxPackage(loader);        loadConnectorPackage(loader);        loadTomcatPackage(loader);    }

当时使用Java SecurityManager时,会提前加载一些必要的java类,以避免触发权限异常AccessControlException

2.server.xml解析框架

2.1 SAX

Tomcat中使用SAX解析server.xml文件。SAX解析方式会逐行的解析XML文档,当遇到标签时会触发解析处理器,采用事件处理的方式解析XML,它的优点是不需要将完整的XML文档加载进内存,可以在读取文档的同时就进行解析,节省内存,适合解析超大XML,主要方法有:

  • startDocument():文档解析开始时调用,该方法只会调用一次

  • startElement(String uri, String localName, String qName, Attributes attributes):标签解析开始时调用

  • endElement(String uri, String localName, String qName):标签(节点)解析结束后调用

  • endDocument():文档解析结束后调用,该方法只会调用一次

2.2 规则Rules


Tomcat将server.xml的解析抽象为规则,利用Java的引用传递,通过有副作用的void方法,对xml进行解析,规则调用的顺序与xml解析的顺序是一致的,即start方法是正序,end方法是逆序。
规则中包含以下方法:

  • begin:Degister.startElement 方法调用

  • body、end:Degister.endElement方法中调用,先调用body,再调用end

  • finish:Degister.endDocument方法中调用

Tomcat中常见的规则类型:

  • ObjectCreateRule 创建对应class的对象实例,并放到Designer的堆栈成员属性中

  • SetPropertiesRule 获取堆栈中栈顶的元素,并将xml元素的属性赋值给对象实例

  • SetNextRule 调用父节点的实例对象,将当前对象作为参数,反射调用某个方法

  • ListenerCreateRule 当Listener标签有optional属性为true时,创建实例异常时,强制添加OptionalListener实例

  • ConnectorCreateRule 创建Connector实例

  • SetAllPropertiesRule 主体功能与SetPropertiesRule 一致,这个Rule可以排除一些属性的设置

  • AddPortOffsetRule Set portOffset on all the connectors based on portOffset in the Server

  • CertificateCreateRule 实例化SSLHostConfigCertificate

3.server.xml解析源代码解析

org.apache.catalina.startup.Catalina.load()
public void load() {        if (loaded) {            return;        }        loaded = true;        long t1 = System.nanoTime();        initDirs();        // Before digester - it may be needed        initNaming();        // 读取conf/server.xml        ConfigFileLoader.setSource(new CatalinaBaseConfigurationSource(Bootstrap.getCatalinaBaseFile(), getConfigFile()));        File file = configFile();        // 创建xml解析Digester         Digester digester = createStartDigester();        try (ConfigurationSource.Resource resource = ConfigFileLoader.getSource().getServerXml()) {            InputStream inputStream = resource.getInputStream();            InputSource inputSource = new InputSource(resource.getURI().toURL().toString());            inputSource.setByteStream(inputStream);            digester.push(this);            digester.parse(inputSource); //解析xml        } catch (Exception e) {            log.warn(sm.getString("catalina.configFail", file.getAbsolutePath()), e);            if (file.exists() && !file.canRead()) {                log.warn(sm.getString("catalina.incorrectPermissions"));            }            return;        }        //设置server的属性        getServer().setCatalina(this);        getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile());        getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile());        // Stream redirection        initStreams();        // 初始化server        try {            getServer().init();        } catch (LifecycleException e) {            if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE")) {                throw new java.lang.Error(e);            } else {                log.error(sm.getString("catalina.initError"), e);            }        }        long t2 = System.nanoTime();        if(log.isInfoEnabled()) {            log.info(sm.getString("catalina.init", Long.valueOf((t2 - t1) / 1000000)));        }    }
  • Bootstrap中start指令逻辑,通过反射调用Catalina.load()

  • Catalina.load() 读取conf/server.xml,创建解析xml的Digester

  • 开始初始化server

org.apache.catalina.startup.Catalina.createStartDigester()
 protected Digester createStartDigester() {        long t1=System.currentTimeMillis();        // Initialize the digester        Digester digester = new Digester();        digester.setValidating(false);        digester.setRulesValidation(true);        Map, List> fakeAttributes = new HashMap<>();        // Ignore className on all elements        List objectAttrs = new ArrayList<>();        objectAttrs.add("className");        fakeAttributes.put(Object.class, objectAttrs);        // Ignore attribute added by Eclipse for its internal tracking        List contextAttrs = new ArrayList<>();        contextAttrs.add("source");        fakeAttributes.put(StandardContext.class, contextAttrs);        // Ignore Connector attribute used internally but set on Server        List connectorAttrs = new ArrayList<>();        connectorAttrs.add("portOffset");        fakeAttributes.put(Connector.class, connectorAttrs);        digester.setFakeAttributes(fakeAttributes);        digester.setUseContextClassLoader(true);        // Configure the actions we will be using        digester.addObjectCreate("Server",                                 "org.apache.catalina.core.StandardServer",                                 "className");        digester.addSetProperties("Server");        digester.addSetNext("Server",                            "setServer",                            "org.apache.catalina.Server");        digester.addObjectCreate("Server/GlobalNamingResources",                                 "org.apache.catalina.deploy.NamingResourcesImpl");        digester.addSetProperties("Server/GlobalNamingResources");        digester.addSetNext("Server/GlobalNamingResources",                            "setGlobalNamingResources",                            "org.apache.catalina.deploy.NamingResourcesImpl");        digester.addRule("Server/Listener",                new ListenerCreateRule(null, "className"));        digester.addSetProperties("Server/Listener");        digester.addSetNext("Server/Listener",                            "addLifecycleListener",                            "org.apache.catalina.LifecycleListener");        digester.addObjectCreate("Server/Service",                                 "org.apache.catalina.core.StandardService",                                 "className");        digester.addSetProperties("Server/Service");        digester.addSetNext("Server/Service",                            "addService",                            "org.apache.catalina.Service");        digester.addObjectCreate("Server/Service/Listener",                                 null, // MUST be specified in the element                                 "className");        digester.addSetProperties("Server/Service/Listener");        digester.addSetNext("Server/Service/Listener",                            "addLifecycleListener",                            "org.apache.catalina.LifecycleListener");        //Executor        digester.addObjectCreate("Server/Service/Executor",                         "org.apache.catalina.core.StandardThreadExecutor",                         "className");        digester.addSetProperties("Server/Service/Executor");        digester.addSetNext("Server/Service/Executor",                            "addExecutor",                            "org.apache.catalina.Executor");        digester.addRule("Server/Service/Connector",                         new ConnectorCreateRule());        digester.addRule("Server/Service/Connector", new SetAllPropertiesRule(                new String[]{"executor", "sslImplementationName", "protocol"}));        digester.addSetNext("Server/Service/Connector",                            "addConnector",                            "org.apache.catalina.connector.Connector");        digester.addRule("Server/Service/Connector", new AddPortOffsetRule());        digester.addObjectCreate("Server/Service/Connector/SSLHostConfig",                                 "org.apache.tomcat.util.net.SSLHostConfig");        digester.addSetProperties("Server/Service/Connector/SSLHostConfig");        digester.addSetNext("Server/Service/Connector/SSLHostConfig",                "addSslHostConfig",                "org.apache.tomcat.util.net.SSLHostConfig");        digester.addRule("Server/Service/Connector/SSLHostConfig/Certificate",                         new CertificateCreateRule());        digester.addRule("Server/Service/Connector/SSLHostConfig/Certificate",                         new SetAllPropertiesRule(new String[]{"type"}));        digester.addSetNext("Server/Service/Connector/SSLHostConfig/Certificate",                            "addCertificate",                            "org.apache.tomcat.util.net.SSLHostConfigCertificate");        digester.addObjectCreate("Server/Service/Connector/SSLHostConfig/OpenSSLConf",                                 "org.apache.tomcat.util.net.openssl.OpenSSLConf");        digester.addSetProperties("Server/Service/Connector/SSLHostConfig/OpenSSLConf");        digester.addSetNext("Server/Service/Connector/SSLHostConfig/OpenSSLConf",                            "setOpenSslConf",                            "org.apache.tomcat.util.net.openssl.OpenSSLConf");        digester.addObjectCreate("Server/Service/Connector/SSLHostConfig/OpenSSLConf/OpenSSLConfCmd",                                 "org.apache.tomcat.util.net.openssl.OpenSSLConfCmd");        digester.addSetProperties("Server/Service/Connector/SSLHostConfig/OpenSSLConf/OpenSSLConfCmd");        digester.addSetNext("Server/Service/Connector/SSLHostConfig/OpenSSLConf/OpenSSLConfCmd",                            "addCmd",                            "org.apache.tomcat.util.net.openssl.OpenSSLConfCmd");        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");        digester.addObjectCreate("Server/Service/Connector/UpgradeProtocol",                                  null, // MUST be specified in the element                                  "className");        digester.addSetProperties("Server/Service/Connector/UpgradeProtocol");        digester.addSetNext("Server/Service/Connector/UpgradeProtocol",                            "addUpgradeProtocol",                            "org.apache.coyote.UpgradeProtocol");        // Add RuleSets for nested elements        digester.addRuleSet(new NamingRuleSet("Server/GlobalNamingResources/"));        digester.addRuleSet(new EngineRuleSet("Server/Service/"));        digester.addRuleSet(new HostRuleSet("Server/Service/Engine/"));        digester.addRuleSet(new ContextRuleSet("Server/Service/Engine/Host/"));        addClusterRuleSet(digester, "Server/Service/Engine/Host/Cluster/");        digester.addRuleSet(new NamingRuleSet("Server/Service/Engine/Host/Context/"));        // When the 'engine' is found, set the parentClassLoader.        digester.addRule("Server/Service/Engine",                         new SetParentClassLoaderRule(parentClassLoader));        addClusterRuleSet(digester, "Server/Service/Engine/Cluster/");        long t2=System.currentTimeMillis();        if (log.isDebugEnabled()) {            log.debug("Digester for server.xml created " + ( t2-t1 ));        }        return digester;    }

此方法创建解析server.xml的Digester,根据server.xml的元素标签,为每个标签设置相应的规则组,在解析标签时进行调用。
由此也可以得到结论,server.xml的结构就是Tomcat容器内部的结构,通过对server.xml的解析规则的执行,实例化出Tomcat容器结构。

以下为Tomcat9默认的server.xml

                                                                                      

其结构见下图:

  • Server代表服务器,一个Tomcat只有一个Server

  • Service 代表服务: 一个Server可以对外提供多个服务

  • Connector连接器: service服务的核心组成之一,主要是链接客户端请求

  • Container容器:service服务的核心组成之一,主要是执行业务逻辑,这里按层级为Engine、Host、Context

  • Wrapper:对应Servlet的定义

以上是"Tomcat9如何加载server.xml"这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注行业资讯频道!

0