深入Tomcat(一)

java不难,难的是那些封装好的框架和工具,由于我们对这些工具没有深入的了解,所以在开发时经常是东查西凑,也就不难想到成果物的质量。所以决定从现在开始阅读Tomcat代码。每天一点点,坚持就是胜利。    

版本:6.0.18 

启动类:org.apache.catalina.startup包下的Bootstrap,下面是main方法:

    public static void main(String args[]) {

        if (daemon == null) {
            daemon = new Bootstrap();
            try {
                daemon.init();
            } catch (Throwable t) {
                t.printStackTrace();
                return;
            }
        }

        try {
            String command = "started";
            if (args.length > 0) {
                command = args[args.length - 1];
            }

            if (command.equals("startd")) {
                args[0] = "start";
                daemon.load(args);
                daemon.start();
            } else if (command.equals("stopd")) {
                args[0] = "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 {
                log.warn("Bootstrap: command \"" + command + "\" does not exist.");
            }
        } catch (Throwable t) {
            t.printStackTrace();
        }

    }

    首先创建启动类的实例,调用init方法:

    public void init()
        throws Exception
    {
        // Set Catalina path
        setCatalinaHome();
        setCatalinaBase();

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

   setCatalinaHome方法是设置Tomcat安装目录的,setCatalinaBase 是设置Tomcat的工作目录的。

   Tomcat是根据工作目录来寻找运行实例所需的资源的。

   如果你只安装了一个Tomcat软件,但是想运行多个Tomcat实例,或者想把Tomcat的工作目录设置在其它位置,那么就需要设置CATALINA_BASE变量了。以下是设置多个Tomcat实例运行的方法。我使用的是Tomcat安装版本。以启动两个Tomcat实例为例。

这是Tomcat的安装路径:


每一个Tomcat实例都有自己的工作目录,它们共享一个安装目录。

bin,lib这两个文件夹是共享的,存放安装文件和lib库。

conf,logs,temp,work,webapps这几个目录是每个Tomcat实例私有的,我们只需要拷贝这几个目录就能实现运行多个实例的目的。现在我对文件夹进行了改造:

 

新建两个工作目录Tomcat 6.0_1和Tomcat 6.0_2,将conf,logs,temp,work,webapps这五个文件夹分别拷贝到这两个工作目录下,为了更好的区分安装目录与工作目录,我将原始的安装目录下除了bin和lib文件夹以外全部删除。这样每个实例的工作目录就配好了。接下来需要配置Tomcat实例运行需要的资源---端口号,我修改了每个实例下面的server.xml。将shutdown端口号,还有http监听端口号,ajp端口号全部修改,我这里把监听http端口号设置成8080,另一个设置成8888。最后就是启动了。我用的是安装版本,不是启动bat文件的。

双击安装目录下的tomcat6w.exe,点击java选项卡,修改-Dcatalina.base=C:\Program Files\Apache Software Foundation\Tomcat 6.0_1,点击应用,确定,双击tomcat6.exe,启动第一个实例。


 

再双击安装目录下的tomcat6w.exe,点击java选项卡,修改-Dcatalina.base=C:\Program Files\Apache Software Foundation\Tomcat 6.0_2,点击应用,确定,双击tomcat6.exe,启动第二个实例。


如果是用startup.bat文件启动,同样修改CATALINA_BASE指向的工作目录即可。

 

说完了安装目录和工作目录的作用和区别。我们来看看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("catalina.home") != null)
            return;
        File bootstrapJar = 
            new File(System.getProperty("user.dir"), "bootstrap.jar");
        if (bootstrapJar.exists()) {
            try {
                System.setProperty
                    ("catalina.home", 
                     (new File(System.getProperty("user.dir"), ".."))
                     .getCanonicalPath());
            } catch (Exception e) {
                // Ignore
                System.setProperty("catalina.home",
                                   System.getProperty("user.dir"));
            }
        } else {
            System.setProperty("catalina.home",
                               System.getProperty("user.dir"));
        }
    }

 如果系统变量中已经设置了catalina.home,直接返回。

 如果没有设定,则使用user.dir系统变量来获取用户的工作目录,这里说的用户工作目录不同于上面所说的Tomcat的工作目录。 关于用户的工作目录,请参考我的博客:用户工作目录

如果用户的工作目录里含有bootstrap.jar,那么认为该目录的上一层目录是Tomcat的安装目录。事实上,Tomcat安装后,在bin下会存在该jar文件,并且通过查看catalina.bat文件可以得知,Tomcat的用户工作目录是%Tomcat_Home% /bin。这就不难解释为什么在Web工程下,获取user.dir系统属性是Tomcat安装目录下的bin目录了。但是这个属性的值是可以变化的(Tomcat的版本不同,该目录也不同,一般是%Tomcat_Home% /bin或者%Tomcat_Home%这两个目录 ),只要更改了运行java命令(启动Tomcat的org.apache.catalina.startup.Bootstrap)的目录,这个值就会相应的改变(通过修改tomcat6w.exe或者bat文件)。所以我们在编写程序时尽量不要使用此属性,尤其在cmd命令行和Eclipse共用时。

这样catalina.home就被设置成Tomcat的安装目录了。

下面是设置Tomcat工作目录的方法

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

        if (System.getProperty("catalina.base") != null)
            return;
        if (System.getProperty("catalina.home") != null)
            System.setProperty("catalina.base",
                               System.getProperty("catalina.home"));
        else
            System.setProperty("catalina.base",
                               System.getProperty("user.dir"));
    }

 这个方法首先检查Tomcat的工作目录(catalina.base)是否已经设置了(参考上文安装目录和工作目录的区别)。如果已经设置,返回,否则如果Tomcat的安装目录(catalina.home)不为空,则设置工作目录为Tomcat的安装目录。如果安装目录没有设置,则认为user.dir为Tomcat的工作目录。

 OK,接下来初始化Tomcat的类装载器。

下面是初始化类装载器的相关方法。

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) {
            log.error("Class loader creation threw exception", t);
            System.exit(1);
        }
    }

 从这个方法可以看出,Tomcat使用了三个类装载器,分别是commonLoader, catalinaLoader,sharedLoader。

 createClassLoader()方法

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

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

        ArrayList repositoryLocations = new ArrayList();
        ArrayList repositoryTypes = new ArrayList();
        int i;
 
        StringTokenizer tokenizer = new StringTokenizer(value, ",");
        while (tokenizer.hasMoreElements()) {
            String repository = tokenizer.nextToken();

            // Local repository
            boolean replace = false;
            String before = repository;
            while ((i=repository.indexOf(CATALINA_HOME_TOKEN))>=0) {
                replace=true;
                if (i>0) {
                repository = repository.substring(0,i) + getCatalinaHome() 
                    + repository.substring(i+CATALINA_HOME_TOKEN.length());
                } else {
                    repository = getCatalinaHome() 
                        + repository.substring(CATALINA_HOME_TOKEN.length());
                }
            }
            while ((i=repository.indexOf(CATALINA_BASE_TOKEN))>=0) {
                replace=true;
                if (i>0) {
                repository = repository.substring(0,i) + getCatalinaBase() 
                    + repository.substring(i+CATALINA_BASE_TOKEN.length());
                } else {
                    repository = getCatalinaBase() 
                        + repository.substring(CATALINA_BASE_TOKEN.length());
                }
            }
            if (replace && log.isDebugEnabled())
                log.debug("Expanded " + before + " to " + replace);

            // Check for a JAR URL repository
            try {//URL的格式:协议://主机地址:端口号/路径
                URL url=new URL(repository); //异常抛出
                repositoryLocations.add(repository);
                repositoryTypes.add(ClassLoaderFactory.IS_URL);
                continue;
            } catch (MalformedURLException e) {
                            
            }
            if (repository.endsWith("*.jar")) {
                repository = repository.substring
                    (0, repository.length() - "*.jar".length());
                repositoryLocations.add(repository);
                repositoryTypes.add(ClassLoaderFactory.IS_GLOB);
            } else if (repository.endsWith(".jar")) {
                repositoryLocations.add(repository);
                repositoryTypes.add(ClassLoaderFactory.IS_JAR);
            } else {
                repositoryLocations.add(repository);
                repositoryTypes.add(ClassLoaderFactory.IS_DIR);
            }
        }

        String[] locations = (String[]) repositoryLocations.toArray(new String[0]);
        Integer[] types = (Integer[]) repositoryTypes.toArray(new Integer[0]);
 
        ClassLoader classLoader = ClassLoaderFactory.createClassLoader
            (locations, types, parent);

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

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

        return classLoader;

    }

   

下面是CatalinaProperties类,负责读取catalina.properties 文件。

 

public class CatalinaProperties {
    // ------------------------------------------------------- Static Variables
    private static org.apache.juli.logging.Log log=
        org.apache.juli.logging.LogFactory.getLog( CatalinaProperties.class );

    private static Properties properties = null;

    static {

        loadProperties();

    }

    /**
     * Return specified property value.
     */
    public static String getProperty(String name) {
	
        return properties.getProperty(name);

    }

    /**
     * Return specified property value.
     */
    public static String getProperty(String name, String defaultValue) {

        return properties.getProperty(name, defaultValue);

    }

    /**
     * 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) {
            // Ignore
        }

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

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

        if (is != null) {
            try {
                properties = new Properties();
                properties.load(is);
                is.close();
            } catch (Throwable 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);
            }
        }
    }

    /**
     * Get the value of the catalina.home environment variable.
     */
    private static String getCatalinaHome() {
        return System.getProperty("catalina.home",
                                  System.getProperty("user.dir"));
    }
    
    /**
     * Get the value of the catalina.base environment variable.
     */
    private static String getCatalinaBase() {
        return System.getProperty("catalina.base", getCatalinaHome());
    }

    /**
     * Get the value of the configuration URL.
     */
    private static String getConfigUrl() {
        return System.getProperty("catalina.config");
    }
}

 

 首先从系统属性catalina.config中寻找,如果没有此文件,则从Tomcat的工作目录下的conf中寻找该文件(存在该文件)。如果仍然没有此文件则从/org/apache/catalina/startup包中寻找catalina.properties文件。说一下getResourceAsStream()的用法,由于“/org/apache/catalina/startup”字符串使用“/”开始的,“/”代表根目录,根目录是以类文件(class文件)package的顶层目录为基准,寻找资源文件的。比如在Web应用中,有一个WEB-INF的目录,WEB-INF目录里面除了web.xml文件外,还有一个classes目录,没错了,它就是你这个WEB应用的package的顶层目录。如果不是以“/”开始,则是相对于该类文件所在目录的相对路径寻找的。

找到catalina.properties文件后, 创建Properties类的实例,装载该文件流。之后将从属性文件中读取到的属性设置在系统属性中。

回退到createClassLoader方法中,传入的参数为“common”和null,查阅Tomcat安装目录下conf文件夹中的catalina.properties文件,可知common.loader=${catalina.home}/lib,${catalina.home}/lib/*.jar,得到的locations{"D://xxxx//bin//lib", "D://xxxx//bin//lib//"},得到的types{ClassLoaderFactory.IS_DIR, ClassLoaderFactory.IS_GLOB}。

ClassLoaderFactory类

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值