马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有帐号?立即注册
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就一样了啊!和料想的分歧。
对,这就是我想要的。
因而我对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地位以下图:
的确与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)与这些框架混在了一起。 |