В предыдущей статье я увидел, что метод main в Bootstrap, наконец, вызовет методы load и start объекта org.apache.catalina.startup.Catalina, так что давайте посмотрим, что делают эти два метода.
Метод ЗАГРУЗКИ:
1 /**
2 * Start a new server instance.
3 */
4 public void load() {
5
6 long t1 = System.nanoTime();
7
8 initDirs();
9
10 // Before digester - it may be needed
11
12 initNaming();
13
14 // Create and execute our Digester
15 Digester digester = createStartDigester();
16
17 InputSource inputSource = null;
18 InputStream inputStream = null;
19 File file = null;
20 try {
21 file = configFile();
22 inputStream = new FileInputStream(file);
23 inputSource = new InputSource(file.toURI().toURL().toString());
24 } catch (Exception e) {
25 if (log.isDebugEnabled()) {
26 log.debug(sm.getString("catalina.configFail", file), e);
27 }
28 }
29 if (inputStream == null) {
30 try {
31 inputStream = getClass().getClassLoader()
32 .getResourceAsStream(getConfigFile());
33 inputSource = new InputSource
34 (getClass().getClassLoader()
35 .getResource(getConfigFile()).toString());
36 } catch (Exception e) {
37 if (log.isDebugEnabled()) {
38 log.debug(sm.getString("catalina.configFail",
39 getConfigFile()), e);
40 }
41 }
42 }
43
44 // This should be included in catalina.jar
45 // Alternative: don't bother with xml, just create it manually.
46 if( inputStream==null ) {
47 try {
48 inputStream = getClass().getClassLoader()
49 .getResourceAsStream("server-embed.xml");
50 inputSource = new InputSource
51 (getClass().getClassLoader()
52 .getResource("server-embed.xml").toString());
53 } catch (Exception e) {
54 if (log.isDebugEnabled()) {
55 log.debug(sm.getString("catalina.configFail",
56 "server-embed.xml"), e);
57 }
58 }
59 }
60
61
62 if (inputStream == null || inputSource == null) {
63 if (file == null) {
64 log.warn(sm.getString("catalina.configFail",
65 getConfigFile() + "] or [server-embed.xml]"));
66 } else {
67 log.warn(sm.getString("catalina.configFail",
68 file.getAbsolutePath()));
69 if (file.exists() && !file.canRead()) {
70 log.warn("Permissions incorrect, read permission is not allowed on the file.");
71 }
72 }
73 return;
74 }
75
76 try {
77 inputSource.setByteStream(inputStream);
78 digester.push(this);
79 digester.parse(inputSource);
80 } catch (SAXParseException spe) {
81 log.warn("Catalina.start using " + getConfigFile() + ": " +
82 spe.getMessage());
83 return;
84 } catch (Exception e) {
85 log.warn("Catalina.start using " + getConfigFile() + ": " , e);
86 return;
87 } finally {
88 try {
89 inputStream.close();
90 } catch (IOException e) {
91 // Ignore
92 }
93 }
94
95 getServer().setCatalina(this);
96
97 // Stream redirection
98 initStreams();
99
100 // Start the new server
101 try {
102 getServer().init();
103 } catch (LifecycleException e) {
104 if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE")) {
105 throw new java.lang.Error(e);
106 } else {
107 log.error("Catalina.start", e);
108 }
109
110 }
111
112 long t2 = System.nanoTime();
113 if(log.isInfoEnabled()) {
114 log.info("Initialization processed in " + ((t2 - t1) / 1000000) + " ms");
115 }
116
117 }
Этот 117-строчный код выглядит многообразно.Отложив в сторону комментарии, генерацию исключений, ведение журнала, закрытие потока и ненулевое суждение, вы обнаружите, что на самом деле есть всего несколько строк кода, которые действительно что-то делают:
Digester digester = createStartDigester();
inputSource.setByteStream(inputStream);
digester.push(this);
digester.parse(inputSource);
getServer().setCatalina(this);
getServer().init();
Есть две вещи, которые нужно сделать: во-первых, создать объект Digester, поместить текущий объект в верхнюю часть стека объектов в Digester и сгенерировать соответствующий объект в соответствии с путем к файлу xml, установленным в inputSource, и содержащимися правилами синтаксического анализа. в созданном объекте Digester и вызовите соответствующий метод, чтобы связать объекты. Второй — вызвать метод init объекта интерфейса сервера.
Я встретил Digester здесь, и необходимо ввести некоторые базовые знания о Digester. Вообще говоря, есть два способа парсить XML-файлы в Java: первый — прочитать все файлы в память, например, Dom4J, и построить дерево Dom в памяти для синтаксического анализа. Одним из них является поток чтения файлов SAX. Когда в потоке встречается соответствующий узел xml, запускается событие соответствующего узла для обратного вызова соответствующего метода. Преимущество метода синтаксического анализа на основе событий состоит в том, что ему не нужно читать все файлы. сначала в память.
Сам Digester использует метод парсинга SAX и предоставляет на нем слой упаковки, который более удобен и дружелюбен для пользователей. Сначала он использовался в struts 1, а позже стал отдельным подпроектом Commons of apache. Он инкапсулирован в другом слое в Tomcat.Для удобства описания мы непосредственно возьмем пример построения отдельного Digester в Tomcat, чтобы представить его.
1 package org.study.digester;
2
3 import java.io.IOException;
4 import java.io.InputStream;
5 import java.util.ArrayList;
6 import java.util.HashMap;
7 import java.util.List;
8
9 import junit.framework.Assert;
10
11 import org.apache.tomcat.util.digester.Digester;
12 import org.xml.sax.InputSource;
13
14 public class MyDigester {
15
16 private MyServer myServer;
17
18 public MyServer getMyServer() {
19 return myServer;
20 }
21
22 public void setMyServer(MyServer myServer) {
23 this.myServer = myServer;
24 }
25
26 private Digester createStartDigester() {
27 // 实例化一个Digester对象
28 Digester digester = new Digester();
29
30 // 设置为false表示解析xml时不需要进行DTD的规则校验
31 digester.setValidating(false);
32
33 // 是否进行节点设置规则校验,如果xml中相应节点没有设置解析规则会在控制台显示提示信息
34 digester.setRulesValidation(true);
35
36 // 将xml节点中的className作为假属性,不必调用默认的setter方法(一般的节点属性在解析时将会以属性值作为入参调用该节点相应对象的setter方法,而className属性的作用是提示解析器用该属性的值来实例化对象)
37 HashMap, List> fakeAttributes = new HashMap, List>();
38 ArrayList attrs = new ArrayList();
39 attrs.add("className");
40 fakeAttributes.put(Object.class, attrs);
41 digester.setFakeAttributes(fakeAttributes);
42
43 // addObjectCreate方法的意思是碰到xml文件中的Server节点则创建一个MyStandardServer对象
44 digester.addObjectCreate("Server",
45 "org.study.digester.MyStandardServer", "className");
46 // 根据Server节点中的属性信息调用相应属性的setter方法,以上面的xml文件为例则会调用setPort、setShutdown方法,入参分别是8005、SHUTDOWN
47 digester.addSetProperties("Server");
48 // 将Server节点对应的对象作为入参调用栈顶对象的setMyServer方法,这里的栈顶对象即下面的digester.push方法所设置的当前类的对象this,就是说调用MyDigester类的setMyServer方法
49 digester.addSetNext("Server", "setMyServer",
50 "org.study.digester.MyServer");
51
52 // 碰到xml的Server节点下的Listener节点时取className属性的值作为实例化类实例化一个对象
53 digester.addObjectCreate("Server/Listener", null, "className");
54 digester.addSetProperties("Server/Listener");
55 digester.addSetNext("Server/Listener", "addLifecycleListener",
56 "org.apache.catalina.LifecycleListener");
57
58 digester.addObjectCreate("Server/Service",
59 "org.study.digester.MyStandardService", "className");
60 digester.addSetProperties("Server/Service");
61 digester.addSetNext("Server/Service", "addMyService",
62 "org.study.digester.MyService");
63
64 digester.addObjectCreate("Server/Service/Listener", null, "className");
65 digester.addSetProperties("Server/Service/Listener");
66 digester.addSetNext("Server/Service/Listener", "addLifecycleListener",
67 "org.apache.catalina.LifecycleListener");
68 return digester;
69 }
70
71 public MyDigester() {
72 Digester digester = createStartDigester();
73
74 InputSource inputSource = null;
75 InputStream inputStream = null;
76 try {
77 String configFile = "myServer.xml";
78 inputStream = getClass().getClassLoader().getResourceAsStream(
79 configFile);
80 inputSource = new InputSource(getClass().getClassLoader()
81 .getResource(configFile).toString());
82
83 inputSource.setByteStream(inputStream);
84 digester.push(this);
85 digester.parse(inputSource);
86 } catch (Exception e) {
87 e.printStackTrace();
88 } finally {
89 try {
90 inputStream.close();
91 } catch (IOException e) {
92 // Ignore
93 }
94 }
95
96 getMyServer().setMyDigester(this);
97 }
98
99 public static void main(String[] agrs) {
100 MyDigester md = new MyDigester();
101 Assert.assertNotNull(md.getMyServer());
102 }
103 }
Выше приведен пример парсинга xml файла классом инструментов Digester в Tomcat.Смысл вызова ключевого метода написан в комментариях.Парсинг представляет собой файл myServer.xml в корневом каталоге, где находится исходный файл проекта опубликован.
<?xml version='1.0' encoding='utf-8'?>
<Server port="8005" shutdown="SHUTDOWN">
<Listener
className="org.apache.catalina.core.JasperListener" />
<Listener
className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />
<Service name="Catalina">
<Listener
className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />
</Service>
</Server>
Использование Digester обычно состоит из 4 шагов:
- Создайте экземпляр объекта Digester и установите соответствующие правила разрешения узлов в объекте.
- Установите файл для анализа в качестве источника ввода ( InputSource ), где InputSource такой же, как в SAX для представления объекта xml.
- Поместить текущий объект в стек.
- Вызовите метод parse Digester, чтобы проанализировать xml и сгенерировать соответствующий объект.
Функция помещения текущего объекта в стек на шаге 3 заключается в сохранении прямой ссылки на ряд объектов, сгенерированных Digester, что удобно для последующего использования, поэтому он не обязательно должен быть текущим объектом, пока есть это место для хранения этой ссылки.
Здесь необходимо пояснить, что во многих статьях описывается так называемая стековая модель Digester по порядку кода для описания вызывающего объекта метода addSetNext, на самом деле я понимаю, какой вызывающий объект метода addSetNext относится к узлу древовидная структура в файле XML. Какой объект является родительским узлом для текущего узла? Этот объект является вызывающим объектом. Здесь можно попробовать нарушить порядок кода и все равно разобрать его по правилам, метод createStartDigester только определяет правила разбора, и конкретный разбор не имеет ничего общего с порядком вызова методов addObjectCreate, addSetProperties и добавитьSetNext. Digester сам добавляет концепцию правила при анализе элементов узла xml. Методы addObjectCreate, addSetProperties, addSetNext фактически добавляют правила. При разборе xml в конце он будет соответствовать соответствующему пути узла в соответствии с прочитанным узлом. вызывает внутренний метод. Принцип анализа Digester в Tomcat можно будет проанализировать позже в отдельной статье.
Код этого примера можно запустить непосредственно во вложении к среде исходного кода Tomcat 7.
Вы должны быть в состоянии понять метод CreateStArtDiGester в приведенном выше примере. Это просто обрабатывает больше типов узлов, чем в примере, и добавляет несколько пользовательских правил анализа. Например, линия 384 сталкивается с сервером / GlobalMangeReSources / Node. Метод AddruleInstances ORG.APACHE.CATALINA.STARTUP.NAMINGRULESET Класс будет призван добавить правила обшивки.
XML-файл для анализа сначала найдет conf/server.xml по умолчанию. Если текущий проект не может быть найден, XML-файл будет найден по другим путям для анализа. Здесь в качестве примера для анализа будет использоваться случай по умолчанию. сервер.xml
<?xml version='1.0' encoding='utf-8'?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<!-- Note: A "Server" is not itself a "Container", so you may not
define subcomponents such as "Valves" at this level.
Documentation at /docs/config/server.html
-->
<Server port="8005" shutdown="SHUTDOWN">
<!-- Security listener. Documentation at /docs/config/listeners.html
<Listener className="org.apache.catalina.security.SecurityListener" />
-->
<!--APR library loader. Documentation at /docs/apr.html -->
<Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
<!--Initialize Jasper prior to webapps are loaded. Documentation at /docs/jasper-howto.html -->
<Listener className="org.apache.catalina.core.JasperListener" />
<!-- Prevent memory leaks due to use of particular java/javax APIs-->
<Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
<Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
<Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />
<!-- Global JNDI resources
Documentation at /docs/jndi-resources-howto.html
-->
<GlobalNamingResources>
<!-- Editable user database that can also be used by
UserDatabaseRealm to authenticate users
-->
<Resource name="UserDatabase" auth="Container"
type="org.apache.catalina.UserDatabase"
description="User database that can be updated and saved"
factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
pathname="conf/tomcat-users.xml" />
</GlobalNamingResources>
<!-- A "Service" is a collection of one or more "Connectors" that share
a single "Container" Note: A "Service" is not itself a "Container",
so you may not define subcomponents such as "Valves" at this level.
Documentation at /docs/config/service.html
-->
<Service name="Catalina">
<!--The connectors can use a shared executor, you can define one or more named thread pools-->
<!--
<Executor name="tomcatThreadPool" namePrefix="catalina-exec-"
maxThreads="150" minSpareThreads="4"/>
-->
<!-- A "Connector" represents an endpoint by which requests are received
and responses are returned. Documentation at :
Java HTTP Connector: /docs/config/http.html (blocking & non-blocking)
Java AJP Connector: /docs/config/ajp.html
APR (HTTP/AJP) Connector: /docs/apr.html
Define a non-SSL HTTP/1.1 Connector on port 8080
-->
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
<!-- A "Connector" using the shared thread pool-->
<!--
<Connector executor="tomcatThreadPool"
port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
-->
<!-- Define a SSL HTTP/1.1 Connector on port 8443
This connector uses the JSSE configuration, when using APR, the
connector should be using the OpenSSL style configuration
described in the APR documentation -->
<!--
<Connector port="8443" protocol="HTTP/1.1" SSLEnabled="true"
maxThreads="150" scheme="https" secure="true"
clientAuth="false" sslProtocol="TLS" />
-->
<!-- Define an AJP 1.3 Connector on port 8009 -->
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
<!-- An Engine represents the entry point (within Catalina) that processes
every request. The Engine implementation for Tomcat stand alone
analyzes the HTTP headers included with the request, and passes them
on to the appropriate Host (virtual host).
Documentation at /docs/config/engine.html -->
<!-- You should set jvmRoute to support load-balancing via AJP ie :
<Engine name="Catalina" defaultHost="localhost" jvmRoute="jvm1">
-->
<Engine name="Catalina" defaultHost="localhost">
<!--For clustering, please take a look at documentation at:
/docs/cluster-howto.html (simple how to)
/docs/config/cluster.html (reference documentation) -->
<!--
<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/>
-->
<!-- Use the LockOutRealm to prevent attempts to guess user passwords
via a brute-force attack -->
<Realm className="org.apache.catalina.realm.LockOutRealm">
<!-- This Realm uses the UserDatabase configured in the global JNDI
resources under the key "UserDatabase". Any edits
that are performed against this UserDatabase are immediately
available for use by the Realm. -->
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase"/>
</Realm>
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true">
<!-- SingleSignOn valve, share authentication between web applications
Documentation at: /docs/config/valve.html -->
<!--
<Valve className="org.apache.catalina.authenticator.SingleSignOn" />
-->
<!-- Access log processes all example.
Documentation at: /docs/config/valve.html
Note: The pattern used is equivalent to using pattern="common" -->
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="localhost_access_log." suffix=".txt"
pattern="%h %l %u %t "%r" %s %b" />
</Host>
</Engine>
</Service>
</Server>
Таким образом, после разбора файла xml он сгенерирует org.apache.catalina.core.StandardServer, org.apache.catalina.core.StandardService, org.apache.catalina.connector.Connector, org.apache.catalina.core. .StandardEngine, ряд объектов, таких как org.apache.catalina.core.StandardHost, org.apache.catalina.core.StandardContext и т. д., спереди назад, предыдущий содержит ссылку на следующий объект (один- связь «к одному» или «один ко многим»).
После синтаксического анализа xml закройте файловый поток, а затем установите ссылку на каталину объекта StandardServer (при анализе xml выше) в качестве текущего объекта.Эта двунаправленная ссылка между объектами встречается во многих местах в Tomcat.
Далее вызывается метод init объекта StandardServer.
Приведенный выше анализ представляет собой метод загрузки Catalina. В предыдущей статье я видел, что метод start объекта Catalina будет вызываться при запуске класса Bootstrap. Код выглядит следующим образом:
1 /**
2 * Start a new server instance.
3 */
4 public void start() {
5
6 if (getServer() == null) {
7 load();
8 }
9
10 if (getServer() == null) {
11 log.fatal("Cannot start server. Server instance is not configured.");
12 return;
13 }
14
15 long t1 = System.nanoTime();
16
17 // Start the new server
18 try {
19 getServer().start();
20 } catch (LifecycleException e) {
21 log.fatal(sm.getString("catalina.serverStartFail"), e);
22 try {
23 getServer().destroy();
24 } catch (LifecycleException e1) {
25 log.debug("destroy() failed for failed Server ", e1);
26 }
27 return;
28 }
29
30 long t2 = System.nanoTime();
31 if(log.isInfoEnabled()) {
32 log.info("Server startup in " + ((t2 - t1) / 1000000) + " ms");
33 }
34
35 // Register shutdown hook
36 if (useShutdownHook) {
37 if (shutdownHook == null) {
38 shutdownHook = new CatalinaShutdownHook();
39 }
40 Runtime.getRuntime().addShutdownHook(shutdownHook);
41
42 // If JULI is being used, disable JULI's shutdown hook since
43 // shutdown hooks run in parallel and log messages may be lost
44 // if JULI's hook completes before the CatalinaShutdownHook()
45 LogManager logManager = LogManager.getLogManager();
46 if (logManager instanceof ClassLoaderLogManager) {
47 ((ClassLoaderLogManager) logManager).setUseShutdownHook(
48 false);
49 }
50 }
51
52 if (await) {
53 await();
54 stop();
55 }
56 }
Вот основные вызовы методов запуска объекта StandardServer.
После приведенного выше анализа обнаружена, что после анализа XML генерировать соответствующую серию объектов, методы init и init of Prancyerver объекта STRUSCENTERVER будут вызывать последовательно. Это включает в себя цикл жизненного цикла Tomcat (жизненный цикл), который будет проанализирован в следующая статья.,