愚人码头

知耻而后勇,知不足而进
随笔 - 33, 文章 - 1, 评论 - 26, 引用 - 0
数据加载中……

2008年1月30日

对hibernate的新认识

     摘要:   阅读全文

posted @ 2008-01-30 11:14 船夫 阅读(1941) | 评论 (7)编辑 收藏

2007年12月26日

转:Javascript事件处理

     摘要:   阅读全文

posted @ 2007-12-26 13:00 船夫 阅读(337) | 评论 (0)编辑 收藏

2007年12月25日

转:提升JSP应用程序的七大绝招

提升JSP应用程序的七大绝招
你时常被客户抱怨JSP页面响应速度很慢吗?你想过当客户访问次数剧增时,你的WEB应用能承受日益增加的访 问量吗?本文讲述了调整JSP和servlet的一些非常实用的方法,它可使你的servlet和JSP页面响应更快,扩展性更强。而且在用户数增加的情 况下,系统负载会呈现出平滑上长的趋势。在本文中,我将通过一些实际例子和配置方法使得你的应用程序的性能有出人意料的提升。其中,某些调优技术是在你的 编程工作中实现的。而另一些技术是与应用服务器的配置相关的。在本文中,我们将详细地描述怎样通过调整servlet和JSP页面,来提高你的应用程序的 总体性能。在阅读本文之前,假设你有基本的servlet和JSP的知识。
方法一:在servlet的init()方法中缓存数据

当应用服务器初始化servlet实例之后,为客户端请求提供服务之前,它会调用这个servlet的init()方法。在一个servlet的生命周 期中,init()方法只会被调用一次。通过在init()方法中缓存一些静态的数据或完成一些只需要执行一次的、耗时的操作,就可大大地提高系统性能。

例如,通过在init()方法中建立一个JDBC连接池是一个最佳例子,假设我们是用jdbc2.0的DataSource接口来取得数据库连接,在通 常的情况下,我们需要通过JNDI来取得具体的数据源。我们可以想象在一个具体的应用中,如果每次SQL请求都要执行一次JNDI查询的话,那系统性能将 会急剧下降。解决方法是如下代码,它通过缓存DataSource,使得下一次SQL调用时仍然可以继续利用它:

public class ControllerServlet extends HttpServlet
{
private javax.sql.DataSource testDS = null;
public void init(ServletConfig config) throws ServletException
{
super.init(config);
Context ctx = null;
try
{
ctx = new InitialContext();
testDS = (javax.sql.DataSource)ctx.lookup("jdbc/testDS");
}
catch(NamingException ne)
{
ne.printStackTrace();
}
catch(Exception e)
{
e.printStackTrace();
}
}

public javax.sql.DataSource getTestDS()
{
return testDS;
}
...
...
}

方法 2:禁止servlet和JSP 自动重载(auto-reloading)

Servlet/JSP提供了一个实用的技术,即自动重载技术,它为开发人员提供了一个好的开发环境,当你改变servlet和JSP页面后而不必重启 应用服务器。然而,这种技术在产品运行阶段对系统的资源是一个极大的损耗,因为它会给JSP引擎的类装载器(classloader)带来极大的负担。因 此关闭自动重载功能对系统性能的提升是一个极大的帮助。

方法 3: 不要滥用HttpSession

在很多应 用中,我们的程序需要保持客户端的状态,以便页面之间可以相互联系。但不幸的是由于HTTP具有天生无状态性,从而无法保存客户端的状态。因此一般的应用 服务器都提供了session来保存客户的状态。在JSP应用服务器中,是通过HttpSession对像来实现session的功能的,但在方便的同 时,它也给系统带来了不小的负担。因为每当你获得或更新session时,系统者要对它进行费时的序列化操作。你可以通过对HttpSession的以下 几种处理方式来提升系统的性能:

? 如果没有必要,就应该关闭JSP页面中对HttpSession的缺省设置: 如果你没有明确指定的话,每个JSP页面都会缺省地创建一个HttpSession。如果你的JSP中不需要使用session的话,那可以通过如下的JSP页面指示符来禁止它:

<%@ page session="false"%>

? 不要在HttpSession中存放大的数据对像:如果你在HttpSession中存放大的数据对像的话,每当对它进行读写时,应用服务器都将对其进行 序列化,从而增加了系统的额外负担。你在HttpSession中存放的数据对像越大,那系统的性能就下降得越快。

? 当你不需要HttpSession时,尽快地释放它:当你不再需要session时,你可以通过调用HttpSession.invalidate()方法来释放它。

? 尽量将session的超时时间设得短一点:在JSP应用服务器中,有一个缺省的session的超时时间。当客户在这个时间之后没有进行任何操作的话, 系统会将相关的session自动从内存中释放。超时时间设得越大,系统的性能就会越低,因此最好的方法就是尽量使得它的值保持在一个较低的水平。

方法 4: 将页面输出进行压缩

压缩是解决数据冗余的一个好的方法,特别是在网络带宽不够发达的今天。有的浏览器支持gzip(GNU zip)进行来对HTML文件进行压缩,这种方法可以戏剧性地减少HTML文件的下载时间。因此,如果你将servlet或JSP页面生成的HTML页面 进行压缩的话,那用户就会觉得页面浏览速度会非常快。但不幸的是,不是所有的浏览器都支持gzip压缩,但你可以通过在你的程序中检查客户的浏览器是否支 持它。下面就是关于这种方法实现的一个代码片段:

public void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException
{
OutputStream out = null
String encoding = request.getHeader("Accept-Encoding");
if (encoding != null && encoding.indexOf("gzip") != -1)
{
request.setHeader("Content-Encoding" , "gzip");
out = new GZIPOutputStream(request.getOutputStream());
}
else if (encoding != null && encoding.indexOf("compress") != -1)
{
request.setHeader("Content-Encoding" , "compress");
out = new ZIPOutputStream(request.getOutputStream());
}
else
{
out = request.getOutputStream();
}
...
...
}

方法 5: 使用线程池

应用服务器缺省地为每个不同的客户端请求创建一个线程进行处理,并为它们分派service()方法,当service()方法调用完成后,与之相应的 线程也随之撤消。由于创建和撤消线程会耗费一定的系统资源,这种缺省模式降低了系统的性能。但所幸的是我们可以通过创建一个线程池来改变这种状况。另外, 我们还要为这个线程池设置一个最小线程数和一个最大线程数。在应用服务器启动时,它会创建数量等于最小线程数的一个线程池,当客户有请求时,相应地从池从 取出一个线程来进行处理,当处理完成后,再将线程重新放入到池中。如果池中的线程不够地话,系统会自动地增加池中线程的数量,但总量不能超过最大线程数。 通过使用线程池,当客户端请求急剧增加时,系统的负载就会呈现的平滑的上升曲线,从而提高的系统的可伸缩性。

方法 6: 选择正确的页面包含机制

在JSP中有两种方法可以用来包含另一个页面:1、使用include指示符(<%@ includee file=”test.jsp” %>)。2、使用jsp指示符(<jsp:includee page=”test.jsp” flush=”true”/>)。在实际中我发现,如果使用第一种方法的话,可以使得系统性能更高。

方法 7:正确地确定javabean的生命周期

JSP的一个强大的地方就是对javabean的支持。通过在JSP页面中使用<jsp:useBean>标签,可以将javabean直接插入到一个JSP页面中。它的使用方法如下:

<jsp:useBean id="name" scope="page|request|session|application" class=
"package.className" type="typeName">
</jsp:useBean>

其中scope属性指出了这个bean的生命周期。缺省的生命周期为page。如果你没有正确地选择bean的生命周期的话,它将影响系统的性能。

举例来说,如果你只想在一次请求中使用某个bean,但你却将这个bean的生命周期设置成了session,那当这次请求结束后,这个bean将仍然 保留在内存中,除非session超时或用户关闭浏览器。这样会耗费一定的内存,并无谓的增加了JVM垃圾收集器的工作量。因此为bean设置正确的生命 周期,并在bean的使命结束后尽快地清理它们,会使用系统性能有一个提高

其它一些有用的方法

? 在字符串连接操作中尽量不使用“+”操作符:在java编程中,我们常常使用“+”操作符来将几个字符串连接起来,但你或许从来没有想到过它居然会对系统 性能造成影响吧?由于字符串是常量,因此JVM会产生一些临时的对像。你使用的“+”越多,生成的临时对像就越多,这样也会给系统性能带来一些影响。解决 的方法是用StringBuffer对像来代替“+”操作符。

? 避免使用System.out.println()方法:由于System.out.println()是一种同步调用,即在调用它时,磁盘I/O操作必 须等待它的完成,因此我们要尽量避免对它的调用。但我们在调试程序时它又是一个必不可少的方便工具,为了解决这个矛盾,我建议你最好使用Log4j工具 (http://Jakarta.apache.org ),它既可以方便调试,而不会产生System.out.println()这样的方法。

? ServletOutputStream 与 PrintWriter的权衡:使用PrintWriter可能会带来一些小的开销,因为它将所有的原始输出都转换为字符流来输出,因此如果使用它来作为 页面输出的话,系统要负担一个转换过程。而使用ServletOutputStream作为页面输出的话就不存在一个问题,但它是以二进制进行输出的。因 此在实际应用中要权衡两者的利弊。

总结

本文的目的是通过对servlet和JSP的一些调优技术来极大地提高你 的应用程序的性能,并因此提升整个J2EE应用的性能。通过这些调优技术,你可以发现其实并不是某种技术平台(比如J2EE和.NET之争)决定了你的应 用程序的性能,重要是你要对这种平台有一个较为深入的了解,这样你才能从根本上对自己的应用程序做一个优化! 

Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1448910

posted @ 2007-12-25 17:04 船夫 阅读(221) | 评论 (0)编辑 收藏

转载一篇测试笔记,用于备忘

     摘要:   阅读全文

posted @ 2007-12-25 14:43 船夫 阅读(257) | 评论 (0)编辑 收藏

2007年12月18日

JSP编译过程

很久以来,都知道JSP其实就是Servlet,今天心血来潮,想把里面的具体代码看一遍,并记录心得。
一个编译好的JSP类如下:
public
 final class index_jsp extends HttpJspBase
    
implements JspSourceDependent
//上述类为index.jsp被编译后得到的类
//HttpJspBase是extends HttpServlet的一个类
//JspSourceDependent是一个接口,只声明了一个方法、、getDependants(),返回当前page所依赖的文件的名称,文件包括以下几种
//1) files that are included by page directives 
//2) files that are included by include-prelude and include-coda in jsp:config 
//3) files that are tag files and referenced 
//4) TLDs referenced
其核心方法为:
  public void _jspService(HttpServletRequest request, HttpServletResponse response)
        
throws IOException, ServletException{
.
}
该方法重载HttpJspBase中的_jspService方法,进行页面解析,包括对scriptlet的加载,html的生成,自定义Tag的解析等等
HttpJspBase的service再对_jspService再进行调用,最终生成HTML页面

posted @ 2007-12-18 12:06 船夫 阅读(1892) | 评论 (1)编辑 收藏

2007年12月17日

Annotation初步了解

今天在看Tapestry代码的时候,突然想了解一下annotation到底是干什么的,有什么好处?花了3个小时的时间,从JDK API开始,大致了解了一下。

annotation实际上就是给一些特定的类和其属性,方法等加上一些注释(annotation),这些注释是以属性name,value进行设置的,这些属性在Annotation Class中以方法的形式存在,如下:
@Documented                //是否需要产生javadoc
@Target(java.lang.annotation.ElementType.METHOD)   //应用目标,这里是应用到field属性上面
@Retention(RetentionPolicy.RUNTIME)   //该策略指明该注释会被加载到jvm中,即在运行时,我们可以得到该注释的内容,另外两个策略,SOURCE, CLASS都不会加载到jvm中
public @interface TestAnnoation {
    String value();   //属性value
    String time();     //属性time
}
以上为自定义的annotation,是应用在成员变量上的,使用该annotation情况如下
public class Test {
    @TestAnnoation(time
="12:30",value="20")
    
public String test(){
          System.out.println("test!");
          return null;
    };
}

可以使用java的反射来进行获取,通过:
Class cls = Class.forName("Test");
cls.isAnnotationPresent(AnnotationClass.
class);//判断是否存在annotation
TestAnnotation ta = (TestAnnotation)cls.getAnnotation(AnnotationClass.class);//获得AnnotationClass实例,后调用这个实例可以获得在AnnotationClass中定义的一些属性
System.out.println(ta.time()); //输出注释time内容
System.out.println(ta.value()); //输出注释value内容

我个人认为annotation的好处是将一些配置直接写在代码上,很直观;以前在使用hibernate的时候,PO对象和对应的mapping xml是分开的,不够直观,若是使用annotation就会很直观的看出这个对象的映射属性以及它的一些特殊属性(如lazy=true)之类的,在JPA中就把这种配置方式换成了annotation。

在JE上有很多关于使用annotation和xml的争论,我认为在小规模使用上可以使用annotation,就像JPA这种,每个PO上面都需要进行配置,即使使用XML也不能简化;而在大规模使用,如spring所维护的一些service的事务配置上避免使用annotation,因为每个service类中都需要配置,而使用AOP根据XML配置可以一下就搞定。

关于ElementType的其他属性:
TYPE(类型), FIELD(属性), METHOD(方法), PARAMETER(参数), CONSTRUCTOR(构造函数),LOCAL_VARIABLE(局部变量), ANNOTATION_TYPE,PACKAGE(包),其中的TYPE(类型)是指可以用在Class,Interface,Enum和 Annotation类型上.

posted @ 2007-12-17 16:33 船夫 阅读(309) | 评论 (1)编辑 收藏

禁食了,肾结石患者还能吃什么?

当肾结石、膀胱结石发生以后,患者在剧烈的疼痛中常向医生讨教:肾脏、尿道里那来的石头呢?手术之后要怎样才能预防它复发呢?
结石的形成,主要原因就是饮食。它是由饮食中可形成结石的有关成分摄入过多引起的。再细一点解释是:
草酸积存过多。体内草酸的大量积存,是导致肾尿结石的 因素之一。如菠菜、豆类、葡萄、可可、茶叶、桔子、番茄、土豆、李子、竹笋等这些人们普遍爱吃的东西,正是含草酸较高的食物。医生通过研究发现:200克 菠菜中,含草酸725.6毫克,如果一人一次将200克菠菜全部吃掉,食后8小时,检查尿中草酸排泄量为20-25毫克,相当于正常人24小时排出的草酸 平均总量。
嘌呤代谢失常。动物内脏、海产食品、花生、豆角、菠菜等,均含有较多的嘌呤成分。嘌呤进入体内后,要进行新陈代谢,它代谢的最终产物是尿酸。尿酸可促使尿中草酸盐沉淀。如果,一次过多地食用了含嘌呤丰富的食物,嘌呤的代谢又失常,草酸盐便在尿中沉积而形成尿结石
脂肪摄取太多。各种动物的肉类,尤其是肥猪肉,都是脂肪多的食品。多吃了体内脂肪必然增高,脂肪会减少肠道中可结合的钙,因而引起对草酸盐的吸收增多,如果一旦出现排泄功能故障,如出汗多、喝水少,尿量少,肾结石很可能就在这种情况下形成。所以,医生们常讲,为了预防得结石病,热天要多喝点水,吃了油水多的食物时,也要多喝点水,以促进排尿畅通,稀释尿液成分,就减少了得结石的危险。
糖分增高。糖是人体的重要养分,要经常适量增补,但一下子增加太多,尤其是乳糖,也会使结石形成创造条件。专家们发现:不论正常人或结石病人,在食用100克蔗糖后,过2小时去检查他们的尿,发现尿中的钙和草酸浓度均上升,若是服用乳糖,它更能促进钙的吸收,更可能导致草酸钙在体内的积存而形成尿结石
蛋白质过量。对肾结石成分进行化验分析,发现结石中的草酸钙占87.5%。这么大比重的草酸钙的来源就是因为蛋白质里除含有草酸的原料——甘氨酸、羟脯氨酸之外,蛋白质还能促进肠道功能对钙的吸收。如果经常过量食用高蛋白质的食物,便使肾脏和尿中的钙、草酸、尿酸的成分普遍增高。如果不能及时有效地通过肾脏功能把多余的钙、草酸、尿酸排出体外,这样,得肾脏结石、输尿管结石症的条件就形成了。当今世界经济发达国家肾结石发病率增高的主要原因就是如此。
从以上几种易形成肾结石的因素来看,要预防肾结石病 的发生,就必须改变只顾单求一种营养和追求营养过甚的观念。这就是说,在人类的日常饮食中,不能因为某种食物好吃、营养价值高,就一味地只顾去吃这种食 物。必须注意食物的搭配,各种食物都适量进食,即使是检查出身体缺乏某种营养素需要某种食物来补充时,也不宜一次大量进食,因为人体的消化、吸收功能是有限的。消化、吸收不了的养分就要通过排泄器官排泄出去,这样也会增加泌尿系统的负担,即便不患肾结石病,也对健康不利。特别是当检查出确认是肾结石症时,在患病期间,要限制病人吃那些易促使结石形成的食物。
结石是尿石症的一种,多在炎热的夏天形成,因为夏天大量出汗,甚至体内脱水,使排尿减少,再加之夏季暴露于阳光下时间长,紫外线照射皮肤有助于体内维生素D和维生素A合成增多,维生素D和维生素A可促进小肠吸收钙离子,尿液中排泄钙增多,尿内结石物质易产生结晶核,从而形成结石。冬季天气寒冷,人的尿量增多,已形成的小结石被尿液冲刷,向下移动,此时引起肾绞痛症状。所以,肾结石常为“夏季形成冬季发病”。

人们的饮食品种是多样的,人体新陈代谢是复杂的,所以肾结石的成分也是多样的。常见结石按成分可分为五种:一草酸钙结石:最为常见,占肾结石的80以上,在酸性或中性尿中形成,发病多为青壮年,以男性多见。二磷酸钙结石:占结石的6-9,在碱性尿中形成,也以男性青壮年多发。三尿酸结石:占结石的6,在酸性尿中形成,当尿PH值大于6.7时结石溶解,以男性多见。四磷酸镁胺结石:占结石的10,在碱性尿中形成,尿PH值小于7.2时结石溶解,以女性多见。五胱氨酸结石:少见,约占结石的1-2,在酸性尿中形成,尿PH值大于7.0时结石溶解。

大量饮水对所有成分尿石都有防治作用。在炎热的夏天,每日尿量少于1200毫升时,尿石生长的危险性显著增大。如能使每日饮水量在2000-4000毫升,这样可维持每日尿量在2000毫升以上。磁化水对防治草酸钙结石更有效,可将全日饮水量分别于晨起、餐间、睡前给予。清晨饮水量可达500-1000毫升。为了保持夜间尿量,睡前饮水500毫升,睡眠中起床排尿后再饮水300-500毫升,余下水分别于餐间饮服。大量饮水可促使小的结石排出,稀释尿液可防止尿石结晶形成,并能延缓结石增长速度。

1985年国外学者Vehlensieck认为,多饮水和饮食疗法可使2/3复发结石病人不再生新结石。下面介绍几种肾结石的饮食疗法。

⑴草酸钙结石:宜低钙及低草酸饮食。少食牛奶及乳制品、豆制品、肉类、动物内脏(如肝、心脏、肾、肠等),还有巧克力、浓茶、芝麻酱、蛋黄、香菇、菠菜、虾皮、萝卜、可可、芹菜、土豆等。近年来发现食物中纤维素可减少尿钙的形成,如麦麸食品中的麦麸面包、米糠也有同样作用,对复发性高钙尿结石有效,维生素B1、维生素B6缺乏使尿草酸增多,应增加富含此类维生素的食物,如谷物、干果、硬果等。

⑵磷酸钙结石及磷酸镁铵结石:其低钙饮食同草酸钙结石相同。在低磷食物中,宜少食肉类、鱼类及骨头汤。

⑶尿结石: 应限制蛋白质的摄入量,每日蛋白质的总摄入量应在48-80克(0.8-1.0克/公斤/日)之间。一般带叶的蔬菜每市斤约含10克蛋白质、瘦肉类每50 克约含蛋白质10克、谷类每市斤含蛋白质35-60克。要增加新鲜蔬菜和水果的食量。蔬菜和水果含维生素B1及维生素C,它们在体内最后代谢产物是碱性 的,尿酸在碱性尿内易于溶解,故有利于治疗。常规治疗:每隔1-2日用一次清凉饮食(生水果、果汁及生菜),至少每周1次清凉饮食。少食或忌用肉类、动物 内脏、肉汤、肉汁、沙丁鱼、蟹、菠菜、浓茶、咖啡,烈性的香料及调味品也宜少用。
现实生活中很多疾病的发生和日常饮食是密切相关的,如果能做到起居有时,饮食有节,甚至大部分癌症也可能避免。现在就肾结石病这一顽症来提醒大家如何用饮食来预防,或使已经患了肾结石者,结石增大的速度减慢,甚至缩小、溶解而排出体外。

(一)多饮白开水 多饮水使尿液得到稀释,钙离子和草酸根的浓度就会降低,形成不了草酸钙结石。研究表明,增加50%的尿量,可使肾结石发病率下降86%。

(二)合理补钙,尤其饮食上补钙 肾结石患者往往“谈钙色变”,错误地认为肾结石的元凶是钙,其实不然,肾结石患者也需要补钙。目前医学界从两个不同的角度来解释,肾结石患者为什么要补钙。

第一是补充钙能与胃肠道中蔬菜含有的草酸结合成不溶性的草酸钙,随粪便排出体外,减少了部分被肠胃吸收和经肾脏排出体外的草酸,从而减少了形成肾结石的几率。

第二是日本学者提出的“酸碱平衡学说”。即血液呈酸性时,结石容易形成。呈碱性时,抑制结石形成。缺钙时血液偏酸性,合理补钙,血液偏碱,这样反而有利于抑制结石形成。

(三)限量摄入糖类 美国科学家最新一项研究结果表明,高糖食品的摄入,可以使患肾结石的机会增加,因此,要注意少吃甜食。

(四)少吃草酸盐含量高的食物 含草酸盐高的食物有番茄、菠菜、草莓、甜菜、巧克力等,过高的草酸盐摄入也是导致肾结石的主要原因之一。

(五)少吃豆制品 大豆食品含草酸盐和磷酸盐都高,能同肾脏中的钙融合,形成结石

(六)睡前慎喝牛奶 睡眠不好的人,睡前喝杯牛奶有助于睡眠。但在睡眠后,尿量减少、浓缩,尿中各种有形物质增加。而饮牛奶后2~3小时,正是钙通过肾脏排泄的高峰。钙通过肾脏在短时间内骤然增多,容易形成结石。因此肾结石患者,睡前就不应喝含钙高的牛奶。

(七)勿过量服用鱼肝油 鱼肝油富含维生素D,有促进肠膜对钙磷吸收的功能,骤然增加尿液中钙磷的排泄,势必产生沉淀,容易形成结石

(八)多食黑木耳 黑木耳中富含多种矿物质和微量元素,能对各种结石产生强烈的化学反应,使结石剥脱、分化、溶解,排出体外。

posted @ 2007-12-17 12:04 船夫 阅读(1385) | 评论 (0)编辑 收藏

2007年12月14日

XSL取得当前循环的位置

最近在一个项目中用到了XSLT,目的是将返回的XML数据记录通过XSL转换为HTML,在for-each的循环中取得当前记录的位置,通过实践,找到了解决的办法,主要是使用xsl的position函数
1 <xsl:for-each select="QRoleInline-list/QRoleInline">
2 <xsl:if test="not(position() = 1)"><xsl:text>,</xsl:text></xsl:if><xsl:value-of select="@roleName"/>
3 </xsl:for-each>
使用position函数可以取得当前行在循环中的位置,从1开始。上述代码是判断如果位置为第一个, 则需要加逗号。

还有一个函数current()是负责取到当前节点对象的。

posted @ 2007-12-14 11:33 船夫 阅读(513) | 评论 (0)编辑 收藏

2006年7月27日

走出ClassLoader迷局 --转至sharajava的博克http://www.blogjava.net/sharajava/archive/2006/07/25/59946.html

: 这个问题经常出现在编写框架代码 , 需要动态加载很多类和资源的时候 . 通常当你需要动态加载资源的时候 , 你至少有三个 ClassLoader 可以选择 :

²        系统类加载器或叫作应用类加载器 (system classloader or application classloader)

²        当前类加载器

²        当前线程类加载器

上面的问题指的是最后一种类加载器 . 哪种类加载器是正确的选择呢 ?

第一种选择可以很容易地排除 : 系统类加载器 (system classloader). 这个类加载器处理 -classpath 下的类加载工作 , 可以通过 ClassLoader.getSystemClassLoader() 方法调用 . ClassLoader 下所有的 getSystemXXX() 的静态方法都是通过这个方法定义的 . 在你的代码中 , 你应该尽量少地调用这个方法 , 以其它的类加载器作为代理 . 否则你的代码将只能工作在简单的命令行应用中 , 这个时候系统类加载器 (system classloader) JVM 最后创建的类加载器 . 一但你把代码移到 EJB, Web 应用或 Java Web Start 应用中 , 一定会出问题 .

      所以我们来看第二种选择 : 当前上下文环境下的类加载器 . 根据定义 , 当前类加载器就是你当前方法所属的类的加载器 . 在运行时类之间动态联编 , 及调用 Class.forName,() Class.getResource() 等类似方法时 , 这个类加载器会被隐含地使用 . It is also used by syntactic constructs like X.class class literals.

    线程上下文类型加载器是在Java 2平台上被引入的. 每一个线程都有一个类加载器与之对应(除非这个线程是被本地代码创建的). 这个类加载器是通过Thread.setContextClassLoaser()方法设置的. 如果你不在线程构造后调用这个方法, 这个线程将从它的父线程中继承相应的上下文类加载器. 如果在整个应用中你不做任何特殊设置, 所有的线程将都以系统类加载器(system classloader)作为自己的线程上下文类加载器. 自从WebJ2EE应用服务器使用成熟的类加载器机制来实现诸如JNDI, 线程池, 组件热部署等功能以来, 这种在整个应用中不做任何线程类加载器设置的情况就很少了.

    为什么线程上下文类加载器存在于如此重要的位置呢? 这个概念在J2SE中的引入并不引人注目. 很多开发人员对这一概念迷惑的原因是Sun公司在这方面缺乏适当的指引和文档.

    事实上, 上下文类加载器提供了类加载机制的后门, 这一点也在J2SE中被引入了. 通常, JVM中的所有类加载器被组织成了有继承层次的结构, 每一个类加载器(除了引导JVM的原始类加载器)都有一个父加载器. 每当被请示加载类时, 类加载器都会首先请求其父类加载器, 只有当父类加载器不能加载时, 才会自己进行类加载.

   有时候这种类加载的顺序安排不能正常工作, 通常当必须动态加载应用程序开发人员提供的资源的时候. JNDI为例: 它的内容(J2SE1.3开始)就在rt.jar中的引导类中实现了, 但是这些JNDI核心类需要动态加载由独立厂商实现并部署在应用程序的classpath下的JNDI提供者. 这种情况就要求一个父classloader(本例, 就是引导类加载器)去加载对于它其中一个子classloader(本例, 系统类加载器)可见的类. 这时通常的类加载代理机制不能实现这个要求. 解决的办法(workaround)就是, JNDI核心类使用当前线程上下文的类加载器, 这样, 就基本的类加载代理机制的相反方向建立了一条有效的途径.

    另外, 上面一段可能让你想起一些其它的事情: XML解析Java API(JAXP). 是的, JAXP只是J2SE的扩展进, 它很自然地用当前类加载器来引导解析器的实现. 而当JAXP被加入到J2SE1.4的核心类库中时, 它的类加载也就改成了用当前线程类加载器, JNDI的情况完全类似(也使很多程序员很迷惑). 明白为什么我说来自Sun的指导很缺乏了吧?

   在以上的介绍之后, 我们来看关键问题: 这两种选择(当前类加载器和当前线程类加载器)都不是在所有环境下都适用. 有些人认为当前线程类加载器应该成为新的标准策略. 但是, 如果这样, 当多个线程通过共享数据进行交互的时, 将会呈现出一幅极其复杂的类加载的画面, 除非它们全部使用了同一个上下文的类加载器. 进一步说, 在某些遗留下来的解决方案中, 委派到当前类加载器的方法已经是标准. 比如对Class.forName(String)的直接调用(这也是我为什么推荐尽量避免对这个方法进行调用的原因). 即使你努力去只调用上下文相关的类加载器, 仍然会有一些代码会不由你控制. 这种不受控制的类加载委派机制是混入是很危险的.

    更严重的问题, 某些应用服务器把环境上下文及当前类加载器设置到不同的类加载器实例上, 而这些类加载器有相同的类路径但却没有委派机制中的父子关系. 想想这为什么十分可怕. 要知道类加载器定义并加载的类实例会带有一个JVM内部的ID. 如果当前类加载器加载一个类X的实例, 这个实例调用JNDI查找类Y的实例, 些时的上下文的类加载器也可以定义了加载类Y实例. 这个类Y的定义就与当前类加载器看到的类Y的定义不同. 如果进行强制类型转换, 则产生异常.

   这种混乱的情况还将在Java中存在一段时间. 对于那些需要动态加载资源的J2SEAPI, 我们来猜想它们的类加策略. 例如:

Ø         JNDI 使用线程上下文类加载器

Ø         Class.getResource() Class.forName()使用当前类加载器

Ø         JAXP(J2SE 1.4 及之后)使用线程上下文类加载器

Ø         java.util.ResourceBundle 使用调用者的当前类加载器

Ø         URL protocol handlers specified via java.protocol.handler.pkgs system property are looked up in the bootstrap and system classloaders only

Ø         Java 序列化API默认使用调用者当前的类加载器

这些类及资源的加载策略问题, 肯定是J2SE领域中文档最及说明最缺乏的部分了.

posted @ 2006-07-27 15:23 船夫 阅读(299) | 评论 (0)编辑 收藏

2006年7月25日

看星星的日子

今天晚上吃完晚饭,往回走的路上,不经意间抬头,看到了很多的星星,我跟同事都停下脚步,抬头分辨哪个是北极星,哪里是北斗七星,聊得非常兴起。
想起来自从到了成都之后,算上这次都只看过两次星星,第一次是大一军训的时候,在华阳看到的,第二次就是今天晚上了。成都市里面不要指望会看到星星,因为浓密的云把所有的星星都遮住了。
昨天听老唐说他看到夜空中有一条“白带”,他说好像是银河,他之前从来都没有看过银河,跟他比起来我真可以算是幸运多了。小时候,到了夏天,家里人和邻居都跑到院子里面纳凉,我就躺在母亲的怀里面,边享受母亲扇子带来的凉爽,边抬头看星星,东北的星空真的是非常清晰,那时候,母亲给我讲哪个是银河,哪个是牛郎织女,哪个是勺子星(北斗七星),还给我讲牛郎织女的故事,说一到了七月初七就会下雨,那是因为牛郎和织女相会的泪水,还说从来没说过谎的小孩子趴在黄瓜架下面可以听到他们的哭声,想想现在认识的那几颗星星都是那个时候记下来的,书本上学的好像都不知道被我丢到哪里去了。那时候看到的星空现在回想起来真的是非常漂亮,就像冰心在繁星中描述的一样。
已经很多年没在家里面过夏天了,过年的时候倒是有回去,但是天气太冷,实在没那种兴致去看星星。
长大了,反而觉得快乐只存在于回忆中了,这是成长的代价吗?

posted @ 2006-07-25 23:53 船夫 阅读(250) | 评论 (0)编辑 收藏