honzeland

记录点滴。。。

常用链接

统计

Famous Websites

Java

Linux

P2P

最新评论

Tomcat ClassLoader and load resources

zz: http://rosonsandy.blogdriver.com/rosonsandy/871539.html

1 - Tomcat的类载入器的结构
Tomcat Server在启动的时候将构造一个ClassLoader树,以保证模块的类库是私有的
Tomcat Server的ClassLoader结构如下:
        +-----------------------------+

        |         Bootstrap           |

        |             |               |

        |          System             |

        |             |               |

        |          Common             |

        |         /      \            |

        |     Catalina  Shared        |

        |               /    \        |

        |          WebApp1  WebApp2   |

        +-----------------------------+

其中:
- Bootstrap - 载入JVM自带的类和$JAVA_HOME/jre/lib/ext/*.jar
- System - 载入$CLASSPATH/*.class
- Common - 载入$CATALINA_HOME/common/...,它们对TOMCAT和所有的WEB APP都可见
- Catalina - 载入$CATALINA_HOME/server/...,它们仅对TOMCAT可见,对所有的WEB APP都不可见
- Shared - 载入$CATALINA_HOME/shared/...,它们仅对所有WEB APP可见,对TOMCAT不可见(也不必见)
- WebApp - 载入ContextBase?/WEB-INF/...,它们仅对该WEB APP可见

2 - ClassLoader的工作原理

每个运行中的线程都有一个成员contextClassLoader,用来在运行时动态地载入其它类
系统默认的contextClassLoader是systemClassLoader,所以一般而言java程序在执行时可以使用JVM自带的类、$JAVA_HOME/jre/lib/ext/中的类和$CLASSPATH/中的类
可以使用Thread.currentThread().setContextClassLoader(...);更改当前线程的contextClassLoader,来改变其载入类的行为

ClassLoader被组织成树形,一般的工作原理是:
1) 线程需要用到某个类,于是contextClassLoader被请求来载入该类
2) contextClassLoader请求它的父ClassLoader来完成该载入请求
3) 如果父ClassLoader无法载入类,则contextClassLoader试图自己来载入

注意:WebApp?ClassLoader的工作原理和上述有少许不同:
它先试图自己载入类(在ContextBase?/WEB-INF/...中载入类),如果无法载入,再请求父ClassLoader完成

由此可得:
- 对于WEB APP线程,它的contextClassLoader是WebApp?ClassLoader
- 对于Tomcat Server线程,它的contextClassLoader是CatalinaClassLoader

3 类的查找

ClassLoader类中loadClass方法为缺省实现,用下面的顺序查找类:
1、调用findLoadedClass方法来检查是否已经被加载。如果没有则继续下面的步骤。
2、如果当前类装载器有一个指定的委托父装载器,则用委托父装载器的loadClass方法加载类,也就是委托给父装载器加载相应的类。
3、如果这个类装载器的委托层级体系没有一个类装载器加载该类,则使用类装载器定位类的特定实现机制,调用findClass方法来查找类。

4 - 部分原代码分析
4.1 - org/apache/catalina/startup/Bootstrap.java
Bootstrap中定义了三个classloader:commonLoader,catalinaLoader,sharedLoader.三者关系如下:
//注意三个自己定置的ClassLoader的层次关系:
            // systemClassLoader (root)
            //   +--- commonLoader
            //          +--- catalinaLoader
            //          +--- sharedLoader

Tomcat Server线程的起点
构造ClassLoader树,通过Thread.currentThread().setContextClassLoader(catalinaLoader)设置当前的classloader为catalinaLoader。
载入若干类,然后转入org.apache.catalina.startup.Catalina类中

4.2 org.apache.catalina.loader.StandardClassLoader.java

通过看loadClass这个方法来看tomcat是如何加载类的,顺序如下:

(0) Check our previously loaded class cache查找已经装载的class
        clazz = findLoadedClass(name);

(1) If a system class, use system class loader通过系统classloader来装载class
        ClassLoader loader = system;
            clazz = loader.loadClass(name);

(2) Delegate to our parent if requested如果有代理则使用父类classloader
            ClassLoader loader = parent;
            if (loader == null)
                loader = system;
            clazz = loader.loadClass(name);

(3) Search local repositories 查找本地类池,比如$CATALINA_HOME/server
           clazz = findClass(name);

(4) Delegate to parent unconditionally 默认使用代理装载器

[查看代码]

4.3 - org/apache/catalina/startup/ClassLoaderFactory.java

根据设置创建并返回StandardClassLoader的实例

[查看代码]

4.4 - org/apache/catalina/loader/StandardClassLoader.java

类载入器

4.5 - org/apache/catalina/startup/SecurityClassLoad.java

该类仅包含一个静态方法,用来为catalinaLoader载入一些类

[查看代码]

Appendix - 参考

[1] http://jakarta.apache.org/tomcat/中的Tomcat 4.1.x文档Class Loader HOW-TO

在一个JVM中可能存在多个ClassLoader,每个ClassLoader拥有自己的NameSpace。一个ClassLoader只能拥有一个class对象类型的实例,但是不同的ClassLoader可能拥有相同的class对象实例,这时可能产生致命的问题。如ClassLoaderA,装载了类A的类型实例A1,而ClassLoaderB,也装载了类A的对象实例A2。逻辑上讲A1=A2,但是由于A1和A2来自于不同的ClassLoader,它们实际上是完全不同的,如果A中定义了一个静态变量c,则c在不同的ClassLoader中的值是不同的。

[2] 深入Java2平台安全

zz: http://mail-archives.apache.org/mod_mbox/tomcat-users/200212.mbox/raw/%3c20021204192034.P86616-100000@icarus.apache.org%3e
try {
    Properties props = new Properties();
    InputStream in = getClass().getResourceAsStream("/conf/db.properties");
    props.load(in);
    ......
    propertie1 = props.getProperty("propertie1");

The examples already given will find properties files for you just fine whether the file is in a directory structure or inside an archive.  How do you think Java loads classes?  It works out of archives, no? here are some various was to access a properties file ( or any resource, for that matter) in whether the app is deployed as a directory or as a .war file (even inside a .jar file in WEB-INF/lib)....

1. This will load a file in WEB-INF/classes/conf or any jar file in the classpath with a package of "conf"...
    getClass().getResourceAsStream("/conf/db.properties");
2. This will load a file relative to the current class.  For instance, if the class is "org.mypackage.MyClass", then the file would be loaded at "org.mypackage.conf.dbproperties".  Note that this is because we didn't prepend "/" to the path.  When that is done, the file is loaded from the root of the current classloader where this loads it relative to the current class...
    getClass().getResourceAsStream("conf/db.properties");
3. This will find db.properties anywhere in the current classloader as long as it exists in a "conf" package...
    getClass().getClassLoader().getResourceAsStream("conf/db.properties");
4. This will find the file in a "conf" directory inside the webapp (starting from the root).  This starts looking in the same directory as contains WEB-INF.  When I say "directory", I don't mean "filesystem".  This could be in a .war file as well as in an actual directory on the filesystem...
    getServletContext().getResourceAsStream("/conf/db.properties");
5. Of course you would probably not want just anyone seeing your db.properties file, so you'd probably want to put in inside WEB-INF of your webapp, so....
    getServletContext().getResourceAsStream("/WEB-INF/conf/db.properties");
6. If your db.properties exists in another classloader which your app has access to, you can reach it by using:
    Thread.currentThread().getContextClassLoader().getResourceAsStream("conf/db.properties");
that will act similar to getClass().getClassLoader(), but it can see across all available classloaders where the latter can only see within the classloader that loaded the current class.

posted on 2008-04-24 15:46 honzeland 阅读(846) 评论(0)  编辑  收藏 所属分类: Java


只有注册用户登录后才能发表评论。


网站导航: