startup.sh
EXECUTABLE=catalina.sh
......
exec "$PRGDIR"/"$EXECUTABLE" start "$@"
catalina.sh
exec "$_RUNJDB" "$LOGGING_CONFIG" $JAVA_OPTS $CATALINA_OPTS \
-Djava.endorsed.dirs="$JAVA_ENDORSED_DIRS" -classpath "$CLASSPATH" \
-sourcepath "$CATALINA_HOME"/../../java \
-Dcatalina.base="$CATALINA_BASE" \
-Dcatalina.home="$CATALINA_HOME" \
-Djava.io.tmpdir="$CATALINA_TMPDIR" \
org.apache.catalina.startup.Bootstrap "$@" start
Bootstrap.java
1.main方法(入口)
(1)静态对象daemon的创建和初始化
public void init() throws Exception
{
// 查询操作系统变量catalina.home
setCatalinaHome();
// 查询操作系统变量catalina.base,若不存在则取catalina.home的值
setCatalinaBase();
// 初始化各个类加载器对象,包括commonLoader、sharedLoader和catalinaLoader
initClassLoaders();
// 将catalinaLoader设置为Bootstrap的一个属性值
Thread.currentThread().setContextClassLoader(this.catalinaLoader);
SecurityClassLoad.securityClassLoad(this.catalinaLoader);
if (log.isDebugEnabled())log.debug("Loading startup class");
// 加载并创建Catalina类的对象catalinaDaemon
Class startupClass = this.catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");
Object startupInstance = startupClass.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] = this.sharedLoader;
// 反射调用Catalina类的setParentClassLoader方法,把sharedLoader作为参数传入。
Method method = startupInstance.getClass().getMethod(methodName, paramTypes);
method.invoke(startupInstance, paramValues);
this.catalinaDaemon = startupInstance;
}
(2)根据命令行参数对daemon调用对应的方法。
} else if (command.equals("start")) {
// 告诉服务器启动后保持运行状态,并开启特定端口监听后续发来的指令,直到收到SHUTDOWN指令,做关闭服务器处理。
daemon.setAwait(true);
// 对整个Tomcat服务器相关的配置文件进行加载和解析处理,并对Tomcat的各个组件进行初始化配置操作。
daemon.load(args);
// 正式启动Catalina
daemon.start();
}
Catalina.java
/**
* Start a new server instance.
*/
public void load() {
// 设置catalina.home和catalina.base
initDirs();
// 设置URL_PKG_PREFIXES和INITIAL_CONTEXT_FACTORY
initNaming();
// 创建Digester对象,其底层采用SAX解析XML文件
// Java里解析xml文件有两种方式:一种是Dom4J之类将文件全部读取到内存中,在内存里构造一棵Dom树的方式来解析。一种是SAX的读取文件流,在流中碰到相应的xml节点触发相应的节点事件回调相应方法,基于事件的解析方式,优点是不需要先将文件全部读取到内存中。
Digester digester = createStartDigester();
InputSource inputSource = null;
InputStream inputStream = null;
File file = null;
try {
// 返回conf/server.xml
file = configFile();
inputStream = new FileInputStream(file);
inputSource = new InputSource("file://" + file.getAbsolutePath());
} catch (Exception e) {
}
try {
// 设置要解析的文件作为输入源
inputSource.setByteStream(inputStream);
// 将当前对象压入Digester里的对象栈顶,可以保存一个到Digester生成的一系列对象直接的引用,方便后续使用而已
digester.push(this);
// 调用Digester的parse方法解析xml,经过对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等等一系列对象,这些对象从前到后前一个包含后一个对象的引用(一对一或一对多的关系)。
digester.parse(inputSource);
inputStream.close();
} catch (SAXParseException spe) {
} catch (Exception e) {
}
// 设置StandardServer对象的catalina的引用为当前对象 StandardServer[8005]
getServer().setCatalina(this);
// 流重定向 Replace System.out and System.err with a custom PrintStream
initStreams();
// Tomcat的容器生命周期开始!
try {
// 参照下处LifecycleBase.java中的抽象方法initInternal(),实际调用是StandardServer.java中的initInternal()方法。
getServer().init();
} catch (LifecycleException e) {
}
}
LifecycleBase.java
public final synchronized void init() throws LifecycleException {
if (!state.equals(LifecycleState.NEW)) {
invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
}
setStateInternal(LifecycleState.INITIALIZING, null, false);
try {
initInternal();
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
setStateInternal(LifecycleState.FAILED, null, false);
throw new LifecycleException(
sm.getString("lifecycleBase.initFail",toString()), t);
}
setStateInternal(LifecycleState.INITIALIZED, null, false);
}
看下LifecycleBase的实现类就会发现,所有的组件类几乎都继承了LifecycleBase类,所以这些组件类一般只会有initInternal方法的定义。(这里所说的组件类就是前面我们分析Digester解析时发现的StandardServer、StandardService、StandardEngine、StandardContext等类)
在各组件代码中经常会看到的setStateInternal方法的调用,实际上就是向该组件中已注册的监听器发布一个事件。
private synchronized void setStateInternal(LifecycleState state,
Object data, boolean check) throws LifecycleException {
if (check) {
// Must have been triggered by one of the abstract methods (assume
// code in this class is correct)
// null is never a valid state
if (state == null) {
invalidTransition("null");
// Unreachable code - here to stop eclipse complaining about
// a possible NPE further down the method
return;
}
// Any method can transition to failed
if (!(state == LifecycleState.FAILED ||
(this.state == LifecycleState.STARTING_PREP &&
state == LifecycleState.STARTING) ||
(this.state == LifecycleState.STOPPING_PREP &&
state == LifecycleState.STOPPING) ||
(this.state == LifecycleState.FAILED &&
state == LifecycleState.STOPPING))) {
// No other transition permitted
invalidTransition(state.name());
}
}
// 入参LifecycleState实例赋值给本类中的实例变量保存起来
this.state = state;
String lifecycleEvent = state.getLifecycleEvent();
// 如果该事件非空,则调用fireLifecycleEvent方法发布该事件
if (lifecycleEvent != null) {
fireLifecycleEvent(lifecycleEvent, data);
}
}
StandardServer.java
protected void initInternal() throws LifecycleException {
super.initInternal();
// Initialize our defined Services
// services[] -> [StandardService[Catalina]]
for (int i = 0; i < services.length; i++) {
services[i].init();
}
}
之后org.apache.catalina.deploy.NamingResources、org.apache.catalina.core.StandardService、org.apache.catalina.core.StandardEngine、org.apache.catalina.connector.Connector等组件的这个方法都会调用到,例如控制台中的日志:
十一月 29, 2016 7:46:30 下午 org.apache.coyote.AbstractProtocol init
信息: Initializing ProtocolHandler ["http-bio-8080"]
十一月 29, 2016 7:46:48 下午 org.apache.coyote.AbstractProtocol init
信息: Initializing ProtocolHandler ["ajp-bio-8009"]
这些日志就是由Connector->initInternal()->protocolHandler.init()方法打出的。
除了initInternal(),startInternal()方法也经过类似的调用过程,org.apache.catalina.core.StandardServer、org.apache.catalina.deploy.NamingResources、org.apache.catalina.core.StandardService、org.apache.catalina.core.StandardEngine、org.apache.catalina.connector.Connector等组件的这个方法都会调用到,控制台同样有starting的日志:
十一月 29, 2016 11:20:54 下午 org.apache.coyote.AbstractProtocol start
信息: Starting ProtocolHandler ["http-bio-8080"]
十一月 29, 2016 11:21:13 下午 org.apache.coyote.AbstractProtocol start
信息: Starting ProtocolHandler ["ajp-bio-8009"]
这些日志就是由Connector->startInternal()->protocolHandler.init()方法打出的。
Tomcat7在内存中为这一连串组件产生对象,建立对象调用关系,调用它们各自的初始化和启动方法,启动的总体过程就介绍完了,这些对象start之后将会响应客户端的请求,为用户服务了。
LifecycleListener.java
上文介绍了Tomcat7各组件的父类LifecycleBase类,该类实现了接口org.apache.catalina.Lifecycle,而init和start方法实际上就是在这个接口里面定义好的。正因为各组件都实现了Lifecycle接口,所以才能支持组件内部的initInternal()、startInternal()方法内对于子组件的init()和start()方法的调用。通过这种方式,只需要调用最外层Server组件的init()和start()方法,就可以将Tomcat内部的各级子组件初始化和启动起来。这种调用方式称为链式调用。实际上Tomcat的关闭机制也是通过这种方式一步步调用各层组件的stop方法的。
查看Lifecycle接口,我们发现下面三个方法都涉及到了LifecycleListener接口。
/**
* Add a LifecycleEvent listener to this component.
* @Description 给该组件添加一个监听器
* @param listener The listener to add
*/
public void addLifecycleListener(LifecycleListener listener);
/**
* Get the life cycle listeners associated with this life cycle. If this
* component has no listeners registered, a zero-length array is returned.
* @Description 获取该组件所有已注册的监听器
*/
public LifecycleListener[] findLifecycleListeners();
/**
* Remove a LifecycleEvent listener from this component.
* @Description 删除该组件中的一个监听器
* @param listener The listener to remove
*/
public void removeLifecycleListener(LifecycleListener listener);
看下这个接口的定义。
public interface LifecycleListener {
/**
* Acknowledge the occurrence of the specified event.
* @Description 这个方法用作某个事件(org.apache.catalina.LifecycleEvent)产生时通知当前监听器的实现类,具体针对该事件如何处理由监听器实现类自己决定。
* @param event LifecycleEvent that has occurred
*/
public void lifecycleEvent(LifecycleEvent event);
}
LifecycleEvent.java
data和type作为类的内置实例变量,唯一特别是使用了jdk内置的java.util.EventObject作为父类来支持事件定义,这里在事件构造函数中将org.apache.catalina.Lifecycle类的实例lifecycle作为事件源,保存lifecycle对象的引用,并提供了getLifecycle方法返回这个引用。
那么Tomcat中是如何实现关于这些事件的监听以及通知的呢?
public abstract class LifecycleBase implements Lifecycle {
/**
* Used to handle firing lifecycle events.
* TODO: Consider merging LifecycleSupport into this class.
*/
private LifecycleSupport lifecycle = new LifecycleSupport(this);
留心一下lifecycle这个实例变量,它并不是org.apache.catalina.Lifecycle类的实例,而是org.apache.catalina.util.LifecycleSupport类的实例。正是这个工具类提供了事件监听和事件通知的功能。
先看下实际代码中是如何给组件发布时间通知的,我们回顾下前文org.apache.catalina.core.StandardServer类的startInternal()方法:
protected void startInternal() throws LifecycleException {
//这里调用了父类org.apache.catalina.util.LifecycleBase里的fireLifecycleEvent方法,表示发布了一个start配置事件。
fireLifecycleEvent(CONFIGURE_START_EVENT, null);
setState(LifecycleState.STARTING);
globalNamingResources.start();
// Start our defined Services
synchronized (services) {
for (int i = 0; i < services.length; i++) {
services[i].start();
}
}
}
org.apache.catalina.util.LifecycleBase类中的fireLifecycleEvent方法里调用的是org.apache.catalina.util.LifecycleSupport类fireLifecycleEvent方法,该方法代码如下:
public void fireLifecycleEvent(String type, Object data) {
LifecycleEvent event = new LifecycleEvent(lifecycle, type, data);
LifecycleListener interested[] = listeners;
for (int i = 0; i < interested.length; i++)
interested[i].lifecycleEvent(event);
}
这里通过传进来的两个参数构造一个LifecycleEvent对象,然后向注册到组件中的所有监听器发布这个新构造的事件对象。
那么,到底什么时候向组件里注册监听器的呢?
以StandardServer举例,org.apache.catalina.startup.Catalina类的createStartDigester方法有这么一段代码:
digester.addObjectCreate("Server/Listener",
null, // MUST be specified in the element
"className");
digester.addSetProperties("Server/Listener");
digester.addSetNext("Server/Listener",
"**addLifecycleListener**",
"org.apache.catalina.LifecycleListener");
上述代码片将调用org.apache.catalina.core.StandardServer类的addLifecycleListener方法,将根据server.xml中配置的Server节点下的Listener节点所定义的className属性构造对象实例,并作为addLifecycleListener方法的入参。所有的监听器都会实现上面提到的org.apache.catalina.LifecycleListener接口。
Server节点下的Listener节点有好几个,这里以org.apache.catalina.core.JasperListener举例。
<Server port="8005" shutdown="SHUTDOWN">
<Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
<Listener className="org.apache.catalina.core.JasperListener" />
<Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
<Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
<Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />
在构造完org.apache.catalina.core.JasperListener类的对象之后,调用addLifecycleListener方法,这个方法并没有直接在org.apache.catalina.core.StandardServer类中定义,实际调用的是org.apache.catalina.util.LifecycleSupport类的addLifecycleListener方法:
public void addLifecycleListener(LifecycleListener listener) {
// 同步给该数组增加一个监听器对象
synchronized (listenersLock) {
LifecycleListener results[] =
new LifecycleListener[listeners.length + 1];
for (int i = 0; i < listeners.length; i++)
results[i] = listeners[i];
results[listeners.length] = listener;
// 内部保存了一个监听器对象实例数组
listeners = results;
}
}
总的来说就是通过一个工具类LifecycleSupport,调用该类的addLifecycleListener方法增加监听器,需要发布事件时还是调用该工具类的fireLifecycleEvent方法,将事件发布给组件上注册的所有监听器,由监听器内部实现来决定是否处理该事件。例如监听器org.apache.catalina.core.JasperListener只关心事件类型为BEFORE_INIT_EVENT的事件,如果发布了该事件,才会做后续处理(这里会产生一个org.apache.jasper.compiler.JspRuntimeContext对象)。
总结
如果对设计模式比较熟悉的话会发现Tomcat的Lifecycle使用的是观察者模式:LifecycleListener代表的是抽象观察者,它定义一个lifecycleEvent方法,而实现该接口的监听器是作为具体的观察者。Lifecycle 接口代表的是抽象主题,它定义了管理观察者的方法和它要所做的其它方法。而各组件代表的是具体主题,它实现了抽象主题的所有方法。通常会由具体主题保存对具体观察者对象有用的内部状态;在这种内部状态改变时给其观察者发出一个通知。*Tomcat对这种模式做了改进,增加了另外两个工具类:LifecycleSupport、LifecycleEvent,它们作为辅助类扩展了观察者的功能。LifecycleEvent中定义了事件类别,不同的事件在具体观察者中可区别处理,更加灵活。**LifecycleSupport 类代理了所有具体主题对观察者的管理,将这个管理抽出来统一实现,以后如果修改只要修改 LifecycleSupport 类就可以了,不需要去修改所有具体主题,因为所有具体主题的对观察者的操作都被代理给 LifecycleSupport 类了。事件的发布使用的是推模式*,即每发布一个事件都会通知主题的所有具体观察者,由各观察者再来决定是否需要对该事件进行后续处理。
本文详细剖析了Tomcat服务器从启动脚本到各组件初始化及启动的全过程,深入探讨了核心类Bootstrap的工作机制,以及组件间如何通过Lifecycle接口实现链式调用。
6475

被折叠的 条评论
为什么被折叠?



