`
54wangyong
  • 浏览: 13690 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

tomcat7源码学习笔记(启动篇)

阅读更多

一.将tomcat源码导入到eclipse

    在apach官网上下载tomcat源码后,学习源码通过导入到eclipse里,然后用其调试功能来学习是的方便。对于eclipse导入tomcat源码可以参考:http://www.cnblogs.com/huangfox/archive/2011/10/20/2218970.html 对于tomcat依赖的jar,可以通过后面的附件来下载。

 

 

二.运行tomcat

    首先,tomcat在启动时,会读取一些配置文件,也就是${CATALINA_HOME}/conf下面的所有配置文件,我们可以将conf文件夹及其里面的所有配置文件拷到eclipse中来,提供tomcat启动需要的配置信息。

    在org.apache.catalina.startup这个包下面的Bootstrap.java是启动tomcat的类,它里面有个main函数,是启动tomcat的入口,代码如下

 

/**
     * Main method and entry point when starting Tomcat via the provided
     * scripts.
     *
     * @param args Command line arguments to be processed
     */
    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 {
            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) {
            // Unwrap the Exception for clearer error reporting
            if (t instanceof InvocationTargetException &&
                    t.getCause() != null) {
                t = t.getCause();
            }
            handleThrowable(t);
            t.printStackTrace();
            System.exit(1);
        }

    }

        通过上面源码可以看出,tomcat先进行初始化,主要进行的功能有:设置catalina的路径(主要有catalina.home和catalina.base这两个路径),初始化catalina的加载类(主要有三个类别:common.loader,server.loader,shared.loader,这些加载类都是通过读取conf下面的catalina.properties配置文件来获取相应的加载类,而且这些类加载都是通过tomcat里设置的安全验证的),设置catalina的(这个类的路径为:org.apache.catalina.startup.Catalina。其中默认加载类为org.apache.catalina.loader.StandardClassLoader,加载时需要的配置文件路径为:conf/server.xml)。       tomcat在初始化完上面的功能后,就根据启动时的命令参数,来进行相应命令的动作,通过上面的源码可以看出,在启动Bootstrap.java这个类时,可以传入的命令参数有:startd(默认值)、stopd、configtest,具体相应命令的功能,就不再进行说明。

 

 

三,分析init初始化哪些内容  

    下面来具体看看tomcat在启动时,都初始化了哪些内容。首先看一下init()这个方法的源码:

/**
     * Initialize daemon.
     */
    public void init() 
        throws Exception
    {

        // Set Catalina path
        setCatalinaHome();
        log.info("catalina.home:" + System.getProperty(Globals.CATALINA_HOME_PROP));
        setCatalinaBase();
        log.info("catalina.base:" + System.getProperty(Globals.CATALINA_BASE_PROP));

        initClassLoaders();

        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);

        catalinaDaemon = startupInstance;

    }

   

    3.1 catalina的两个路径设置

    其中,setCatalinaHome()和setCatalinaBase();这两个方法就是设置catalina的两个路径的,具体的实现,首先参看setCatalinaHome()方法的源码:

/**
     * Set the <code>catalina.home</code> System property to the current
     * working directory if it has not been set.
     */
    private void setCatalinaHome() {

        if (System.getProperty(Globals.CATALINA_HOME_PROP) != null)
            return;
        File bootstrapJar =
            new File(System.getProperty("user.dir"), "bootstrap.jar");
        if (bootstrapJar.exists()) {
            try {
                System.setProperty
                    (Globals.CATALINA_HOME_PROP,
                     (new File(System.getProperty("user.dir"), ".."))
                     .getCanonicalPath());
            } catch (Exception e) {
                // Ignore
                System.setProperty(Globals.CATALINA_HOME_PROP,
                                   System.getProperty("user.dir"));
            }
        } else {
            System.setProperty(Globals.CATALINA_HOME_PROP,
                               System.getProperty("user.dir"));
        }

    }

    通过上面的代码可以看出,它设置的catalina.home的值,就是获取系统user.dir属性的值,也就是你当前eclipse导入tomcat源码建的项目的路径,如果你项目路径如下所示:

       它的location在E:\study\tomcat7下面,那么System.getProperty("user.dir")的值也是E:\study\tomcat7.因为我们导入的源码中不会有bootstrap.jar,所以它在E:\study\tomcat7\bootstrap.jar是不存在的,最后setCatalinaHome()这个方法执行的代码是:

else {
            System.setProperty(Globals.CATALINA_HOME_PROP,
                               System.getProperty("user.dir"));
        }

       也就是将catalina.home的值设置成为:E:\study\tomcat7。对于setCatalinaBase()的源码类似,就不再贴出,分析。

 

       3.2  初始化作为catalina的加载类

      对于这个功能,我们可以参看initClassLoaders()这个方法的源码:

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(String name, ClassLoader parent)这个方法来创建三个类型(commonLoader、catalinaLoader、sharedLoader)的加载类的。那我们就通过源码来看下这个方法的功能:

private ClassLoader createClassLoader(String name, ClassLoader parent)
        throws Exception {

        String value = CatalinaProperties.getProperty(name + ".loader");
        if ((value == null) || (value.equals("")))
            return parent;

        value = replace(value);

        List<Repository> repositories = new ArrayList<Repository>();

        StringTokenizer tokenizer = new StringTokenizer(value, ",");
        while (tokenizer.hasMoreElements()) {
            String repository = tokenizer.nextToken().trim();
            if (repository.length() == 0) {
                continue;
            }

            // Check for a JAR URL repository
            try {
                @SuppressWarnings("unused")
                URL url = new URL(repository);
                repositories.add(
                        new Repository(repository, RepositoryType.URL));
                continue;
            } catch (MalformedURLException e) {
                // Ignore
            }

            // Local repository
            if (repository.endsWith("*.jar")) {
                repository = repository.substring
                    (0, repository.length() - "*.jar".length());
                repositories.add(
                        new Repository(repository, RepositoryType.GLOB));
            } else if (repository.endsWith(".jar")) {
                repositories.add(
                        new Repository(repository, RepositoryType.JAR));
            } else {
                repositories.add(
                        new Repository(repository, RepositoryType.DIR));
            }
        }

        ClassLoader classLoader = ClassLoaderFactory.createClassLoader
            (repositories, parent);

        // Retrieving MBean server
        MBeanServer mBeanServer = null;
        if (MBeanServerFactory.findMBeanServer(null).size() > 0) {
            mBeanServer = MBeanServerFactory.findMBeanServer(null).get(0);
        } else {
            mBeanServer = ManagementFactory.getPlatformMBeanServer();
        }

        // Register the server classloader
        ObjectName objectName =
            new ObjectName("Catalina:type=ServerClassLoader,name=" + name);
        mBeanServer.registerMBean(classLoader, objectName);

        return classLoader;

    }

        通过源码可以看出,这个方法主要是两个功能:首先,通过CatalinaProperties这个类,来获取相应要加载进来的jar包,然后通过调用replace(),来定位到相应的jar包,也就是把jar包的路径解析出来。然后,通过jmx将这些jar包的组件注册到tomcat中来;

        对于这个方法中用到的CatalinaProperties,这个类主要是读取catalina配置的,这个类首先会执行静态块中的loadProperties()方法

/**
     * Load properties.
     */
    private static void loadProperties() {

        InputStream is = null;
        Throwable error = null;

        try {
            String configUrl = getConfigUrl();
            if (configUrl != null) {
                is = (new URL(configUrl)).openStream();
            }
        } catch (Throwable t) {
            handleThrowable(t);
        }

        if (is == null) {
            try {
                File home = new File(getCatalinaBase());
                File conf = new File(home, "conf");
                File propsFile = new File(conf, "catalina.properties");
                is = new FileInputStream(propsFile);
            } catch (Throwable t) {
                handleThrowable(t);
            }
        }

        if (is == null) {
            try {
                is = CatalinaProperties.class.getResourceAsStream
                    ("/org/apache/catalina/startup/catalina.properties");
            } catch (Throwable t) {
                handleThrowable(t);
            }
        }

        if (is != null) {
            try {
                properties = new Properties();
                properties.load(is);
                is.close();
            } catch (Throwable t) {
                handleThrowable(t);
                error = t;
            }
        }

        if ((is == null) || (error != null)) {
            // Do something
            log.warn("Failed to load catalina.properties", error);
            // That's fine - we have reasonable defaults.
            properties=new Properties();
        }

        // Register the properties as system properties
        Enumeration<?> enumeration = properties.propertyNames();
        while (enumeration.hasMoreElements()) {
            String name = (String) enumeration.nextElement();
            String value = properties.getProperty(name);
            if (value != null) {
                System.setProperty(name, value);
            }
        }

    }

       因为,这时还没有设置catalina.config系统属性,所以String configUrl = getConfigUrl();获取不到值,代码会接着执行:

File home = new File(getCatalinaBase());
 File conf = new File(home, "conf");
 File propsFile = new File(conf, "catalina.properties");
 is = new FileInputStream(propsFile);

     通过这可以看出是读取conf/catalina.properties这个属性配置文件,然后通过下面代码加载到属性文件中

 if (is != null) {
            try {
                properties = new Properties();
                properties.load(is);
                is.close();
            } catch (Throwable t) {
                handleThrowable(t);
                error = t;
            }
        }

      所以,通过以上的代码可以看出CatalinaProperties这个类最终读取的是conf/catalina.properties配置文件,这样在Bootstrap.java这个类中的createClassLoader(String name, ClassLoader parent)方法的第一句

String value = CatalinaProperties.getProperty(name + ".loader");

      读取的值就是conf/catalina.properties里面配置的值。这个方法后面的代码就是对于加载进行来conf/catalina.properties配置中的server.loader、common.loader、shared.loader三个值中的组件的注册工作。到些初始化catalina类的加载类分析工作已经完成,那接着看初始catalina。

 

     3.3 初始化catalina

      初始化catalina类的代码主要是Bootstrap.java类中init()方法后面的代码:

 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);

        catalinaDaemon = startupInstance;

        首先通过前面已经初始化好的catalina的加载类,也就是org.apache.catalina.loader.StandardClassLoader来加载org.apache.catalina.startup.Catalina这个类,然后设置org.apache.catalina.startup.Catalina类的parentClassLoader为org.apache.catalina.loader.StandardClassLoader(这其中用到反射机制)。

 

       通过以上的简单讲解,让大家大致了解到tomcat在启动时做的部分工作。

   

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics