嗯,以前大体上看过jetty6.0的源码,算是对java EE应用容器有了一定的了解,但是在实际的线上环境中应用的最多的应该还是tomcat,而且据说性能方面tomcat也略好一些。。。那么就趁着现在还比较闲就看看它的源码吧。。。
首先在实际开始之前先来说说tomcat对ClassLoader方面的处理。。。当然这部分是参考了网络上其他的人的博客。。。
一般情况下java应用程序的classLoader父子关系大概如下:
具体他们之间的关系以及作用我前面的博客都有说过。。。 那么作为应用容器,因为可能要同时容纳多个应用,为了实现各个应用之间的隔离,那么就需要由专门的classLoader来加载个个应用自己的代码,这部分jetty的处理方式是为每一个web应用程序都创建一个自己的classLoader,这个web应用程序需要用到的所有代码都有这个classLoader来处理。。。其实还蛮简单的。。。但是有一个缺点就是一些公有的代码每个web应用程序都会重复加载,会造成一些浪费吧。。。。例如defaultservlet啥的。。。但是优点就是简单。。。
在这方面,tomcat的设计就稍微复杂了一点点。。。父子关系如下图:
这里可以每个web应用程序也有自己专属的classLoader,也就是最下层的AppClassLoader。。。
这里可以看到总的来说classLoader分成了两条线,左边那条线是tomcat服务器用的,右边的则是web应用程序用的。。。。。嗯,其实也不复杂。。。。
好了,上面的内容算是对tomcat的classLoader有了比较简单的了解。。那么接下来来看看整个tomcat服务器的启动类Bootstrap类型是怎么工作的吧。。。。
先来看看它的几个静态属性的定义:
private static Bootstrap daemon = null; //当前类型的一个引用 //这两个其实一般都被赋值为tomcat的根路径 private static final File catalinaBaseFile; private static final File catalinaHomeFile; private static final Pattern PATH_PATTERN = Pattern.compile("(\".*?\")|(([^,])*)"); //正则表达式验证
前面其实就是一个本身类型对象的一个引用,后面两个file对象其实一般情况下就是tomcat服务器的根路径,如果在启动的时候没有特别指定的话。。。。
接下来来看看一段静态代码块:
static { //这里主要是进行一些路径的初始化 // Will always be non-null String userDir = System.getProperty("user.dir"); //当前tomcat的用户路径,说白了就是应用程序起点路径 // Home first String home = System.getProperty(Globals.CATALINA_HOME_PROP); File homeFile = null; if (home != null) { File f = new File(home); try { homeFile = f.getCanonicalFile(); } catch (IOException ioe) { homeFile = f.getAbsoluteFile(); } } if (homeFile == null) { // First fall-back. See if current directory is a bin directory // in a normal Tomcat install File bootstrapJar = new File(userDir, "bootstrap.jar"); //启动jar包,在eclipse里面用源码运行的时候没有它 if (bootstrapJar.exists()) { File f = new File(userDir, ".."); try { homeFile = f.getCanonicalFile(); } catch (IOException ioe) { homeFile = f.getAbsoluteFile(); } } } if (homeFile == null) { // Second fall-back. Use current directory File f = new File(userDir); //获取当前程序根路径的文件夹 try { homeFile = f.getCanonicalFile(); //将路径保存到homeFile } catch (IOException ioe) { homeFile = f.getAbsoluteFile(); } } catalinaHomeFile = homeFile; System.setProperty( Globals.CATALINA_HOME_PROP, catalinaHomeFile.getPath()); // Then base String base = System.getProperty(Globals.CATALINA_BASE_PROP); if (base == null) { catalinaBaseFile = catalinaHomeFile; //这里其实一般也都是应用程序的根路径 } else { File baseFile = new File(base); try { baseFile = baseFile.getCanonicalFile(); } catch (IOException ioe) { baseFile = baseFile.getAbsoluteFile(); } catalinaBaseFile = baseFile; } System.setProperty( Globals.CATALINA_BASE_PROP, catalinaBaseFile.getPath()); }
其实这里的处理基本上就是设置一些路径方面的类容,也就是在类型加载的时候这部分就设置好了。。。
好啦,。接下来来看看几个属性的定义:
private Object catalinaDaemon = null; //当前tomcat的后台,org.apache.catalina.startup.Catalinad对象,它才是用于负责具体的server的启动 //3个层级的classLoader ,commonloader是下面两个loader的父loader,嗯,他们3个甚至可能引用的都是同一个classLoader protected ClassLoader commonLoader = null; //tomcat与app都能见 protected ClassLoader catalinaLoader = null; //只有tomcat能见 protected ClassLoader sharedLoader = null; //只有app们可以看见,tomcat看不到
嗯,他们具体是干嘛的应该很清楚了吧。。。注释应该就说的比较清楚了。。。
嗯,接下来来看整个tomcat的入口吧,main函数:
public static void main(String args[]) { if (daemon == null) { // Don‘t set daemon until init() has completed Bootstrap bootstrap = new Bootstrap(); //这里创建当前Bootstarp类型的对象 try { bootstrap.init(); //初始化,其实这里主要是创建org.apache.catalina.startup.Catalina对象并调用setParentClassLoader设置classLoader,用的是shareLoader } 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) { //这里有可能是其他的参数,但是默认命令是start 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(); //启动当前bootstrap对象,其实主要是调用前面生成的org.apache.catalina.startup.Catalina的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) { // 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类型的对象,如果只是启动的话,这里首先载入启动时候的参数,然后在执行start方法,那么接下来来看看start方法:
//这里就是真正的启动tomcat public void start() throws Exception { if( catalinaDaemon==null ) init(); //初始化catalinaDaemon,其实主要是初始化org.apache.catalina.startup.Catalina对象 Method method = catalinaDaemon.getClass().getMethod("start", (Class [] )null); //调用org.apache.catalina.startup.Catalina对象的start方法 method.invoke(catalinaDaemon, (Object [])null); }
这个好像没啥意思。。。这里说白了就是先创建和初始化org.apache.catalina.startup.Catalina对象,接着在调用它的start方法。。。那么来看看这个init方法都做了啥吧:
//初始化当前的tomcat后台,主要是创建org.apache.catalina.startup.Catalina对象,并且设置它的classLoader为catalinaLoader public void init() throws Exception { initClassLoaders(); //先初始化classLoader Thread.currentThread().setContextClassLoader(catalinaLoader); //设置当前线程classLoader SecurityClassLoad.securityClassLoad(catalinaLoader); //安全classLoader? // 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"); //获取org.apache.catalina.startup.Catalina类型 Object startupInstance = startupClass.newInstance(); //创建org.apache.catalina.startup.Catalina对象 // 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; //传进这个classLoader Method method = startupInstance.getClass().getMethod(methodName, paramTypes); method.invoke(startupInstance, paramValues); //调用刚刚创建的org.apache.catalina.startup.Catalina对象的setParentClassLoader设置classLoader,shareloader catalinaDaemon = startupInstance; //将这个启动的实例保存起来 }
嗯,首先初始化了classLoader,也就是上面提到的那三个classLoader,然后这里可以看到将当前的线程classLoader设置成了catalinaLoader,也就是专属于tomcat使用的。。。接着创建了org.apache.catalina.startup.Catalina对象,然后调用了它的setParentClassLoader方法,这里可以看到设置的classLoader是sharedLoader,前面已经提到它是属于web程序才能访问的。。。那么这里为啥分别这样设置这两个classLoader基本上猜也能猜出来原因了吧。。。哈哈。。。。。。
好啦,剩下来的事情就是调用org.apache.catalina.startup.Catalina对象的start方法来启动服务器了。。。
下一篇文章再来分析。。。。
Tomcat源码阅读之Bootstrap启动流程与classLoader设计,布布扣,bubuko.com
Tomcat源码阅读之Bootstrap启动流程与classLoader设计
原文:http://blog.csdn.net/fjslovejhl/article/details/19995383