环境搭建好了,将项目启动起来,就可以打断点进行调试了。看看tomcat到底是怎么启动的。
之前我们配置环境的时候就已经说了,tomcat的启动是从org.apache.catalina.startup.Bootstrap
的main
方法开始的。我们就看下这个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 public static void main (String args[]) { 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(); } 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 ); } }
daemon
是在里面定义过的,类型也是Bootstrap类型的,值为null,流程继续往下走,执行初始化(init)方法。
我们看下初始化方法。
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 public void init () throws Exception { setCatalinaHome(); setCatalinaBase(); initClassLoaders(); Thread.currentThread().setContextClassLoader(catalinaLoader); SecurityClassLoad.securityClassLoad(catalinaLoader); if (log.isDebugEnabled()) log.debug("Loading startup class" ); Class<?> startupClass = catalinaLoader.loadClass ("org.apache.catalina.startup.Catalina" ); Object startupInstance = startupClass.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; }
其中这个方法主要干的事情我们通过源码可以了解到
设置了catalina.home
和catalina.base
属性
初始化类的加载器(initClassLoaders),分别创建了(Class loader creation)commonLoader,catalinaLoader,sharedLoader这几个ClassLoader。
然后实例化了一个org.apache.catalina.startup.Catalina
对象,通过反射方式调用了Catalina
对象的setParentClassLoader
方法。
好了这个init方法就完了,其实实际上里面还是很复杂的,有时间可以研究一下,为什么这样做,有么有更好的方法实现。
继续我们可以看到默认的command
值为start
,开始执行load
方法。看下load的源码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 private void load (String[] arguments) throws Exception { String methodName = "load" ; Object param[]; Class<?> paramTypes[]; if (arguments==null || arguments.length==0 ) { paramTypes = null ; param = null ; } else { paramTypes = new Class [1 ]; paramTypes[0 ] = arguments.getClass(); param = new Object [1 ]; param[0 ] = arguments; } Method method = catalinaDaemon.getClass().getMethod(methodName, paramTypes); if (log.isDebugEnabled()) log.debug("Calling startup class " + method); method.invoke(catalinaDaemon, param); }
这个里面的内容并不复杂,也是通过反射的形式调用Catalina
类的load
方法。
load执行完了之后,就执行start方法了。一样的内容也是通过反射调用的Catalina
类的start
方法。
到这里的时候,这个main方法也已经结束了。