Tomcat 源码分析-启动分析(2)
文章目录
Bootstrap 的main方法
启动脚本 startup.bat 开始到最后,所调用的就是Bootstrap.main()方法,接下来就来看一下这个方法。
源码以及简单说明
首先创建Bootstrap 类赋值到类本身的静态变量中,并初始化
Bootstrap.init()
;
public static void main(String args[]) { 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 {//这里一堆的判断看一个就可以了,start的。 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); daemon.load(args); //load 方法中主要加载了server.xml。用Digester这个玩意来解析xml。 daemon.start(); //直接掉该接口的实现类WebappClassLoaderBase 的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); } }
main方法初始化Bootstrap之后,将参数加载到类中
load()
,并执行对应的.start()
或者.stop()
等方法;接下来就,先重Bootstrap的初始化话方法,init()开始。
Bootstrap.init()初始化方法分析。
public void init() throws Exception { // Set Catalina path 设置系统变量 setCatalinaHome(); setCatalinaBase(); initClassLoaders(); // 初始化 类加载器 这个是重点 [接下来会分析一丢丢] //设置Tomcat的主线程类加载器为catalinaLoader //这里为了避免出现两个相同的类导致ClassNotFound异常, //使用上下文类加载器进行加载Tomcat自己的类 Thread.currentThread().setContextClassLoader(catalinaLoader); SecurityClassLoad.securityClassLoad(catalinaLoader); // Load our startup class and call its process() method // 负载启动类和调用过程()方法 if (log.isDebugEnabled()) log.debug("Loading startup class"); Class<?> startupClass = catalinaLoader.loadClass ("org.apache.catalina.startup.Catalina"); Object startupInstance = startupClass.newInstance(); // Set the shared extensions class loader 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] = sharedLoader; Method method = startupInstance.getClass().getMethod(methodName, paramTypes); method.invoke(startupInstance, paramValues); //调用 Catalina.setParentClassLoader()设置类加载器为shareLoader [同一个] catalinaDaemon = startupInstance; }
这里主要做的事情:
- 设置 catalina.home、catalina.base 系统属性
initClassLoaders();
:就是初始化commonLoader、catalinaLoader、sharedLoader类加载器
- 这个初始化做的东西还是很多的,装载 catalina.properties 里配置的目录下的文件和jar包
- 注册了 MBean,可以用于 JVM 监控该对象
- 使用反射实例化 org.apache.catalina.startup.Catalina 对象,并赋值给静态成员 catalinaDaemon,以 sharedLoader 作为入参通过反射调用该对象的 setParentClassLoader 方法。
初始化类加载器:initClassLoaders()
先贴上部分源码:
ClassLoader commonLoader = null; ClassLoader catalinaLoader = null; ClassLoader sharedLoader = null; private void initClassLoaders() { try { commonLoader = createClassLoader("common", null); if( commonLoader == null ) { // no config file, default to this loader - we might be in a 'single' env. commonLoader=this.getClass().getClassLoader(); } catalinaLoader = createClassLoader("server", commonLoader); sharedLoader = createClassLoader("shared", commonLoader); } catch (Throwable t) { handleThrowable(t); log.error("Class loader creation threw exception", t); System.exit(1); } }
这里初始化了三个类加载器, 而
createClassLoader
方法中则是根据传递进来的参数拼接.loader
读取conf
目录下的catalina.properties
文件中所对应的jar文件的目录而进行加载。【
createClassLoader
方法作为创建类加载的方法,给出部分源码,可以看看的】createClassLoader部分代码:
private ClassLoader createClassLoader(String name, ClassLoader parent) throws Exception { String value = CatalinaProperties.getProperty(name + ".loader"); //vaule = ${catalina.base}/lib,${catalina.base}/lib/*.jar,${catalina.home}/lib,${catalina.home}/lib/*.jar if ((value == null) || (value.equals(""))) return parent; // 如果读取server 和shared目录为空返回的还是传递进去的commonLoader 。。。。 return ClassLoaderFactory.createClassLoader(repositories, parent); //返回构建的对应目录的特权计算的类加载器[这个在里面的代码连类都不认识了o(╥﹏╥)o] }
加载完结果:
common.loader="${catalina.base}/lib","${catalina.base}/lib/*.jar","${catalina.home}/lib","${catalina.home}/lib/*.jar" server.loader=common.loader shared.loader=common.loader //这里的结果3个加载器都是一样的
备注:server.loader 与 shared.loader 在旧版本里面是有对应目录的(tomcat5.5),新版本根目录就包含了所有的jar包了,所以另外两个就不用管了。
最后执行的是***load***方法和***start***方法了
daemon.load,daemon.start
两个方法在最后被调用,这里daemon就是bootstrap,而他的load和start方法如下:/** * Start the Catalina daemon. */ public void start() throws Exception { if( catalinaDaemon==null ) init(); Method method = catalinaDaemon.getClass().getMethod("start", (Class [] )null); method.invoke(catalinaDaemon, (Object [])null); }
catalinaDaemon 在初始化init()方法的时候已经创建了,这里的start 方法做做的内容就是了Catalina类的load和start 方法。
Bootstrap 的main方法,到这里就结束了,由于最后调用了Catalina的load和start方法,这就是接下来要分析的了。
参考资料
http://www.iocoder.cn/Tomcat/yuliu/Start-analysis-2-Bootstrap/
小杭
邮箱:xiao_hang0_0@163.com