分享tomcat源码系列一
原文连接:http://simpleframework.net/blog/v/16313.html
最近在看Tomcat的源码,下面用博客记下看源码的一些心得。
Tomcat是从org.apache.catalina.startup.Bootstrap#main()开始启动.大致分为三个步骤,即init、load和start。代码如下:
Java代码
publicstaticvoidmain(Stringargs[]){
try{
//AttempttoloadJMXclass
newObjectName("test:foo=bar");
}catch(Throwablet){
System.out.println(JMX_ERROR_MESSAGE);
try{
//Giveuserssometimetoreadthemessagebeforeexiting
Thread.sleep(5000);
}catch(Exceptionex){
}
return;
}
if(daemon==null){
daemon=newBootstrap();
try{
daemon.init();★1
}catch(Throwablet){
t.printStackTrace();
return;
}
}
try{
Stringcommand="start";
if(args.length>0){
command=args[args.length-1];
}
if(command.equals("startd")){
args[0]="start";
daemon.load(args);
daemon.start();
}elseif(command.equals("stopd")){
args[0]="stop";
daemon.stop();
}elseif(command.equals("start")){
daemon.setAwait(true);
daemon.load(args);★2
//反射调用Catalina的start方法
daemon.start();★3
}elseif(command.equals("stop")){
daemon.stopServer(args);
}
}catch(Throwablet){
t.printStackTrace();
}
}
从以上可以很清楚的看出tomcat是通过参数的不同进行相应的命令调用。
★1启动、初始化(加载类)
启动之前要进行相应的init()初始化,进行相应的环境设置以及包的加,以下是init()方法。(org.apache.catalina.startup.Bootstrap.init())
Java代码
publicvoidinit()
throwsException
{
setCatalinaHome();//设置Catalina安装目录
setCatalinaBase();//设置Catalina工作目录
initClassLoaders();//加载jar包
//将classload设置进线程,以便我们使用时进行调用
Thread.currentThread().
setContextClassLoader(catalinaLoader);
SecurityClassLoad.securityClassLoad(catalinaLoader);
//加载启动类和调用它的process方法
if(log.isDebugEnabled())
log.debug("Loadingstartupclass");
ClassstartupClass=
catalinaLoader.loadClass
("org.apache.catalina.startup.Catalina");
ObjectstartupInstance=startupClass.newInstance();
//设置共享扩张类加载器
if(log.isDebugEnabled())
log.debug("Settingstartupclassproperties");
Stringmethodname="setParentClassLoader";
ClassparamTypes[]=newClass[1];
paramTypes[0]=Class.forName("java.lang.ClassLoader");
ObjectparamValues[]=newObject[1];
paramValues[0]=sharedLoader;
Methodmethod=
startupInstance.getClass().getMethod(methodName,
paramTypes);
method.invoke(startupInstance,paramValues);
catalinaDaemon=startupInstance;
}
在加载jar的时候,需要初始化classloader,代码如下:(org.apache.catalina.startup.Bootstrap)
Java代码
privatevoidinitClassLoaders(){
try{
commonLoader=createClassLoader("common",null);
catalinaLoader=createClassLoader("server",commonLoader);
sharedLoader=createClassLoader("shared",commonLoader);
}catch(Throwablet){
log.error("Classloadercreationthrewexception",t);
System.exit(1);
}
}
tomcat中的加载方式是:
|-------commonLoader(common)->SystemLoader
|-------sharedLoader(shared)->commonLoader->SystemLoader
|-------catalinaLoader(server)->commonLoader->SystemLoader
Common是公共类加载器,负责加载tomcat内部和web应用程序可以看到的类(%CATALINA_HOME%/bin/common下的jar文件),Catalina负责加载的是tomcat内部使用的类(%CATALINA_HOME%/server下的jar文件),这些类对web应用程序不可见。Shared负责加载的是web应用程序之间共享的类(%CATALINA_BASE%/shared下的jar文件),这些类对于tomcat内部是不可见的。如果%CATALINA_HOME%/conf/catalina.Properties中没有指定Common的搜索路径,则用当前的类的类加载器即系统类加载器作为Common。
★2装载相应的资源
下面主要讲解tomcat的load()方法。下图是Catalina.load方法的时序图。
(1)从上面的时序图可以看出首先调用Catalina类的load()方法,具体代码如下:
(org.apache.catalina.startup.Catalina)。
Java代码
publicvoidload(){
initDirs();
//Beforedigester-itmaybeneeded
initNaming();
//CreateandexecuteourDigester
Digesterdigester=createStartDigester();
try{
inputSource.setByteStream(inputStream);
digester.push(this);
digester.parse(inputSource);//对server.xml进行解析
inputStream.close();
}
......
//Startthenewserver
if(serverinstanceofLifecycle){
try{
server.initialize();//server初始化工作
}catch(LifecycleExceptione){
log.error("Catalina.start",e);
}
}
longt2=System.currentTimeMillis();
log.info("Initializationprocessedin"+(t2-t1)+"ms");
}
(2)在上面的load()方法中需要进行server的初始化工作,下图为Catalina.initialize的时序图,从图中可以看出server初始化所完成的工作。
至此,load方法结束,初期化的工作结束,下面开始进入start方法。
★3容器启动
容器启动时,会调用Catalina.start(),下图为它的时序图。从图中可以看出StandardService的start方法被调用后会分别对Container和Connector进行start方法的调用。
1.Bootstrap调用Catalina的start方法
Catalina.start()方法(org.apache.catalina.startup.Catalina.start())
Java代码
publicvoidstart(){
//启动server
if(serverinstanceofLifecycle){
try{
((Lifecycle)server).start();
......
}
2.Catalina调用StandardServer的start方法
StandardServer.start()(org.apache.catalina.core.StandardServer.start())
Java代码
publicvoidstart()throwsLifecycleException{
synchronized(services){
for(inti=0;i<services.length;i++){
if(services[i]instanceofLifecycle)
((Lifecycle)services[i]).start();
}
}
3.StandardServer调用StandardService的start方法
Java代码
org.apache.catalina.core.StandardService.start())
publicvoidstart()throwsLifecycleException{
if(container!=null){
synchronized(container){
if(containerinstanceofLifecycle){
//standardEngine的启动
((Lifecycle)container).start();
}
}
//两个connector的启动,8080和8009
synchronized(connectors){
for(inti=0;i<connectors.length;i++){
if(connectors[i]instanceofLifecycle)
((Lifecycle)connectors[i]).start();
}
}
}
以上StandardService.start()方法主要实现了两个功能,standardEngine的启动和connector的启动,下面分别来介绍。