愚人码头

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

2005年11月4日

对hibernate的新认识

     摘要:   阅读全文

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

转:Javascript事件处理

     摘要:   阅读全文

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

转:提升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)编辑 收藏

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)编辑 收藏

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)编辑 收藏

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 船夫 阅读(514) | 评论 (0)编辑 收藏

走出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)编辑 收藏

看星星的日子

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

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

惰性已经在充分的滋长

已经封闭开发了近三个月了,由刚开始的热情高涨到现在的热情全无,我已经逐渐被自己的惰性所吞噬,已经快慢慢的丢失了真正的自我。

经过了这么一段时间,曾经对新技术非常感兴趣的我已经完全失去了这种热情。

知耻而后勇,我要克服自己的这种情绪,重新开始,用崭新的眼光去对待每一天

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

项目中遇到的一个Spring事务管理的问题

今天从CVS上checkout项目的时候,出现了一个问题,我以前写的模块功能本来是好的,但是checkout之后就出了问题,我想一定是配置文件更新出错了,努力寻找,发现没有问题.后来经同事说他更改了一个方法,而我的代码中有对那个方法的调用,终于知道了问题的所在.
程序中抛出的异常是
java.lang.IllegalStateException: No value for key [org.hibernate.impl.SessionFactoryImpl@7a3d45f0] bound to thread [Servlet.Engine.Transports : 0]
我发现是在我spring的sevice方法中调用了他的方法,他的方法又调用了另一个service方法,导致出现了这个问题.

这个问题只要把他的那个方法修改一下,或者从我的service方法中移出就可以解决.

我想spring中肯定应该有些配置能使两个service方法处于同一个事务中,但是我还没找到.希望知道的高手能提点一下,谢谢

posted @ 2006-07-24 18:40 船夫 阅读(644) | 评论 (0)编辑 收藏

在WSAD 5.1中使用log4j遇到的问题

使用log4j作为日志输出工具,设置根级别为warn,然后分别设置了hibernate和spring等的级别为debug,自己项目的级别也设置为debug,但是很奇怪的事情出现了:
我启动wasd的测试服务器,打出来的日志只有我本项目的debug级以上的信息,spring和hibernate的都打不出来;
我使用main函数运行我项目中的一个方法,所有的debug信息,spring和hibernate以及我本项目的debug信息一样都没少。
我的配置是绝对没有问题的,我实在想不出来怎么会出现这种问题,见鬼了,哪位高手遇到过这种情况,告知一下解决方法,谢谢

posted @ 2006-07-21 01:23 船夫 阅读(541) | 评论 (4)编辑 收藏

好久没有思考过什么了,整个大脑都仿佛已经失去了这个能力,每天忙着要么是做项目,要么就是忙着发呆,发呆自己能想些什么,做些什么,脑袋真的已经生锈了。
整个人再也没有高中时候的那种反应,看来这个大学读的是亏了,直到现在还改变不了这种坏习惯。其实我很清楚,我这样对自己说完全是找个借口,这样才能使自己感到没有那么的空虚,少些对自己的自责。不过我发现,很久以来,都没有那种自责的心理了,不会在为自己的懒散感到自责,真的是很奇怪,在高中的时候,中午或周末多玩儿了一会儿,或是做错了一道不该做错的题,都会感到非常的愧疚,为此而一直自责。现在长大了,每天充斥在脑子里面的都是,怎样才能赚钱,学什么才能让自己更加有发展,思考的越来越现实,反而发现自己的动力越来越不足,天哪,我是怎么了?
买房子,结婚?现实围绕在我的身边,把我压得好紧,女友说我脾气太好,没有男子气概?难道我对她发脾气才是好的吗?两地分居,为了生活出去奔波,希望能赚回来一栋房子,难道我愿意吗?她爸和她妈对我的印象也不是很好,我知道我这个人太笨了,不会说话,不会讨别人喜欢,啊~~~~~~~~~~~我实在是受不了了,我已经对自己失望透顶了,谁能告诉我这个时候应该能做什么?????
我要改变这一切,我要改变自己,我要提高自己的能力,我要改变他人对我的看法,我要做一个让所有人都满意的人,我他妈的是为了什么活得这么累啊,爱情,不是应该两个人之间事吗?干嘛有那么多外在因素要影响我呢?
25岁,这个世界让我变得衰老~~~

posted @ 2006-07-21 01:14 船夫 阅读(188) | 评论 (0)编辑 收藏

表的外键约束[ZT]

1。创建测试表
SQL> create table lesson(lesson_name varchar2(20), classroom varchar2(10));

表已创建。

SQL> create table teacher(name varchar2(20),lesson_name varchar2(20));

表已创建。

SQL> alter table lesson add constraint pk_lesson primary key(lesson_name);

表已更改。

SQL> alter table teacher add constraint fk_lessonname foreign key(lesson_name)
2 references lesson(lesson_name);

表已更改。


2。插入测试数据
SQL> insert into lesson values('english','class 1');

已创建 1 行。

SQL> insert into lesson values('music','class 2');

已创建 1 行。


3。测试有外键的字段是否可以为空
SQL> insert into teacher values('wang','hello');
insert into teacher values('wang','hello')
*
ERROR 位于第 1 行:
ORA-02291: 违反完整约束条件 (SYSTEM.FK_LESSONNAME) - 未找到父项关键字

SQL> insert into teacher values('wang','music');

已创建 1 行。

SQL> insert into teacher values('wang',null);

已创建 1 行。


结论:有外键约束的字段可以为空。如果不为空的话,则一定要满足外键的约束关系

posted @ 2006-03-16 15:44 船夫 阅读(315) | 评论 (0)编辑 收藏

outer和left outer join有什么区别??

outer和left outer join有什么区别??

使用关系代数合并数据
1 关系代数
合并数据集合的理论基础是关系代数,它是由E.F.Codd于1970年提出的。
在关系代数的形式化语言中:
        用表、或者数据集合表示关系或者实体。
        用行表示元组。
        用列表示属性。
关系代数包含以下8个关系运算符
        选取――返回满足指定条件的行。
        投影――从数据集合中返回指定的列。
        笛卡尔积――是关系的乘法,它将分别来自两个数据集合中的行以所有可能的方式进行组合。
        并――关系的加法和减法,它可以在行的方向上合并两个表中的数据,就像把一个表垒在另一个表之上一样。
        交――返回两个数据集合所共有的行。
        差――返回只属于一个数据集合的行。
        连接――在水平方向上合并两个表,其方法是:将两个表中在共同数据项上相互匹配的那些行合并起来。
        除――返回两个数据集之间的精确匹配。
此外,作为一种实现现代关系代数运算的方法,SQL还提供了:
        子查询――类似于连接,但更灵活;在外部查询中,方式可以使用表达式、列表或者数据集合的地方都可以使用子查询的结果。
本章将主要讲述多种类型的连接、简单的和相关的子查询、几种类型的并、关系除以及其他的内容。
2 使用连接
2.1 连接类型
在关系代数中,连接运算是由一个笛卡尔积运算和一个选取运算构成的。首先用笛卡尔积完成对两个数据集合的乘运算,然后对生成的结果集合进行选取运算,确保只把分别来自两个数据集合并且具有重叠部分的行合并在一起。连接的全部意义在于在水平方向上合并两个数据集合(通常是表),并产生一个新的结果集合,其方法是将一个数据源中的行于另一个数据源中和它匹配的行组合成一个新元组。
SQL提供了多种类型的连接方式,它们之间的区别在于:从相互交叠的不同数据集合中选择用于连接的行时所采用的方法不同。
连接类型        定义
内连接        只连接匹配的行
左外连接        包含左边表的全部行(不管右边的表中是否存在与它们匹配的行),以及右边表中全部匹配的行
右外连接        包含右边表的全部行(不管左边的表中是否存在与它们匹配的行),以及左边表中全部匹配的行
全外连接        包含左、右两个表的全部行,不管另外一边的表中是否存在与它们匹配的行。
(H)(theta)连接        使用等值以外的条件来匹配左、右两个表中的行
交叉连接        生成笛卡尔积-它不使用任何匹配或者选取条件,而是直接将一个数据源中的每个行与另一个数据源的每个行都一一匹配
在INFORMIX中连接表的查询
如果FROM子句指定了多于一个表引用,则查询会连接来自多个表的行。连接条件指定各列之间(每个表至少一列)进行连接的关系。因为正在比较连接条件中的列,所以它们必须具有一致的数据类型。
SELECT语句的FROM子句可以指定以下几种类型的连接
FROM子句关键字        相应的结果集
CROSS JOIN        笛卡尔乘积(所有可能的行对)
INNER JOIN        仅对满足连接条件的CROSS中的列
LEFT OUTER JOIN        一个表满足条件的行,和另一个表的所有行
RIGHT OUTER JOIN        与LEFT相同,但两个表的角色互换
FULL OUTER JOIN        LEFT OUTER 和 RIGHT OUTER中所有行的超集

2.2 内连接(Inner Join)
内连接是最常见的一种连接,它页被称为普通连接,而E.FCodd最早称之为自然连接。
下面是ANSI SQL-92标准
select *
from  t_institution i
inner join t_teller t
on i.inst_no = t.inst_no
where i.inst_no = "5801"
其中inner可以省略。
等价于早期的连接语法
select *
from t_institution i, t_teller t
where i.inst_no = t.inst_no
and i.inst_no = "5801"

2.3 外连接
2.3.1        左外连接(Left Outer Jion)
select *
from  t_institution i
left outer join t_teller t
on i.inst_no = t.inst_no
其中outer可以省略。
2.3.2        右外连接(Rigt Outer Jion)
select *
from  t_institution i
right outer join t_teller t
on i.inst_no = t.inst_no
2.3.3        全外连接(Full Outer)
全外连接返回参与连接的两个数据集合中的全部数据,无论它们是否具有与之相匹配的行。在功能上,它等价于对这两个数据集合分别进行左外连接和右外连接,然后再使用消去重复行的并操作将上述两个结果集合并为一个结果集。
在现实生活中,参照完整性约束可以减少对于全外连接的使用,一般情况下左外连接就足够了。在数据库中没有利用清晰、规范的约束来防范错误数据情况下,全外连接就变得非常有用了,你可以使用它来清理数据库中的数据。
select *
from  t_institution i
full outer join t_teller t
on i.inst_no = t.inst_no
2.3.4        外连接与条件配合使用
当在内连接查询中加入条件是,无论是将它加入到join子句,还是加入到where子句,其效果是完全一样的,但对于外连接情况就不同了。当把条件加入到join子句时,SQL Server、Informix会返回外连接表的全部行,然后使用指定的条件返回第二个表的行。如果将条件放到where子句中,SQL Server将会首先进行连接操作,然后使用where子句对连接后的行进行筛选。下面的两个查询展示了条件放置位子对执行结果的影响:
条件在join子句
select *
from  t_institution i
left outer join t_teller t
on i.inst_no = t.inst_no
and i.inst_no = “5801”
结果是:
inst_no    inst_name            inst_no    teller_no  teller_name
5801       天河区               5801       0001       tom
5801       天河区               5801       0002       david
5802       越秀区
5803       白云区
条件在where子句
select *
from  t_institution i
left outer join t_teller t
on i.inst_no = t.inst_no
where i.inst_no = “5801”
结果是:
inst_no    inst_name            inst_no    teller_no  teller_name
5801       天河区               5801       0001       tom
5801       天河区               5801       0002       david

2.4 自身连接
自身连接是指同一个表自己与自己进行连接。这种一元连接通常用于从自反关系(也称作递归关系)中抽取数据。例如人力资源数据库中雇员与老板的关系。
下面例子是在机构表中查找本机构和上级机构的信息。
select s.inst_no superior_inst, s.inst_name sup_inst_name, i.inst_no, i.inst_name
from t_institution i
join t_institution s
on i.superior_inst = s.inst_no

结果是:
superior_inst sup_inst_name        inst_no    inst_name
800           广州市               5801       天河区
800           广州市               5802       越秀区
800           广州市               5803       白云区

2.5 交叉(无限制) 连接
交叉连接用于对两个源表进行纯关系代数的乘运算。它不使用连接条件来限制结果集合,而是将分别来自两个数据源中的行以所有可能的方式进行组合。数据集合中一的每个行都要与数据集合二中的每一个行分别组成一个新的行。例如,如果第一个数据源中有5个行,而第二个数据源中有4个行,那么在它们之间进行交叉连接就会产生20个行。人们将这种类型的结果集称为笛卡尔乘积。
大多数交叉连接都是由于错误操作而造成的;但是它们却非常适合向数据库中填充例子数据,或者预先创建一些空行以便为程序执行期间所要填充的数据保留空间。
select *
from  t_institution i
cross join t_teller t
在交叉连接中没有on条件子句

3 APPENDIX
3.1 A 参考资料与资源
        《Microsoft SQL Server 2000 Bile》Paul Nielsen
        Paul Nielsen的Web站点
www.isnotnull.com
3.2 注文章所有SQL在IBM Informix Dynamic Server Version 9.40.TC2E1测试通过

posted @ 2006-03-06 13:02 船夫 阅读(1633) | 评论 (0)编辑 收藏

HTML中Target的四个保留字

_parent:在当前FRAMESET位置显示新href。  
_top:在当前整个窗口位置显示新href,比如本身FRAMESET位于另一个FRAMESET中。  
_self:强制在当前FRAME中显示新href。  
_blank:在新窗口中显示href

posted @ 2005-12-21 09:40 船夫 阅读(2018) | 评论 (0)编辑 收藏

java实现文件传输

     摘要:   1import java.awt.*;   2import java.awt.event.*;   3import javax.swing.*;   4import javax.swing.event.*;   5i...  阅读全文

posted @ 2005-12-20 11:22 船夫 阅读(3042) | 评论 (5)编辑 收藏

Document 和 Document.all 分别什么时候用

如果与a,form对象,image对象,applet对象相对应的html标记中设定了name性质,它的值将被用作document对象的属性名,用来引用相应的对象,其他的对象则不可以。

  另外,input等如果作为form的子元素,则直接用inputName或者document.inputName来引用此对象就是错误的,必须使用formName.inputName引用,否则就可以使用inputName来引用.

  另外应该注意到有很多平时用的元素都没有name.

  如果想引用一个有id的元素,只能用Id或者document.getElementById,document.all.id来引用
但是象这样的元素,所以象<a href="......" name="linkname" id="linkid">......</a>这样的
可以用

linkid.href;
linkname.href;
document.all.linkid.href;
document.all.linkname.href;
document.getElementById("linkid").href;
document.getElementsByName("linkname")[0].href来引用

  all是一个集合,包含所有html对像的集合,写一个程式,可以存取到所有的对像。像这样:

<script language="javascript">
var obj="";
for(i=0;i<document.all.length;i++)
obj+=document.all[i].tagName+";";
alert(obj);
</script>

  注意要把程式放到</html>之后哦。

作者:  来源:CSDN

posted @ 2005-12-20 10:24 船夫 阅读(664) | 评论 (0)编辑 收藏

气死老师的试卷答案


1.地理
地理教过,中国产煤最多的地方是辽宁省抚顺,产铁最多是辽宁省鞍山, 所以抚顺被称为中国的“煤都”,鞍山称为“铁都”。某次考试,试卷上:中国的煤都是(黑的),中国的铁都是(硬的)。考完还说:老师怎么出那么简单的题目?

2、 语文考试

一次语文考试的附加题,问普罗米修斯是什么文学作品里面的人物,一个同学填:哈里-波特。还有一次,问左忠毅公叫什么名字,一个同学写:左冷禅。

3、泰坦尼克号


一次政治时政题:我国的( )号考察船去北极考察。我的答案:泰坦尼克号。

4 . 解释“逝世”一词


语文考试。解释“逝世”一词。我答:“去死”(本来想写“死去”)老师大怒……

5 .一行白鹭上西天


小学,一同学背诗,前面3句背得那么费劲。最后一句:“一行白鹭上西天。”全班晕!

6、 高一语文摸底考试

高一语文摸底考试,“何当共剪西窗烛”,填下句。我答:“夫妻对坐到天明”。正确答案:“却话巴山夜雨时”。

7.诗句填空

一次语文考试,诗句填空是白居易的《题大林寺桃花》中的一句“( ),不知转入此中 来”,正解应是“常恨春归无觅处”,我前排一个同学愣是填了“常恨村姑无觅处”。

8、 生物考试

生物考试一填图题,问一个细胞图是什么生物的,正解是“母果蝇”,我班一人答:“女果蝇”。生物组老师开会研究N久,决定给0分。

9、填诗词下句

高中时候也是填诗词下句。上句是:“洛阳亲友如相问”;我一个同学填:“就说我在岳阳楼

10、 一动也不动

高中一次语文考试,也是填下一句:“蚍蜉撼大树,( )”。我有一同学填:一动也不动。很符合事实。

11、鸡型!

高中时,生物考试,问:鸡的消化类型是什么型?我不会,答:鸡型!结果老师在全班点名批评!

12、对子

上句是:“西塞山前白鹭飞”,同学憋半天憋不出,于是瞎写:“东村河边黑龟爬”!

13、古诗下句

高中语文考试,写古诗下句。上句是:“待到山花烂漫时”,我们班一人居然填了:我便奋力把花采。

14、初中时考语文

初中时考语文,题目问老舍先生的著名作品的名字。一同学想不起,我告诉他:茶馆。结果那位听成:茶壶盖。被老师痛骂!

15、拿出芭蕉

以前同学读课文,其中有一句:拿出芭蕉扇扇扇。本来停顿应该是拿出芭蕉扇,扇扇。那同学直接读成:拿出芭蕉,扇扇扇!

16、“卖”娥冤

初中的时候一次上课把窦娥冤的窦读成“卖”娥冤。全班爆笑我还不知道为什么

17、 数学考试

还有一次是数学考试,最后一道大题是两个解法判断哪个正确。我想了半天没想出来,顺便提了几个词:公说公有理!婆说婆有理!看看都没理!想想全有理……结果数学老师把我的解法整个年级她教的四个班都读一遍以后,我就闻名了!

18、《咏梅》

高中语文课正上毛泽东的《咏梅》,早自习要求背诵。老师点人回答:“——她在丛中笑下面,XX回答!” XX正在吃东西,半天说不出话来,憋出一句“笑也不出声!”全班爆笑!


19.天才学生

考试写:“天生我才必有用”的下句。有位天才学生答:“老鼠儿子会打洞”。我们整办公室的语文老师集体毫无形象的狂笑!


20.英语考试

英语考试: HOW ARE YOU?这么翻译?答案——怎么是你? HOW OLD ARE YOU?怎么翻译?答案——怎么老是你?

21.中国学生在美国

试题:如果一位中国学生在美国加州目睹了一起交通事故,警察来了以后问你知不知道事情的经过,应该怎么对他说?一个人回答:one car come one car go,two car peng peng,one car die。

22.名字太短

A君在做语文试卷时,被一道填空题“《这里的黎明静悄悄》的作者是谁”难住。苦思良久,A君毅然在空栏上写着“霍利菲尔德”。
一旁的监考老师笑问:“怎么不写泰森呢?”
A君道:“他的名字太短了,不像!”

23.一副跳棋

B君在作文中要描述一个人的外貌,遇一字不会,遂悄声问同桌:“一副眼镜的‘副’字怎么写?”
同桌告诉他:“就是一副跳棋的‘副’嘛。”
后老师批阅B君的作文,见上面写道:“他高高的鼻梁上架着一副跳棋。”(

24.一代更比一代浪


题目:长江后浪推前浪__________________

有位学生答:一代更比一代浪

posted @ 2005-12-15 11:18 船夫 阅读(379) | 评论 (0)编辑 收藏

Velocity 简介

1.Velocity 的使用

Velocity是一个开放源吗的模版引擎,由apache.org小组负责开发,现在最新的版本是Velocity1.3.1,http://jakarta.apache.org/velocity/index.html 可以了解Velocity的最新信息。
Velocity允许我们在模版中设定变量,然后在运行时,动态的将数据插入到模版中,替换这些变量。
例如:
<html>
<body>HELLO $CUSTOMERNAME</body>
</html>
我们可以在运行时得到客户的名字,然后把它插入到这个模版中替换变量$CUSTOMERNAME,整个替换过程是由Velocity进行控制的,而且java的调用代码也非常简单,如我们可以在java代码中这样调用
/***********************************************************/
//这个文件中设定了Velocity使用的log4j的配置和Velocity的模版文件所在的目录
Velocity.init("D:\\Template\\resource\\jt.properties");
//模版文件名,模版文件所在的路径在上一条语句中已经设置了
Template template = Velocity.getTemplate("hello.vm", "gb2312");
//实例化一个Context
VelocityContext context = new VelocityContext();
//把模版变量的值设置到context中
context.put("CUSTOMERNAME", "My First Template Engine ---- Velocity.");
//开始模版的替换
template.merge(context, writer);
//写到文件中
PrintWriter filewriter = new PrintWriter(new FileOutputStream(outpath),true);
filewriter.println(writer.toString());
filewriter.close();
/***********************************************************/

这就是整个java的代码,非常的简单。如果我们有多个模版变量,我们仅需要把这些模版变量的值设置到context中。
下面我们简单的分析一下,Velocity引擎读取模板文件时,它直接输出文件中所有的文本,但以$字符开头的除外,$符号标识着一个模版变量位置,
context.put("CUSTOMERNAME", "My First Template Engine ---- Velocity.");
当 Velocity 模板引擎解析并输出模板的结果时,模板中所有出现$CUSTOMERNAME的地方都将插入客户的名字,即被加入到VelocityContext的对象的toString()方法返回值将替代Velocity变量(模板中以$开头的变量)。
模板引擎中最强大、使用最频繁的功能之一是它通过内建的映像(Reflection)引擎查找对象信息的能力。这个映像引擎允许用一种方便的Java“.”类似的操作符,提取任意加入到VelocityContext的对象的任何公用方法的值,或对象的任意数据成员。
映像引擎还带来了另外一个改进:快速引用JavaBean的属性。使用JavaBean属性的时候,我们可以忽略get方法和括号。请看下面这个模板的例子。
<html>
<body>
Name:$Customer.Name()
Address:$Customer.Address()
Age:$Customer.Age()
</body>
</html>

java的代码:
/***********************************************************/
//设置客户信息
Customer mycustomer = new Customer();
mycustomer.setName("Velocity");
mycustomer.setAddress("jakarta.apache.org/velocity/index.html");
mycustomer.setAge(2);
//这个文件中设定了 Velocity 使用的 Log4j 的配置和Velocity的模版文件所在的目录Velocity.init("D:\\Template\\resource\\jt.properties");
//模版文件名,模版文件所在的路径在上一条语句中已经设置了
Template template = Velocity.getTemplate("hello.vm", "gb2312");
//实例化一个Context
VelocityContext context = new VelocityContext();
//把模版变量的值设置到context中
context.put("Customer", mycustomer);
//开始模版的替换
template.merge(context, writer);
//写到文件中
PrintWriter filewriter = new PrintWriter(new FileOutputStream(outpath),true);
filewriter.println(writer.toString());
filewriter.close();
输出结果:
<html>
<body>
Name:Velocity
Address:jakarta.apache.org/velocity/index.html
Age:2
</body>
</html>
除了替换变量之外,象Velocity高级引擎还能做其他许多事情,它们有用来比较和迭代的内建指令,通过这些指令我们可以完成程序语言中的条件判断语句和循环语句等。
例如,我们想要输出年龄等于2的所有客户的信息,我们可以这样定义我们的模版
模版:
<html>
<body>
<table>
<tr>
<td>名称</td>
<td>地址</td>
<td>年龄</td>
</tr>
#foreach ($Customer in $allCustomer)
#if($Customer.Age()=="2")
<tr>
<td>$Customer.Name()</td>
<td>$Customer.Address()</td>
<td>$Customer.Age()</td>
</tr>
#end
#end
</table>
</body>
</html>

java的代码:
/******************************************************/
//设置客户信息
ArrayList allMyCustomer = new ArrayList();
//客户1
Customer mycustomer1 = new Customer();
mycustomer1.setName("Velocity");
mycustomer1.setAddress("jakarta.apache.org/velocity/index.html");
mycustomer1.setAge(2);
//客户2
Customer mycustomer2 = new Customer();
mycustomer2.setName("Tomcat");
mycustomer2.setAddress("jakarta.apache.org/tomcat/index.html");
mycustomer2.setAge(3);
//客户3
Customer mycustomer3 = new Customer();
mycustomer3.setName("Log4J");
mycustomer3.setAddress("jakarta.apache.org/log4j/docs/index.html");
mycustomer3.setAge(2);
//添加到allMyCustomer(ArrayList)中.
allMyCustomer.add(mycustomer1);
allMyCustomer.add(mycustomer2);
allMyCustomer.add(mycustomer3);
//这个文件中设定了Velocity使用的log4j的配置和Velocity的模版文件所在的目
Velocity.init("D:\\Template\\resource\\jt.properties");
//模版文件名,模版文件所在的路径在上一条语句中已经设置了
Template template =Velocity.getTemplate("hello.vm", "gb2312");
//实例化一个Context
VelocityContext context = new VelocityContext();
/** 注意这里我们仅仅需要给一个模版变量负值 */
context.put("allCustomer", allMyCustomer);
//开始模版的替换
template.merge(context, writer);
//写到文件中
PrintWriter filewriter = new PrintWriter(new FileOutputStream(outpath),true);
filewriter.println(writer.toString());
filewriter.close();
/******************************************************/
结果:
<html>
<body>
<table>
<tr>
<td>名称</td>
<td>地址</td>
<td>年龄</td>
</tr>
<tr>
<td>Velocity</td>
<td>jakarta.apache.org/velocity/index.html</td>
<td>2</td>
</tr>
<tr>
<td>Log4J</td>
<td>jakarta.apache.org/log4j/docs/index.html</td>
<td>2</td>
</tr>
</table>
</body>
</html>

#if 语句完成逻辑判断,这个我想不用多说了。
allCustomer对象包含零个或者多个Customer对象。由于ArrayList (List, HashMap, HashTable, Iterator, Vector等)属于Java Collections Framework的一部分,我们可以用#foreach指令迭代其内容。我们不用担心如何定型对象的类型——映像引擎会为我们完成这个任务。#foreach指令的一般格式是“#foreach in ”。#foreach指令迭代list,把list中的每个元素放入item参数,然后解析#foreach块内的内容。对于list内的每个元素,#foreach块的内容都会重复解析一次。从效果上看,它相当于告诉模板引擎说:“把list中的每一个元素依次放入item变量,每次放入一个元素,输出一次#foreach块的内容”。

2.MVC设计模型

使用模板引擎最大的好处在于,它分离了代码(或程序逻辑)和表现(输出)。由于这种分离,你可以修改程序逻辑而不必担心邮件消息本身;类似地,你(或公关部门的职员)可以在不重新编译程序的情况下,重新编写客户列表。实际上,我们分离了系统的数据模式(Data Model,即提供数据的类)、控制器(Controller,即客户列表程序)以及视图(View,即模板)。这种三层体系称为Model-View-Controller模型(MVC)。
如果遵从MVC模型,代码分成三个截然不同的层,简化了软件开发过程中所有相关人员的工作。
结合模板引擎使用的数据模式可以是任何Java对象,最好是使用Java Collection Framework的对象。控制器只要了解模板的环境(如VelocityContext),一般这种环境都很容易使用。
一些关系数据库的“对象-关系”映射工具能够和模板引擎很好地协同,简化JDBC操作;对于EJB,情形也类似。 模板引擎与MVC中视图这一部分的关系更为密切。模板语言的功能很丰富、强大,足以处理所有必需的视图功能,同时它往往很简单,不熟悉编程的人也可以使用它。模板语言不仅使得设计者从过于复杂的编程环境中解脱出来,而且它保护了系统,避免了有意或无意带来危险的代码。例如,模板的编写者不可能编写出导致无限循环的代码,或侵占大量内存的代码。不要轻估这些安全机制的价值;大多数模板编写者不懂得编程,从长远来看,避免他们接触复杂的编程环境相当于节省了你自己的时间。 许多模板引擎的用户相信,在采用模板引擎的方案中,控制器部分和视图部分的明确分离,再加上模板引擎固有的安全机制,使得模板引擎足以成为其他内容发布系统(比如JSP)的替代方案。因此,Java模板引擎最常见的用途是替代JSP也就不足为奇了。

3.HTML处理

由于人们总是看重模板引擎用来替换JSP的作用,有时他们会忘记模板还有更广泛的用途。到目前为止,模板引擎最常见的用途是处理HTML Web内容。但我还用模板引擎生成过SQL、email、XML甚至Java源代码。

posted @ 2005-12-01 08:36 船夫 阅读(513) | 评论 (0)编辑 收藏

一篇关于web.xml配置的详细说明

1 定义头和根元素

部署描述符文件就像所有XML文件一样,必须以一个XML头开始。这个头声明可以使用的XML版本并给出文件的字符编码。
DOCYTPE声明必须立即出现在此头之后。这个声明告诉服务器适用的servlet规范的版本(如2.2或2.3)并指定管理此文件其余部分内容的语法的DTD(Document Type Definition,文档类型定义)。
所有部署描述符文件的顶层(根)元素为web-app。请注意,XML元素不像HTML,他们是大小写敏感的。因此,web-App和WEB-APP都是不合法的,web-app必须用小写。

2 部署描述符文件内的元素次序

XML 元素不仅是大小写敏感的,而且它们还对出现在其他元素中的次序敏感。例如,XML头必须是文件中的第一项,DOCTYPE声明必须是第二项,而web- app元素必须是第三项。在web-app元素内,元素的次序也很重要。服务器不一定强制要求这种次序,但它们允许(实际上有些服务器就是这样做的)完全拒绝执行含有次序不正确的元素的Web应用。这表示使用非标准元素次序的web.xml文件是不可移植的。
下面的列表给出了所有可直接出现在web-app元素内的合法元素所必需的次序。例如,此列表说明servlet元素必须出现在所有servlet-mapping元素之前。请注意,所有这些元素都是可选的。因此,可以省略掉某一元素,但不能把它放于不正确的位置。
l icon icon元素指出IDE和GUI工具用来表示Web应用的一个和两个图像文件的位置。
l display-name display-name元素提供GUI工具可能会用来标记这个特定的Web应用的一个名称。
l description description元素给出与此有关的说明性文本。
l context-param context-param元素声明应用范围内的初始化参数。
l filter 过滤器元素将一个名字与一个实现javax.servlet.Filter接口的类相关联。
l filter-mapping 一旦命名了一个过滤器,就要利用filter-mapping元素把它与一个或多个servlet或JSP页面相关联。
l listener servlet API的版本2.3增加了对事件监听程序的支持,事件监听程序在建立、修改和删除会话或servlet环境时得到通知。Listener元素指出事件监听程序类。
l servlet 在向servlet或JSP页面制定初始化参数或定制URL时,必须首先命名servlet或JSP页面。Servlet元素就是用来完成此项任务的。
l servlet-mapping 服务器一般为servlet提供一个缺省的URL:
http://host/webAppPrefix/servlet/ServletName。但是,常常会更改这个URL,以便servlet可以访问初始化参数或更容易地处理相对URL。在更改缺省URL时,使用servlet-mapping元素。
l session-config 如果某个会话在一定时间内未被访问,服务器可以抛弃它以节省内存。可通过使用HttpSession的setMaxInactiveInterval方法明确设置单个会话对象的超时值,或者可利用session-config元素制定缺省超时值。
l mime-mapping 如果Web应用具有想到特殊的文件,希望能保证给他们分配特定的MIME类型,则mime-mapping元素提供这种保证。
l welcom-file-list welcome-file-list元素指示服务器在收到引用一个目录名而不是文件名的URL时,使用哪个文件。
l error-page error-page元素使得在返回特定HTTP状态代码时,或者特定类型的异常被抛出时,能够制定将要显示的页面。
l taglib taglib元素对标记库描述符文件(Tag Libraryu Descriptor file)指定别名。此功能使你能够更改TLD文件的位置,而不用编辑使用这些文件的JSP页面。
l resource-env-ref resource-env-ref元素声明与资源相关的一个管理对象。
l resource-ref resource-ref元素声明一个资源工厂使用的外部资源。
l security-constraint security-constraint元素制定应该保护的URL。它与login-config元素联合使用
l login-config 用login-config元素来指定服务器应该怎样给试图访问受保护页面的用户授权。它与sercurity-constraint元素联合使用。
l security-role security-role元素给出安全角色的一个列表,这些角色将出现在servlet元素内的security-role-ref元素的role-name子元素中。分别地声明角色可使高级IDE处理安全信息更为容易。
l env-entry env-entry元素声明Web应用的环境项。
l ejb-ref ejb-ref元素声明一个EJB的主目录的引用。
l ejb-local-ref ejb-local-ref元素声明一个EJB的本地主目录的应用。

3 分配名称和定制的UL

在web.xml中完成的一个最常见的任务是对servlet或JSP页面给出名称和定制的URL。用servlet元素分配名称,使用servlet-mapping元素将定制的URL与刚分配的名称相关联。
3.1 分配名称
为了提供初始化参数,对servlet或JSP页面定义一个定制URL或分配一个安全角色,必须首先给servlet或JSP页面一个名称。可通过 servlet元素分配一个名称。最常见的格式包括servlet-name和servlet-class子元素(在web-app元素内),如下所示:
<servlet>
<servlet-name>Test</servlet-name>
<servlet-class>moreservlets.TestServlet</servlet-class>
</servlet>
这表示位于WEB-INF/classes/moreservlets/TestServlet的servlet已经得到了注册名Test。给 servlet一个名称具有两个主要的含义。首先,初始化参数、定制的URL模式以及其他定制通过此注册名而不是类名引用此servlet。其次,可在 URL而不是类名中使用此名称。因此,利用刚才给出的定义,URL
http://host/webAppPrefix/servlet/Test 可用于 http://host/webAppPrefix/servlet/moreservlets.TestServlet 的场所。
请记住:XML元素不仅是大小写敏感的,而且定义它们的次序也很重要。例如,web-app元素内所有servlet元素必须位于所有servlet- mapping元素(下一小节介绍)之前,而且还要位于5.6节和5.11节讨论的与过滤器或文档相关的元素(如果有的话)之前。类似地,servlet 的servlet-name子元素也必须出现在servlet-class之前。5.2节"部署描述符文件内的元素次序"将详细介绍这种必需的次序。
例如,程序清单5-1给出了一个名为TestServlet的简单servlet,它驻留在moreservlets程序包中。因为此servlet是扎根在一个名为deployDemo的目录中的Web应用的组成部分,所以TestServlet.class放在deployDemo/WEB- INF/classes/moreservlets中。程序清单5-2给出将放置在deployDemo/WEB-INF/内的web.xml文件的一部分。此web.xml文件使用servlet-name和servlet-class元素将名称Test与TestServlet.class相关联。图 5-1和图5-2分别显示利用缺省URL和注册名调用TestServlet时的结果。

程序清单5-1 TestServlet.java
package moreservlets;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

/** Simple servlet used to illustrate servlet naming
* and custom URLs.
* <P>
* Taken from More Servlets and JavaServer Pages
* from Prentice Hall and Sun Microsystems Press,
*
http://www.moreservlets.com/.
* © 2002 Marty Hall; may be freely used or adapted.
*/

public class TestServlet extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
String uri = request.getRequestURI();
out.println(ServletUtilities.headWithTitle("Test Servlet") +
"<BODY BGCOLOR=\"#FDF5E6\">\n" +
"<H2>URI: " + uri + "</H2>\n" +
"</BODY></HTML>");
}
}


程序清单5-2 web.xml(说明servlet名称的摘录)
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"
http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>
<!-- … -->
<servlet>
<servlet-name>Test</servlet-name>
<servlet-class>moreservlets.TestServlet</servlet-class>
</servlet>
<!-- … -->
</web-app>

3.2 定义定制的URL
大多数服务器具有一个缺省的serlvet URL:
http://host/webAppPrefix/servlet/packageName.ServletName。虽然在开发中使用这个URL很方便,但是我们常常会希望另一个URL用于部署。例如,可能会需要一个出现在Web应用顶层的URL(如,http: //host/webAppPrefix/Anyname),并且在此URL中没有servlet项。位于顶层的URL简化了相对URL的使用。此外,对许多开发人员来说,顶层URL看上去比更长更麻烦的缺省URL更简短。
事实上,有时需要使用定制的URL。比如,你可能想关闭缺省URL映射,以便更好地强制实施安全限制或防止用户意外地访问无初始化参数的servlet。如果你禁止了缺省的URL,那么你怎样访问servlet呢?这时只有使用定制的URL了。
为了分配一个定制的URL,可使用servlet-mapping元素及其servlet-name和url-pattern子元素。Servlet- name元素提供了一个任意名称,可利用此名称引用相应的servlet;url-pattern描述了相对于Web应用的根目录的URL。url- pattern元素的值必须以斜杠(/)起始。
下面给出一个简单的web.xml摘录,它允许使用URL
http://host/webAppPrefix/UrlTest而不是http://host/webAppPrefix/servlet/Test
http: //host/webAppPrefix/servlet/moreservlets.TestServlet。请注意,仍然需要XML头、 DOCTYPE声明以及web-app封闭元素。此外,可回忆一下,XML元素出现地次序不是随意的。特别是,需要把所有servlet元素放在所有 servlet-mapping元素之前。
<servlet>
<servlet-name>Test</servlet-name>
<servlet-class>moreservlets.TestServlet</servlet-class>
</servlet>
<!-- ... -->
<servlet-mapping>
<servlet-name>Test</servlet-name>
<url-pattern>/UrlTest</url-pattern>
</servlet-mapping>
URL模式还可以包含通配符。例如,下面的小程序指示服务器发送所有以Web应用的URL前缀开始,以..asp结束的请求到名为BashMS的servlet。
<servlet>
<servlet-name>BashMS</servlet-name>
<servlet-class>msUtils.ASPTranslator</servlet-class>
</servlet>
<!-- ... -->
<servlet-mapping>
<servlet-name>BashMS</servlet-name>
<url-pattern>/*.asp</url-pattern>
</servlet-mapping>
3.3 命名JSP页面
因为JSP页面要转换成sevlet,自然希望就像命名servlet一样命名JSP页面。毕竟,JSP页面可能会从初始化参数、安全设置或定制的URL中受益,正如普通的serlvet那样。虽然JSP页面的后台实际上是servlet这句话是正确的,但存在一个关键的猜疑:即,你不知道JSP页面的实际类名(因为系统自己挑选这个名字)。因此,为了命名JSP页面,可将jsp-file元素替换为servlet-calss元素,如下所示:
<servlet>
<servlet-name>Test</servlet-name>
<jsp-file>/TestPage.jsp</jsp-file>
</servlet>
命名JSP页面的原因与命名servlet的原因完全相同:即为了提供一个与定制设置(如,初始化参数和安全设置)一起使用的名称,并且,以便能更改激活 JSP页面的URL(比方说,以便多个URL通过相同页面得以处理,或者从URL中去掉.jsp扩展名)。但是,在设置初始化参数时,应该注意,JSP页面是利用jspInit方法,而不是init方法读取初始化参数的。
例如,程序清单5-3给出一个名为TestPage.jsp的简单JSP页面,它的工作只是打印出用来激活它的URL的本地部分。TestPage.jsp放置在deployDemo应用的顶层。程序清单5-4给出了用来分配一个注册名PageName,然后将此注册名与
http://host/webAppPrefix/UrlTest2/anything 形式的URL相关联的web.xml文件(即,deployDemo/WEB-INF/web.xml)的一部分。

程序清单5-3 TestPage.jsp
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<HEAD>
<TITLE>
JSP Test Page
</TITLE>
</HEAD>
<BODY BGCOLOR="#FDF5E6">
<H2>URI: <%= request.getRequestURI() %></H2>
</BODY>
</HTML>


程序清单5-4 web.xml(说明JSP页命名的摘录)
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"
http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>
<!-- ... -->
<servlet>
<servlet-name>PageName</servlet-name>
<jsp-file>/TestPage.jsp</jsp-file>
</servlet>
<!-- ... -->
<servlet-mapping>
<servlet-name> PageName </servlet-name>
<url-pattern>/UrlTest2/*</url-pattern>
</servlet-mapping>
<!-- ... -->
</web-app>


4 禁止激活器servlet

对servlet或JSP页面建立定制URL的一个原因是,这样做可以注册从 init(servlet)或jspInit(JSP页面)方法中读取得初始化参数。但是,初始化参数只在是利用定制URL模式或注册名访问 servlet或JSP页面时可以使用,用缺省URL
http://host/webAppPrefix/servlet/ServletName 访问时不能使用。因此,你可能会希望关闭缺省URL,这样就不会有人意外地调用初始化servlet了。这个过程有时称为禁止激活器servlet,因为多数服务器具有一个用缺省的servlet URL注册的标准servlet,并激活缺省的URL应用的实际servlet。
有两种禁止此缺省URL的主要方法:
l 在每个Web应用中重新映射/servlet/模式。
l 全局关闭激活器servlet。
重要的是应该注意到,虽然重新映射每个Web应用中的/servlet/模式比彻底禁止激活servlet所做的工作更多,但重新映射可以用一种完全可移植的方式来完成。相反,全局禁止激活器servlet完全是针对具体机器的,事实上有的服务器(如ServletExec)没有这样的选择。下面的讨论对每个Web应用重新映射/servlet/ URL模式的策略。后面提供在Tomcat中全局禁止激活器servlet的详细内容。
4.1 重新映射/servlet/URL模式
在一个特定的Web应用中禁止以
http://host/webAppPrefix/servlet/ 开始的URL的处理非常简单。所需做的事情就是建立一个错误消息servlet,并使用前一节讨论的url-pattern元素将所有匹配请求转向该 servlet。只要简单地使用:
<url-pattern>/servlet/*</url-pattern>
作为servlet-mapping元素中的模式即可。
例如,程序清单5-5给出了将SorryServlet servlet(程序清单5-6)与所有以
http://host/webAppPrefix/servlet/ 开头的URL相关联的部署描述符文件的一部分。

程序清单5-5 web.xml(说明JSP页命名的摘录)
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"
http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>
<!-- ... -->
<servlet>
<servlet-name>Sorry</servlet-name>
<servlet-class>moreservlets.SorryServlet</servlet-class>
</servlet>
<!-- ... -->
<servlet-mapping>
<servlet-name> Sorry </servlet-name>
<url-pattern>/servlet/*</url-pattern>
</servlet-mapping>
<!-- ... -->
</web-app>


程序清单5-6 SorryServlet.java
package moreservlets;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

/** Simple servlet used to give error messages to
* users who try to access default servlet URLs
* (i.e.,
http://host/webAppPrefix/servlet/ServletName)
* in Web applications that have disabled this
* behavior.
* <P>
* Taken from More Servlets and JavaServer Pages
* from Prentice Hall and Sun Microsystems Press,
*
http://www.moreservlets.com/.
* © 2002 Marty Hall; may be freely used or adapted.
*/

public class SorryServlet extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
String title = "Invoker Servlet Disabled.";
out.println(ServletUtilities.headWithTitle(title) +
"<BODY BGCOLOR=\"#FDF5E6\">\n" +
"<H2>" + title + "</H2>\n" +
"Sorry, access to servlets by means of\n" +
"URLs that begin with\n" +
"
http://host/webAppPrefix/servlet/\n" +
"has been disabled.\n" +
"</BODY></HTML>");
}

public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}


4.2 全局禁止激活器:Tomcat
Tomcat 4中用来关闭缺省URL的方法与Tomcat 3中所用的很不相同。下面介绍这两种方法:
1.禁止激活器: Tomcat 4
Tomcat 4用与前面相同的方法关闭激活器servlet,即利用web.xml中的url-mapping元素进行关闭。不同之处在于Tomcat使用了放在 install_dir/conf中的一个服务器专用的全局web.xml文件,而前面使用的是存放在每个Web应用的WEB-INF目录中的标准 web.xml文件。
因此,为了在Tomcat 4中关闭激活器servlet,只需在install_dir/conf/web.xml中简单地注释出/servlet/* URL映射项即可,如下所示:
<!--
<servlet-mapping>
<servlet-name>invoker</servlet-name>
<url-pattern>/servlet/*</url-pattern>
</servlet-mapping>
-->
再次提醒,应该注意这个项是位于存放在install_dir/conf的Tomcat专用的web.xml文件中的,此文件不是存放在每个Web应用的WEB-INF目录中的标准web.xml。
2.禁止激活器:Tomcat3
在Apache Tomcat的版本3中,通过在install_dir/conf/server.xml中注释出InvokerInterceptor项全局禁止缺省 servlet URL。例如,下面是禁止使用缺省servlet URL的server.xml文件的一部分。
<!--
<RequsetInterceptor
className="org.apache.tomcat.request.InvokerInterceptor"
debug="0" prefix="/servlet/" />
-->

5 初始化和预装载servlet与JSP页面

这里讨论控制servlet和JSP页面的启动行为的方法。特别是,说明了怎样分配初始化参数以及怎样更改服务器生存期中装载servlet和JSP页面的时刻。
5.1 分配servlet初始化参数
利用init-param元素向servlet提供初始化参数,init-param元素具有param-name和param-value子元素。例如,在下面的例子中,如果initServlet servlet是利用它的注册名(InitTest)访问的,它将能够从其方法中调用getServletConfig(). getInitParameter("param1")获得"Value 1",调用getServletConfig().getInitParameter("param2")获得"2"。
<servlet>
<servlet-name>InitTest</servlet-name>
<servlet-class>moreservlets.InitServlet</servlet-class>
<init-param>
<param-name>param1</param-name>
<param-value>value1</param-value>
</init-param>
<init-param>
<param-name>param2</param-name>
<param-value>2</param-value>
</init-param>
</servlet>
在涉及初始化参数时,有几点需要注意:
l 返回值。GetInitParameter的返回值总是一个String。因此,在前一个例子中,可对param2使用Integer.parseInt获得一个int。
l JSP中的初始化。JSP页面使用jspInit而不是init。JSP页面还需要使用jsp-file元素代替servlet-class。
l 缺省URL。初始化参数只在通过它们的注册名或与它们注册名相关的定制URL模式访问Servlet时可以使用。因此,在这个例子中,param1和 param2初始化参数将能够在使用URL
http://host/webAppPrefix/servlet/InitTest 时可用,但在使用URL http://host/webAppPrefix/servlet/myPackage.InitServlet 时不能使用。
例如,程序清单5-7给出一个名为InitServlet的简单servlet,它使用init方法设置firstName和emailAddress字段。程序清单5-8给出分配名称InitTest给servlet的web.xml文件。
程序清单5-7 InitServlet.java
package moreservlets;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

/** Simple servlet used to illustrate servlet
* initialization parameters.
* <P>
* Taken from More Servlets and JavaServer Pages
* from Prentice Hall and Sun Microsystems Press,
*
http://www.moreservlets.com/.
* © 2002 Marty Hall; may be freely used or adapted.
*/

public class InitServlet extends HttpServlet {
private String firstName, emailAddress;

public void init() {
ServletConfig config = getServletConfig();
firstName = config.getInitParameter("firstName");
emailAddress = config.getInitParameter("emailAddress");
}

public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
String uri = request.getRequestURI();
out.println(ServletUtilities.headWithTitle("Init Servlet") +
"<BODY BGCOLOR=\"#FDF5E6\">\n" +
"<H2>Init Parameters:</H2>\n" +
"<UL>\n" +
"<LI>First name: " + firstName + "\n" +
"<LI>Email address: " + emailAddress + "\n" +
"</UL>\n" +
"</BODY></HTML>");
}
}


程序清单5-8 web.xml(说明初始化参数的摘录)
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"
http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>
<!-- ... -->
<servlet>
<servlet-name>InitTest</servlet-name>
<servlet-class>moreservlets.InitServlet</servlet-class>
<init-param>
<param-name>firstName</param-name>
<param-value>Larry</param-value>
</init-param>
<init-param>
<param-name>emailAddress</param-name>
<param-value>
Ellison@Microsoft.com</param-value>
</init-param>
</servlet>
<!-- ... -->
</web-app>

5.2 分配JSP初始化参数
给JSP页面提供初始化参数在三个方面不同于给servlet提供初始化参数。
1)使用jsp-file而不是servlet-class。因此,WEB-INF/web.xml文件的servlet元素如下所示:
<servlet>
<servlet-name>PageName</servlet-name>
<jsp-file>/RealPage.jsp</jsp-file>
<init-param>
<param-name>...</param-name>
<param-value>...</param-value>
</init-param>
...
</servlet>
2) 几乎总是分配一个明确的URL模式。对servlet,一般相应地使用以
http://host/webAppPrefix/servlet/ 开始的缺省URL。只需记住,使用注册名而不是原名称即可。这对于JSP页面在技术上也是合法的。例如,在上面给出的例子中,可用URL http://host/webAppPrefix/servlet/PageName 访问RealPage.jsp的对初始化参数具有访问权的版本。但在用于JSP页面时,许多用户似乎不喜欢应用常规的servlet的URL。此外,如果 JSP页面位于服务器为其提供了目录清单的目录中(如,一个既没有index.html也没有index.jsp文件的目录),则用户可能会连接到此 JSP页面,单击它,从而意外地激活未初始化的页面。因此,好的办法是使用url-pattern(5.3节)将JSP页面的原URL与注册的 servlet名相关联。这样,客户机可使用JSP页面的普通名称,但仍然激活定制的版本。例如,给定来自项目1的servlet定义,可使用下面的 servlet-mapping定义:
<servlet-mapping>
<servlet-name>PageName</servlet-name>
<url-pattern>/RealPage.jsp</url-pattern>
</servlet-mapping>
3)JSP页使用jspInit而不是init。自动从JSP页面建立的servlet或许已经使用了inti方法。因此,使用JSP声明提供一个init方法是不合法的,必须制定jspInit方法。
为了说明初始化JSP页面的过程,程序清单5-9给出了一个名为InitPage.jsp的JSP页面,它包含一个jspInit方法且放置于 deployDemo Web应用层次结构的顶层。一般,
http://host/deployDemo/InitPage.jsp 形式的URL将激活此页面的不具有初始化参数访问权的版本,从而将对firstName和emailAddress变量显示null。但是, web.xml文件(程序清单5-10)分配了一个注册名,然后将该注册名与URL模式/InitPage.jsp相关联。

程序清单5-9 InitPage.jsp
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<HEAD><TITLE>JSP Init Test</TITLE></HEAD>
<BODY BGCOLOR="#FDF5E6">
<H2>Init Parameters:</H2>
<UL>
<LI>First name: <%= firstName %>
<LI>Email address: <%= emailAddress %>
</UL>
</BODY></HTML>
<%!
private String firstName, emailAddress;

public void jspInit() {
ServletConfig config = getServletConfig();
firstName = config.getInitParameter("firstName");
emailAddress = config.getInitParameter("emailAddress");
}
%>


程序清单5-10 web.xml(说明JSP页面的init参数的摘录)
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"
http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>
<!-- ... -->
<servlet>
<servlet-name>InitPage</servlet-name>
<jsp-file>/InitPage.jsp</jsp-file>
<init-param>
<param-name>firstName</param-name>
<param-value>Bill</param-value>
</init-param>
<init-param>
<param-name>emailAddress</param-name>
<param-value>
gates@oracle.com</param-value>
</init-param>
</servlet>
<!-- ... -->
<servlet-mapping>
<servlet-name> InitPage</servlet-name>
<url-pattern>/InitPage.jsp</url-pattern>
</servlet-mapping>
<!-- ... -->
</web-app>


5.3 提供应用范围内的初始化参数
一般,对单个地servlet或JSP页面分配初始化参数。指定的servlet或JSP页面利用ServletConfig的getInitParameter方法读取这些参数。但是,在某些情形下,希望提供可由任意servlet或JSP页面借助ServletContext的getInitParameter方法读取的系统范围内的初始化参数。
可利用context-param元素声明这些系统范围内的初始化值。context-param元素应该包含param-name、param-value以及可选的description子元素,如下所示:
<context-param>
<param-name>support-email</param-name>
<param-value>
blackhole@mycompany.com</param-value>
</context-param>
可回忆一下,为了保证可移植性,web.xml内的元素必须以正确的次序声明。但这里应该注意,context-param元素必须出现任意与文档有关的元素(icon、display-name或description)之后及filter、filter-mapping、listener或 servlet元素之前。
5.4 在服务器启动时装载servlet
假如servlet或JSP页面有一个要花很长时间执行的init (servlet)或jspInit(JSP)方法。例如,假如init或jspInit方法从某个数据库或ResourceBundle查找产量。这种情况下,在第一个客户机请求时装载servlet的缺省行为将对第一个客户机产生较长时间的延迟。因此,可利用servlet的load-on- startup元素规定服务器在第一次启动时装载servlet。下面是一个例子。
<servlet>
<servlet-name> … </servlet-name>
<servlet-class> … </servlet-class> <!-- Or jsp-file -->
<load-on-startup/>
</servlet>
可以为此元素体提供一个整数而不是使用一个空的load-on-startup。想法是服务器应该在装载较大数目的servlet或JSP页面之前装载较少数目的servlet或JSP页面。例如,下面的servlet项(放置在Web应用的WEB-INF目录下的web.xml文件中的web-app元素内)将指示服务器首先装载和初始化SearchServlet,然后装载和初始化由位于Web应用的result目录中的index.jsp文件产生的 servlet。
<servlet>
<servlet-name>Search</servlet-name>
<servlet-class>myPackage.SearchServlet</servlet-class> <!-- Or jsp-file -->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet>
<servlet-name>Results</servlet-name>
<servlet-class>/results/index.jsp</servlet-class> <!-- Or jsp-file -->
<load-on-startup>2</load-on-startup>
</servlet>

6 声明过滤器

servlet版本2.3引入了过滤器的概念。虽然所有支持servlet API版本2.3的服务器都支持过滤器,但为了使用与过滤器有关的元素,必须在web.xml中使用版本2.3的DTD。
过滤器可截取和修改进入一个servlet或JSP页面的请求或从一个servlet或JSP页面发出的相应。在执行一个servlet或JSP页面之前,必须执行第一个相关的过滤器的doFilter方法。在该过滤器对其FilterChain对象调用doFilter时,执行链中的下一个过滤器。如果没有其他过滤器,servlet或JSP页面被执行。过滤器具有对到来的ServletRequest对象的全部访问权,因此,它们可以查看客户机名、查找到来的cookie等。为了访问servlet或JSP页面的输出,过滤器可将响应对象包裹在一个替身对象(stand-in object)中,比方说把输出累加到一个缓冲区。在调用FilterChain对象的doFilter方法之后,过滤器可检查缓冲区,如有必要,就对它进行修改,然后传送到客户机。
例如,程序清单5-11帝国难以了一个简单的过滤器,只要访问相关的servlet或JSP页面,它就截取请求并在标准输出上打印一个报告(开发过程中在桌面系统上运行时,大多数服务器都可以使用这个过滤器)。

程序清单5-11 ReportFilter.java
package moreservlets;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.*;

/** Simple filter that prints a report on the standard output
* whenever the associated servlet or JSP page is accessed.
* <P>
* Taken from More Servlets and JavaServer Pages
* from Prentice Hall and Sun Microsystems Press,
*
http://www.moreservlets.com/.
* © 2002 Marty Hall; may be freely used or adapted.
*/

public class ReportFilter implements Filter {
public void doFilter(ServletRequest request,
ServletResponse response,
FilterChain chain)
throws ServletException, IOException {
HttpServletRequest req = (HttpServletRequest)request;
System.out.println(req.getRemoteHost() +
" tried to access " +
req.getRequestURL() +
" on " + new Date() + ".");
chain.doFilter(request,response);
}

public void init(FilterConfig config)
throws ServletException {
}

public void destroy() {}
}

一旦建立了一个过滤器,可以在web.xml中利用filter元素以及filter-name(任意名称)、file-class(完全限定的类名)和(可选的)init-params子元素声明它。请注意,元素在web.xml的web-app元素中出现的次序不是任意的;允许服务器(但不是必需的)强制所需的次序,并且实际中有些服务器也是这样做的。但这里要注意,所有filter元素必须出现在任意filter-mapping元素之前, filter-mapping元素又必须出现在所有servlet或servlet-mapping元素之前。
例如,给定上述的ReportFilter类,可在web.xml中作出下面的filter声明。它把名称Reporter与实际的类ReportFilter(位于moreservlets程序包中)相关联。
<filter>
<filter-name>Reporter</filter-name>
<filter-class>moresevlets.ReportFilter</filter-class>
</filter>
一旦命名了一个过滤器,可利用filter-mapping元素把它与一个或多个servlet或JSP页面相关联。关于此项工作有两种选择。
首先,可使用filter-name和servlet-name子元素把此过滤器与一个特定的servlet名(此servlet名必须稍后在相同的 web.xml文件中使用servlet元素声明)关联。例如,下面的程序片断指示系统只要利用一个定制的URL访问名为SomeServletName 的servlet或JSP页面,就运行名为Reporter的过滤器。
<filter-mapping>
<filter-name>Reporter</filter-name>
<servlet-name>SomeServletName</servlet-name>
</filter-mapping>
其次,可利用filter-name和url-pattern子元素将过滤器与一组servlet、JSP页面或静态内容相关联。例如,相面的程序片段指示系统只要访问Web应用中的任意URL,就运行名为Reporter的过滤器。
<filter-mapping>
<filter-name>Reporter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
例如,程序清单5-12给出了将ReportFilter过滤器与名为PageName的servlet相关联的web.xml文件的一部分。名字 PageName依次又与一个名为TestPage.jsp的JSP页面以及以模式http: //host/webAppPrefix/UrlTest2/ 开头的URL相关联。TestPage.jsp的源代码已经JSP页面命名的谈论在前面的3节"分配名称和定制的URL"中给出。事实上,程序清单5- 12中的servlet和servlet-name项从该节原封不动地拿过来的。给定这些web.xml项,可看到下面的标准输出形式的调试报告(换行是为了容易阅读)。
audit.irs.gov tried to access
http://mycompany.com/deployDemo/UrlTest2/business/tax-plan.html
on Tue Dec 25 13:12:29 EDT 2001.

程序清单5-12 Web.xml(说明filter用法的摘录)
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"
http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>
<filter>
<filter-name>Reporter</filter-name>
<filter-class>moresevlets.ReportFilter</filter-class>
</filter>
<!-- ... -->
<filter-mapping>
<filter-name>Reporter</filter-name>
<servlet-name>PageName</servlet-name>
</filter-mapping>
<!-- ... -->
<servlet>
<servlet-name>PageName</servlet-name>
<jsp-file>/RealPage.jsp</jsp-file>
</servlet>
<!-- ... -->
<servlet-mapping>
<servlet-name> PageName </servlet-name>
<url-pattern>/UrlTest2/*</url-pattern>
</servlet-mapping>
<!-- ... -->
</web-app>


7 指定欢迎页

假如用户提供了一个像http: //host/webAppPrefix/directoryName/ 这样的包含一个目录名但没有包含文件名的URL,会发生什么事情呢?用户能得到一个目录表?一个错误?还是标准文件的内容?如果得到标准文件内容,是 index.html、index.jsp、default.html、default.htm或别的什么东西呢?
Welcome-file-list 元素及其辅助的welcome-file元素解决了这个模糊的问题。例如,下面的web.xml项指出,如果一个URL给出一个目录名但未给出文件名,服务器应该首先试用index.jsp,然后再试用index.html。如果两者都没有找到,则结果有赖于所用的服务器(如一个目录列表)。
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
虽然许多服务器缺省遵循这种行为,但不一定必须这样。因此,明确地使用welcom-file-list保证可移植性是一种良好的习惯。

8 指定处理错误的页面

现在我了解到,你在开发servlet和JSP页面时从不会犯错误,而且你的所有页面是那样的清晰,一般的程序员都不会被它们的搞糊涂。但是,是人总会犯错误的,用户可能会提供不合规定的参数,使用不正确的URL或者不能提供必需的表单字段值。除此之外,其它开发人员可能不那么细心,他们应该有些工具来克服自己的不足。
error-page元素就是用来克服这些问题的。它有两个可能的子元素,分别是:error-code和exception- type。第一个子元素error-code指出在给定的HTTP错误代码出现时使用的URL。第二个子元素excpetion-type指出在出现某个给定的Java异常但未捕捉到时使用的URL。error-code和exception-type都利用location元素指出相应的URL。此 URL必须以/开始。location所指出的位置处的页面可通过查找HttpServletRequest对象的两个专门的属性来访问关于错误的信息,这两个属性分别是:javax.servlet.error.status_code和javax.servlet.error.message。
可回忆一下,在web.xml内以正确的次序声明web-app的子元素很重要。这里只要记住,error-page出现在web.xml文件的末尾附近,servlet、servlet-name和welcome-file-list之后即可。

8.1 error-code元素
为了更好地了解error-code元素的值,可考虑一下如果不正确地输入文件名,大多数站点会作出什么反映。这样做一般会出现一个404错误信息,它表示不能找到该文件,但几乎没提供更多有用的信息。另一方面,可以试一下在
www.microsoft.comwww.ibm.com 处或者特别是在www.bea.com 处输出未知的文件名。这是会得出有用的消息,这些消息提供可选择的位置,以便查找感兴趣的页面。提供这样有用的错误页面对于Web应用来说是很有价值得。事实上rm-error-page子元素)。由form-login-page给出的HTML表单必须具有一个j_security_check的 ACTION属性、一个名为j_username的用户名文本字段以及一个名为j_password的口令字段。
例如,程序清单5-19指示服务器使用基于表单的验证。Web应用的顶层目录中的一个名为login.jsp的页面将收集用户名和口令,并且失败的登陆将由相同目录中名为login-error.jsp的页面报告。

程序清单5-19 web.xml(说明login-config的摘录)
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"
http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>
<!-- ... -->
<security-constraint> ... </security-constraint>
<login-config>
<auth-method> FORM </auth-method>
<form-login-config>
<form-login-page>/login.jsp</form-login-page>
<form-error-page>/login-error.jsp</form-error-page>
</form-login-config>
</login-config>
<!-- ... -->
</web-app>


9.2 限制对Web资源的访问
现在,可以指示服务器使用何种验证方法了。"了不起,"你说道,"除非我能指定一个来收到保护的 URL,否则没有多大用处。"没错。指出这些URL并说明他们应该得到何种保护正是security-constriaint元素的用途。此元素在 web.xml中应该出现在login-config的紧前面。它包含是个可能的子元素,分别是:web-resource-collection、 auth-constraint、user-data-constraint和display-name。下面各小节对它们进行介绍。
l web-resource-collection
此元素确定应该保护的资源。所有security-constraint元素都必须包含至少一个web-resource-collection项。此元素由一个给出任意标识名称的web-resource-name元素、一个确定应该保护的URL的url-pattern元素、一个指出此保护所适用的 HTTP命令(GET、POST等,缺省为所有方法)的http-method元素和一个提供资料的可选description元素组成。例如,下面的 Web-resource-collection项(在security-constratint元素内)指出Web应用的proprietary目录中所有文档应该受到保护。
<security-constraint>
<web-resource-coolection>
<web-resource-name>Proprietary</web-resource-name>
<url-pattern>/propritary/*</url-pattern>
</web-resource-coolection>
<!-- ... -->
</security-constraint>
重要的是应该注意到,url-pattern仅适用于直接访问这些资源的客户机。特别是,它不适合于通过MVC体系结构利用 RequestDispatcher来访问的页面,或者不适合于利用类似jsp:forward的手段来访问的页面。这种不匀称如果利用得当的话很有好处。例如,servlet可利用MVC体系结构查找数据,把它放到bean中,发送请求到从bean中提取数据的JSP页面并显示它。我们希望保证决不直接访问受保护的JSP页面,而只是通过建立该页面将使用的bean的servlet来访问它。url-pattern和auth-contraint元素可通过声明不允许任何用户直接访问JSP页面来提供这种保证。但是,这种不匀称的行为可能让开发人员放松警惕,使他们偶然对应受保护的资源提供不受限制的访问。
l auth-constraint
尽管web-resource-collention元素质出了哪些URL应该受到保护,但是auth-constraint元素却指出哪些用户应该具有受保护资源的访问权。此元素应该包含一个或多个标识具有访问权限的用户类别role- name元素,以及包含(可选)一个描述角色的description元素。例如,下面web.xml中的security-constraint元素部门规定只有指定为Administrator或Big Kahuna(或两者)的用户具有指定资源的访问权。
<security-constraint>
<web-resource-coolection> ... </web-resource-coolection>
<auth-constraint>
<role-name>administrator</role-name>
<role-name>kahuna</role-name>
</auth-constraint>
</security-constraint>
重要的是认识到,到此为止,这个过程的可移植部分结束了。服务器怎样确定哪些用户处于任何角色以及它怎样存放用户的口令,完全有赖于具体的系统。
例如,Tomcat使用install_dir/conf/tomcat-users.xml将用户名与角色名和口令相关联,正如下面例子中所示,它指出用户joe(口令bigshot)和jane(口令enaj)属于administrator和kahuna角色。
<tomcat-users>
<user name="joe" password="bigshot" roles="administrator,kahuna" />
<user name="jane" password="enaj" roles="kahuna" />
</tomcat-users>
l user-data-constraint
这个可选的元素指出在访问相关资源时使用任何传输层保护。它必须包含一个transport-guarantee子元素(合法值为NONE、 INTEGRAL或CONFIDENTIAL),并且可选地包含一个description元素。transport-guarantee为NONE值将对所用的通讯协议不加限制。INTEGRAL值表示数据必须以一种防止截取它的人阅读它的方式传送。虽然原理上(并且在未来的HTTP版本中),在 INTEGRAL和CONFIDENTIAL之间可能会有差别,但在当前实践中,他们都只是简单地要求用SSL。例如,下面指示服务器只允许对相关资源做 HTTPS连接:
<security-constraint>
<!-- ... -->
<user-data-constraint>
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>
</security-constraint>
l display-name
security-constraint的这个很少使用的子元素给予可能由GUI工具使用的安全约束项一个名称。
9.3 分配角色名
迄今为止,讨论已经集中到完全由容器(服务器)处理的安全问题之上了。但servlet以及JSP页面也能够处理它们自己的安全问题。
例如,容器可能允许用户从bigwig或bigcheese角色访问一个显示主管人员额外紧贴的页面,但只允许bigwig用户修改此页面的参数。完成这种更细致的控制的一种常见方法是调用HttpServletRequset的isUserInRole方法,并据此修改访问。
Servlet的 security-role-ref子元素提供出现在服务器专用口令文件中的安全角色名的一个别名。例如,假如编写了一个调用 request.isUserInRole("boss")的servlet,但后来该servlet被用在了一个其口令文件调用角色manager而不是boss的服务器中。下面的程序段使该servlet能够使用这两个名称中的任何一个。
<servlet>
<!-- ... -->
<security-role-ref>
<role-name>boss</role-name> <!-- New alias -->
<role-link>manager</role-link> <!-- Real name -->
</security-role-ref>
</servlet>
也可以在web-app内利用security-role元素提供将出现在role-name元素中的所有安全角色的一个全局列表。分别地生命角色使高级IDE容易处理安全信息。

10 控制会话超时

如果某个会话在一定的时间内未被访问,服务器可把它扔掉以节约内存。可利用HttpSession的setMaxInactiveInterval方法直接设置个别会话对象的超时值。如果不采用这种方法,则缺省的超时值由具体的服务器决定。但可利用session-config和session- timeout元素来给出一个适用于所有服务器的明确的超时值。超时值的单位为分钟,因此,下面的例子设置缺省会话超时值为三个小时(180分钟)。
<session-config>
<session-timeout>180</session-timeout>
</session-config>

11 Web应用的文档化

越来越多的开发环境开始提供servlet和JSP的直接支持。例子有Borland Jbuilder Enterprise Edition、Macromedia UltraDev、Allaire JRun Studio(写此文时,已被Macromedia收购)以及IBM VisuaAge for Java等。
大量的web.xml元素不仅是为服务器设计的,而且还是为可视开发环境设计的。它们包括icon、display-name和discription等。
可回忆一下,在web.xml内以适当地次序声明web-app子元素很重要。不过,这里只要记住icon、display-name和description是web.xml的web-app元素内的前三个合法元素即可。
l icon
icon元素指出GUI工具可用来代表Web应用的一个和两个图像文件。可利用small-icon元素指定一幅16 x 16的GIF或JPEG图像,用large-icon元素指定一幅32 x 32的图像。下面举一个例子:
<icon>
<small-icon>/images/small-book.gif</small-icon>
<large-icon>/images/tome.jpg</large-icon>
</icon>
l display-name
display-name元素提供GUI工具可能会用来标记此Web应用的一个名称。下面是个例子。
<display-name>Rare Books</display-name>
l description
description元素提供解释性文本,如下所示:
<description>
This Web application represents the store developed for
rare-books.com, an online bookstore specializing in rare
and limited-edition books.
</description>

12 关联文件与MIME类型

服务器一般都具有一种让Web站点管理员将文件扩展名与媒体相关联的方法。例如,将会自动给予名为mom.jpg的文件一个image/jpeg的MIME 类型。但是,假如你的Web应用具有几个不寻常的文件,你希望保证它们在发送到客户机时分配为某种MIME类型。mime-mapping元素(具有 extension和mime-type子元素)可提供这种保证。例如,下面的代码指示服务器将application/x-fubar的MIME类型分配给所有以.foo结尾的文件。
<mime-mapping>
<extension>foo</extension>
<mime-type>application/x-fubar</mime-type>
</mime-mapping>
或许,你的Web应用希望重载(override)标准的映射。例如,下面的代码将告诉服务器在发送到客户机时指定.ps文件作为纯文本(text/plain)而不是作为PostScript(application/postscript)。
<mime-mapping>
<extension>ps</extension>
<mime-type>application/postscript</mime-type>
</mime-mapping>


13 定位TLD

JSP taglib元素具有一个必要的uri属性,它给出一个TLD(Tag Library Descriptor)文件相对于Web应用的根的位置。TLD文件的实际名称在发布新的标签库版本时可能会改变,但我们希望避免更改所有现有JSP页面。此外,可能还希望使用保持taglib元素的简练性的一个简短的uri。这就是部署描述符文件的taglib元素派用场的所在了。Taglib包含两个子元素:taglib-uri和taglib-location。taglib-uri元素应该与用于JSP taglib元素的uri属性的东西相匹配。Taglib-location元素给出TLD文件的实际位置。例如,假如你将文件chart-tags- 1.3beta.tld放在WebApp/WEB-INF/tlds中。现在,假如web.xml在web-app元素内包含下列内容。
<taglib>
<taglib-uri>/charts.tld</taglib-uri>
<taglib-location>
/WEB-INF/tlds/chart-tags-1.3beta.tld
</taglib-location>
</taglib>
给出这个说明后,JSP页面可通过下面的简化形式使用标签库。
<%@ taglib uri="/charts.tld" prefix="somePrefix" %>

14 指定应用事件监听程序

应用事件监听器程序是建立或修改servlet环境或会话对象时通知的类。它们是servlet规范的版本2.3中的新内容。这里只简单地说明用来向Web应用注册一个监听程序的web.xml的用法。
注册一个监听程序涉及在web.xml的web-app元素内放置一个listener元素。在listener元素内,listener-class元素列出监听程序的完整的限定类名,如下所示:
<listener>
<listener-class>package.ListenerClass</listener-class>
</listener>
虽然listener元素的结构很简单,但请不要忘记,必须正确地给出web-app元素内的子元素的次序。listener元素位于所有的servlet 元素之前以及所有filter-mapping元素之后。此外,因为应用生存期监听程序是serlvet规范的2.3版本中的新内容,所以必须使用 web.xml DTD的2.3版本,而不是2.2版本。
例如,程序清单5-20给出一个名为ContextReporter的简单的监听程序,只要Web应用的Servlet-Context建立(如装载Web应用)或消除(如服务器关闭)时,它就在标准输出上显示一条消息。程序清单5-21给出此监听程序注册所需要的web.xml文件的一部分。

程序清单5-20 ContextReporterjava
package moreservlets;

import javax.servlet.*;
import java.util.*;

/** Simple listener that prints a report on the standard output
* when the ServletContext is created or destroyed.
* <P>
* Taken from More Servlets and JavaServer Pages
* from Prentice Hall and Sun Microsystems Press,
*
http://www.moreservlets.com/.
* © 2002 Marty Hall; may be freely used or adapted.
*/

public class ContextReporter implements ServletContextListener {
public void contextInitialized(ServletContextEvent event) {
System.out.println("Context created on " +
new Date() + ".");
}

public void contextDestroyed(ServletContextEvent event) {
System.out.println("Context destroyed on " +
new Date() + ".");
}
}


程序清单5-21 web.xml(声明一个监听程序的摘录)
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"
http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>
<!-- ... -->
<filter-mapping> … </filter-mapping>
<listener>
<listener-class>package.ListenerClass</listener-class>
</listener>
<servlet> ... </servlet>
<!-- ... -->
</web-app>


15 J2EE元素

本节描述用作J2EE环境组成部分的Web应用的web.xml元素。这里将提供一个简明的介绍,详细内容可以参阅
http://java.sun.com/j2ee/j2ee-1_3-fr-spec.pdf的Java 2 Plantform Enterprise Edition版本1.3规范的第5章。
l distributable
distributable 元素指出,Web应用是以这样的方式编程的:即,支持集群的服务器可安全地在多个服务器上分布Web应用。例如,一个可分布的应用必须只使用 Serializable对象作为其HttpSession对象的属性,而且必须避免用实例变量(字段)来实现持续性。distributable元素直接出现在discription元素之后,并且不包含子元素或数据,它只是一个如下的标志。
<distributable />
l resource-env-ref
resource -env-ref元素声明一个与某个资源有关的管理对象。此元素由一个可选的description元素、一个resource-env-ref- name元素(一个相对于java:comp/env环境的JNDI名)以及一个resource-env-type元素(指定资源类型的完全限定的类),如下所示:
<resource-env-ref>
<resource-env-ref-name>
jms/StockQueue
</resource-env-ref-name>
<resource-env-ref-type>
javax.jms.Queue
</resource-env-ref-type>
</resource-env-ref>
l env-entry
env -entry元素声明Web应用的环境项。它由一个可选的description元素、一个env-entry-name元素(一个相对于java: comp/env环境JNDI名)、一个env-entry-value元素(项值)以及一个env-entry-type元素(java.lang程序包中一个类型的完全限定类名,java.lang.Boolean、java.lang.String等)组成。下面是一个例子:
<env-entry>
<env-entry-name>minAmout</env-entry-name>
<env-entry-value>100.00</env-entry-value>
<env-entry-type>minAmout</env-entry-type>
</env-entry>
l ejb-ref
ejb -ref元素声明对一个EJB的主目录的应用。它由一个可选的description元素、一个ejb-ref-name元素(相对于java: comp/env的EJB应用)、一个ejb-ref-type元素(bean的类型,Entity或Session)、一个home元素(bean的主目录接口的完全限定名)、一个remote元素(bean的远程接口的完全限定名)以及一个可选的ejb-link元素(当前bean链接的另一个 bean的名称)组成。
l ejb-local-ref
ejb-local-ref元素声明一个EJB的本地主目录的引用。除了用local-home代替home外,此元素具有与ejb-ref元素相同的属性并以相同的方式使用。

posted @ 2005-11-29 17:38 船夫 阅读(371) | 评论 (0)编辑 收藏

↑→40种网页常用小技巧(javascript)←↓------[不时之需](转自CJSDN)


1. oncontextmenu="window.event.returnValue=false" 将彻底屏蔽鼠标右键
<table border oncontextmenu=return(false)><td>no</table> 可用于Table

2. <body onselectstart="return false"> 取消选取、防止复制

3. onpaste="return false" 不准粘贴

4. oncopy="return false;" oncut="return false;" 防止复制

5. <link rel="Shortcut Icon" href="favicon.ico"> IE地址栏前换成自己的图标

6. <link rel="Bookmark" href="favicon.ico"> 可以在收藏夹中显示出你的图标

7. <input style="ime-mode:disabled"> 关闭输入法

8. 永远都会带着框架
<script language="JavaScript"><!--
if (window == top)top.location.href = "frames.htm"; //frames.htm为框架网页
// --></script>

9. 防止被人frame
<SCRIPT LANGUAGE=JAVASCRIPT><!--
if (top.location != self.location)top.location=self.location;
// --></SCRIPT>

10. 网页将不能被另存为
<noscript><iframe src=*.html></iframe></noscript>

11. <input type=button value=查看网页源代码
onclick="window.location = "view-source:"+ "http://www.pconline.com.cn"">

12.删除时确认
<a href="javascript:if(confirm("确实要删除吗?"))location="boos.asp?&areyou=删除&page=1"">删除</a>

13. 取得控件的绝对位置
//Javascript
<script language="Javascript">
function getIE(e){
var t=e.offsetTop;
var l=e.offsetLeft;
while(e=e.offsetParent){
t+=e.offsetTop;
l+=e.offsetLeft;
}
alert("top="+t+"/nleft="+l);
}
</script>

//VBScript
<script language="VBScript"><!--
function getIE()
dim t,l,a,b
set a=document.all.img1
t=document.all.img1.offsetTop
l=document.all.img1.offsetLeft
while a.tagName<>"BODY"
set a = a.offsetParent
t=t+a.offsetTop
l=l+a.offsetLeft
wend
msgbox "top="&t&chr(13)&"left="&l,64,"得到控件的位置"
end function
--></script>

14. 光标是停在文本框文字的最后
<script language="javascript">
function cc()
{
var e = event.srcElement;
var r =e.createTextRange();
r.moveStart("character",e.value.length);
r.collapse(true);
r.select();
}
</script>
<input type=text name=text1 value="123" onfocus="cc()">

15. 判断上一页的来源
javascript:
document.referrer

16. 最小化、最大化、关闭窗口
<object id=hh1 classid="clsid:ADB880A6-D8FF-11CF-9377-00AA003B7A11">
<param name="Command" value="Minimize"></object>
<object id=hh2 classid="clsid:ADB880A6-D8FF-11CF-9377-00AA003B7A11">
<param name="Command" value="Maximize"></object>
<OBJECT id=hh3 classid="clsid:adb880a6-d8ff-11cf-9377-00aa003b7a11">
<PARAM NAME="Command" VALUE="Close"></OBJECT>
<input type=button value=最小化 onclick=hh1.Click()>
<input type=button value=最大化 onclick=hh2.Click()>
<input type=button value=关闭 onclick=hh3.Click()>
本例适用于IE

17.屏蔽功能键Shift,Alt,Ctrl
<script>
function look(){
if(event.shiftKey)
alert("禁止按Shift键!"); //可以换成ALT CTRL
}
document.onkeydown=look;
</script>

18. 网页不会被缓存
<META HTTP-EQUIV="pragma" CONTENT="no-cache">
<META HTTP-EQUIV="Cache-Control" CONTENT="no-cache, must-revalidate">
<META HTTP-EQUIV="expires" CONTENT="Wed, 26 Feb 1997 08:21:57 GMT">
或者<META HTTP-EQUIV="expires" CONTENT="0">

19.怎样让表单没有凹凸感?
<input type=text style="border:1 solid #000000">

<input type=text style="border-left:none; border-right:none; border-top:none; border-bottom:

1 solid #000000"></textarea>

20.<div><span>&<layer>的区别?
<div>(division)用来定义大段的页面元素,会产生转行
<span>用来定义同一行内的元素,跟<div>的唯一区别是不产生转行
<layer>是ns的标记,ie不支持,相当于<div>

21.让弹出窗口总是在最上面:
<body onblur="this.focus();">

22.不要滚动条?
让竖条没有:
<body style="overflow:scroll;overflow-y:hidden">
</body>
让横条没有:
<body style="overflow:scroll;overflow-x:hidden">
</body>
两个都去掉?更简单了
<body scroll="no">
</body>

23.怎样去掉图片链接点击后,图片周围的虚线?
<a href="#" onFocus="this.blur()"><img src="logo.jpg" border=0></a>

24.电子邮件处理提交表单
<form name="form1" method="post" action="mailto:****@***.com" enctype="text/plain">
<input type=submit>
</form>

25.在打开的子窗口刷新父窗口的代码里如何写?
window.opener.location.reload()

26.如何设定打开页面的大小
<body onload="top.resizeTo(300,200);">
打开页面的位置<body onload="top.moveBy(300,200);">

27.在页面中如何加入不是满铺的背景图片,拉动页面时背景图不动
<STYLE>
body
{background-image:url(logo.gif); background-repeat:no-repeat;
background-position:center;background-attachment: fixed}
</STYLE>

28. 检查一段字符串是否全由数字组成
<script language="Javascript"><!--
function checkNum(str){return str.match(//D/)==null}
alert(checkNum("1232142141"))
alert(checkNum("123214214a1"))
// --></script>

29. 获得一个窗口的大小
document.body.clientWidth; document.body.clientHeight

30. 怎么判断是否是字符
if (/[^/x00-/xff]/g.test(s)) alert("含有汉字");
else alert("全是字符");

31.TEXTAREA自适应文字行数的多少
<textarea rows=1 name=s1 cols=27 onpropertychange="this.style.posHeight=this.scrollHeight">
</textarea>

32. 日期减去天数等于第二个日期
<script language=Javascript>
function cc(dd,dadd)
{
//可以加上错误处理
var a = new Date(dd)
a = a.valueOf()
a = a - dadd * 24 * 60 * 60 * 1000
a = new Date(a)
alert(a.getFullYear() + "年" + (a.getMonth() + 1) + "月" + a.getDate() + "日")
}
cc("12/23/2002",2)
</script>

33. 选择了哪一个Radio
<HTML><script language="vbscript">
function checkme()
for each ob in radio1
if ob.checked then window.alert ob.value
next
end function
</script><BODY>
<INPUT name="radio1" type="radio" value="style" checked>Style
<INPUT name="radio1" type="radio" value="barcode">Barcode
<INPUT type="button" value="check" onclick="checkme()">
</BODY></HTML>

34.脚本永不出错
<SCRIPT LANGUAGE="JavaScript">
<!-- Hide
function killErrors() {
return true;
}
window.onerror = killErrors;
// -->
</SCRIPT>

35.ENTER键可以让光标移到下一个输入框
<input onkeydown="if(event.keyCode==13)event.keyCode=9">

36. 检测某个网站的链接速度:
把如下代码加入<body>区域中:
<script language=Javascript>
tim=1
setInterval("tim++",100)
b=1
var autourl=new Array()
autourl[1]="www.njcatv.net"
autourl[2]="javacool.3322.net"
autourl[3]="www.sina.com.cn"
autourl[4]="www.nuaa.edu.cn"
autourl[5]="www.cctv.com"
function butt(){
document.write("<form name=autof>")
for(var i=1;i<autourl.length;i++)
document.write("<input type=text name=txt"+i+" size=10 value=测试中……> =》<input type=text
name=url"+i+" size=40> =》<input type=button value=GO

onclick=window.open(this.form.url"+i+".value)><br>")
document.write("<input type=submit value=刷新></form>")
}
butt()
function auto(url){
document.forms[0]["url"+b].value=url
if(tim>200)
{document.forms[0]["txt"+b].value="链接超时"}
else
{document.forms[0]["txt"+b].value="时间"+tim/10+"秒"}
b++
}
function run(){for(var i=1;i<autourl.length;i++)document.write("<img src=http://"+autourl+"/"+Math.random()+"

width=1 height=1

onerror=auto("http://"+autourl+"")>")}
run()</script>

37. 各种样式的光标
auto :标准光标
default :标准箭头
hand :手形光标
wait :等待光标
text :I形光标
vertical-text :水平I形光标
no-drop :不可拖动光标
not-allowed :无效光标
help :?帮助光标
all-scroll :三角方向标
move :移动标
crosshair :十字标
e-resize
n-resize
nw-resize
w-resize
s-resize
se-resize
sw-resize

38.页面进入和退出的特效
进入页面<meta http-equiv="Page-Enter" content="revealTrans(duration=x, transition=y)">
推出页面<meta http-equiv="Page-Exit" content="revealTrans(duration=x, transition=y)">
这个是页面被载入和调出时的一些特效。duration表示特效的持续时间,以秒为单位。transition表示使用哪种特效,取值为

1-23:
  0 矩形缩小
  1 矩形扩大
  2 圆形缩小
  3 圆形扩大
  4 下到上刷新
  5 上到下刷新
  6 左到右刷新
  7 右到左刷新
  8 竖百叶窗
  9 横百叶窗
  10 错位横百叶窗
  11 错位竖百叶窗
  12 点扩散
  13 左右到中间刷新
  14 中间到左右刷新
  15 中间到上下
  16 上下到中间
  17 右下到左上
  18 右上到左下
  19 左上到右下
  20 左下到右上
  21 横条
  22 竖条
  23 以上22种随机选择一种

39.在规定时间内跳转
<META http-equiv=V="REFRESH" content="5;URL=http://www.51js.com">

40.网页是否被检索
<meta name="ROBOTS" content="属性值">
  其中属性值有以下一些:
  属性值为"all": 文件将被检索,且页上链接可被查询;
  属性值为"none": 文件不被检索,而且不查询页上的链接;
  属性值为"index": 文件将被检索;
  属性值为"follow": 查询页上的链接;
  属性值为"noindex": 文件不检索,但可被查询链接;
  属性值为"nofollow": 文件不被检索,但可查询页上的链接。

posted @ 2005-11-29 09:27 船夫 阅读(222) | 评论 (0)编辑 收藏

无法刷新include的jsp的问题

      这几天在写web表现层的东西,由于对jsp不是很熟悉,在使用include的时候就遇到了问题。
      可能是用OOP久了,写什么都想封装起来,写jsp的时候就想使用include。我在一个parent.jsp中使用
<%@include file="sub.jsp"%>
include了一个sub.jsp,之后就发现我在刷新parent.jsp的时候不能同时刷新sub.jsp,百思不得其解,听高手说把IE的Cache禁用了就行了,但使用
<META HTTP-EQUIV="Cache-Control" CONTENT="no-cache">
这个禁了之后还是不行,最后到论坛上逛了一圈儿才发现原来是我的标签使用错了,应该使用jsp:include标签,@include file主要用于静态的内容引用,就相当于把被引用的文件代码(sub.jsp)直接copy到主页面(parent.jsp)中。而jsp:include可以随时更新sub.jsp的内容,只要把属性flush设为true。
要详细了解的可以看看以下两篇文章:
http://www-900.ibm.com/developerWorks/cn/java/j-jsp04153/
http://www-900.ibm.com/developerWorks/cn/java/j-jsp04293/

posted @ 2005-11-23 15:26 船夫 阅读(1610) | 评论 (1)编辑 收藏

网页中META标签的使用

Meta 标签放在每个网页的<head>...</head>中,我们大家比较熟悉的如: 

<meta name="GENERATOR" content="Microsoft FrontPage 3.0">说明编辑工具;
<meta name="KEYWORDS" content="...">说明关键词;
<meta name="DESCRIPTION" content="...">说明主页描述;

<meta http-equiv="Content-Type" content="text/html; charset=gb_2312-80">和
<meta http-equiv="Content-Language" content="zh-CN">说明所用语言及文字... 

可见META有两种,name和http-equiv。 

name主要用于描述网页,对应于content,以便于搜索引擎机器人查找、分类(目 前几乎所有的搜索引擎都使用网上机器人自动查找META值来给你的网页分类)。这其中最重要的是DESCRIPTION(你的站点在引擎上的描述)和KEYWORDS(搜索引 擎籍以分类的关键词),应该给你的“每一页”都插入这两个META值。当然你也可以不要搜索引擎检索,可用: 
<meta name="ROBOTS" content="all | none | index | noindex | follow | nofollow"> 来确定:
设定为"all"时文件将被检索,且页上链接可被查询;
设定为"none"则表示文件不被检索,而且不查询页上的链接;
设定为"index"时文件将被检索;
设定为"follow"则可查询页上的链接;
设定为"noindex"时文件不检索,但可被查询链接;
设定为"nofollow"则表示文件不被检索,但可查询页上的链接.

http-equiv顾名思义相当于http文件头的作用,可以直接影响网页的传输。比较 直接的例子如: 

1、自动刷新,并指向新网页
<meta http-equiv="Refresh" content="10; url= http://yourlink"> 10秒后刷新到http://yourlink;

2、网页间转换时加入效果
<meta http-equiv="Page-Enter" content="revealTrans(duration=10, transition=50)"> 
<meta http-equiv="Page-Exit" content="revealTrans(duration=20, transition=6)"> 
加在一个网页中,进出时有一些特殊效果,这个功能即FrontPage 98的Format/Page Transition.不过注意所加网页不能是一个Frame页;

3、强制网页不被存入Cache中
<meta http-equiv="pragma" content="no-cache"> 
<meta http-equiv="expires" content="wed, 26 Feb 1997 08:21:57 GMT"> 
大家可以到http://www.internet.com上看看,它的首页当你断线后,就无法在cache中再调出。(本身是关于建站很棒的站点)

4、定义指向窗口
<meta http-equiv="window-target" content="_top">
可以防止网页被别人作为一个Frame调用.(不过,我试了一下,似乎不灵) 

Meta还有很多功能, 如大家关心的 "怎样在搜索引擎中,被放在搜索结果前面的 位置"( http://vancouver-webpages.com/VWbot/mk-metas.html). 你可以在以下站点进一步查询:http://webdeveloper.com/categories/html/ html_metatag_res.html
http://vancouver-webpages.com/META/
http://www.nlc-bnc.ca/ifla/II/metadata.htm

posted @ 2005-11-17 17:05 船夫 阅读(324) | 评论 (0)编辑 收藏

纠错步骤

1.仔细认真的检查以确定是否是真的代码问题
2.

posted @ 2005-11-17 15:57 船夫 阅读(230) | 评论 (0)编辑 收藏

HTML标签详解

--  HTML标签详解
HTML指令详解
结构
<html>
<head>
<title>标题<title>
</head>
<body>..........文件内容..........
</body>
</html>
1.文件标题
<title>..........</title>
2.文件更新--<meta>
 【1】10秒后自动更新一次
  <meta http-equiv="refresh" content=10>
 【2】10秒後自动连结到另一文件
  <meta http-equiv="refresh" content="10;URL=欲连结文件之URL">
3.查询用表单--<isindex>
   若欲设定查询栏位前的提示文字:
  <isindex prompt="提示文字">
4.预设的基准路径--<base>
   <base href="放置文件的主机之URL">
版面
1.标题文字 <h#>..........</h#> #=1~6;h1为最大字,h6为最小字
2.字体变化 <font>..........</font> 
【1】字体大小 <font size=#>..........</font> #=1~7;数字愈大字也愈大 
【2】指定字型 <font face="字型名称">..........</font> 
【3】文字颜色 <font color=#rrggbb>..........</font> rr:表红色(red)色码 gg:表绿色(green)色码 bb:表蓝色(blue)色码
3.显示小字体 <small>..........</small> 
4.显示大字体 <big>..........</big>
5.粗体字 <b>..........</b>
6.斜体字 <i>..........</i> 
7.打字机字体 <tt>..........</tt>
8.底线 <u>..........</u>
9.删除线 <strike>..........</strike>
10.下标字 <sub>..........</sub>
11.上标字 <sup>..........</sup>
12.文字闪烁效果 <blink>..........</blink>
13.换行 <br>
14.分段 <p> 
15.文字的对齐方向 <p align="#"> #号可为 left:表向左对齐(预设值) center:表向中对齐 right:表向右对齐 P.S.<p align="#">之後的文字都会以所设的对齐方式显示, 直到出现另一个<p align="#">改变其对齐方向,或遇到 <hr>ⅱ<h#>标签时会自动设回预设的向左对齐。
16.分隔线 <hr> 
【1】分隔线的粗细 <hr size=点数> 
【2】分隔线的宽度 <hr size=点数或百分比> 
【3】分隔线对齐方向 <hr align="#"> #号可为 left:表向左对齐(预设值) center:表向中对齐 right:表向右对齐 
【4】分隔线的颜色 <hr color=#rrggbb> 
【5】实心分隔线 <hr noshade>
17.向中对齐 <center>..........</center>
18.依原始样式显示 <pre>..........</pre>
19.<body>指令的属性 
【1】背景颜色 -- bgcolor <body bgcolor=#rrggbb> 
【2】背景图案 -- background <body background="图形文件名"> 
【3】设定背景图案不会卷动 -- bgproperties <body bgproperties=fixed> 
【4】文件内容文字的颜色 -- text <body text=#rrggbb> 
【5】超连结文字颜色 -- link <body link=#rrggbb> 
【6】正被选取的超连结文字颜色 -- vlink <body vlink=#rrggbb> 
【7】已连结过的超连结文字颜色 -- alink <body alink=#rrggbb>
20.注解 <!--..........-->21.特殊字元表示法 
符 号   语 法  
<     &lt  
>     &gt  
&     &amp  
"     &quot  
空白    &nbsp  

 

图片
1.插入图片 <img src="图形文件名">
2.设定图框 -- border <img src="图形文件名" border=点数>
3.设定图形大小 -- widthⅱheight <img src="图形文件名" width=宽度点数 height=高度点数>
4.设定图形上下左右留空 -- vspaceⅱhspace <img src="图形文件名" vspace=上下留空点数 hspace=左右留空点数>
5.图形附注 <img src="图形文件名" alt="说明文字">
6.预载图片
<img src="高解析度图形文件名" lowsrc="低解析度图形文件名"> P.S.两个图的图形大小最好一致
7.影像地图(Image Map) <img src="图形文件名" usemap="#图的名称"> <map name="图的名称">
       <area shape=形状 coords=区域座标列表 href="连结点之URL">
       <area shape=形状 coords=区域座标列表 href="连结点之URL">
       <area shape=形状 coords=区域座标列表 href="连结点之URL">
       <area shape=形状 coords=区域座标列表 href="连结点之URL">    </map>    
【1】定义形状 -- shape
       shape=rect:矩形         shape=circle:圆形         shape=poly:多边形    
【2】定义区域 -- coords
a.矩形:必须使用四个数字,前两个数字为左上角座标,后两个数字为右下角座标
例:<area shape=rect coords=100,50,200,75 href="URL">
b.圆形:必须使用三个数字,前两个数字为圆心的座标,最后一个数字为半径长度
例:<area shape=circle coords=85,155,30 href="URL">
c.任意图形(多边形):将图形之每一转折点座标依序填入
例:<area shape=poly coords=232,70,285,70,300,90,250,90,200,78 href="URL">
表格
1.定义表格 <table>..........</table> 
【1】设定边框的厚度 -- border
<table border=点数> 
【2】设定格线的宽度 -- cellspacing
<table cellspacing=点数> 
【3】设定资料与格线的距离 -- cellpadding
<table cellpadding=点数> 
【4】调整表格宽度 -- width
<table width=点数或百分比> 
【5】调整表格高度 -- height
<table height=点数或百分比> 
【6】设定表格背景色彩 -- bgcolor
<table bgcolor=#rrggbb> 
【7】设定表格边框色彩 -- bordercolor
<table bordercolor=#rrggbb>
2.显示格线 <table border>
3.表格标题
<caption>..........</caption> 
表格标题位置 -- align
<caption align="#"> #号可为 top:表标题置于表格上方(预设值)
bottom:表标题置于表格下方
4.定义列 <tr>
5.定义栏位 《1》<td>:靠左对齐
《2》<th>:靠中对齐ⅱ粗体 
【1】水平位置 -- align <th align="#">
#号可为 left:向左对齐
center:向中对齐 right:向右对齐
【2】垂直位置 -- align <th align="#"> #号可为
top:向上对齐 middle:向中对齐
bottom:向下对齐 
【3】栏位宽度 -- width
<th width=点数或百分比> 
【4】栏位垂直合并 -- rowspan
<th rowspan=欲合并栏位数> 
【5】栏位横向合并 -- colspan
<th colspan=欲合并栏位数>
清单
一、目录式清单 
<dir> <li>项目1 <li>项目2 <li>项目3 </dir> P.S.目录式清单每一个项目不能超过20个字元(即10个中文字)
二、选项式清单 <menu> <li>项目1 <li>项目2 <li>项目3 </menu>
三、有序号的清单 <ol> <li>项目1 <li>项目2 <li>项目3 </ol> 
【1】序号形式 -- type <ol type=#>或<li type=#> #号可为 A:表以大写英文字母AⅱBⅱCⅱD...做为项目编号 a:表以小写英文字母aⅱbⅱcⅱd...做为项目编号 I:表以大写罗马数字做为项目编号 i:表以小写罗马数字做为项目编号 1:表以阿拉伯数字做为项目编号(预设值) 
【2】起始数字 -- start <ol start=欲开始计数的序数> 
【3】指定编号 -- value <li value=欲指定的序数>
四、无序号的清单 <ul> <li>项目1 <li>项目2 <li>项目3 </ul> 
【1】项目符号形式 -- type <ul type=#>或<li type=#> #号可为 disc:实心圆点(预设值) circle:空心圆点 square:实心方块 
【2】原始清单 -- plain <ul plain> 
【3】清单排列方式 -- warp 《1》清单垂直排列 <ul warp = vert> 《2》清单水平排列 <ul warp = horiz>
五、定义式清单 <dl> <dt>项目1 <dd>项目1说明 <dt>项目2 <dd>项目2说明 <dt>项目3 <dd>项目3说明 </dl> 
紧密排列 -- compact <dl compact> P.S.如此可使<dt>的内容与<dd>的内容在同一行,仅 以数格空白相隔而不换行,但若<dt>的文字超过一 定的长度后,compact的作用就消失了!
表单
一、基本架构 <form action="处理资料用的CGI程式之URL"或"mailto:电子信箱的URL" method="get或post"> .......... .......... .......... </form> 
二、输入文件型表单 <form action="URL" method="post"> <input> <input> .......... .......... </form> 
【1】栏位类型 -- type <input type=#> #号可为 text:文字输入 password:密码 checkbox:多选钮 radio:单选钮 submit:接受按钮 reset:重设按钮 image:图形钮 hidden:隐藏栏位 
【2】栏位名称 -- name <input name="资料栏名"> P.S.若type为submitⅱreset则name不必设定 
【3】文件上的预设值 -- value <input value="预设之字串"> 
【4】设定栏位的宽度 -- size <input size=字元数> 
【5】限制最大输入字串的长度 -- maxlength <input maxlength=字元数> 
【6】预设checkbox或radio的初值 -- checked <input type=checkbox checked> <input type=radio checked> 
【7】指定图形的URL -- src <input type=image src="图档名"> 
【8】图文对齐 -- align <input type=image align="#"> #号可为 top:文字对齐图片之顶端 middle:文字对齐图片之中间 buttom:文字对齐图片之底部
三、选择式表单 <form action="URL" method="post"> <select> <option> <option> .......... .......... </select> </form> 
A、<select>的属性 
【1】栏位名称 -- name <select name="资料栏位名"> 
【2】设定显示的选项数 -- size <select size=个数> 
【3】多重选项 -- multiple <select multiple> 
B、<option>的属性 
【1】定义选项的传回值 -- value <option value="传回值"> 
【2】预先选取的选项 -- selected <option selected>
四、多列输入文字区表单 <form action="URL" method="post"> <textarea> .......... .......... </textarea> </form> 
【1】文字区的变数名称 -- name <textarea name=变数名称> 
【2】设定文字输入区宽度 -- cols <textarea cols=字元数> 
【3】设定文字输入区高度 -- rows <textarea rows=列数> 
【4】输入区设定预设字串 <textarea> 预设文字 </textarea> 
【5】自动换行与否 -- wrap <textarea wrap=#> #号可为 off:表输入的文字超过栏宽时,不会自动换行(预设值) virtual:表输入的文字在超过栏宽时会自动换行
链接
一、连结至其他文件 <a href="URL">说明文字或图片</a>
二、连结至文件内之某一处(外部连结) 
《1》起点
<a href="档名#名称">..........</a> 
《2》终点 <a name="名称">
三、frame的超连结 
【1】开启新的浏览器来显示连结文件 -- _blank <a href="URL" target=_blank>
【2】显示连结文件於目前的frame -- _self <a href="URL" target=_self>
【3】以上一层的分割视窗显示连结文件 -- _parent <a href="URL" target=_parent>
【4】以全视窗显示连结文件 -- _top <a href="URL" target=_top> 
【5】以特定视窗显示连结文件 --<a href="URL" target="特定视窗名称">
FRAME
一、分割视窗指令 <frameset>..........</frameset> 
【1】垂直(上下)分割 -- rows
<frameset rows=#> #号可为点数:如欲分割为100,200,300三个视窗,则
<frameset rows=100,200,300>;亦可以*号代表,如<frameset rows=*,500,*>
百分比:如<frameset rows=30%,70%>,各 项总和最好为100%
【2】水平(左右)分割 -- cols <frameset cols=点数或百分比>
二ⅱ指定视窗内容 -- <frame>
<frameset cols=30%,70%>    <frame>    <frame> </frameset>
【1】指定视窗的文件名称 -- src <frame src=HTML档名> 
【2】定义视窗的名称 -- name
<frame name=视窗名称> 
【3】设定文件与上下边框的距离 -- marginheight
<frame marginheight=点数> 
【4】设定文件与左右边框的距离 -- marginwidth
<frame marginwidth=点数> 
【5】设定分割视窗卷轴 -- scrolling
<frame scrolling=#> #号可为 yes:固定出现卷轴
no:不出现卷轴 auto:自动判断文件大小需不需要卷轴(预设值)
【6】锁住分割视窗的大小 -- noresize <frame noresize>

posted @ 2005-11-17 15:21 船夫 阅读(360) | 评论 (1)编辑 收藏

IBATIS SQLMap详解

     摘要: Tag: IBATIS    SQL    Maps                           &n...  阅读全文

posted @ 2005-11-09 19:17 船夫 阅读(9903) | 评论 (6)编辑 收藏

[数据库]JDBC连接数据库经验技巧集萃(转载自Blog For Everyone)

Java数据库连接(JDBC)由一组用 Java 编程语言编写的类和接口组成。JDBC 为工具/数据库开发人员提供了一个标准的 API,使他们能够用纯Java API 来编写数据库应用程序。然而各个开发商的接口并不完全相同,所以开发环境的变化会带来一定的配置变化。本文主要集合了不同数据库的连接方式。

  一、连接各种数据库方式速查表

  下面罗列了各种数据库使用JDBC连接的方式,可以作为一个手册使用。

  1、Oracle8/8i/9i数据库(thin模式)

Class.forName("oracle.jdbc.driver.OracleDriver").newInstance();
String url="jdbc:oracle:thin:@localhost:1521:orcl"; //orcl为数据库的SID
String user="test";
String password="test";
Connection conn= DriverManager.getConnection(url,user,password);

  2、DB2数据库

Class.forName("com.ibm.db2.jdbc.app.DB2Driver ").newInstance();
String url="jdbc:db2://localhost:5000/sample"; //sample为你的数据库名
String user="admin";
String password="";
Connection conn= DriverManager.getConnection(url,user,password);

  3、Sql Server7.0/2000数据库

Class.forName("com.microsoft.jdbc.sqlserver.SQLServerDriver").newInstance();
String url="jdbc:microsoft:sqlserver://localhost:1433;DatabaseName=mydb";
//mydb为数据库
String user="sa";
String password="";
Connection conn= DriverManager.getConnection(url,user,password);

  4、Sybase数据库

Class.forName("com.sybase.jdbc.SybDriver").newInstance();
String url =" jdbc:sybase:Tds:localhost:5007/myDB";//myDB为你的数据库名
Properties sysProps = System.getProperties();
SysProps.put("user","userid");
SysProps.put("password","user_password");
Connection conn= DriverManager.getConnection(url, SysProps);

  5、Informix数据库

Class.forName("com.informix.jdbc.IfxDriver").newInstance();
String url = "jdbc:informix-sqli://123.45.67.89:1533/myDB:INFORMIXSERVER=myserver;
user=testuser;password=testpassword"; //myDB为数据库名
Connection conn= DriverManager.getConnection(url);

  6、MySQL数据库

Class.forName("org.gjt.mm.mysql.Driver").newInstance();
String url ="jdbc:mysql://localhost/myDB?user=soft&password=soft1234&useUnicode=true&characterEncoding=8859_1"
//myDB为数据库名
Connection conn= DriverManager.getConnection(url);

  7、PostgreSQL数据库

Class.forName("org.postgresql.Driver").newInstance();
String url ="jdbc:postgresql://localhost/myDB" //myDB为数据库名
String user="myuser";
String password="mypassword";
Connection conn= DriverManager.getConnection(url,user,password);

  8、access数据库直连用ODBC的

Class.forName("sun.jdbc.odbc.JdbcOdbcDriver") ;
String url="jdbc:odbc:Driver={MicroSoft Access Driver (*.mdb)};DBQ="+application.getRealPath("/Data/ReportDemo.mdb");
Connection conn = DriverManager.getConnection(url,"","");
Statement stmtNew=conn.createStatement() ;

  二、JDBC连接MySql方式

  下面是使用JDBC连接MySql的一个小的教程

  1、查找驱动程序

  MySQL目前提供的java驱动程序为Connection/J,可以从MySQL官方网站下载,并找到mysql-connector-java-3.0.15-ga-bin.jar文件,此驱动程序为纯java驱动程序,不需做其他配置。

  2、动态指定classpath

  如果需要执行时动态指定classpath,就在执行时采用-cp方式。否则将上面的.jar文件加入到classpath环境变量中。

  3、加载驱动程序

try{
 Class.forName(com.mysql.jdbc.Driver);
 System.out.println(Success loading Mysql Driver!);
}catch(Exception e)
{
 System.out.println(Error loading Mysql Driver!);
 e.printStackTrace();
}

  4、设置连接的url

jdbc:mysql://localhost/databasename[?pa=va][&pa=va]

  三、以下列出了在使用JDBC来连接Oracle数据库时可以使用的一些技巧

  1、在客户端软件开发中使用Thin驱动程序

  在开发Java软件方面,Oracle的数据库提供了四种类型的驱动程序,二种用于应用软件、applets、servlets等客户端软件,另外二种用于数据库中的Java存储过程等服务器端软件。在客户机端软件的开发中,我们可以选择OCI驱动程序或Thin驱动程序。OCI驱动程序利用Java本地化接口(JNI),通过Oracle客户端软件与数据库进行通讯。Thin驱动程序是纯Java驱动程序,它直接与数据库进行通讯。为了获得最高的性能,Oracle建议在客户端软件的开发中使用OCI驱动程序,这似乎是正确的。但我建议使用Thin驱动程序,因为通过多次测试发现,在通常情况下,Thin驱动程序的性能都超过了OCI驱动程序。

  2、关闭自动提交功能,提高系统性能

  在第一次建立与数据库的连接时,在缺省情况下,连接是在自动提交模式下的。为了获得更好的性能,可以通过调用带布尔值false参数的Connection类的setAutoCommit()方法关闭自动提交功能,如下所示:

  conn.setAutoCommit(false);

  值得注意的是,一旦关闭了自动提交功能,我们就需要通过调用Connection类的commit()和rollback()方法来人工的方式对事务进行管理。

  3、在动态SQL或有时间限制的命令中使用Statement对象

  在执行SQL命令时,我们有二种选择:可以使用PreparedStatement对象,也可以使用Statement对象。无论多少次地使用同一个SQL命令,PreparedStatement都只对它解析和编译一次。当使用Statement对象时,每次执行一个SQL命令时,都会对它进行解析和编译。这可能会使你认为,使用PreparedStatement对象比使用Statement对象的速度更快。然而,我进行的测试表明,在客户端软件中,情况并非如此。因此,在有时间限制的SQL操作中,除非成批地处理SQL命令,我们应当考虑使用Statement对象。

  此外,使用Statement对象也使得编写动态SQL命令更加简单,因为我们可以将字符串连接在一起,建立一个有效的SQL命令。因此,我认为,Statement对象可以使动态SQL命令的创建和执行变得更加简单。

  4、利用helper函数对动态SQL命令进行格式化

  在创建使用Statement对象执行的动态SQL命令时,我们需要处理一些格式化方面的问题。例如,如果我们想创建一个将名字O'Reilly插入表中的SQL命令,则必须使用二个相连的“''”号替换O'Reilly中的“'”号。完成这些工作的最好的方法是创建一个完成替换操作的helper方法,然后在连接字符串心服用公式表达一个SQL命令时,使用创建的helper方法。与此类似的是,我们可以让helper方法接受一个Date型的值,然后让它输出基于Oracle的to_date()函数的字符串表达式。

  5、利用PreparedStatement对象提高数据库的总体效率

  在使用PreparedStatement对象执行SQL命令时,命令被数据库进行解析和编译,然后被放到命令缓冲区。然后,每当执行同一个PreparedStatement对象时,它就会被再解析一次,但不会被再次编译。在缓冲区中可以发现预编译的命令,并且可以重新使用。在有大量用户的企业级应用软件中,经常会重复执行相同的SQL命令,使用PreparedStatement对象带来的编译次数的减少能够提高数据库的总体性能。如果不是在客户端创建、预备、执行PreparedStatement任务需要的时间长于Statement任务,我会建议在除动态SQL命令之外的所有情况下使用PreparedStatement对象。

  6、在成批处理重复的插入或更新操作中使用PreparedStatement对象

  如果成批地处理插入和更新操作,就能够显著地减少它们所需要的时间。Oracle提供的Statement和 CallableStatement并不真正地支持批处理,只有PreparedStatement对象才真正地支持批处理。我们可以使用addBatch()和executeBatch()方法选择标准的JDBC批处理,或者通过利用PreparedStatement对象的setExecuteBatch()方法和标准的executeUpdate()方法选择速度更快的Oracle专有的方法。要使用Oracle专有的批处理机制,可以以如下所示的方式调用setExecuteBatch():

PreparedStatement pstmt3D null;
try {
 ((OraclePreparedStatement)pstmt).setExecuteBatch(30);
 ...
 pstmt.executeUpdate();
}

  调用setExecuteBatch()时指定的值是一个上限,当达到该值时,就会自动地引发SQL命令执行,标准的executeUpdate()方法就会被作为批处理送到数据库中。我们可以通过调用PreparedStatement类的sendBatch()方法随时传输批处理任务。

  7、使用Oracle locator方法插入、更新大对象(LOB)

  Oracle的PreparedStatement类不完全支持BLOB和CLOB等大对象的处理,尤其是Thin驱动程序不支持利用PreparedStatement对象的setObject()和setBinaryStream()方法设置BLOB的值,也不支持利用setCharacterStream()方法设置CLOB的值。只有locator本身中的方法才能够从数据库中获取LOB类型的值。可以使用PreparedStatement对象插入或更新LOB,但需要使用locator才能获取LOB的值。由于存在这二个问题,因此,我建议使用locator的方法来插入、更新或获取LOB的值。

  8、使用SQL92语法调用存储过程

  在调用存储过程时,我们可以使用SQL92或Oracle PL/SQL,由于使用Oracle PL/SQL并没有什么实际的好处,而且会给以后维护你的应用程序的开发人员带来麻烦,因此,我建议在调用存储过程时使用SQL92。

  9、使用Object SQL将对象模式转移到数据库中

  既然可以将Oracle的数据库作为一种面向对象的数据库来使用,就可以考虑将应用程序中的面向对象模式转到数据库中。目前的方法是创建Java bean作为伪装的数据库对象,将它们的属性映射到关系表中,然后在这些bean中添加方法。尽管这样作在Java中没有什么问题,但由于操作都是在数据库之外进行的,因此其他访问数据库的应用软件无法利用对象模式。如果利用Oracle的面向对象的技术,可以通过创建一个新的数据库对象类型在数据库中模仿其数据和操作,然后使用JPublisher等工具生成自己的Java bean类。如果使用这种方式,不但Java应用程序可以使用应用软件的对象模式,其他需要共享你的应用中的数据和操作的应用软件也可以使用应用软件中的对象模式。

  10、利用SQL完成数据库内的操作

  我要向大家介绍的最重要的经验是充分利用SQL的面向集合的方法来解决数据库处理需求,而不是使用Java等过程化的编程语言。

  如果编程人员要在一个表中查找许多行,结果中的每个行都会查找其他表中的数据,最后,编程人员创建了独立的UPDATE命令来成批地更新第一个表中的数据。与此类似的任务可以通过在set子句中使用多列子查询而在一个UPDATE命令中完成。当能够在单一的SQL命令中完成任务,何必要让数据在网上流来流去的?我建议用户认真学习如何最大限度地发挥SQL的功能。

posted @ 2005-11-08 11:46 船夫 阅读(373) | 评论 (0)编辑 收藏

通过Jpetstore学习WSAD、ROSE、WEBSPHERE的基本用法(摘自Matrix)

driver=org.gjt.mm.mysql.Driver
url=jdbc:mysql://localhost/jpetstore
username=root
password=root


第三步:运行工程
右键点击“jpetstoreWeb”工程,选择“在服务器上运行”
image

由于WSAD带WebSphere测试环境,全部默认就可以。

在IE运行 http://localhost:9080/jpetstoreWeb/

控制台右下有“服务器”,可以启动或者停止服务器。

第四步:重构项目

1.右键com.ibatis.jpetstore.persistence.iface.AccountDao,选择“重命名”,如为IAccountDao(我个人的习惯,接口都以I开头),所有引用AccountDao的java文件中,AccountDao也全部更改名称了。其余的几个Dao也重命名。

2.打开com.ibatis.jpetstore.service.AccountService,选择“重构”->“抽取接口”,命名IAccountService,形成接口和实现两个文件,所有对AccountService的引用也同时修改。

第五步:导出EAR文件。
二、在Websphere5.1中运行jpetstore
进入Webshpere的管理控制台,选择安装新的应用程序,导入并发布jpetstore.ear后,就可以在IE中直接运行了( http://localhost:9080/jpetstoreWeb/)。

三、用ROSE为jpetstore建模

本部分的模型,首先通过ROSE工具的反向工程(Reverse Engineer)、然后进行整理生成。

(一)、UseCase用例

?????
普通用户(user)可以搜索产品(searchProducts)、查看产品分类(viewCategory)、查看产品(viewProduct)、查看产品项目(viewItem)、注册新账号(newAccount)、登陆(sign-In)。

登陆后的用户(signedUser)可以注销登陆(sign-Out)、编辑账号(editAccount)、将产品项目放入购物车(addItemToCart)、从购物车删除产品项目(removeItemFromCart)、修改购物数量(updateCartQuantities)、新订单(newOrder)、查看订单(viewOrder)、查看购物车(viewCart)、结算(checkout)。

(二)、数据模型

?????
jpetstore的数据模型可以分三大类:

1.产品相关:
产品分类(CATEGORY)
产品(PRODUCT),也可以说是产品的小分类
单个产品(ITEM),描述具体产品的详细属性
供应商(SUPPLIER)
产品库存(INVENTORY)

2.用户账号相关:
用户账号(ACCOUNT)
用户口令(SIGNON),ibatis纯粹为了展示表关联的用法。
用户配置文件(PROFILE),存放用户个性化信息。
系统BANNER信息(BANNERDATA)

3.订单相关:
用户订单(ORDERS)
订单状态(ORDERSTATUS)
订单详情(LINEITEM)
 

(三)、WEB模型

WEB模型一向是各个建模工具的弱项,ROSE的WEB建模能力也不强,有时不能表达准确的含义。

1.新增、修改账号
?????

2.浏览产品
image

3.订单
image

4.购物车(略)

对于WEB模型,配合struts的导航图(NitroX生成,部分截图),会更清楚些。

image

(四)、类图

image

兰色方框内是通过spring+hibernate实现的。可以看到,表现层的AccountBean是面向接口调用Service的,当改为用hibernate实现O/R mapping时,只需要修改接口的实现类(DAO持久层),表现层的程序不需要任何修改。

资源:
附件一:ROSE,jpetstore.mdl
[下载文件]
附件二:jpetstoreWeb工程(不包括spring.jar、hibernate3.jar,太大了)
[下载文件]

posted @ 2005-11-06 17:26 船夫 阅读(462) | 评论 (0)编辑 收藏

开发线程安全的Spring Web应用


开发线程安全的Spring Web应用

作者:罗时飞 译    来自:open-v.com

  前言

  如果开发者正开发或维护基于Servlet的Web应用,则Servlet规范建议最好能够看看。因为它含有的内容对于Web应用开发者理解Servlet容器的工作机理很有帮助。
  其中,规范给出了Servlet容器是如何处理客户请求的。Servlet容器将会根据web.xml配置文件中定义的各个Servet而创建相应的单例。因此,多个客户请求可能同时访问这些单例,即多个线程同时访问它们。在Web应用中保证线程安全是很重要的。开发者应该对这个问题保持警惕,而且必须确保各自的代码必须以线程安全的方式运行。

  温故线程安全

  好了,本文将介绍线程安全的基本知识以及如何实现线程安全。有关线程安全,现有的资料有很多,作者很喜欢Wikipedia[译者注:http://en.wikipedia.org/wiki/Thread-safe ]这篇文章。开发者基本上可以认为,如果代码是可重入的(reentrant),或者通过某种形式的互斥而实现对并发访问的保护,则代码是线程安全的。
  大部分Java开发者都应该听过synchronized关键字。在不采用任何第三方库的前提下,Java本身对线程提供了原生支持,而且synchronized关键字往往是Java应用中实现线程安全最重要的因素。Java中的同步提供了互斥支持。通过同步一块代码或整个方法能够保证同时最多只有单个线程执行它,从而实现了线程安全。引入同步具有副作用,即阻塞。比如,大公司或律师办公室的前台小姐同时需要处理电话、邮件、受访客户等等。这使得她的工作很繁忙,而且导致一些事情不能够及时处理。
  在Web应用中需要警惕阻塞。受同步保护的代码块使得其同时处理客户请求的吞吐量降低,而且很多客户处于阻塞状态,除非某客户处理完成。而且互斥不仅会带来阻塞,还会带来死锁。通常,死锁是不可恢复的。如下条件将触发死锁的发生:线程A锁住了线程B等待的资源,而且线程B锁住了线程A等待的资源,即线程B一直在等待线程A释放锁,线程A也是如此。因此,对于多线程的应用而言,死锁的预防和处理通常都是很头疼的。
  另外,synchronized关键字还使得大量的同步对象到处使用,从而引入了死锁的可能性。比如,java.util.Hashtable和java.util.Vector中提供的方法都是受互斥保护的,因此除非确实需要使用它们,否则尽量不用。开发者只需要使用java.util.HashMap和java.util.ArrayList即可。当然,java.util.Collections中的同步方法也使用了synchronized关键字。
  尽管可重入更易于管理,但它引入了其他问题。可重入代码避免了线程间数据的共享。考虑如下代码(姑且认为Java中的方法是线程安全的):

  public Double pi() {
  int a = 22;
  int b = 7;
  return new Double(a / b);
  }

  不管同时进入该方法的线程有多少,它总是线程安全的。各个线程都维护了属于各个线程的栈,并不同其他线程共享。其中,各个线程在当前方法(包括静态方法)中创建的方法变量仅属于当前线程,即存储在当前线程的栈中。因此,当线程A和B同时进入上述方法时,它们都将创建a和b。由于上述方法不存在数据共享,因此上述方法是线程安全的。请注意:22/7值同PI值较接近,但它们不相等。

  接下来,看看如何优化上述代码吧。

  private Double pi = null;

  public Double pi() {
  if (pi == null) {
   pi = new Double(22 / 7);
  }

  return pi;
  }

  尽管改进后的方法能够提高性能,但并不是线程安全的。比如:如果pi为null,而且线程A和B同时进入第4行。因此,线程A和B会同时测试pi是否为空,它们都将返回true。接下来,如果线程A继续执行(线程B由于某种原因被暂挂),然后返回对内存地址的引用。其中,该内存地址含有22/7的结果,即pi值。最后,线程A退出方法。当线程B再次进入第5行时,新的内存地址将覆盖原先的内存地址(线程A提供的)。这太危险了,而且这种问题往往难于调试。
如果使用ThreadLocal,则不仅能够保证pi()方法是线程安全,而且能够提供性能的改善。

  private static ThreadLocal pi = new ThreadLocal();

  public Double pi() {
  if (pi.get() == null) {
  pi.set(new Double(22 / 7));
  }

  return (Double)pi.get();
  }

  ThreadLocal类能够包裹任何对象,而且能够将对象绑定到当前线程,使得它仅仅供当前线程使用。当线程初次执行pi()方法时,由于没有对象绑定到ThreadLocal实例pi上,因此get()方法返回null。借助于set()方法能够将对象绑定到当前线程,而且不供其它线程使用。因此,如果不同线程需要经常访问pi()方法,则借助于ThreadLocal不仅能够保证线程安全,而且能够提高性能。
  目前,存在很多关于如何使用ThreadLocal的资源。在Java 1.4之前,ThreadLocal的性能确实很差,但是现已解决了这个问题。另外,由于对ThreadLocal的错误理解,使得很多开发者对它的误用。注意,上述实例使用ThreadLocal的方式是绝对没问题的。在引入ThreadLocal后,上述方法的行为并未发生改变,但是方法已经是线程安全的了。
  通过可重入的方式开发线程安全的代码要求开发者谨慎使用实例变量或静态变量,尤其对于修改那些其他线程需要使用的对象而言。某些场合,使用同步可能更为合适。然而,为识别由于同步而引起的应用性能瓶颈往往只能借助于专业的性能评测工具或负载测试完成。

  Web应用中的线程安全

  好了,在温故线程安全的知识后,来研究Web应用中是如何线程安全的吧!开发者通过创建Web页面来操作数据库。比如,在Web层和业务逻辑层都能够操作RDBMS。本文使用Hibernate将业务模型持久化到数据库中。在Web层,开发者可以使用Tapestry、Wicket、Struts、WebWork、JSF、Spring MVC,或者其他运行在Web容器中的Web框架。
  至于Web层的具体实现并不是本文的重点。本文将关注如何管理数据库连接,这也是Web应用中处理线程安全问题是经常要考虑的资源。数据库连接对象,比如连接、结果集、Statement、Hibernate Session,是有状态对象。当然,它们不是线程安全的,因此不能够同时供多个线程访问。在本文前面已经提到,开发者应尽量避免使用同步。无论是synchronized关键字,还是那些同步类(Hashtable或Vector),应尽量避免使用。因此,如果使用可重入,则不用处理阻塞或死锁。
  当然,通过可重入实现线程安全以访问数据库并不是件简单的工作。比如,有些开发者可能会在Servlet容器配置中添加过滤器。因此,在客户请求到来时,过滤器将创建JDBC连接或Hibernate Session,并借助于ThreadLocal类将它们绑定到当前线程中,从而供业务逻辑使用。如果直接使用J2EE API,则开发者除了需要做很多同业务逻辑无关的操作外,还需要管理事务、DB错误等等开发内容。请注意,这些同业务逻辑无关的操作的维护工作往往很费时间。

  Spring的闯入

  一些Java开发者可能听说过Spring提供的DAO抽象。当然,一些开发者也有可能使用过它。借助于Spring提供的模板,开发者能够使用DAO代码的重用。借助于Spring AOP,开发者还能够使用声明式事务。因此,本文来研究Spring是如何实现以线程安全方式访问RDBMS的。比如,Spring允许以JDBC、Hibernate、JDO、iBATIS、TopLink等方式访问数据库。如下给出的实例是企业应用中很常见的情景。

  首先,定义数据源和用于Hibernate SessionFactory。

id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">


WEB-INF/jdbc.properties



${jdbc.driverClassName}
${jdbc.url}
${jdbc.username}
${jdbc.password}







classpath:




net.sf.hibernate.dialect.HSQLDialect
true


  这是使用Hibernate的典型配置,即通过定义的数据源连接到数据库、通过本地SessionFactory创建Hibernate SessionFactory。接下来,需要定义业务对象(实现对DB的访问)和事务管理器(通过Hibernate Session管理本地事务)。其中,业务对象暴露的方法能够在数据库中添加新的纪录,而事务管理器能够将方法包裹在事务中。它们的定义如下。

public interface CustomerDAO {
public void createCustomer(Customer customer);
}

public class HibernateCustomerDAO implements CustomerDAO {

private HibernateTemplate hibernateTemplate = null;

public void setSessionFactory(SessionFactory sessionFactory) {
this.hibernateTemplate = new HibernateTemplate(sessionFactory, false);
}

public void createCustomer(Customer customer) {
this.hibernateTemplate.save(customer);
}
}

  开发者应该已经看到,上述类使用了Spring提供的HibernateTemplate。注意,模板的开发遵循了业界最佳实践,并且将一些同业务不相关,但J2EE API规定要处理的那些代码处理掉了。与此同时,它通过DAO抽象将受查异常转换为非受查异常。当然,Spring不只是为使用Hibernate提供模板,它还为JDBC、iBATIS、SqlMap、JDO、TopLink提供类似模板。由于这些模板类及其实例变量实现了可重入,即都是线程安全的,因此允许并发线程同时使用模板。使用这些模板不仅能够实现代码的重用,还提供了最佳实践。除了以线程安全方式访问DB外,模板还提供了其他很多有意义的内容。好了,来看看如何定义业务对象和事务管理器吧!










PROPAGATION_REQUIRED
PROPAGATION_REQUIRED


  如果开发者对Spring中事务管理的配置不熟悉,则本文正好满足你们。首先,上述Spring配置片断定义了业务对象HibernateCustomerDAO,它包裹了Hibernate SessionFactory。注意,默认时,Spring中定义的JavaBean都是单例的,HibernateCustomerDAO也不例外。这意味:多个线程可能同时执行createCustomer()方法。
  其次,配置了Hibernate事务管理器,它包裹了同一Hibernate SessionFactory实例。在事务管理器每次执行时,它都会完成如下几件事情。其一,检查Hibernate Session是否绑定到当前线程。如果已绑定,则直接使用它。如果还未绑定,事务管理器将告知Hibernate SessionFactory创建新的Session,然后将创建的Session绑定到当前线程。其二,如果当前没有处于活动的事务,则事务管理器将启动新的事务,并将Session包裹进来。否则,直接参与到活动事务中。
  整个过程是通过使用Spring提供的TransactionProxyFactoryBean实现的。当然,这是一种以声明方式实现的事务管理过程。TransactionProxyFactoryBean能够为业务对象创建代理对象,从而通过事务管理器管理事务。当每次通过代理对象调用createCustomer()方法时,事务管理器将根据事务属性管理事务。当前,Spring除了提供HibernateTransactionManager事务管理器外,还为JDBC数据源、JDO、TopLink提供了相应的事务管理器。
  好了,再来看看业务对象吧!当调用createCustomer()方法时,HibernateTemplate将查找绑定到当前线程的Hibernate Session。由于上述配置文件片断传入到HibernateTemplate构建器的第二个参数为false,因此如果没有绑定Hibernate Session,则将抛出未受查异常。这对于那些未正确配置事务管理功能的场和特别有用(注意,事务管理器很重要)。一旦事务管理配置好后,Hibernate Session将绑定到当前线程,从而启动事务。请注意,HibernateTemplate不会去检查事务是否激活,也不会显示地启动或终止事务。也请注意,如果在声明的方法(事务属性中给出的)中抛出了未受查异常,则当前活动事务将回滚。至于事务属性的研究,本文不再给出。

  结论

  最后,来总结一下Spring以线程安全方式实现数据访问吧。通过使用事务管理和权衡ThreadLocal提供的功能,Spring将数据库连接(JDBC连接、Hibernate Session、JDO持久化管理器)绑定到当前线程,从而供DAO模板使用。本文在最开始研究了数据库连接并没有在线程间共享。Spring不仅提供了声明式事务管理、J2EE API抽象、最佳实践,而且其提供的模板是线程安全的。当使用Spring访问DB时,通过可重入实现应用的线程安全是最为可靠、常见的做法。

posted @ 2005-11-04 09:52 船夫 阅读(231) | 评论 (0)编辑 收藏