tomcat源码解析(二)–类的加载过程
tocmat的启动入口
查找Bootstrap类,在其main()上打上断点
public static void main(String args[]) {
synchronized (daemonLock) {
if (daemon == null) {
// Don't set daemon until init() has completed
Bootstrap bootstrap = new Bootstrap();
try {
bootstrap.init();
} catch (Throwable t) {
handleThrowable(t);
t.printStackTrace();
return;
}
daemon = bootstrap;
} else {
// When running as a service the call to stop will be on a new
// thread so make sure the correct class loader is used to
// prevent a range of class not found exceptions.
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);
//加载了http协议以及endpoint
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) {
// Unwrap the Exception for clearer error reporting
if (t instanceof InvocationTargetException &&
t.getCause() != null) {
t = t.getCause();
}
handleThrowable(t);
t.printStackTrace();
System.exit(1);
}
}
首先会进入到 bootstrap.init();方法中
该方法主要是:
- 创建了三个类加载器
- 创建commonLoader,如果未创建成果的话,则使用应用程序类加载器作为commonLoader
- 创建catalinaLoader,父类加载器为commonLoader
- 创建sharedLoader,父类加载器为commonLoader
- 设置线程上下文加载器
- 线程安全的加载tomcat中各个包
- 加载主类Catalina
- 通过反射获取实例对象
- 传入sharedLoader 设置它的父类加载器
然后执行bootstrap.load();方法
实际上执行的是Catalina.load();方法
- 创建的是一个server实例
- initDirs();和initNaming();只是初始化了一些环境变量,并没有起到其他作用
- ConfigFileLoader 这个类的作用,是从给定的位置上获取配置文件(例如从文件系统中直接获取改配置文件),所以ConfigFileLoader.setSource()只是将server.xml的文件路径加载进来
- 获取server.xml文件
- 创建Digester 用于解析server.xml文件
- 从文件系统中获取server.xml文件流
- 进行解析,并存储在Digester 对象中
- 通过parse 进行解析
- 将server.xml的文件中的内容进行解析,并且进行绑定
- 使用到的规则 createStartDigester 所设置对应的规则进行匹配,在缓存中取
- digester.addObjectCreate(“Server”, “org.apache.catalina.core.StandardServer”, “className”);这个方法表示的含义是,碰到Sever这个标签,则创建一个StandardServer的对象
- 调用了addChildInternal等方法
- 只是进行了加载,并未启动子组件
- 接下来设置server的Catalina 相关属性
- 初始化流
- 然后执行StandardServer 的initInternal()方法
- 将容器托管到JMX,便于运维管理
- 初始化效用执行人
- 往jmx中注册全局的String cache,尽管这个cache是全局的,但是如果在同一个jvm中存在多个Server,那么则会注册多个不同名字的StringCache,这种情况在内嵌的tomcat中可能会出现
- 注册MBeanFactory
- 初始化 globalNamingResources
- 将server.xml中的GlobalNamingResources标签创建一个 Mbean对象 - 寻启动子容器的 init 方法,进入子容器方法中
- StandardService的initInternal方法
- 执行StandardEngine的initInternal方法
- ealm域提供了一种用户密码与web应用的映射关系。
- 完成 http 和ajp 的初始化,创建CoyoteAdapter 对象,初始化 Http11Protocol,endpoint进行初始化
然后进入到 bootstrap.start();方法中
实际上执行的是Catalina.start();方法
- 然后执行StandardServer 的startInternal()方法
- 循环通知到需要监听的事件中
- globalNamingResources启动
- 启动所有子容器
- 执行StandardEngine的startInternal方法
- 启动左右Container的start方法
- 获取Cluster 集群信息
- 获取 Realm 安全策略
- 寻找该Container下的子容器,并另外线程启动
- StandardPipeline[StandardEngine[Catalina].StandardHost[localhost]]启动
- ExecutorService的submit方法会执行然后将结果保存到Future result中
- 触发Host ContainerBase的call方法,进行子容器启动 然后获取获取Cluster 集群信息,获取 Realm 安全策略,看是否存在子容器,判断是那种部署类型,然后将项目(文件夹,war包或者Context设置下的文件夹进行部署),解析context.xml文件
- 完成init()方法,然后执行的startInternal()方法,实际上是执行的StandardContext的startInternal方法
- 跳转到 StandardContext的startInternal方法中
- 初始化tomcat的ApplicationContext对象并会生成门面对象
- 添加缺少的组件和资源,判断需要重新加载的资源,
- 进行项目的启动
- 解析项目中的web.xml文件
- 然后启动spi机制将所有jar包下的META-INF/services/javax.servlet.ServletContainerInitializer通过反射实例化进来
- 合并tomcatWebXml defaults到web.xml里面
- 添加META-INF/resources/下的静态资源到standardContext
- 启动完成