重慶分公司,新征程啟航
為企業提供網站建設、域名注冊、服務器等服務
為企業提供網站建設、域名注冊、服務器等服務
小編給大家分享一下Tomcat9如何加載server.xml,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!
成都創新互聯堅持“要么做到,要么別承諾”的工作理念,服務領域包括:網站制作、成都網站制作、企業官網、英文網站、手機端網站、網站推廣等服務,滿足客戶于互聯網時代的饒河網站設計、移動媒體設計的需求,幫助企業找到有效的互聯網解決方案。努力成為您成熟可靠的網絡建設合作伙伴!
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()方法
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
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
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():文檔解析結束后調用,該方法只會調用一次
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
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
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”這篇文章的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注創新互聯行業資訊頻道!