仓酷云

 找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 821|回复: 19
打印 上一主题 下一主题

[学习教程] JAVA网站制作之关于ClassLoader中getResource与getResourceAsStream的疑问仓酷云

[复制链接]
爱飞 该用户已被删除
跳转到指定楼层
楼主
发表于 2015-1-18 11:29:30 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
到时我们不用学struts,不用学spring,不用学Hibernate,只要能把jsf学会了,完全可以替代所有的框架,包括AJAX,都知道AJAX并不是新技术,虽说我没深入学习jsf但我认为jsf应该已经能通过其它技术替代AJAX,实现无缝刷新。背景

某日邻近上班,一个同事欲取任何类中猎取项目相对路径,欠亨过Request体例猎取,但是一直猎取不到料想的路径。因而早晨回家google了一下,误觉得是System.getProperty(“java.class.path”)-未实践举行测试,早下去和同事相同,提出了利用这个内置办法,了局人家早已考证过,该办法是打印出CLASSPATH情况变量的值。
因而乎,持续google,找到了Class的getResource与getResourceAsStream两个办法。这两个办法会托付给ClassLoader对应的同名办法。觉得如许就能够弄定(实践上的确能够弄定),但考证过程当中却产生了奇异的事变。
软件情况:WindowsXP、Resin3、Tomcat6.0、Myeclipse、JDK1.5
开展

我的考证思绪是如许的:

  • 界说一个Servlet,然后在该Servlet中挪用Path类的getPath办法,getPath办法前往工程classpath的相对路径,显现在jsp中。
  • 别的在Path类中,经由过程Class的getResourceAsStream读取以后工程classpath路径中的a.txt文件,写进到getResource路径下的b.txt。
因为工夫匆仓促,代码没有好好往构造。大抵能看出上述两个功效,很复杂不做注释。

Path.java12345678910111213141516publicclassPath{publicStringgetPath()throwsIOException{InputStreamis=this.getClass().getResourceAsStream("/a.txt");Filefile=newFile(Path.class.getResource("/").getPath()+"/b.txt");OutputStreamos=newFileOutputStream(file);intbytesRead=0;byte[]buffer=newbyte[8192];while((bytesRead=is.read(buffer,0,8192))!=-1){os.write(buffer,0,bytesRead);}os.close();is.close();returnthis.getClass().getResource("/").getPath();}}PathServlet.java1234567891011121314151617publicclassPathServletextendsHttpServlet{privatestaticfinallongserialVersionUID=4443655831011903288L;publicvoiddoGet(HttpServletRequestrequest,HttpServletResponseresponse)throwsServletException,IOException{Pathpath=newPath();request.setAttribute("path",path.getPath());PrintWriterout=response.getWriter();out.println("Class.getResource(/).getPath():"+path.getPath());}publicvoiddoPost(HttpServletRequestrequest,HttpServletResponseresponse)throwsServletException,IOException{doGet(request,response);}}在此之前利用main函数测试Path.class.getResource(“/”).getPath()打印出料想的路径为:/D:/work/project/EhCacheTestAnnotation/WebRoot/WEB-INF/classes/
因而将WEB使用部署到Resin下,运转界说好的Servlet,出人意料的了局是:/D:/work/resin-3.0.23/webapps/WEB-INF/classes/。出格奇异,怎样会丢失落项目称号:EhCacheTestAnnotation呢?
另有一点值得注重,getPath办法中利用getResourceAsStream(“/a.txt”)却一般的读到了位于下图的a.txt。

<br>
然后写到了以下图的b.txt中。代码中是如许完成的:Filefile=newFile(Path.class.getResource(“/”).getPath()+”/b.txt”);本意是想在a.txt文件目次下进b.txt。了局却和意料的纷歧样。

<br>

请注重,区分仍是丢失落了项目称号。
写的对照乱,略微总结下:
程序中利用ClassLoader的两个办法:getResourceAsStream和getResource。可是现实证实在WEB使用的场景下却失掉了分歧的了局。人人别误解啊,看名字他们两个办法一定纷歧样,这个我晓得,可是getResourceAsStream总会猎取指定路径下的文件吧,示例中的参数为”/a.txt”,准确读取“/D:/work/resin-3.0.23/webapps/EhCacheTestAnnotation/WEB-INF/classes/”下的a.txt,但是将文件写到getResource办法的getPath前往路径的b.txt文件。两个地位的不同在项目称号(EhCacheTestAnnotation)。
如许我临时得出一个结论:经由过程getResourceAsStream和getResource两个办法猎取的路径是分歧的。可是为何呢?
因而检察了ClassLoader的源码,贴出getResource和getResourceAsStream的源码。
12345678910111213141516171819202122publicURLgetResource(Stringname){URLurl;if(parent!=null){url=parent.getResource(name);}else{url=getBootstrapResource(name);}if(url==null){url=findResource(name);}returnurl;}publicInputStreamgetResourceAsStream(Stringname){URLurl=getResource(name);try{returnurl!=null?url.openStream():null;}catch(IOExceptione){returnnull;}}从代码中看,getResourceAsStream将猎取URL托付给了getResource办法。天啊,这是怎样回事儿?由此我完全渺茫了,百思不得其解。
可是没有因而就保持,持续回忆了一遍全部历程:

  • 在main函数中,测试getResource与getResourceAsStream是完整不异的,准确的。
  • 将其部署到Resin下,招致了getResource与getResourceAsStream猎取的路径纷歧致。
一个闪光点,是否是与web容器有关啊,因而换成Tomcat6.0。OMG,“事业”呈现了,真的是如许子啊,换成Tomcat就一样了啊!和料想的分歧。
在Tomcat下运转了局以下图:

<br>

对,这就是我想要的。
因而我对Resin发生了讨厌感,之前也由于在Resin下程序报错,在Tomcat下一般运转而纠结了很久。记得看《松本行弘的程序天下》中对C++中的多承继是如许评价的(也许意义):多重承继带来的负面影响多半是因为利用不妥酿成的。是否是由于对Resin利用不妥当才使得和Tomcat下失掉分歧的了局。
终极,在查阅Resin设置文件resin.conf时分在<host-default>标签下发明了如许一段:
1234<class-loader><compiling-loaderpath="webapps/WEB-INF/classes"/><library-loaderpath="webapps/WEB-INF/lib"/></class-loader>个中的compiling-loader极可能与之有关,遂将其正文失落,统统一般。忧虑是错觉,因而将compiling-loader的path属性改成:webapps/WEB-INF/classes1,然后运转pathServlet,b.txt地位以下图:

<br>

的确与compiling-loader有关。
结论

终究经由过程将<class-loader>标签正文失落,一样能够在Resin中猎取“料想”的路径。考证了切实其实是利用Resin的人出了成绩。
疑问

可是没有如许就停止,我持续对getResource的源码举行了跟进,因为才能无限,没有弄分明getResource的道理。
终极留下了两个疑问:
1、假如追踪到getResource办法的最底层(大概是JVM层面),它完成的道理是甚么?
2、为什么Resin中<class-loader>的设置会对getResource发生影响,可是对getResourceAsStream毫无影响(getResourceAsStream但是将猎取路径托付给getResource的啊)。仍是这里我了解大概利用毛病了?
原本文章到这里就停止了,原本是想问问牛人的,可是这个成绩引发了良多的猎奇心,因而我又花了一两周做了上面的查询拜访。
Resin中类加载器

在我懂得的ClassLoader是在com.caucho.loader包下,布局请看下图:

<br>


<br>

从下面两幅图中能够看出,是与Jdk有联系关系的,承继自java.net.URLClassLoader。DynamicClassLoader的正文是如许的:
12345678/***Classloaderwhichchecksforchangesinclassfilesandautomatically*picksupnewjars.**DynamicClassLoaderscanbechainedcreatingonevirtualclassloader.*FromtheperspectiveoftheJDK,itsalloneclassloader.Internally,*theclassloaderchainsearcheslikeaclasspath.*/EnvironmentClassLoader又承继了DynamicClassLoader。
应当是Resin自己的ClassLoader,个中Loader是一个笼统类,包括了各类子类类加载器。
从两幅图中是看不出Resin本身的Loader系统与承继自JVM的类加载器存在干系,那是否是他们就不存在某种联系关系呢?实在不是如许子的。请看上面DynamicClassLoader源码的片断:
1234//ListofresourceloadersprivateArrayList_loaders=newArrayList();privateJarLoader_jarLoader;privatePathLoader_pathLoader;分明了吧,这两个Loader分支经由过程组合的体例合作。
类加载器按次

既然Resin尺度的Loader及其子类以组合的体例嵌进到DynamicClassLoader中,那末在加载一个“资本”时,Loader分支和java.net.URLClassLoader分支的前后按次是甚么模样的呢?
起首利用上面这段代码,将类加载器称号打印到把持台:
12345ClassLoaderloader=PathServlet.class.getClassLoader();while(loader!=null){System.out.println(loader.toString());loader=loader.getParent();}输入的了局为:
EnvironmentClassLoader[web-app:http://localhost:8080/Test]
EnvironmentClassLoader[web-app:http://localhost:8080]
EnvironmentClassLoader[cluster]
EnvironmentClassLoader[]
sun.misc.Launcher$AppClassLoader@cac268
sun.misc.Launcher$ExtClassLoader@1a16869
额,没有任何一个Resin的Loader被打印出来啊,仇人,有就错了。上面就让我们看看DynamicClassLoader中getResource的源码来解答。
getResource(Stringname)123456789101112131415161718192021222324252627282930313233343536373839404142434445464748/***Getsthenamedresource**@paramnamenameoftheresource*/publicURLgetResource(Stringname){if(_resourceCache==null){longexpireInterval=getDependencyCheckInterval();_resourceCache=newTimedCache(256,expireInterval);}URLurl=_resourceCache.get(name);if(url==NULL_URL)returnnull;elseif(url!=null)returnurl;booleanisNormalJdkOrder=isNormalJdkOrder(name);if(isNormalJdkOrder){url=getParentResource(name);if(url!=null)returnurl;}ArrayListloaders=_loaders;for(inti=0;loaders!=null&&i<loaders.size();i++){Loaderloader=loaders.get(i);url=loader.getResource(name);if(url!=null){_resourceCache.put(name,url);returnurl;}}if(!isNormalJdkOrder){url=getParentResource(name);if(url!=null)returnurl;}_resourceCache.put(name,NULL_URL);returnnull;}代码不难明,我画了一张流程图,不标准,对付看下。

<br>
总结

1booleanisNormalJdkOrder=isNormalJdkOrder(name);这行代码把持着Resin类加载的按次,假如是惯例的类加载按次(向上代办署理,原文:Returnstrueiftheclassloadershouldusethenormalorder,i.e.lookingattheparentsfirst.),则先url=getParentResource(name),后遍历_loaders。不然是依照先遍历_loaders再url=getParentResource(name)向上代办署理。
在我的调试履历中,一向都是先向上代办署理,后遍历_loaders的按次,未碰到第二种体例。
笔墨对先向上代办署理,后遍历的按次做点儿申明:

  • 起首利用“最下层”的sun.misc.Launcher$ExtClassLoader@1a16869加载name资本,假如找到就前往URL不然前往null
  • 程序前往到sun.misc.Launcher$AppClassLoader@cac268,起首判别父类加载器前往的url是不是为null,假如不为null则前往url,前往null。
  • EnvironmentClassLoader[]
  • 程序前往到EnvironmentClassLoader[cluster]的getParentResource,再前往到getResource,假如url不为null,则间接前往,不然遍历ArrayList<Loader>loaders=_loaders;从各个loader中加载name,假如加载乐成,即不为null,则前往,不然持续遍历,直至遍历完成。
  • EnvironmentClassLoader[web-app:http://localhost:8080]同4
  • EnvironmentClassLoader[web-app:http://localhost:8080/Test]同4
OK,完事儿,后续另有,筹办好好写几篇。

因为能用到多少功能就用多少,不能用就不用!总的来说:要简单要性能好,可以不用框架。你说java复杂,就是因为你把java(j2ee)与这些框架混在了一起。
愤怒的大鸟 该用户已被删除
沙发
发表于 2015-1-21 07:51:57 | 只看该作者
Java语言支持Internet应用的开发,在基本的Java应用编程接口中有一个网络应用编程接口(java net),它提供了用于网络应用编程的类库,包括URL、URLConnection、Socket、ServerSocket等。Java的RMI(远程方法激活)机制也是开发分布式应用的重要手段。
小女巫 该用户已被删除
板凳
发表于 2015-1-30 12:29:32 | 只看该作者
还好,SUN提供了Javabean可以把你的JSP中的 Java代码封装起来,便于调用也便于重用。
飘灵儿 该用户已被删除
地板
发表于 2015-1-31 12:54:32 | 只看该作者
接着就是EJB了,EJB就是Enterprise JavaBean, 看名字好象它是Javabean,可是它和Javabean还是有区别的。它是一个体系结构,你可以搭建更安全、更稳定的企业应用。它的大量代码已由中间件(也就是我们常听到的 Weblogic,Websphere这些J2EE服务器)完成了,所以我们要做的程序代码量很少,大部分工作都在设计和配置中间件上。
因胸联盟 该用户已被删除
5#
发表于 2015-2-6 12:58:27 | 只看该作者
所以现在应用最广泛又最好学的就是J2EE了。 J2EE又包括许多组件,如Jsp,Servlet,JavaBean,EJB,JDBC,JavaMail等。要学习起来可不是一两天的事。那么又该如何学习J2EE呢?当然Java语法得先看一看的,I/O包,Util包,Lang包你都熟悉了吗?然后再从JSP学起。
乐观 该用户已被删除
6#
发表于 2015-2-9 15:53:25 | 只看该作者
多重继承(以接口取代)等特性,增加了垃圾回收器功能用于回收不再被引用的对象所占据的内存空间,使得程序员不用再为内存管理而担忧。在 Java 1.5 版本中,Java 又引入了泛型编程(Generic Programming)、类型安全的枚举、不定长参数和自动装/拆箱等语言特性。
精灵巫婆 该用户已被删除
7#
发表于 2015-2-11 09:32:02 | 只看该作者
是一种使网页(Web Page)产生生动活泼画面的语言
8#
发表于 2015-2-18 16:48:20 | 只看该作者
有时间再研究一下MVC结构(把Model-View-Control分离开的设计思想)
分手快乐 该用户已被删除
9#
发表于 2015-2-25 05:26:01 | 只看该作者
科学超级计算机、移动电话和互联网,同时拥有全球最大的开发者专业社群。
冷月葬花魂 该用户已被删除
10#
发表于 2015-2-26 19:06:48 | 只看该作者
Jive的资料在很多网站上都有,大家可以找来研究一下。相信你读完代码后,会有脱胎换骨的感觉。遗憾的是Jive从2.5以后就不再无条件的开放源代码,同时有licence限制。不过幸好还有中国一流的Java程序员关注它,外国人不开源了,中国人就不能开源吗?这里向大家推荐一个汉化的Jive版本—J道。Jive(J道版)是由中国Java界大名 鼎鼎的banq在Jive 2.1版本基础上改编而成, 全中文,增加了一些实用功能,如贴图,用户头像和用户资料查询等,而且有一个开发团队在不断升级。你可以访问banq的网站
第二个灵魂 该用户已被删除
11#
发表于 2015-3-8 16:41:08 | 只看该作者
其实说这种话的人就如当年小日本号称“三个月拿下中国”一样大言不惭。不是Tomjava泼你冷水,你现在只是学到了Java的骨架,却还没有学到Java的精髓。接下来你得研究设计模式了。
再现理想 该用户已被删除
12#
发表于 2015-3-10 12:53:37 | 只看该作者
当然你也可以参加一些开源项目,一方面可以提高自己,另一方面也是为中国软件事业做贡献嘛!开发者在互联网上用CVS合作开发,用QQ,MSN,E-mail讨论联系,天南海北的程序员分散在各地却同时开发同一个软件,是不是很有意思呢?
蒙在股里 该用户已被删除
13#
发表于 2015-3-17 07:11:06 | 只看该作者
一般学编程语言都是从C语开始学的,我也不例外,但还是可能不学过程语言而直接学面向对象语言的,你是刚接触语言,还是从C开始学比较好,基础会很深点,如果你直接学习JAVA也能上手,一般大家在学语言的时候都记一些语言的关键词,常有的包和接口等。再去做逻辑代码的编写,以后的学习过程都是从逻辑代码编写中提升的,所以这方面都是经验积累的。你要开始学习就从
活着的死人 该用户已被删除
14#
发表于 2015-3-17 16:00:20 | 只看该作者
Java语言支持Internet应用的开发,在基本的Java应用编程接口中有一个网络应用编程接口(java net),它提供了用于网络应用编程的类库,包括URL、URLConnection、Socket、ServerSocket等。Java的RMI(远程方法激活)机制也是开发分布式应用的重要手段。
爱飞 该用户已被删除
15#
 楼主| 发表于 2015-3-24 10:14:55 | 只看该作者
关于设计模式的资料,还是向大家推荐banq的网站 [url]http://www.jdon.com/[/url],他把GOF的23种模式以通俗易懂的方式诠释出来,纯Java描述,真是经典中的经典。
灵魂腐蚀 该用户已被删除
16#
发表于 2015-4-3 05:06:27 | 只看该作者
学Java必读的两个开源程序就是Jive和Pet Store.。 Jive是国外一个非常著名的BBS程序,完全开放源码。论坛的设计采用了很多先进的技术,如Cache、用户认证、Filter、XML等,而且论坛完全屏蔽了对数据库的访问,可以很轻易的在不同数据库中移植。论坛还有方便的安装和管理程序,这是我们平时编程时容易忽略的一部份(中国程序员一般只注重编程的技术含量,却完全不考虑用户的感受,这就是我们与国外软件的差距所在)。
不帅 该用户已被删除
17#
发表于 2015-4-5 01:32:09 | 只看该作者
Sun公司看见Oak在互联网上应用的前景,于是改造了Oak,于1995年5月以Java的名称正式发布。Java伴随着互联网的迅猛发展而发展,逐渐成为重要的网络编程语言。
山那边是海 该用户已被删除
18#
发表于 2015-4-8 15:13:18 | 只看该作者
设计模式是高级程序员真正掌握面向对象核心思想的必修课。设计模式并不是一种具体"技术",它讲述的是思想,它不仅仅展示了接口或抽象类在实际案例中的灵活应用和智慧
若相依 该用户已被删除
19#
发表于 2015-4-16 06:12:54 | 只看该作者
任职于太阳微系统的詹姆斯·高斯林等人于1990年代初开发Java语言的雏形,最初被命名为Oak,目标设置在家用电器等小型系统的程序语言
谁可相欹 该用户已被删除
20#
发表于 2015-4-17 02:37:17 | 只看该作者
Java 不同于一般的编译执行计算机语言和解释执行计算机语言。它首先将源代码编译成二进制字节码(bytecode),然后依赖各种不同平台上的虚拟机来解释执行字节码。从而实现了“一次编译、到处执行”的跨平台特性。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|Archiver|手机版|仓酷云 鄂ICP备14007578号-2

GMT+8, 2024-12-23 07:13

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表