Tomcat作为平时工作中出镜率最高的web容器,今天我们就来对其源码一探究竟。通过github获取了tomcat的最新代码,获取地址如下
https://github.com/apache/tomcat
这里使用idea的git直接从github上拉去了最新源码,同时方便获取最新修改。
获得源码后,首先来探索一下Tomcat的启动过程,那就要来看看他的启动入口类org.apache.catalina.startup.BootStrap。 找到了入口类,从main方法开始入手,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 public static void main (String args[]) { synchronized (daemonLock) { if (daemon == null ) { Bootstrap bootstrap = new Bootstrap (); try { bootstrap.init(); } catch (Throwable t) { handleThrowable(t); t.printStackTrace(); return ; } daemon = bootstrap; } else { 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 ); 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) { if (t instanceof InvocationTargetException && t.getCause() != null ) { t = t.getCause(); } handleThrowable(t); t.printStackTrace(); System.exit(1 ); } }
上述是tomcat的main方法,如果我只看tomcat的start过程,代码可以简化为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 if (daemon == null ) { Bootstrap bootstrap = new Bootstrap (); bootstrap.init(); daemon = bootstrap; } String command = "start" ;if (command.equals("start" )) { daemon.setAwait(true ); daemon.load(args); daemon.start(); if (null == daemon.getServer()) { System.exit(1 ); } }
从上面代码就可以清晰的看出来tomcat 启动流程,首先创建bootstrap对象,并执行init方法初始化bootstrap,初始化完成后将新创建的bootstrap实例赋值给daemon对象,在启动时,daemon会先调用Bootstrap的load方法和start方法来完成Tomcat的启动工作。
从main方法中可以发现启动过程中主要的三个方法init,load和start。依次来看一下这三个方法的实现,来了解一下tomcat启动具体做了些什么。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 public void init () throws Exception { initClassLoaders(); Thread.currentThread().setContextClassLoader(catalinaLoader); SecurityClassLoad.securityClassLoad(catalinaLoader); Class<?> startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina" ); Object startupInstance = startupClass.getConstructor().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 ] = sharedLoader; Method method = startupInstance.getClass().getMethod(methodName, paramTypes); method.invoke(startupInstance, paramValues); catalinaDaemon = startupInstance; }
可以看到init方法是设置了类加载器,同时通过类加载器,生成了一个Catalina对象实例,同时设置了catalina对象的父类加载器。并将生成的catalina对象赋值给了bootstrap对象的catalinaDaemon属性。上述操作,总的来看init方法做了一件事情,就是给bootstarp的catalinaDanmon属性进行了初始化。 再来看看load方法做了什么事情,核心代码如下:
1 2 3 4 5 6 7 8 Method method = catalinaDaemon.getClass().getMethod(methodName, paramTypes); if (log.isDebugEnabled()) log.debug("Calling startup class " + method); method.invoke(catalinaDaemon, param);
可以看到load方法其实是通过反射的方法调动了catalina的load方法。进行基础信息的加载。catalina的load将在后续文章中详细介绍。
1 2 3 4 if ( catalinaDaemon==null ) init(); Method method = catalinaDaemon.getClass().getMethod("start" , (Class [] )null ); method.invoke(catalinaDaemon, (Object [])null );
start方法也很简单,依然是通过反射的方法调用了catalina的start 方法。
翻看了其他的Bootstarp类方法,大部分都是通过反射去调用了对应的Catalina类的对应方法来完成对应操作。
可以看到Bootstrap类如其名,作为tomcat的入口类,同时起到了一个引导的作用,通过阅读和分析,也引出了tomcat启动过程中一个重要的类Catalina。
对于源码中同步疑问,在万能的stackoverflow提问后,理解了为什么要对daemon的初始化过程进行同步操作传送门