Resin中类加载器结构

六只 发布于 2011/12/27 20:51
阅读 1K+
收藏 0

开源之夏第三届火热来袭,高校学生参与赢万元奖金!>>>

Resin中类加载器

在我了解的ClassLoader是在com.caucho.loader包下,结构请看下图:


图1


图2

从上面两幅图中可以看出,图1是与Jdk有关联的,继承自java.net.URLClassLoaderDynamicClassLoader的注释是这样的:

/**
 * Class loader which checks for changes in class files and automatically
 * picks up new jars.
 *
 * <p>DynamicClassLoaders can be chained creating one virtual class loader.
 * From the perspective of the JDK, it's all one classloader.  Internally,
 * the class loader chain searches like a classpath.
 */

EnvironmentClassLoader又继承了DynamicClassLoader

2应该是Resin本身的ClassLoader,其中Loader是一个抽象类,包含了各种子类类加载器。

从两幅图中是看不出Resin自身的Loader体系与继承自JVM的类加载器存在关系,那是不是他们就不存在某种关联呢?其实不是这样子的。请看下面DynamicClassLoader源码的片段:

// List of resource loaders
  private ArrayList<Loader> _loaders = new ArrayList<Loader>();

  private JarLoader _jarLoader;
  private PathLoader _pathLoader;

清楚了吧,这两个Loader分支通过组合的方式协作。

类加载器顺序

既然Resin标准的Loader及其子类以组合的方式嵌入到DynamicClassLoader中,那么在加载一个“资源”时,Loader分支和java.net.URLClassLoader分支的先后顺序是什么样子的呢?

首先使用下面这段代码,将类加载器名称打印到控制台:

ClassLoader loader = PathServlet.class.getClassLoader();
while (loader != null) {
	System.out.println(loader.toString());
	loader = loader.getParent();
}

输出的结果为:

EnvironmentClassLoader$30708295[web-app:http://localhost:8787/EhCacheTestAnnotation]

EnvironmentClassLoader$1067475[host:http://localhost:8787]

EnvironmentClassLoader$9090824[servlet-server:]

sun.misc.Launcher$AppClassLoader@15601ea

sun.misc.Launcher$ExtClassLoader@197d257

额,没有任何一个ResinLoader被打印出来啊,对头,有就错了。下面就让我们看看DynamicClassLoadergetResource的源码来解答。

/**
   * Gets the named resource
   *
   * @param name name of the resource
   */
  public URL getResource(String name)
  {
    if (_resourceCache == null) {
      long expireInterval = getDependencyCheckInterval();

      _resourceCache = new TimedCache<String,URL>(256, expireInterval);
    }

    URL url = _resourceCache.get(name);

    if (url == NULL_URL)
      return null;
    else if (url != null)
      return url;

    boolean isNormalJdkOrder = isNormalJdkOrder(name);

    if (isNormalJdkOrder) {
      url = getParentResource(name);

      if (url != null)
        return url;
    }

    ArrayList<Loader> loaders = _loaders;

    for (int i = 0; loaders != null && i < loaders.size(); i++) {
      Loader loader = loaders.get(i);

      url = loader.getResource(name);

      if (url != null) {
        _resourceCache.put(name, url);

        return url;
      }
    }

    if (! isNormalJdkOrder) {
      url = getParentResource(name);

      if (url != null)
        return url;
    }

    _resourceCache.put(name, NULL_URL);

    return null;
  }

代码不难懂,我画了一张流程图,不规范,凑合看下。


总结:

boolean isNormalJdkOrder = isNormalJdkOrder(name);

这行代码控制着Resin类加载的顺序,如果是常规的类加载顺序(向上代理,原文:Returns true if the class loader should use the normal order, i.e. looking at the parents first.),则先url = getParentResource(name),后遍历_loaders。否则是按照先遍历_loadersurl = getParentResource(name)向上代理。

在我的调试经历中,一直都是先向上代理,后遍历_loaders的顺序,未遇到第二种方式。

文字对先向上代理,后遍历的顺序做点儿说明:

1、首先使用“最上层”的sun.misc.Launcher$ExtClassLoader@197d257加载name资源,如果找到就返回URL否则返回null

2、程序返回到sun.misc.Launcher$AppClassLoader@15601ea,首先判断父类加载器返回的url是否为null,如果不为null则返回url,返回null

3、程序返回到EnvironmentClassLoader$9090824[servlet-server:]getParentResource,再返回到getResource,如果url不为null,则直接返回,否则遍历ArrayList<Loader> loaders = _loaders;从各个loader中加载name,如果加载成功,即不为null,则返回,否则继续遍历,直至遍历完成。

4、EnvironmentClassLoader$1067475[host:http://localhost:8787]3

5、EnvironmentClassLoader$30708295[web-app:http://localhost:8787/EhCacheTestAnnotation]3

OK,完事儿,后续还有,准备好好写几篇。

加载中
0
sunyh
sunyh
天涯社区  就用这个。。
OSCHINA
登录后可查看更多优质内容
返回顶部
顶部
返回顶部
顶部