首页 > 其他 > 详细

Tomcat源码阅读之Bootstrap启动流程与classLoader设计

时间:2014-02-28 12:26:06      阅读:643      评论:0      收藏:0      [点我收藏+]

嗯,以前大体上看过jetty6.0的源码,算是对java EE应用容器有了一定的了解,但是在实际的线上环境中应用的最多的应该还是tomcat,而且据说性能方面tomcat也略好一些。。。那么就趁着现在还比较闲就看看它的源码吧。。。

首先在实际开始之前先来说说tomcat对ClassLoader方面的处理。。。当然这部分是参考了网络上其他的人的博客。。。

一般情况下java应用程序的classLoader父子关系大概如下:

bubuko.com,布布扣

具体他们之间的关系以及作用我前面的博客都有说过。。。 那么作为应用容器,因为可能要同时容纳多个应用,为了实现各个应用之间的隔离,那么就需要由专门的classLoader来加载个个应用自己的代码,这部分jetty的处理方式是为每一个web应用程序都创建一个自己的classLoader,这个web应用程序需要用到的所有代码都有这个classLoader来处理。。。其实还蛮简单的。。。但是有一个缺点就是一些公有的代码每个web应用程序都会重复加载,会造成一些浪费吧。。。。例如defaultservlet啥的。。。但是优点就是简单。。。

在这方面,tomcat的设计就稍微复杂了一点点。。。父子关系如下图:

bubuko.com,布布扣

这里可以每个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

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!