写在前面:
学习是什么?学习就是模仿,重复,实践,总结。
最近看的这本书里面有tomcat的简单源码部分,就研究一下,会有收获的。
这几天在看源码是用的是idea这款工具,几个月前还是挺抵触它的,感觉它没有eclipse好用,但是本着挑战未知领域的精神使用了3天,已经慢慢喜欢上这款工具了,推荐一下。
文章开始:
Server 服务器
Service 服务
Connector 连接器
Container 容器
顶层结构图:
Tomcat中最顶层的容器叫server,代表整个服务器,Server中至少包含一个Service,用于提供具体的服务。Service包含俩部分:container和connector。container用于封装和管理servlet,以及具体的request请求。Connector用于处理连接相关的事情,并提供Socket与request,response的转换。
它们之间的关系:(服务于服务器,server与service)
一个tomcat中只有一个server
一个server可以有多个service
一个service只有一个Container,但是可以有多个Connectors,因为一个服务可以有多个连接,如提供http和https连接,也可以提供相同协议不同端口的连接
Bootstrap是Tomcat的入口,正常情况下启动Tomcat就是调用Bootstrap的main方法
单词解释:
catalina可以理解为一个Servlet容器
await 等待
实现类:
org.apache.catalina.startup.Bootstrap
代码开始(篇幅问题,只说最主要的代码)
Bootstrap的main方法如下:
private static Bootstrap daemon = null;
private Object catalinaDaemon = null;
public static void main(String[] args) {
//新建一个Bootstrap
if(daemon == null) {
Bootstrap t = new Bootstrap();
try {
//初始化了ClassLoader,并用ClassLoader创建了Catalina实例,并赋值给catalinaDaemon变量
t.init();
} catch (Throwable var3) {
handleThrowable(var3);
var3.printStackTrace();
return;
}
daemon = t;
} else {
Thread.currentThread().setContextClassLoader(daemon.catalinaLoader);
}
try {
String t2 = "start";
if(args.length > 0) {
t2 = args[args.length - 1];
}
if(t2.equals("startd")) {
args[args.length - 1] = "start";
daemon.load(args);
daemon.start();
} else if(t2.equals("stopd")) {
args[args.length - 1] = "stop";
daemon.stop();
} else if(t2.equals("start")) {
daemon.setAwait(true);
daemon.load(args);
daemon.start();
} else if(t2.equals("stop")) {
daemon.stopServer(args);
} else if(t2.equals("configtest")) {
daemon.load(args);
if(null == daemon.getServer()) {
System.exit(1);
}
System.exit(0);
} else {
log.warn("Bootstrap: command \"" + t2 + "\" does not exist.");
}
} catch (Throwable var4) {
Throwable t1 = var4;
if(var4 instanceof InvocationTargetException && var4.getCause() != null) {
t1 = var4.getCause();
}
handleThrowable(t1);
t1.printStackTrace();
System.exit(1);
}
}
main方法包含俩部分内容:
1、新建Bootstrap,并执行init方法
2、处理main方法中传入的命令,如果args参数为空,则默认执行start方法
新建Bootstrap后会执行init方法,也就是上面代码中的init方法:
public void init() throws Exception {
初始化了ClassLoader
this.initClassLoaders();
Thread.currentThread().setContextClassLoader(this.catalinaLoader);
SecurityClassLoad.securityClassLoad(this.catalinaLoader);
if(log.isDebugEnabled()) {
log.debug("Loading startup class");
}
Class startupClass = this.catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");
并用ClassLoader创建了Catalina实例
Object startupInstance = startupClass.newInstance();
if(log.isDebugEnabled()) {
log.debug("Setting startup class properties");
}
String methodName = "setParentClassLoader";
Class[] paramTypes = new Class[]{Class.forName("java.lang.ClassLoader")};
Object[] paramValues = new Object[]{this.sharedLoader};
Method method = startupInstance.getClass().getMethod(methodName, paramTypes);
method.invoke(startupInstance, paramValues);
并赋值给catalinaDaemon变量
this.catalinaDaemon = startupInstance;
}
这个方法主要做了三件事:
1、初始化了ClassLoader
2、用ClassLoader创建了Catalina实例,并赋值给catalinaDaemon变量
3、使用catalinaDaemon来执行命令操作
该main方法当为空参时,也就是默认执行start命令:
它执行了下面三个方法:这个三个方法内部都调用了Catalina的相关方法进行具体执行,而且都是用反射来执行的。
daemon.setAwait(true);
daemon.load(args);
daemon.start();
以start方法为例:
public void start() throws Exception {
if(this.catalinaDaemon == null) {
this.init();
}
Method method = this.catalinaDaemon.getClass().getMethod("start", (Class[])null);
method.invoke(this.catalinaDaemon, (Object[])null);
}
做了三件事:
1、判断catalinaDaemon是否初始化,如果没有则对其初始化
2、使用Method进行反射调用它的空参start方法(涉及反射相关知识点内容后期会进行补充)
3、反射后的调用效果就是:((Catalina)catalinaDaemon).start()
而daemon.setAwait(true)与daemon.load(args)这俩个方法也是用类似的方法调用了Catalina中的setAwait和load方法
下一篇学习Catalina的启动过程