<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>getResourceAsStream | 酷 壳 - CoolShell</title>
	<atom:link href="https://coolshell.cn/tag/getresourceasstream/feed" rel="self" type="application/rss+xml" />
	<link>https://coolshell.cn</link>
	<description>享受编程和技术所带来的快乐 - Coding Your Ambition</description>
	<lastBuildDate>Wed, 17 Oct 2012 09:06:39 +0000</lastBuildDate>
	<language>zh-CN</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.2</generator>
	<item>
		<title>Resin服务器getResource揭秘</title>
		<link>https://coolshell.cn/articles/6335.html</link>
					<comments>https://coolshell.cn/articles/6335.html#comments</comments>
		
		<dc:creator><![CDATA[liuxiaori]]></dc:creator>
		<pubDate>Thu, 05 Jan 2012 00:28:59 +0000</pubDate>
				<category><![CDATA[Java语言]]></category>
		<category><![CDATA[编程语言]]></category>
		<category><![CDATA[ClassLoader]]></category>
		<category><![CDATA[getResource]]></category>
		<category><![CDATA[getResourceAsStream]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[Resin]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=6335</guid>

					<description><![CDATA[<p>（感谢网友 liuxiaori 继续分享其经历）这样的详细的图文并茂的文章让我很佩服！ 前言 接上文“由一个问题到Resin ClassLoader的学习”，本...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/6335.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/6335.html">Resin服务器getResource揭秘</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script><strong>（<span style="color: #cc0000;">感谢网友 liuxiaori 继续分享其经历</span>）这样的详细的图文并茂的文章让我很佩服！</strong></p>
<h4>前言</h4>
<p>接上文“<a title="由一个问题到 Resin ClassLoader 的学习" href="https://coolshell.cn/articles/6112.html" target="_blank">由一个问题到Resin ClassLoader的学习</a>”，本文将以this.getClass().getResource(&#8220;/&#8221;).getPath()和this.getClass().getResourceAsStream(&#8220;/a.txt&#8221;)为例，一步步解析加载的过程。</p>
<h4>调试环境</h4>
<ol>
<li>下载resin3.0.23的源码(<a href="http://www.caucho.com/download/resin-3.0.23-src.zip">http://www.caucho.com/download/resin-3.0.23-src.zip</a>)。</li>
<li>部署到myeclipse中，有错误，本人忽略了。Resin可运行。</li>
<li>将EhCacheTestAnnotation部署到resin3.0.23中。</li>
<li>调试this.getClass().getResource(&#8220;/&#8221;).getPath()。</li>
</ol>
<p>问题来了，无论如何也模拟不出来&lt;compiling-loader&gt;所造成的影响，一直输出：/D:/work_other/project/resin-3.0.23/bin/ 。无奈之下，采用了这种方式：使用两个eclipse，一个使用发布版本的，部署EhCacheTestAnnotation进行调试；另外一个部署resin3.0.23源码，调试到哪里对照看源码。</p>
<h4>开始</h4>
<h5>1) this.getClass().getResource(&#8220;/&#8221;).getPath()</h5>
<p>本次调试涉及的所有类加载器为：</p>
<blockquote><p>EnvironmentClassLoader$24156236[web-app:http://localhost:8787/EhCacheTestAnnotation]</p>
<p>EnvironmentClassLoader$7806641[host:http://localhost:8787]</p>
<p>EnvironmentClassLoader$22459270[servlet-server:]</p>
<p>sun.misc.Launcher$AppClassLoader@7259da</p>
<p>sun.misc.Launcher$ExtClassLoader@16930e2</p></blockquote>
<p>首先进入Class的getResource(String name)方法，如下图：</p>
<p><span id="more-6335"></span></p>
<figure id="attachment_6390" aria-describedby="caption-attachment-6390" style="width: 553px" class="wp-caption aligncenter"><img decoding="async" loading="lazy" class="size-full wp-image-6390" title="图片1" src="https://coolshell.cn/wp-content/uploads/2012/01/图片1.png" alt="图片1" width="553" height="182" srcset="https://coolshell.cn/wp-content/uploads/2012/01/图片1.png 553w, https://coolshell.cn/wp-content/uploads/2012/01/图片1-300x99.png 300w" sizes="(max-width: 553px) 100vw, 553px" /><figcaption id="caption-attachment-6390" class="wp-caption-text">图1</figcaption></figure>
<p>最后委托给ClassLoader的getResource方法。那么这个ClassLoader是哪个呢？一看下图便知：</p>
<figure id="attachment_6391" aria-describedby="caption-attachment-6391" style="width: 553px" class="wp-caption aligncenter"><img decoding="async" loading="lazy" class="size-full wp-image-6391" title="图片2" src="https://coolshell.cn/wp-content/uploads/2012/01/图片2.png" alt="图片2" width="553" height="85" srcset="https://coolshell.cn/wp-content/uploads/2012/01/图片2.png 553w, https://coolshell.cn/wp-content/uploads/2012/01/图片2-300x46.png 300w" sizes="(max-width: 553px) 100vw, 553px" /><figcaption id="caption-attachment-6391" class="wp-caption-text">图2</figcaption></figure>
<p>是DynamicClassLoader的getResource方法，原理上文已述。</p>
<p>最终会委托给sun.misc.Launcher$ExtClassLoader@16930e2类加载器的getResource方法，返回null，然后开始回溯。</p>
<p>还记得吗？当java.net.URLClassLoader分支的ClassLoader的getResource方法返回值为null后，就要遍历嵌入DynamicClassLoader中的Resin的Loader(即_loaders集合)。</p>
<p>当然回溯到EnvironmentClassLoader$22459270[servlet-server:]中，那么它中_loaders这个集合中的Loader又有哪些呢？</p>
<p>以图为证，当天确实回溯到该ClassLoader，而且开始准备遍历_loaders集合。</p>
<figure id="attachment_6392" aria-describedby="caption-attachment-6392" style="width: 554px" class="wp-caption aligncenter"><img decoding="async" loading="lazy" class="size-full wp-image-6392" title="图3" src="https://coolshell.cn/wp-content/uploads/2012/01/图片3.png" alt="图3" width="554" height="74" srcset="https://coolshell.cn/wp-content/uploads/2012/01/图片3.png 554w, https://coolshell.cn/wp-content/uploads/2012/01/图片3-300x40.png 300w" sizes="(max-width: 554px) 100vw, 554px" /><figcaption id="caption-attachment-6392" class="wp-caption-text">图3</figcaption></figure>
<p>DynamicClassLoader的1306行，没问题，resin3.0.23源码截图为证：</p>
<figure id="attachment_6393" aria-describedby="caption-attachment-6393" style="width: 554px" class="wp-caption aligncenter"><img decoding="async" loading="lazy" class="size-full wp-image-6393" title="图4" src="https://coolshell.cn/wp-content/uploads/2012/01/图片4.png" alt="图4" width="554" height="236" srcset="https://coolshell.cn/wp-content/uploads/2012/01/图片4.png 554w, https://coolshell.cn/wp-content/uploads/2012/01/图片4-300x127.png 300w" sizes="(max-width: 554px) 100vw, 554px" /><figcaption id="caption-attachment-6393" class="wp-caption-text">图4</figcaption></figure>
<p>不做多余解释，那么“servlet-server”这个ClassLoader中的_loaders集合中都放了一些什么呢？</p>
<figure id="attachment_6394" aria-describedby="caption-attachment-6394" style="width: 554px" class="wp-caption aligncenter"><img decoding="async" loading="lazy" class="size-full wp-image-6394" title="图5" src="https://coolshell.cn/wp-content/uploads/2012/01/图片5.png" alt="图5" width="554" height="121" srcset="https://coolshell.cn/wp-content/uploads/2012/01/图片5.png 554w, https://coolshell.cn/wp-content/uploads/2012/01/图片5-300x65.png 300w" sizes="(max-width: 554px) 100vw, 554px" /><figcaption id="caption-attachment-6394" class="wp-caption-text">图5</figcaption></figure>
<p>存放了两个TreeLoader(Loader的子类)，然未找到结果，返回null。继续回溯。</p>
<p>这次轮到遍历EnvironmentClassLoader$7806641[host:http://localhost:8787]的_loaders。下图为证：</p>
<figure id="attachment_6395" aria-describedby="caption-attachment-6395" style="width: 554px" class="wp-caption aligncenter"><img decoding="async" loading="lazy" class="size-full wp-image-6395" title="图6" src="https://coolshell.cn/wp-content/uploads/2012/01/图片6.png" alt="图6" width="554" height="170" srcset="https://coolshell.cn/wp-content/uploads/2012/01/图片6.png 554w, https://coolshell.cn/wp-content/uploads/2012/01/图片6-300x92.png 300w" sizes="(max-width: 554px) 100vw, 554px" /><figcaption id="caption-attachment-6395" class="wp-caption-text">图6</figcaption></figure>
<p>_loaders中的内容如下图：</p>
<figure id="attachment_6396" aria-describedby="caption-attachment-6396" style="width: 554px" class="wp-caption aligncenter"><img decoding="async" loading="lazy" class="size-full wp-image-6396" title="图7" src="https://coolshell.cn/wp-content/uploads/2012/01/图片7.png" alt="图7" width="554" height="193" srcset="https://coolshell.cn/wp-content/uploads/2012/01/图片7.png 554w, https://coolshell.cn/wp-content/uploads/2012/01/图片7-300x104.png 300w" sizes="(max-width: 554px) 100vw, 554px" /><figcaption id="caption-attachment-6396" class="wp-caption-text">图7</figcaption></figure>
<p>比较长，我贴出来：</p>
<blockquote><p>[CompilingLoader[src:/D:/work/resin-3.0.23/webapps/WEB-INF/classes], LibraryLoader[com.caucho.config.types.FileSetType@fb6763], CompilingLoader[src:/D:/work/resin-3.0.23/webapps/WEB-INF/classes], LibraryLoader[com.caucho.config.types.FileSetType@140b8fd], CompilingLoader[src:/D:/work/resin-3.0.23/webapps/WEB-INF/classes], LibraryLoader[com.caucho.config.types.FileSetType@30fc1f]]</p></blockquote>
<p>注意到了吧，主角来了。那仔细调试下把。爆料一下：CompilingLoader[src:/D:/work/resin-3.0.23/webapps/WEB-INF/classes]就是主角。</p>
<figure id="attachment_6397" aria-describedby="caption-attachment-6397" style="width: 554px" class="wp-caption aligncenter"><img decoding="async" loading="lazy" class="size-full wp-image-6397" title="图8" src="https://coolshell.cn/wp-content/uploads/2012/01/图片8.png" alt="图8" width="554" height="251" srcset="https://coolshell.cn/wp-content/uploads/2012/01/图片8.png 554w, https://coolshell.cn/wp-content/uploads/2012/01/图片8-300x135.png 300w" sizes="(max-width: 554px) 100vw, 554px" /><figcaption id="caption-attachment-6397" class="wp-caption-text">图8</figcaption></figure>
<p>看到了吧，遍历时，当前的Loader为CompilingLoader[src:/D:/work/resin-3.0.23/webapps/WEB-INF/classes]，而且url可是不为null了哦。再贴一张，看看url的值到底是什么！</p>
<figure id="attachment_6400" aria-describedby="caption-attachment-6400" style="width: 554px" class="wp-caption aligncenter"><img decoding="async" loading="lazy" class="size-full wp-image-6400" title="图9" src="https://coolshell.cn/wp-content/uploads/2012/01/图片9.png" alt="图9" width="554" height="250" srcset="https://coolshell.cn/wp-content/uploads/2012/01/图片9.png 554w, https://coolshell.cn/wp-content/uploads/2012/01/图片9-300x135.png 300w" sizes="(max-width: 554px) 100vw, 554px" /><figcaption id="caption-attachment-6400" class="wp-caption-text">图9</figcaption></figure>
<p>嗯，不用多做解释了吧。</p>
<p>最后看看程序输出是否吻合，如下图：</p>
<figure id="attachment_6401" aria-describedby="caption-attachment-6401" style="width: 554px" class="wp-caption aligncenter"><img decoding="async" loading="lazy" class="size-full wp-image-6401" title="图10" src="https://coolshell.cn/wp-content/uploads/2012/01/图片10.png" alt="图10" width="554" height="81" srcset="https://coolshell.cn/wp-content/uploads/2012/01/图片10.png 554w, https://coolshell.cn/wp-content/uploads/2012/01/图片10-300x43.png 300w" sizes="(max-width: 554px) 100vw, 554px" /><figcaption id="caption-attachment-6401" class="wp-caption-text">图10</figcaption></figure>
<p>然后修改resin.conf中的&lt;compiling-loader&gt;将其注释掉，看看程序结果会不会是我们期望的：/D:/work/resin-3.0.23/webapps/EhCacheTestAnnotation/WEB-INF/classes/。拭目以待。</p>
<figure id="attachment_6402" aria-describedby="caption-attachment-6402" style="width: 554px" class="wp-caption aligncenter"><img decoding="async" loading="lazy" class="size-full wp-image-6402" title="图11" src="https://coolshell.cn/wp-content/uploads/2012/01/图片11.png" alt="图11" width="554" height="106" srcset="https://coolshell.cn/wp-content/uploads/2012/01/图片11.png 554w, https://coolshell.cn/wp-content/uploads/2012/01/图片11-300x57.png 300w" sizes="(max-width: 554px) 100vw, 554px" /><figcaption id="caption-attachment-6402" class="wp-caption-text">图11</figcaption></figure>
<p>为节省篇幅，一下只关注关键位置。</p>
<p>首先调试到EnvironmentClassLoader$7806641[host:http://localhost:8787]，我们需要停下来一下。</p>
<figure id="attachment_6403" aria-describedby="caption-attachment-6403" style="width: 554px" class="wp-caption aligncenter"><img decoding="async" loading="lazy" class="size-full wp-image-6403" title="图12" src="https://coolshell.cn/wp-content/uploads/2012/01/图片12.png" alt="图12" width="554" height="154" srcset="https://coolshell.cn/wp-content/uploads/2012/01/图片12.png 554w, https://coolshell.cn/wp-content/uploads/2012/01/图片12-300x83.png 300w" sizes="(max-width: 554px) 100vw, 554px" /><figcaption id="caption-attachment-6403" class="wp-caption-text">图12</figcaption></figure>
<p>再看一下_loaders的值。</p>
<figure id="attachment_6404" aria-describedby="caption-attachment-6404" style="width: 554px" class="wp-caption aligncenter"><img decoding="async" loading="lazy" class="size-full wp-image-6404" title="图13" src="https://coolshell.cn/wp-content/uploads/2012/01/图片13.png" alt="图13" width="554" height="157" srcset="https://coolshell.cn/wp-content/uploads/2012/01/图片13.png 554w, https://coolshell.cn/wp-content/uploads/2012/01/图片13-300x85.png 300w" sizes="(max-width: 554px) 100vw, 554px" /><figcaption id="caption-attachment-6404" class="wp-caption-text">图13</figcaption></figure>
<p>贴一个详细的：</p>
<blockquote><p>[LibraryLoader[com.caucho.config.types.FileSetType@1299f7e], LibraryLoader[com.caucho.config.types.FileSetType@1a631cc], LibraryLoader[com.caucho.config.types.FileSetType@f6398]]</p></blockquote>
<p>对比一下，在注释掉&lt;compiling-loader&gt;后，loaders中是没有CompilingClassLoader实例的。</p>
<p>继续，下面就轮到EnvironmentClassLoader$24156236[web-app:http://localhost:8787/EhCacheTestAnnotation]这个ClassLoader了，会是什么样子呢？</p>
<figure id="attachment_6405" aria-describedby="caption-attachment-6405" style="width: 554px" class="wp-caption aligncenter"><img decoding="async" loading="lazy" class="size-full wp-image-6405" title="图14" src="https://coolshell.cn/wp-content/uploads/2012/01/图片14.png" alt="图14" width="554" height="154" srcset="https://coolshell.cn/wp-content/uploads/2012/01/图片14.png 554w, https://coolshell.cn/wp-content/uploads/2012/01/图片14-300x83.png 300w" sizes="(max-width: 554px) 100vw, 554px" /><figcaption id="caption-attachment-6405" class="wp-caption-text">图14</figcaption></figure>
<p>进入该ClassLoader时，url值依旧为null，那_loaders会有变化吗？如下图：</p>
<figure id="attachment_6406" aria-describedby="caption-attachment-6406" style="width: 554px" class="wp-caption aligncenter"><img decoding="async" loading="lazy" class="size-full wp-image-6406" title="图15" src="https://coolshell.cn/wp-content/uploads/2012/01/图片15.png" alt="图15" width="554" height="149" srcset="https://coolshell.cn/wp-content/uploads/2012/01/图片15.png 554w, https://coolshell.cn/wp-content/uploads/2012/01/图片15-300x80.png 300w" sizes="(max-width: 554px) 100vw, 554px" /><figcaption id="caption-attachment-6406" class="wp-caption-text">图15</figcaption></figure>
<p>继续遍历_loaders。</p>
<figure id="attachment_6407" aria-describedby="caption-attachment-6407" style="width: 554px" class="wp-caption aligncenter"><img decoding="async" loading="lazy" class="size-full wp-image-6407" title="图16" src="https://coolshell.cn/wp-content/uploads/2012/01/图片16.png" alt="图16" width="554" height="211" srcset="https://coolshell.cn/wp-content/uploads/2012/01/图片16.png 554w, https://coolshell.cn/wp-content/uploads/2012/01/图片16-300x114.png 300w" sizes="(max-width: 554px) 100vw, 554px" /><figcaption id="caption-attachment-6407" class="wp-caption-text">图16</figcaption></figure>
<p>到这里就结束了，url在EnvironmentClassLoader$24156236[web-app:http://localhost:8787/EhCacheTestAnnotation]中被加载。</p>
<h5>1) this.getClass().getResourceAsStream(&#8220;/a.txt&#8221;)</h5>
<p>getResourceAsStream(String name)方法也是采用双亲委派的方式。在前一篇文章中提出“getResourceAsStream可是将获取路径委托给getResource，&lt;compiling-loader&gt;却没有对getResourceAsStream产生影响”</p>
<p>ClassLoader中getResourceAsStream源码也确实是委托为getResource了，可是为什么呢？</p>
<p>getResourceAsStream(String name)方法。</p>
<pre data-enlighter-language="java" class="EnlighterJSRAW">
public InputStream getResourceAsStream(String name) {
    URL url = getResource(name);
    try {
        return url != null ? url.openStream() : null;
    } catch (IOException e) {
        return null;
    }
}
</pre>
<p>其实不难解释，JVM中ClassLoader的getResourceAsStream(&#8220;/a.txt&#8221;)返回了null，然后开始回溯，与getResource方法的原理一致，直到某个ClassLoader及其子类或者Loader及其子类找到了&#8221;/a.txt&#8221;，并以流的形式返回，当然谁都没找到就返回null。</p>
<p>捡重点的说。</p>
<p>调试到sun.misc.Launcher$AppClassLoader@18d107f，即ClassLoader的子类，情形如下图：</p>
<figure id="attachment_6408" aria-describedby="caption-attachment-6408" style="width: 554px" class="wp-caption aligncenter"><img decoding="async" loading="lazy" class="size-full wp-image-6408" title="图17" src="https://coolshell.cn/wp-content/uploads/2012/01/图片17.png" alt="图17" width="554" height="249" srcset="https://coolshell.cn/wp-content/uploads/2012/01/图片17.png 554w, https://coolshell.cn/wp-content/uploads/2012/01/图片17-300x134.png 300w" sizes="(max-width: 554px) 100vw, 554px" /><figcaption id="caption-attachment-6408" class="wp-caption-text">图17</figcaption></figure>
<p>看见getResource(name)喽，按F5进去看个究竟。如下图，其parent为：sun.misc.Launcher$ExtClassLoader@360be0，其返回null。</p>
<figure id="attachment_6398" aria-describedby="caption-attachment-6398" style="width: 554px" class="wp-caption aligncenter"><img decoding="async" loading="lazy" class="size-full wp-image-6398" title="图18" src="https://coolshell.cn/wp-content/uploads/2012/01/图片18.png" alt="图18" width="554" height="278" srcset="https://coolshell.cn/wp-content/uploads/2012/01/图片18.png 554w, https://coolshell.cn/wp-content/uploads/2012/01/图片18-300x150.png 300w" sizes="(max-width: 554px) 100vw, 554px" /><figcaption id="caption-attachment-6398" class="wp-caption-text">图18</figcaption></figure>
<p>开始回溯到：EnvironmentClassLoader$1497769[servlet-server:]，与getResource方法一致，开始遍历_loaders集合。</p>
<p>这样就可以解释为何&lt;compiling-loader&gt;没有影响到getResourceAsStream了。因为资源(这里是/a.txt)，就不是由AppClassLoader和ExtClassLoader加载的，而是由DynamicClassLoader或者其内部的_loaders集合完成的加载。或者更确切的说是由CompilingClassLoader获取到的URL，再转换成InputStream。</p>
<p><span style="color: #ff0000;"><strong>&lt;comiling-loader&gt;其实对getResourceAsStream还是有点影响的，如果配置中配置了&lt;comiling-loader&gt;，并且&lt;comiling-loader&gt;配置的路径下，与实际项目的指定路径下，都放置了同名资源，则会先加载&lt;comiling-loader&gt;配置路径下的资源。</strong></span></p>
<p>比如，下图所示：</p>
<figure id="attachment_6399" aria-describedby="caption-attachment-6399" style="width: 554px" class="wp-caption aligncenter"><img decoding="async" loading="lazy" class="size-full wp-image-6399" title="图19" src="https://coolshell.cn/wp-content/uploads/2012/01/图片19.png" alt="图19" width="554" height="266" srcset="https://coolshell.cn/wp-content/uploads/2012/01/图片19.png 554w, https://coolshell.cn/wp-content/uploads/2012/01/图片19-300x144.png 300w" sizes="(max-width: 554px) 100vw, 554px" /><figcaption id="caption-attachment-6399" class="wp-caption-text">图19</figcaption></figure>
<p>&lt;compiling-loader&gt;配置的路径为：&lt;compiling-loader path=&#8221;webapps/WEB-INF/classes&#8221;/&gt;</p>
<p>在加载&#8221;/a.txt&#8221;时，优先加载webapps/WEB-INF/classes/a.txt。</p>
<h4>总结</h4>
<ol>
<li>&lt;compiling-loader&gt;如被注释掉，则只会在EnvironmentClassLoader$24156236[web-app:http://localhost:8787/EhCacheTestAnnotation]中的_loaders中被初始化，否则会在EnvironmentClassLoader$24156236[web-app:http://localhost:8787/EhCacheTestAnnotation]和EnvironmentClassLoader$7806641[host:http://localhost:8787两个类加载器各自的_loaders集合中被初始化。(通过调试this.getClass().getResource(&#8220;/test&#8221;).getPath()验证)</li>
<li>&lt;compiling-loader&gt;未注释掉，&#8221;/&#8221;(根路径)由EnvironmentClassLoader$7806641[host:http://localhost:8787]加载，注释掉后由EnvironmentClassLoader$24156236[web-app:http://localhost:8787/EhCacheTestAnnotation]加载。</li>
<li>EnvironmentClassLoader$7806641[host:http://localhost:8787]为Resin server的类加载器实例，EnvironmentClassLoader$24156236[web-app:http://localhost:8787/EhCacheTestAnnotation]为Web应用程序的类加载器实例。他们都属于java.net.URLClassLoader的实例。</li>
<li>&lt;compiling-loader&gt;某种程度上对getResourceAsStream方法有影响。</li>
</ol>
<p>现在&lt;compiling-loader&gt;如何影响getResource(&#8220;/&#8221;)，以及getResourceAsStream“不”被影响全部真相大白。</p>
<p><span style="color: #ff0000;">注：&lt;compiling-loader&gt;只对获取根路径产生影响，也就是参数为&#8221;/&#8221;。比如加载&#8221;/test/Path.class&#8221;不会产生影响。</span></p>
<p>（全文完）<!--



<p align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" id="wp_rp_first"><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="https://coolshell.cn/articles/6112.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2011/12/resin01-150x150.png" alt="由一个问题到 Resin ClassLoader 的学习" width="150" height="150" /></a><a href="https://coolshell.cn/articles/6112.html" class="wp_rp_title">由一个问题到 Resin ClassLoader 的学习</a></li><li ><a href="https://coolshell.cn/articles/20845.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg" alt="Rust语言的编程范式" width="150" height="150" /></a><a href="https://coolshell.cn/articles/20845.html" class="wp_rp_title">Rust语言的编程范式</a></li><li ><a href="https://coolshell.cn/articles/18360.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg" alt="程序员练级攻略（2018)  与我的专栏" width="150" height="150" /></a><a href="https://coolshell.cn/articles/18360.html" class="wp_rp_title">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href="https://coolshell.cn/articles/11541.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg" alt="面向GC的Java编程" width="150" height="150" /></a><a href="https://coolshell.cn/articles/11541.html" class="wp_rp_title">面向GC的Java编程</a></li><li ><a href="https://coolshell.cn/articles/11454.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg" alt="从LongAdder看更高效的无锁实现" width="150" height="150" /></a><a href="https://coolshell.cn/articles/11454.html" class="wp_rp_title">从LongAdder看更高效的无锁实现</a></li><li ><a href="https://coolshell.cn/articles/11175.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2014/03/cow-copy-150x150.jpg" alt="Java中的CopyOnWrite容器" width="150" height="150" /></a><a href="https://coolshell.cn/articles/11175.html" class="wp_rp_title">Java中的CopyOnWrite容器</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/6335.html">Resin服务器getResource揭秘</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/6335.html/feed</wfw:commentRss>
			<slash:comments>14</slash:comments>
		
		
			</item>
		<item>
		<title>由一个问题到 Resin ClassLoader 的学习</title>
		<link>https://coolshell.cn/articles/6112.html</link>
					<comments>https://coolshell.cn/articles/6112.html#comments</comments>
		
		<dc:creator><![CDATA[liuxiaori]]></dc:creator>
		<pubDate>Wed, 28 Dec 2011 04:22:55 +0000</pubDate>
				<category><![CDATA[Java语言]]></category>
		<category><![CDATA[ClassLoader]]></category>
		<category><![CDATA[getResource]]></category>
		<category><![CDATA[getResourceAsStream]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[Resin]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=6112</guid>

					<description><![CDATA[<p>（感谢网友 liuxiaori 分享其经历） 背景 某日临近下班，一个同事欲取任何类中获取项目绝对路径，不通过Request方式获取，可是始终获取不到预想的路径...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/6112.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/6112.html">由一个问题到 Resin ClassLoader 的学习</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script><strong>（<span style="color: #cc0000;">感谢网友 liuxiaori 分享其经历</span>）<br />
</strong></p>
<h4>背景</h4>
<p>某日临近下班，一个同事欲取任何类中获取项目绝对路径，不通过Request方式获取，可是始终获取不到预想的路径。于是晚上回家google了一下，误以为是System.getProperty(&#8220;java.class.path&#8221;)-未实际进行测试，早上来和同事沟通，提出了使用这个内置方法，结果人家早已验证过，该方法是打印出CLASSPATH环境变量的值。</p>
<p>于是乎，继续google，找到了Class的getResource与getResourceAsStream两个方法。这两个方法会委托给ClassLoader对应的同名方法。以为这样就可以搞定(实际上确实可以搞定)，但验证过程中却发生了奇怪的事情。</p>
<p>软件环境：Windows XP、Resin 3、Tomcat6.0、Myeclipse、JDK1.5</p>
<h4>发展</h4>
<p>我的验证思路是这样的：</p>
<ol>
<li>定义一个Servlet，然后在该Servlet中调用Path类的getPath方法，getPath方法返回工程classpath的绝对路径，显示在jsp中。</li>
<li>另外在Path类中，通过Class的getResourceAsStream读取当前工程classpath路径中的a.txt文件，写入到getResource路径下的b.txt。</li>
</ol>
<p>由于时间匆忙，代码没有好好去组织。大致能看出上述两个功能，很简单不做解释。</p>
<p><span id="more-6112"></span></p>
<pre data-enlighter-language="java" class="EnlighterJSRAW">
public class Path {
    public String getPath() throws IOException
    {
        InputStream is = this.getClass().getResourceAsStream(&quot;/a.txt&quot;);
        File file = new File(Path.class.getResource(&quot;/&quot;).getPath()+&quot;/b.txt&quot;);
        OutputStream os = new FileOutputStream(file);
        int bytesRead = 0;
        byte[] buffer = new byte[8192];
        while ((bytesRead = is.read(buffer, 0, 8192)) != -1) {
            os.write(buffer, 0, bytesRead);
        }
        os.close();
        is.close();
        return this.getClass().getResource(&quot;/&quot;).getPath();
    }
}
</pre>
<p>&nbsp;</p>
<pre data-enlighter-language="java" class="EnlighterJSRAW">
public class PathServlet extends HttpServlet {
    private static final long serialVersionUID = 4443655831011903288L;
    public void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException
    {
        Path path = new Path();
        request.setAttribute(&quot;path&quot;, path.getPath());
        PrintWriter out = response.getWriter();
        out.println(&quot;Class.getResource(&#039;/&#039;).getPath():&quot; + path.getPath());
    }

    public void doPost(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException
    {
        doGet(request, response);
    }
}</pre>
<p>在此之前使用main函数测试Path.class.getResource(&#8220;/&#8221;).getPath()打印出预想的路径为：/D:/work/project/EhCacheTestAnnotation/WebRoot/WEB-INF/classes/</p>
<p>于是将WEB应用部署到Resin下，运行定义好的Servlet，出乎意料的结果是：/D:/work/resin-3.0.23/webapps/WEB-INF/classes/ 。特别奇怪，怎么会丢掉项目名称：EhCacheTestAnnotation呢？</p>
<p>还有一点值得注意，getPath方法中使用getResourceAsStream(&#8220;/a.txt&#8221;)却正常的读到了位于下图的a.txt。<br />
<img decoding="async" loading="lazy" class="aligncenter size-full wp-image-6280" title="resin01" src="https://coolshell.cn/wp-content/uploads/2011/12/resin01.png" alt="" width="547" height="154" srcset="https://coolshell.cn/wp-content/uploads/2011/12/resin01.png 547w, https://coolshell.cn/wp-content/uploads/2011/12/resin01-300x84.png 300w" sizes="(max-width: 547px) 100vw, 547px" /></p>
<p style="text-align: center;">然后写到了如下图的b.txt中。代码中是这样实现的：File file = new File(Path.class.getResource(&#8220;/&#8221;).getPath()+&#8221;/b.txt&#8221;);本意是想在a.txt文件目录下入b.txt。结果却和料想的不一样。<br />
<img decoding="async" loading="lazy" class="aligncenter" src="https://coolshell.cn/wp-content/uploads/2011/12/resin02.png" alt="" width="430" height="119" /></p>
<p>请注意，区别还是丢掉了项目名称。</p>
<p>写的比较乱，稍微总结下：</p>
<p>程序中使用ClassLoader的两个方法：getResourceAsStream和getResource。但是事实证明在WEB应用的场景下却得到了不同的结果。大家别误会啊，看名字他们两个方法肯定不一样，这个我知道，但是getResourceAsStream总会获取指定路径下的文件吧，示例中的参数为&#8221;/a.txt&#8221;，正确读取“/D:/work/resin-3.0.23/webapps/EhCacheTestAnnotation/WEB-INF/classes/ ”下的a.txt，可是将文件写到getResource方法的getPath返回路径的b.txt文件。两个位置的差别在项目名称(EhCacheTestAnnotation)。</p>
<p>这样我暂且得出一个结论：通过getResourceAsStream和getResource两个方法获取的路径是不同的。但是为什么呢？</p>
<p>于是查看了ClassLoader的源码，贴出getResource和getResourceAsStream的源码。</p>
<pre data-enlighter-language="java" class="EnlighterJSRAW">
public URL getResource(String name) {
    URL url;
    if (parent != null) {
        url = parent.getResource(name);
    } else {
        url = getBootstrapResource(name);
    }

    if (url == null) {
        url = findResource(name);
    }
    return url;
}

public InputStream getResourceAsStream(String name) {
    URL url = getResource(name);
    try {
        return url != null ? url.openStream() : null;
    } catch (IOException e) {
        return null;
    }
}</pre>
<p>从代码中看，getResourceAsStream将获取URL委托给了getResource方法。天啊，这是怎么回事儿？由此我彻底迷茫了，百思不得其解。</p>
<p>但是没有因此就放弃，继续回想了一遍整个过程：</p>
<ol>
<li>在main函数中，测试getResource与getResourceAsStream是完全相同的，正确的。</li>
<li>将其部署到Resin下，导致了getResource与getResourceAsStream获取的路径不一致。</li>
</ol>
<p>一个闪光点，是不是与web容器有关啊，于是换成Tomcat6.0。OMG，“奇迹”出现了，真的是这样子啊，换成Tomcat就一样了啊！和预想的一致。</p>
<p style="text-align: center;">在Tomcat下运行结果如下图：<br />
<img decoding="async" loading="lazy" class="aligncenter" src="https://coolshell.cn/wp-content/uploads/2011/12/resin03.png" alt="" width="554" height="107" /></p>
<p>对，这就是我想要的。</p>
<p>因此我对Resin产生了厌恶感，之前也因为在Resin下程序报错，在Tomcat下正常运行而纠结了好久。记得看《松本行弘的程序世界》中对C++中的多继承是这样评价的(大概意思)：多重继承带来的负面影响多数是由于使用不当造成的。是不是因为对Resin使用不得当才使得和Tomcat下得到不同的结果。</p>
<p>最终，在查阅Resin配置文件resin.conf时候在&lt;host-default&gt;标签下发现了这样一段：</p>
<pre data-enlighter-language="xml" class="EnlighterJSRAW">
&lt;class-loader&gt;
&lt;compiling-loader path=&quot;webapps/WEB-INF/classes&quot;/&gt;
&lt;library-loader path=&quot;webapps/WEB-INF/lib&quot;/&gt;
&lt;/class-loader&gt;</pre>
<p>其中的compiling-loader很可能与之有关，遂将其注释掉，一切正常。担心是错觉，于是将compiling-loader的path属性改成：webapps/WEB-INF/classes1，然后运行pathServlet，b.txt位置如下图：</p>
<p style="text-align: center;"><img decoding="async" loading="lazy" class="aligncenter" src="https://coolshell.cn/wp-content/uploads/2011/12/resin04.png" alt="" width="483" height="93" /></p>
<p>确实与compiling-loader有关。</p>
<h4>结论</h4>
<p>终于通过将&lt;class-loader&gt;标签注释掉，同样可以在Resin中获取“预想”的路径。验证了的确是使用Resin的人出了问题。</p>
<h4>疑问</h4>
<div>
<p>但是没有这样就结束，我继续对getResource的源码进行了跟进，由于能力有限，没有弄清楚getResource的原理。</p>
<p>最终留下了两个疑问：</p>
<p>1、如果追踪到getResource方法的最底层(也许是JVM层面)，它实现的原理是什么？</p>
<p>2、为何Resin中&lt;class-loader&gt;的配置会对getResource产生影响，但是对getResourceAsStream毫无影响(getResourceAsStream可是将获取路径委托给getResource的啊)。还是这里我理解或者使用错误了？</p>
<p>本来文章到这里就结束了，本来是想问问牛人的，但是这个问题引起了很多的好奇心，于是我又花了一两周做了下面的调查。</p>
<h4>Resin中类加载器</h4>
<p>在我了解的<span style="font-family: 'Times New Roman';">ClassLoader</span><span style="font-family: 宋体;">是在</span>com.caucho.loader包下，结构请看下图：<br />
<img decoding="async" loading="lazy" class="aligncenter" src="https://coolshell.cn/wp-content/uploads/2011/12/resin05.png" alt="图1" width="390" height="810" /><br />
图1<br />
<a href="https://coolshell.cn/wp-content/uploads/2011/12/resin06.png"><img decoding="async" loading="lazy" class="aligncenter" src="https://coolshell.cn/wp-content/uploads/2011/12/resin06.png" alt="图2 （点击看大图）" width="677" height="354" /></a><br />
图2</p>
<p>从上面两幅图中可以看出，图1是与Jdk有关联的，继承自java.net.URLClassLoader。DynamicClassLoader的注释是这样的：</p>
<pre data-enlighter-language="java" class="EnlighterJSRAW">
/**
* Class	loader which checks for changes in class files and automatically
* picks up new jars.
*
* DynamicClassLoaders can be chained creating one virtual class loader.
* From the perspective of the JDK, it&#039;s all one classloader.  Internally,
* the class loader chain searches like a classpath.
*/
</pre>
<p>EnvironmentClassLoader又继承了DynamicClassLoader<span style="font-family: 宋体;">。</span></p>
<p>图<span style="font-family: 'Times New Roman';">2</span><span style="font-family: 宋体;">应该是</span><span style="font-family: 'Times New Roman';">Resin</span><span style="font-family: 宋体;">本身的</span><span style="font-family: 'Times New Roman';">ClassLoader</span><span style="font-family: 宋体;">，其中</span><span style="font-family: 'Times New Roman';">Loader</span><span style="font-family: 宋体;">是一个抽象类，包含了各种子类类加载器。</span></p>
<p>从两幅图中是看不出<span style="font-family: 'Times New Roman';">Resin</span><span style="font-family: 宋体;">自身的</span><span style="font-family: 'Times New Roman';">Loader</span><span style="font-family: 宋体;">体系与继承自</span><span style="font-family: 'Times New Roman';">JVM</span><span style="font-family: 宋体;">的类加载器存在关系，那是不是他们就不存在某种关联呢？其实不是这样子的。请看下面</span><span style="font-family: 'Times New Roman';">DynamicClassLoader</span><span style="font-family: 宋体;">源码的片段：</span></p>
<pre data-enlighter-language="java" class="EnlighterJSRAW">
// List of resource loaders
private ArrayList _loaders = new ArrayList();
private JarLoader _jarLoader;
private PathLoader _pathLoader;
</pre>
<p>清楚了吧，这两个Loader<span style="font-family: 宋体;">分支通过组合的方式协作。</span></p>
<h4>类加载器顺序</h4>
<p>既然<span style="font-family: 'Times New Roman';">Resin</span><span style="font-family: 宋体;">标准的</span><span style="font-family: 'Times New Roman';">Loader</span><span style="font-family: 宋体;">及其子类以组合的方式嵌入到</span><span style="font-family: 'Times New Roman';">DynamicClassLoader</span><span style="font-family: 宋体;">中，那么在加载一个“资源”时，</span><span style="font-family: 'Times New Roman';">Loader</span><span style="font-family: 宋体;">分支和</span><span style="font-family: 'Times New Roman';">java.net.URLClassLoader</span><span style="font-family: 宋体;">分支的先后顺序是什么样子的呢？</span></p>
<p>首先使用下面这段代码，将类加载器名称打印到控制台：</p>
<pre data-enlighter-language="java" class="EnlighterJSRAW">
ClassLoader loader = PathServlet.class.getClassLoader();
while (loader != null) {
    System.out.println(loader.toString());
    loader = loader.getParent();
}
</pre>
<p>输出的结果为：</p>
<blockquote><p><em>EnvironmentClassLoader[web-app:http://localhost:8080/Test]</em></p>
<p><em><em>EnvironmentClassLoader[web-app:http://localhost:8080]</em></em></p>
<p><em>EnvironmentClassLoader[cluster ]</em></p>
<p><em><em>EnvironmentClassLoader[]</em></em></p>
<p><em>sun.misc.Launcher$AppClassLoader@cac268</em></p>
<p><em>sun.misc.Launcher$ExtClassLoader@1a16869</em></p></blockquote>
<p>额，没有任何一个Resin<span style="font-family: 宋体;">的</span><span style="font-family: 'Courier New';">Loader</span><span style="font-family: 宋体;">被打印出来啊，对头，有就错了。下面就让我们看看</span><span style="font-family: 'Courier New';">DynamicClassLoader</span><span style="font-family: 宋体;">中</span><span style="font-family: 'Courier New';">getResource</span><span style="font-family: 宋体;">的源码来解答。</span></p>
<pre data-enlighter-language="java" class="EnlighterJSRAW">
/**
* Gets the named resource
*
* @param name name of the resource
*/

public URL getResource(String name)
{
    if (_resourceCache == null) {
        long expireInterval = getDependencyCheckInterval();
        _resourceCache = new TimedCache(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 loaders = _loaders;
    for (int i = 0; loaders != null &amp;&amp; i &lt; 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;
}

</pre>
<p>代码不难懂，我画了一张流程图，不规范，凑合看下。<br />
<img decoding="async" loading="lazy" class="aligncenter size-full wp-image-6286" title="resin07" src="https://coolshell.cn/wp-content/uploads/2011/12/resin07.png" alt="" width="326" height="601" srcset="https://coolshell.cn/wp-content/uploads/2011/12/resin07.png 326w, https://coolshell.cn/wp-content/uploads/2011/12/resin07-162x300.png 162w" sizes="(max-width: 326px) 100vw, 326px" /></p>
<h4>总结</h4>
<pre data-enlighter-language="java" class="EnlighterJSRAW">
boolean isNormalJdkOrder = isNormalJdkOrder(name);
</pre>
<p>这行代码控制着Resin<span style="font-family: 宋体;">类加载的顺序，如果是常规的类加载顺序</span><span style="font-family: 'Courier New';">(</span><span style="font-family: 宋体;">向上代理，原文：</span>Returns true if the class loader should use the normal order, i.e. looking at the parents first.)<span style="font-family: 宋体;">，则先</span>url = getParentResource(name)，后遍历_loaders。否则是按照先遍历_loaders<span style="font-family: 宋体;">再</span>url = getParentResource(name)向上代理。</p>
<p>在我的调试经历中，一直都是先向上代理，后遍历_loaders<span style="font-family: 宋体;">的顺序，未遇到第二种方式。</span></p>
<p>文字对先向上代理，后遍历的顺序做点儿说明：</p>
<ol>
<li>首先使用“最上层”的<em>sun.misc.Launcher$ExtClassLoader@1a16869</em>加载name<span style="font-family: 宋体;">资源，如果找到就返回</span><span style="font-family: 'Courier New';">URL</span><span style="font-family: 宋体;">否则返回</span><span style="font-family: 'Courier New';">null</span></li>
<li>程序返回到<em>sun.misc.Launcher$AppClassLoader@cac268</em>，首先判断父类加载器返回的<span style="font-family: 'Courier New';">url</span><span style="font-family: 宋体;">是否为</span><span style="font-family: 'Courier New';">null</span><span style="font-family: 宋体;">，如果不为</span><span style="font-family: 'Courier New';">null</span><span style="font-family: 宋体;">则返回</span><span style="font-family: 'Courier New';">url</span><span style="font-family: 宋体;">，返回</span><span style="font-family: 'Courier New';">null</span><span style="font-family: 宋体;">。</span></li>
<li><span style="font-family: 宋体;"><em><em><em>EnvironmentClassLoader[]</em></em></em></span></li>
<li>程序返回到<em>EnvironmentClassLoader[cluster ]</em>的getParentResource<span style="font-family: 宋体;">，再返回到</span><span style="font-family: 'Courier New';">getResource</span><span style="font-family: 宋体;">，如果</span><span style="font-family: 'Courier New';">url</span><span style="font-family: 宋体;">不为</span><span style="font-family: 'Courier New';">null</span><span style="font-family: 宋体;">，则直接返回，否则遍历</span>ArrayList&lt;Loader&gt; loaders = _loaders;从各个loader<span style="font-family: 宋体;">中加载</span><span style="font-family: 'Courier New';">name</span><span style="font-family: 宋体;">，如果加载成功，即不为</span><span style="font-family: 'Courier New';">null</span><span style="font-family: 宋体;">，则返回，否则继续遍历，直至遍历完成。</span></li>
<li><em><em>EnvironmentClassLoader[web-app:http://localhost:8080]</em></em>同4</li>
<li><em>EnvironmentClassLoader[web-app:http://localhost:8080/Test]</em>同4</li>
</ol>
<p>OK<span style="font-family: 宋体;">，完事儿，后续还有，准备好好写几篇。</span></p>
<p>本文同时发布于：</p>
<ul>
<li><a href="http://www.oschina.net/question/129471_34225">http://www.oschina.net/question/129471_34225</a>。</li>
<li><a href="http://www.oschina.net/question/129471_35231#AnchorAnswer143898">http://www.oschina.net/question/129471_35231#AnchorAnswer143898</a></li>
</ul>
</div>
<p style="text-align: left;">（全文完）</p>
<p><!--



<p align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" ><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="https://coolshell.cn/articles/6335.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2012/01/图片1-150x150.png" alt="Resin服务器getResource揭秘" width="150" height="150" /></a><a href="https://coolshell.cn/articles/6335.html" class="wp_rp_title">Resin服务器getResource揭秘</a></li><li ><a href="https://coolshell.cn/articles/20845.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg" alt="Rust语言的编程范式" width="150" height="150" /></a><a href="https://coolshell.cn/articles/20845.html" class="wp_rp_title">Rust语言的编程范式</a></li><li ><a href="https://coolshell.cn/articles/18360.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg" alt="程序员练级攻略（2018)  与我的专栏" width="150" height="150" /></a><a href="https://coolshell.cn/articles/18360.html" class="wp_rp_title">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href="https://coolshell.cn/articles/11541.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg" alt="面向GC的Java编程" width="150" height="150" /></a><a href="https://coolshell.cn/articles/11541.html" class="wp_rp_title">面向GC的Java编程</a></li><li ><a href="https://coolshell.cn/articles/11454.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg" alt="从LongAdder看更高效的无锁实现" width="150" height="150" /></a><a href="https://coolshell.cn/articles/11454.html" class="wp_rp_title">从LongAdder看更高效的无锁实现</a></li><li ><a href="https://coolshell.cn/articles/11175.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2014/03/cow-copy-150x150.jpg" alt="Java中的CopyOnWrite容器" width="150" height="150" /></a><a href="https://coolshell.cn/articles/11175.html" class="wp_rp_title">Java中的CopyOnWrite容器</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/6112.html">由一个问题到 Resin ClassLoader 的学习</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/6112.html/feed</wfw:commentRss>
			<slash:comments>30</slash:comments>
		
		
			</item>
	</channel>
</rss>
