2006年8月23日
北京英语角(BeiJing)www.52en.com
=================================
北大英语角:每周六晚7:00 俄文楼前空地
清华英语角:每周三晚7:00-10:00 西阶(west of Great Hall)
人大英语角:每周五晚 人大东门花园,老外多,质量不错
北外英语角:每周五晚6:30-8:30外研社后花园
朝阳文化馆:每周六下午3:00-5:00 门票6元地点在小庄,收费,但质量好
posted @
2007-04-25 09:29 阿成 阅读(2113) |
评论 (6) |
编辑 收藏
1.未到火候,千万别说。有些男孩才跟人家约过几次,就提出要建立“更进一步”的关系,这十有八九要坏汤。爱情上的事妙就妙在一切尽在不言中。说清楚了,话挑明了,反而不美。本来人家肯单独出来跟你说话,这说明女孩对你不无好感。但是这种好感有时连她们自己都说不清,你却急于挑明,破坏了这种朦胧美,那就别怪人家敬而远之以致退避三舍了。看看周围那些还没开始就夭折的爱情,许多都是由男孩没有掌握好火候造成的。
2.收起拳头是为了更好的出击。被女孩拒绝了怎么办呢?有人说应该穷追猛打、坚持不懈。其实不然。除非你是一个啥也不在乎的人,否则你很难承受女孩的白眼和同伴们嘲弄的目光。倒不如偃旗息鼓、暂时撤退。这不仅保护了你的尊严而且还留了条后路,从而为日后反攻创造机会。就让她在人群里闯荡。等她碰得头破血流时,你再去找她,她才能认识到原来你才是最爱她的人。即使实在割舍不下,要去找她,也应注意方式方法。千万别让她觉得你讨厌。而这一点往往是很难做到的。
3.不要太露骨。要学会不声不响地关心她,用你的诚实和善意对待她。只有这样你才能在一大帮围着她呱呱乱叫的男孩当中引起她的注意。记住,只有特别的你才会引起她特别的关注。
4.非请勿入。一个老是往女孩寝室跑的男孩是不会引起女孩太多的好感的。有些学生会干部借口工作常往女生寝室跑,去了后就老赖在那不走,结果给人家带来了诸多不便,效果只会适得其反。产生这种结果的根本原因还是因为太露骨。
5.战略上藐视...,战术上重视...。有时你喜爱的女孩会和你有些接触,比如谈谈话、聊聊天、一起工作等等。你最好能以平常的心态看待这些事情,不要背上包袱、患得患失。例如不要太在意她无意中说的话,有些男孩容易自作多情,源出于此。但对每一次这样的机会则应引起足够的重视,例如初次谈话要注意掌握好分寸,最好不要涉及情爱范畴,不妨说说小时候的事。你如果只奔主体那就要糟!女孩非得象警惕狼一样地警惕你。
6.马屁最好少拍。你夸她长得漂亮,如果她真的漂亮,那么你不过是第七百个夸她漂亮的人,而她映象最深的恐怕是第一个这样夸她的人;如果她相貌普通,你这样夸她能产生什么样的结果谁也说不准。要是让她发现你言不由衷,那你就死定了。记住,哄女孩只有在女孩成为你的女朋友之后才能哄,还没追到手就开始哄是没有掌握好火候的表现。
7.少来点大男子主义。有个男孩好不容易请得他倾慕已久的女孩去吃饭,花了他半个月生活费。后来他去付账时发现女孩已经替他付了,他就要还她钱。女孩不愿意。他觉得女孩剥了他的面子,大为光火。女孩气得哭了。本来女孩替他付账这说明她对他有好感,他不思讨好反而发脾气,如此就难怪他要打光棍了。几乎没有一个女孩会对那些不尊重女性的男孩有好感,切记!切记!。
8.团结大多数。几乎每个女孩都有一两个最知心的女友,当其他道路都不通时,她们是你通往胜利的成功之路。你可以通过她们了解女孩到底对你有没有意思,还可以通过她们传达你的意思。这比你直接说要好得多。可千万别忽视这一支生力军,我不止一次地看到男孩通过这个方法大功告成的。
最后要提醒大家的是,爱情从来都没有固定的公式。别人成功的方法也许正是你的败着;别人失败的方法也许恰是你的妙着。我这里只提一点原则性的东西,切不可生搬硬套。如果因此而坏了你的好事切莫来找我。女孩要是不喜欢你,就是玉皇大帝也没有法子。还是古人说的好:不战而屈人之兵,上之上策也。
posted @
2007-04-23 19:12 阿成 阅读(494) |
评论 (0) |
编辑 收藏
ThreadLocal
类是悄悄地出现在 Java 平台版本 1.2 中的。虽然支持线程局部变量早就是许多线程工具(例如 Posix pthreads
工具)的一部分,但 Java Threads API 的最初设计却没有这项有用的功能。而且,最初的实现也相当低效。由于这些原因, ThreadLocal
极少受到关注,但对简化线程安全并发程序的开发来说,它却是很方便的。在 轻松使用线程的第 3 部分,Java 软件顾问 Brian Goetz 研究了 ThreadLocal
并提供了一些使用技巧。
参加 Brian 的 多线程 Java 编程讨论论坛以获得您工程中的线程和并发问题的帮助。
编写线程安全类是困难的。它不但要求仔细分析在什么条件可以对变量进行读写,而且要求仔细分析其它类能如何使用某个类。 有时,要在不影响类的功能、易用性或性能的情况下使类成为线程安全的是很困难的。有些类保留从一个方法调用到下一个方法调用的状态信息,要在实践中使这样的类成为线程安全的是困难的。
管理非线程安全类的使用比试图使类成为线程安全的要更容易些。非线程安全类通常可以安全地在多线程程序中使用,只要您能确保一个线程所用的类的实例不被其它线程使用。例如,JDBC Connection
类是非线程安全的 — 两个线程不能在小粒度级上安全地共享一个 Connection
— 但如果每个线程都有它自己的 Connection
,那么多个线程就可以同时安全地进行数据库操作。
不使用 ThreadLocal
为每个线程维护一个单独的 JDBC 连接(或任何其它对象)当然是可能的;Thread API 给了我们把对象和线程联系起来所需的所有工具。而 ThreadLocal 则使我们能更容易地把线程和它的每线程(per-thread)数据成功地联系起来。
什么是线程局部变量(thread-local variable)?
线程局部变量高效地为每个使用它的线程提供单独的线程局部变量值的副本。每个线程只能看到与自己相联系的值,而不知道别的线程可能正在使用或修改它们自己的副本。一些编译器(例如 Microsoft Visual C++ 编译器或 IBM XL FORTRAN 编译器)用存储类别修饰符(像 static
或 volatile
)把对线程局部变量的支持集成到了其语言中。Java 编译器对线程局部变量不提供特别的语言支持;相反地,它用 ThreadLocal
类实现这些支持, 核心 Thread
类中有这个类的特别支持。
因为线程局部变量是通过一个类来实现的,而不是作为 Java 语言本身的一部分,所以 Java 语言线程局部变量的使用语法比内建线程局部变量语言的使用语法要笨拙一些。要创建一个线程局部变量,请实例化类 ThreadLocal
的一个对象。 ThreadLocal
类的行为与 java.lang.ref
中的各种 Reference
类的行为很相似; ThreadLocal
类充当存储或检索一个值时的间接句柄。清单 1 显示了 ThreadLocal
接口。
清单 1. ThreadLocal 接口
public class ThreadLocal {
public Object get();
public void set(Object newValue);
public Object initialValue();
}
|
get()
访问器检索变量的当前线程的值; set()
访问器修改当前线程的值。 initialValue()
方法是可选的,如果线程未使用过某个变量,那么您可以用这个方法来设置这个变量的初始值;它允许延迟初始化。用一个示例实现来说明 ThreadLocal 的工作方式是最好的方法。清单 2 显示了 ThreadLocal
的一个实现方式。它不是一个特别好的实现(虽然它与最初实现非常相似),所以很可能性能不佳,但它清楚地说明了 ThreadLocal 的工作方式。
清单 2. ThreadLocal 的糟糕实现
public class ThreadLocal {
private Map values = Collections.synchronizedMap(new HashMap());
public Object get() {
Thread curThread = Thread.currentThread();
Object o = values.get(curThread);
if (o == null && !values.containsKey(curThread)) {
o = initialValue();
values.put(curThread, o);
}
return o;
}
public void set(Object newValue) {
values.put(Thread.currentThread(), newValue);
}
public Object initialValue() {
return null;
}
}
|
这个实现的性能不会很好,因为每个 get()
和 set()
操作都需要 values
映射表上的同步,而且如果多个线程同时访问同一个 ThreadLocal
,那么将发生争用。此外,这个实现也是不切实际的,因为用 Thread
对象做 values
映射表中的关键字将导致无法在线程退出后对 Thread
进行垃圾回收,而且也无法对死线程的 ThreadLocal
的特定于线程的值进行垃圾回收。
用 ThreadLocal 实现每线程 Singleton
线程局部变量常被用来描绘有状态“单子”(Singleton) 或线程安全的共享对象,或者是通过把不安全的整个变量封装进 ThreadLocal
,或者是通过把对象的特定于线程的状态封装进 ThreadLocal
。例如,在与数据库有紧密联系的应用程序中,程序的很多方法可能都需要访问数据库。在系统的每个方法中都包含一个 Connection
作为参数是不方便的 — 用“单子”来访问连接可能是一个虽然更粗糙,但却方便得多的技术。然而,多个线程不能安全地共享一个 JDBC Connection
。如清单 3 所示,通过使用“单子”中的 ThreadLocal
,我们就能让我们的程序中的任何类容易地获取每线程 Connection
的一个引用。这样,我们可以认为 ThreadLocal
允许我们创建 每线程单子。
清单 3. 把一个 JDBC 连接存储到一个每线程 Singleton 中
public class ConnectionDispenser {
private static class ThreadLocalConnection extends ThreadLocal {
public Object initialValue() {
return DriverManager.getConnection(ConfigurationSingleton.getDbUrl());
}
}
private ThreadLocalConnection conn = new ThreadLocalConnection();
public static Connection getConnection() {
return (Connection) conn.get();
}
}
|
任何创建的花费比使用的花费相对昂贵些的有状态或非线程安全的对象,例如 JDBC Connection
或正则表达式匹配器,都是可以使用每线程单子(singleton)技术的好地方。当然,在类似这样的地方,您可以使用其它技术,例如用池,来安全地管理共享访问。然而,从可伸缩性角度看,即使是用池也存在一些潜在缺陷。因为池实现必须使用同步,以维护池数据结构的完整性,如果所有线程使用同一个池,那么在有很多线程频繁地对池进行访问的系统中,程序性能将因争用而降低。
用 ThreadLocal 简化调试日志纪录
其它适合使用 ThreadLocal
但用池却不能成为很好的替代技术的应用程序包括存储或累积每线程上下文信息以备稍后检索之用这样的应用程序。例如,假设您想创建一个用于管理多线程应用程序调试信息的工具。您可以用如清单 4 所示的 DebugLogger
类作为线程局部容器来累积调试信息。在一个工作单元的开头,您清空容器,而当一个错误出现时,您查询该容器以检索这个工作单元迄今为止生成的所有调试信息。
清单 4. 用 ThreadLocal 管理每线程调试日志
public class DebugLogger {
private static class ThreadLocalList extends ThreadLocal {
public Object initialValue() {
return new ArrayList();
}
public List getList() {
return (List) super.get();
}
}
private ThreadLocalList list = new ThreadLocalList();
private static String[] stringArray = new String[0];
public void clear() {
list.getList().clear();
}
public void put(String text) {
list.getList().add(text);
}
public String[] get() {
return list.getList().toArray(stringArray);
}
}
|
在您的代码中,您可以调用 DebugLogger.put()
来保存您的程序正在做什么的信息,而且,稍后如果有必要(例如发生了一个错误),您能够容易地检索与某个特定线程相关的调试信息。 与简单地把所有信息转储到一个日志文件,然后努力找出哪个日志记录来自哪个线程(还要担心线程争用日志纪录对象)相比,这种技术简便得多,也有效得多。
ThreadLocal
在基于 servlet 的应用程序或工作单元是一个整体请求的任何多线程应用程序服务器中也是很有用的,因为在处理请求的整个过程中将要用到单个线程。您可以通过前面讲述的每线程单子技术用 ThreadLocal
变量来存储各种每请求(per-request)上下文信息。
ThreadLocal 的线程安全性稍差的堂兄弟,InheritableThreadLocal
ThreadLocal 类有一个亲戚,InheritableThreadLocal,它以相似的方式工作,但适用于种类完全不同的应用程序。创建一个线程时如果保存了所有 InheritableThreadLocal
对象的值,那么这些值也将自动传递给子线程。如果一个子线程调用 InheritableThreadLocal
的 get()
,那么它将与它的父线程看到同一个对象。为保护线程安全性,您应该只对不可变对象(一旦创建,其状态就永远不会被改变的对象)使用 InheritableThreadLocal
,因为对象被多个线程共享。 InheritableThreadLocal
很合适用于把数据从父线程传到子线程,例如用户标识(user id)或事务标识(transaction id),但不能是有状态对象,例如 JDBC Connection
。
ThreadLocal 的性能
虽然线程局部变量早已赫赫有名并被包括 Posix pthreads
规范在内的很多线程框架支持,但最初的 Java 线程设计中却省略了它,只是在 Java 平台的版本 1.2 中才添加上去。在很多方面, ThreadLocal
仍在发展之中;在版本 1.3 中它被重写,版本 1.4 中又重写了一次,两次都专门是为了性能问题。
在 JDK 1.2 中, ThreadLocal
的实现方式与清单 2 中的方式非常相似,除了用同步 WeakHashMap
代替 HashMap
来存储 values 之外。(以一些额外的性能开销为代价,使用 WeakHashMap 解决了无法对 Thread 对象进行垃圾回收的问题。)不用说, ThreadLocal
的性能是相当差的。
Java 平台版本 1.3 提供的 ThreadLocal
版本已经尽量更好了;它不使用任何同步,从而不存在可伸缩性问题,而且它也不使用弱引用。相反地,人们通过给 Thread
添加一个实例变量(该变量用于保存当前线程的从线程局部变量到它的值的映射的 HashMap
)来修改 Thread
类以支持 ThreadLocal
。因为检索或设置一个线程局部变量的过程不涉及对可能被另一个线程读写的数据的读写操作,所以您可以不用任何同步就实现 ThreadLocal.get()
和 set()
。而且,因为每线程值的引用被存储在自已的 Thread
对象中,所以当对 Thread
进行垃圾回收时,也能对该 Thread
的每线程值进行垃圾回收。
不幸的是,即使有了这些改进,Java 1.3 中的 ThreadLocal
的性能仍然出奇地慢。据我的粗略测量,在双处理器 Linux 系统上的 Sun 1.3 JDK 中进行 ThreadLocal.get()
操作,所耗费的时间大约是无争用同步的两倍。性能这么差的原因是 Thread.currentThread()
方法的花费非常大,占了 ThreadLocal.get()
运行时间的三分之二还多。虽然有这些缺点,JDK 1.3 ThreadLocal.get()
仍然比争用同步快得多,所以如果在任何存在严重争用的地方(可能是有非常多的线程,或者同步块被频繁地执行,或者同步块很大), ThreadLocal
可能仍然要高效得多。
在 Java 平台的最新版本,即版本 1.4b2 中, ThreadLocal
和 Thread.currentThread()
的性能都有了很大提高。有了这些提高, ThreadLocal
应该比其它技术,如用池,更快。由于它比其它技术更简单,也更不易出错,人们最终将发现它是避免线程间出现不希望的交互的有效途径。
ThreadLocal 的好处
ThreadLocal
能带来很多好处。它常常是把有状态类描绘成线程安全的,或者封装非线程安全类以使它们能够在多线程环境中安全地使用的最容易的方式。使用 ThreadLocal
使我们可以绕过为实现线程安全而对何时需要同步进行判断的复杂过程,而且因为它不需要任何同步,所以也改善了可伸缩性。除简单之外,用 ThreadLocal
存储每线程单子或每线程上下文信息在归档方面还有一个颇有价值好处 — 通过使用 ThreadLocal
,存储在 ThreadLocal
中的对象都是 不被线程共享的是清晰的,从而简化了判断一个类是否线程安全的工作。
我希望您从这个系列中得到了乐趣,也学到了知识,我也鼓励您到我的 讨论论坛中来深入研究多线程问题。
posted @
2007-04-09 20:52 阿成 阅读(2406) |
评论 (1) |
编辑 收藏
Servlet是在多线程环境下的。即可能有多个请求发给一个servelt实例,每个请求是一个线程。
struts下的action也类似,同样在多线程环境下。可以参考struts user guide: http://struts.apache.org/struts-action/userGuide/building_controller.html 中的Action Class Design Guidelines一节: Write code for a multi-threaded environment - Our controller servlet creates only one instance of your Action class, and uses this one instance to service all requests. Thus, you need to write thread-safe Action classes. Follow the same guidelines you would use to write thread-safe Servlets.
译:为多线程环境编写代码。我们的controller servlet指挥创建你的Action 类的一个实例,用此实例来服务所有的请求。因此,你必须编写线程安全的Action类。遵循与写线程安全的servlet同样的方针。
1.什么是线程安全的代码
在多线程环境下能正确执行的代码就是线程安全的。
安全的意思是能正确执行,否则后果是程序执行错误,可能出现各种异常情况。
2.如何编写线程安全的代码
很多书籍里都详细讲解了如何这方面的问题,他们主要讲解的是如何同步线程对共享资源的使用的问题。主要是对synchronized关键字的各种用法,以及锁的概念。
Java1.5中也提供了如读写锁这类的工具类。这些都需要较高的技巧,而且相对难于调试。
但是,线程同步是不得以的方法,是比较复杂的,而且会带来性能的损失。等效的代码中,不需要同步在编写容易度和性能上会更好些。
我这里强调的是什么代码是始终为线程安全的、是不需要同步的。如下:
1)常量始终是线程安全的,因为只存在读操作。
2)对构造器的访问(new 操作)是线程安全的,因为每次都新建一个实例,不会访问共享的资源。
3)最重要的是:局部变量是线程安全的。因为每执行一个方法,都会在独立的空间创建局部变量,它不是共享的资源。局部变量包括方法的参数变量。
struts user guide里有:
Only Use Local Variables - The most important principle that aids in thread-safe coding is to use only local variables, not instance variables , in your Action class.
译:只使用用局部变量。--编写线程安全的代码最重要的原则就是,在Action类中只使用局部变量,不使用实例变量。
总结:
在Java的Web服务器环境下开发,要注意线程安全的问题。最简单的实现方式就是在Servlet和Struts Action里不要使用类变量、实例变量,但可以使用类常量和实例常量。
如果有这些变量,可以将它们转换为方法的参数传入,以消除它们。
注意一个容易混淆的地方:被Servlet或Action调用的类中(如值对象、领域模型类)中是否可以安全的使用实例变量?如果你在每次方法调用时
新建一个对象,再调用它们的方法,则不存在同步问题---因为它们不是多个线程共享的资源,只有共享的资源才需要同步---而Servlet和Action的实例对于多个线程是共享的。
换句话说,Servlet和Action的实例会被多个线程同时调用,而过了这一层,如果在你自己的代码中没有另外启动线程,且每次调用后续业务对象时都是先新建一个实例再调用,则都是线程安全的。
posted @
2007-04-09 20:31 阿成 阅读(473) |
评论 (0) |
编辑 收藏
java.lang.Object
|
+--javax.servlet.GenericServlet
|
+--javax.servlet.http.HttpServlet
|
+--org.apache.struts.action.ActionServlet
Struts提供了一个缺省版本的ActionServlet类,你可以继承这个类,覆盖其中的一些方法来达到你的特殊处理的需要。ActionServlet继承与javax.servlet.http.HttpServlet,所以在本质上它和一个普通的servlet没有区别,你完全可以把它当做一个servlet来看待,只是在其中完成的功能不同罢了。ActionServlet主要完成如下功能:
将一个来自客户端的URI映射到一个相应的Action类
- 如果是这个Action类是第一次被调用,那么实例化一个并放入缓存
- 如果在配置文件(struts-config.xml)中指定了相应的ActionForm,那么从Request中抓取数据填充FormBean
- 调用这个Action类的perform()方法,传入ActionMapping的一个引用,对应的ActionForm、以及由容器传给ActionServlet的HttpServletRequest、HttpServletResponse对象。
确省版本的ActionServlet会从配置文件web.xml中读取如下初始化参数:
- application
应用使用的资源包(resources bundle)的基类
- factory
用于创建应用的MessageResources对象的MessageResourcesFactory的类名。确省是org.apache.struts.util.PropertyMessageResourcesFactory。
- config
Struts的配置文件,确省是/WEB-INF/struts-config.xml。注意这儿是与应用Context关联的相对路径。
- content
定义了确省的内容类型和编码格式,它会被自动地被设置到每个response中,如果JSP/Servlet中没有明确的设置。确省是text/html。
- debug
调试信息的级别。默认为0,比当前级别高的调试信息会被log到日志文件中。
- detail
与debug的作用类似,只是这个detail是initMapping()时专用的。调试信息会被打印到System.out,而不是日志文件。
- formBean
ActionFormBean的实现类,确省为org.apache.struts.action.ActionFormBean
- forward
应用中使用的ActionForward类,确省是org.apache.struts.action.ActionForward。
- locale
指定了确省使用的Locale对象。设为true,当得到一个session时,会自动在session中存储一个以Action.LOCALE_KEY标示的Locale对象,如果session中还没有与Action.LOCALE_KEY绑定的Locale对象。
- mapping
应用中使用的ActionMapping类,确省是org.apache.struts.action.ActionMapping。
- multipartClass
文件上传使用的MutipartRequestHandler的实现类。确省为org.apache.struts.upload.DiskMultipartRequestHandler
- nocache
如果设为true,那么ActionServlet会自动在每个到客户端的响应中添加nocache的HTML头,这样客户端就不会对应用中的页面进行缓存。确省为false
- null
如果设置为true,那么应用在得到一个未定义的message资源时,会返回null,而不是返回一个错误信息。确省是true。
- maxFileSize
文件上传的大小上限,确省为250M
- bufferSize
文件上传时的缓冲区的大小,确省为4M
- tempDir
设置用于上传时的临时目录。工作目录会作为一个Servlet环境(Context)的属性提供。
- validate
Are we using the new configuration file format?确省为true。
- validating
在解析配置XML文件是是否进行有效性的验证。确省为true
ActionServlet中应用了命令设计模式。
一个Servlet在由容器生成时,首先会调用init()方法进行初始化,在接到一个HTTP请求时,调用相应的方法进行处理;比如GET请求调用doGet()方法,POST请求调用doPost()方法。所以首先看看ActionServlet的init()方法,你就会很清楚为什么ActionServlet可以完成这些功能了。
init()
在它的init()方法中,ActionServlet依次调用如下protected的方法完成初始化:
- initActions() - 大家可能还曾有这个疑问:Struts为什么可以找到一个请求URI对应的action类呢?答案就在这儿,ActionServlet有一个actions属性,类型为org.apache.struts.util.FastHashMap,用于存储以类的全名为key的已实例化的Action类。在init()时首先调用的就是initActions()方法,在这个方法中只是简单的清除map中的所有的名值对,
-
- synchronized (actions) {
- actions.setFast(false);
- actions.clear();
- actions.setFast(true);
- }
首先把actions设为slow模式,这时对FastHashMap的访问是线程同步的,然后清除actions中的所有的已存在的名/值对,最后再把actions的模式设为fast。由于FastHashMap是struts在java.util.HashMap的基础上的一个扩展类,是为了适应多线程、并且对HashMap的访问大部分是只读的特殊环境的需要。大家知道java.util.HashMap是非线程安全的,所以HashMap一般适用于单线程环境下。org.apache.struts.FastHashMap就是继承于java.util.HashMap,在其中添加多线程的支持产生的。在fast模式下的工作方式是这样的:读取是非线程同步的;写入时首先克隆当前map,然后在这个克隆上做写入操做,完成后用这个修改后的克隆版本替换原来的map。那么在什么时候会把Actions类添加到这个map中呢?我们已经提到了struts是动态的生成Action类的实例的,在每次ActionServlet接收到一个GET或POST的HTTP请求时,会在这个map中查找对应的Action类的实例,如果不存在,那么就实例化一个,并放入map中。可见这个actions属性起到了对Action类实例的缓存的作用。
- initInternal() - 初始化ActionServlet内部使用的资源包MessageResources,使用MessageResources.getMessageResources(internalName)得到 internalName为"org.apache.struts.action.ActionResources"对应的ActionResources.properties文件。这个资源包主要用于ActionServlet处理过程中的用到的提示信息,这儿不展开讨论。
- initDebug() - 从web.xml中读取本应用的debug级别参数getServletConfig().getInitParameter("debug"),然后赋给debug属性。
- initApplication()- 初始化应用资源包,并放置入ServletContext中。
-
- String factory =getServletConfig().getInitParameter(“factory”);
- String oldFacory = MessageResourcesFactory.getFactoryClass();
- if (factory !=null)
- MessageResourcesFactory.setFactoryClass(factory);
- String value = getServletConfig().getInitParameter("application");
- MessageResourcesFactory factoryObject =
- MessageResourcesFactory.createFactory();
- application = factoryObject.createResources(value);
- MessageResourcesFactory.setFactory(oldFactory);
- getServletContext().setAttribute(Action.MESSAGES_KEY, application);
说明:文中引用的代码片断可能会省略了一些例外检查等非主线的内容,敬请注意。
首先从配置文件中读取factory参数,如果这个参数不为空,那么就在MessageResourcesFactory中使用这个指定的Factory类;否则,使用默认的工厂类org.apche.struts.util.PropertyMessageResourceFactory。然后调用MessageResourcesFactory的静态createFactory()方法,生成一个具体的MessageResourceFactory对象(注意:MessageResourcesFactory是抽象类)。这样就可以调用这个具体的MessageResourceFactory的createResource()方法得到配置文件(web.xml)中定义的资源文件了。
上面的application对象类型为MessageResources。在web.xml中在配置ActionServlet时可以指定一个特定的工厂类。不能直接MessageResourcesFactory的createResources()方法,因为这个方法是abstract的。创建factoryObject的过程如下:
-
- MessageResourceFactory factoryObject=
- MessageResourcesFactory.createFactory();
- application = factoryObject.createResources(value);
<li>initMapping() - 为应用初始化mapping信息ActionServlet有一个protected的属性:mapping,封装了一个ActionMapping的对象集合,以便于管理、查找ActionMapping。mappings是org.apache.struts.action.ActionMappings类的实例。主要有两个方法:addMapping(ActionMapping mapping)和findMapping(String path)。ActionMapping也是使用上面提到的org.apache.struts.util.FastHashMap类来存储所有的ActionMapping对象。
-
- mappings.setServlet(this);
- ……
- // Initialize the name of our ActionFormBean implementation class
- value = getServletConfig().getInitParameter("formBean");
- if (value != null)
- formBeanClass = value;
-
- // Initialize the name of our ActionForward implementation class
- value = getServletConfig().getInitParameter("forward");
- if (value != null)
- forwardClass = value;
-
- // Initialize the name of our ActionMapping implementation class
- value = getServletConfig().getInitParameter("mapping");
- if (value != null)
- mappingClass = value;
在initMapping()中,首先链接mappings对象到本servlet实例。其实这句话的作用很简单,在ActionMappings中会有一个ActionServlet类型的属性,这个属性就界定了这个ActionMappings对象所属的ActionServlet。Struts的实现比较灵活,其中的ActionFormBean、ActionForward、ActionMapping类你完全可以使用自己实现的子类,来定制Struts的工作方式。上面的代码就从配置文件(web.xml)中读取formBean、forward、mapping参数,这些参数就是你定制的ActionFormBean、ActionForward、ActionMapping类名。
-
- // Initialize the context-relative path to our configuration resources
- value = getServletConfig().getInitParameter("config");
- if (value != null)
- config = value;
- // Acquire an input stream to our configuration resource
- InputStream input = getServletContext().getResourceAsStream(config);
- Digester digester = null;
- digester = initDigester(detail);
- try {
- formBeans.setFast(false);
- forwards.setFast(false);
- mappings.setFast(false);
- digester.parse(input);
- mappings.setFast(true);
- forwards.setFast(true);
- formBeans.setFast(true);
- } catch (SAXException e) {
- throw new ServletException
- (internal.getMessage("configParse", config), e);
- } finally {
- input.close();
- }
从web.xml读取Struts的配置文件的位置。使用org.apache.struts.digester.Digester解析config参数标示的配置文件,通常为“/WEB-INF/struts-config.xml”,解析出所有的data-source、form-bean、action-mapping、forward。从上面的程序片断看到,Digester仅仅调用了一个parse()方法,那么,Digester是怎样把解析struts-config.xml文件并把解析的结果form-bean等信息存储到属性变量formBeans等中的呢?你可以注意到在调用digester.parse(InputStream)之前,首先调用了initDigester()方法:
-
- Digester digester = new Digester();
- digester.push(this);
- digester.addObjectCreate("struts-config/action-mappings/action",
- mappingClass, "className");
- digester.addSetProperties("struts-config/action-mappings/action");
- digester.addSetNext("struts-config/action-mappings/action",
- "addMapping",
- "org.apache.struts.action.ActionMapping");
-
- digester.addSetProperty
- ("struts-config/action-mappings/action/set-property",
- "property", "value");
在这个方法中首先生成一个Digester对象,然后设置解析的规则和回调,如果你对XML、SAX不是很熟,这儿不必纠缠太深。要注意的是addSetNext()方法,设置了每一个要解析元素的Set Next回调方法,而这个方法就是由digester解析器的父提供的。上面的片断中的“addMapping”就是ActionServlet本身定义的一个方法,将由Digester回调。Digester就是籍此把解析出的每一个FormBean、ActionForward、ActionMapping等存储到属性变量formBeans、forwards、mappings等中的。
- initUpload() - 初始化有关Upload的一些参数,比如:bufferSize、tempDir。
- initDataSource() -取出在initMapping()中从配置文件中读取的每一个DataSource,设置LogWriter,如果为GenericDataSource的实例,则打开数据源。然后,把每个dataSource放入Context中。
dataSource.setLogWriter(scw);
((GenericDataSource)dataSource).open();
getServletContext().setAttribute(key,dataSource);
- initOther() - 设置其它尚未初始化的的参数(content、locale、nocache),并发布formBeans、forwards、mappings到Context:
getServletContext().setAttribute(Action.FORM_BEANS_KEY, formBeans);
getServletContext().setAttribute(Action.FORWARDS_KEY, forwards);
getServletContext().setAttribute(Action.MAPPINGS_KEY, mappings);
- initServlet() - 初始化Controller Servlet的Servlet Mapping。这儿也使用了Digester工具,扫描web.xml所有的<web-app/servlet-mapping>,寻找servlet-name与当前Servlet相同的mapping,置入Context。代码如下;
-
- Digester digester = new Digester();
- digester.push(this);
- digester.setDebug(debug);
- digester.setValidating(validating);
- digester.addCallMethod(“web-appservlet-mapping”,“addServletMapping”, 2);
- digester.addCallParm(“web-appservlet-mappingservlet-name”, 0);
- digester.addCallParm(“web-appservlet-mappingurl-pattern”, 1);
- InputStream is = getServletContext().getResourceAsStream(“/WEB-INFweb.xml”);
- digester.parse(is);
- getServletContext().setAttribute(Action.SERVLET_KEY,servletMapping);
posted @
2007-04-06 09:48 阿成 阅读(435) |
评论 (0) |
编辑 收藏
关键字: 转贴
CSS全称Cascading Style Sheet。层叠式样式表。从三年前就开始使用CSS了,但一直以来都小看了它。CSS的出现其实是一次革命,它试图将网站的内容与表现分开。 一、CSS的四种实现方式: 1.内嵌式:
2.外链式:
3.导入式
3.属性式: 二.CSS的定义: 选择对象{属性1:值1;属性2:值2;属性3:值3;属性n:值n……} 如: td{font-size:12px;color:#FFFF00} .myname{font-size:12px;color:#FFFF00} a:hover{font-size:12px;color:#FFFF00;text-decoration: underline;} 三.四种选择对象 1.HTML selector (TagName) 2.class selector (.NAME) 3.ID selector (#IDname) 4.特殊对象 (a:hover a:link a:visited a:active) 1.HTML selector HTML selector就是HTML的置标符,如:DIV、TD、H1。HTML selector的作用范围是应用了该样式的所有页面中的所有该置标符。 例: td { color: #FF0000; } --> 注意:在中没有应用什么,其中文字自动变红色。 2.class selector 定义class selector需要往其名称其加一个点“.”。如“.classname”。class selector的作用范围是所有包含“class="classname"”的置标符。 例: .fontRed { color: #FF0000; } --> 注意:在第二个中没有“class="fontRed"”,所以文字没有变红色。 3.ID selector 定义ID selector需要往其名称其加一个点“#”。如“#IDname”。ID selector的作用范围是所有包含“ID="classname"”的置标符。 例:
#fontRed { color: #FF0000; } --> 注意:在第二个中没有“ID="fontRed"”,所以文字没有变红色。 4.特殊对象 特殊对象包括四种,是针对链接对象设置的: a:hover 鼠标移上时的超链接 a:link 常规,非访问超链接 a:visited 访问过的超链接 a:active 鼠标点击时的超链接 特殊对象的作用范围是所有置标符(这句话有待商榷,因为下面很快就有一种方法可以把“所有”两个字推翻)。 例: a:hover { color: #0000FF; text-decoration: underline; } --> 注意下面,很有用!!! a.classname:hover a#IDname:hover 这两种写法,是分别配合.classname与#IDname使用的。它的作用范围变成了所有包含“class="classname"”或“ID="IDname"”的置标符。这种写法,可以帮助你在同一页面中实现多种a:hover效果,可以看一下艺网的主页上导航栏文字与普通文章标题在鼠标时的区别。 四.应用: 1.置标符 自动应用 2.特制类 class="NAME" 3.ID ID="IDname" 4.特殊对象 自动应用 五.CSS属性 CSS的属性有很多,像上文中用到最多的color,表示文字的颜色。background-color表示背景色。这个是最主要的,但是因为没有什么难度,参考一下相关手册就可以了。
注明两点 第一点:css的方式现在一般都采用外部连接,这个用起来方便,编译起来也方便 第二点:我用的是frontpage2003,很多css的参数都会自动提示,似乎好像以前我没有用到过~~dw有的
CSS 标签属性/属性参考 这里列出了目前支持的样式表(CSS)标签属性。标有星号(*)的标签属性可于 Microsoft® Internet Explorer 5 中使用。标有两个星号(**)的标签属性可于 Internet Explorer 5.5 中使用。带有两个加号(++)的标签属性可于 Internet Explorer 6 中使用。如果某个标签属性或属性已经提交给万维网协会(W3C)但尚未作为标准发布,则标有“已提交”。若某个标签属性或属性既未提交给 W3C 也不是标准,将标有“扩展”。
CSS 标签属性/属性
行为属性 behavior
字体和文本属性 direction*
direction font font-family font-size font-style font-variant font-weight ime-mode layout-grid layout-grid-char layout-grid-line layout-grid-mode layout-grid-type letter-spacing line-break line-height min-height ++ ruby-align ruby-overhang ruby-position text-align text-autospace text-decoration text-indent text-justify text-kashida-space text-overflow ++ text-transform text-underline-position unicode-bidi vertical-align white-space** word-break word-spacing ++(于 Macintosh 版本 4.0 中可用) word-wrap writing-mode
颜色和背景属性 background-attachment background-color background-image background-position background-position-x background-position-y background-repeat color
版面属性 border border-bottom border-bottom-color border-bottom-style border-bottom-width border-collapse* border-color border-left border-left-color border-left-style border-left-width border-right border-right-color border-right-style border-right-width border-style border-top border-top-color border-top-style border-top-width border-width clear float layout-flow margin margin-bottom margin-left margin-right margin-top padding padding-bottom padding-left padding-right padding-top scrollbar-3dlight-color scrollbar-arrow-color scrollbar-base-color scrollbar-darkshadow-color scrollbar-face-color scrollbar-highlight-color scrollbar-shadow-color table-layout* zoom
分类属性 display
list-style list-style-image list-style-position list-style-type
定位属性 bottom*
clip height left overflow overflow-x overflow-y position right* top visibility width z-index
打印属性 page**
pageBreakAfter pageBreakBefore
滤镜属性 filter
伪类和其它属性 :active
@charset cursor :first-letter** :first-line** @font-face :hover @import !important :link @media* @page** :visited 有关表格边框的css语法整理
我们知道Dreamweaver在表格制作方面做得非常出色,但是在某些时候还是必须结合css才能达到一些特定效果,下面我们先把有关表格边框的css语法整理出来,然后另外介绍怎样用css美化表格的边框。
有关表格边框的css语法
具体内容包括:上边框宽度、右边框宽度、下边框宽度、左边框宽度、边框宽度、边框颜色、边框样式、上边框、下边框、左边框、右边框、边框、宽度、高度、有关标签等。
1.上边框宽度
语法: border-top-width: <值>
允许值: thin | medium | thick | <长度>
初始值: medium
适用于: 所有对象
向下兼容: 否
上边框宽度属性用于指定一个元素上边框的宽度。值可以是三个关键字其中的一个,都不受字体大小或长度的影响,可以用于实现成比例的宽度。不允许使用负值。也可以用在上边框、边框的宽度或边框的属性略写。
2.右边框宽度
语法: border-right-width: <值>
允许值: thin | medium | thick | <长度>
初始值: medium
适用于: 所有对象
向下兼容: 否
右边框宽度属性用于指定元素的右边框的宽度。值可以是三个关键字其中的一个,都不受字体大小或长度的影响,可以用于实现成比例的宽度。不允许使用负值。也可以用在右边框、边框的宽度或边框的属性略写。
3.下边框宽度
语法: border-bottom-width: <值>
允许值: thin | medium | thick | <长度>
初始值: medium
适用于: 所有对象
向下兼容: 否
下边框宽度属性用于指定元素的下边框的宽度。值可以是三个关键字其中的一个,都不受字体大小或长度的影响,可以用于实现成比例的宽度。不允许使用负值。也可以用在下边框、边框的宽度或边框的属性略写。
4.左边框宽度
语法: border-left-width: <值>
允许值: thin | medium | thick | <长度>
初始值: medium
适用于: 所有对象
向下兼容: 否
左边框宽度属性用于指定元素的左边框的宽度。值可以是三个关键字其中的一个,都不受字体大小或长度的影响,可以用于实现成比例的宽度。不允许使用负值。也可以用在左边框、边框的宽度或边框的属性略写。
5.边框宽度
语法: border-width: <值>
允许值: [ thin | medium | thick | <长度> ]{1,4}
初始值: 未定义
适用于: 所有对象
向下兼容: 否
边框宽度属性用一到四个值来设置元素的边界,值是一个关键字或长度。不允许使用负值长度。如果四个值都给出了,它们分别应用于上、右、下和左边框的式样。如果给出一个值,它将被运用到各边上。如果两个或三个值给出了,省略了的值与对边相等。 这个属性是上边框宽度、右边框宽度、下边框宽度和左边框宽度属性的略写。也可以使用略写的边框属性。
6.边框颜色
语法: border-color: <值>
允许值: <颜色>{1,4}
初始值: 颜色属性的值
适用于: 所有对象
向下兼容: 否
边框颜色属性设置一个元素的边框颜色。可以使用一到四个关键字。如果四个值都给出了,它们分别应用于上、右、下和左边框的式样。如果给出一个值,它将被运用到各边上。如果两个或三个值给出了,省略了的值与对边相等。也可以使用略写的边框属性。
7.边框样式
语法: border-style: <值>
允许值: [ none | dotted | dashed | solid | double | groove | ridge | inset | outset ]{1,4}
初始值: none
适用于: 所有对象
向下兼容: 否
边框样式属性用于设置一个元素边框的样式。这个属性必须用于指定可见的边框。可以使用一到四个关键字。如果四个值都给出了,它们分别应用于上、右、下和左边框的式样。如果给出一个值,它将被运用到各边上。如果两个或三个值给出了,省略了的值与对边相等。也可以使用略写的边框属性。
none:无样式; dotted:点线; dashed:虚线; solid:实线; double:双线; groove:槽线; ridge:脊线; inset:内凹; outset:外凸。 8.上边框
语法: border-top: <值>
允许值: <上边框宽度> || <边框式样> || <颜色>
初始值: 未定义
适用于: 所有对象
向下兼容: 否
上边框属性是一个用于设置一个元素上边框的宽度、式样和颜色的略写。注意只能给出一个边框式样。也可以使用略写的边框属性。
9.右边框
语法: border-right: <值>
允许值: <右边框宽度> || <边框式样> || <颜色>
初始值: 未定义
适用于: 所有对象
向下兼容: 否
右边框属性是一个用于设置一个元素右边框的宽度、式样、和颜色的略写。注意只能给出一个边框式样。也可以使用略写的边框属性。
10.下边框
语法: border-bottom: <值>
允许值: <下边框宽度> || <边框式样> || <颜色>
初始值: 未定义
适用于: 所有对象
向下兼容: 否
下边框属性是一个用于设置一个元素的下边框的宽度、式样和颜色的略写。注意只能给出一个边框式样。也可以使用略写的边框属性。
11.左边框
语法: border-left: <值>
允许值: <左边框宽度> || <边框式样> || <颜色>
初始值: 未定义
适用于: 所有对象
向下兼容: 否
左边框属性是一个用于设置一个元素左边框的宽度、式样和颜色的略写。注意只能给出一个边框式样。也可以使用略写的边框属性。
12.边框
语法: border: <值>
允许值: <边框宽度> || <边框式样> || <颜色>
初始值: 未定义
适用于: 所有对象
向下兼容: 否
边框属性是一个用于设置一个元素边框的宽度、式样和颜色的略写。
边框声明的例子包括:
H2 { border: groove 3em } A:link { border: solid blue } A:visited { border: thin dotted #800080 } A:active { border: thick double red }
边框属性只能设置四种边框;只能给出一组边框的宽度和式样。为了给出一个元素的四种边框的不同的值,网页制作者必须用一个或更多的属性,如:上边框、右边框、下边框、左边框、边框颜色、边框宽度、边框式样、上边框宽度、右边框宽度、下边框宽度或左边框宽度。
13.宽度
语法: width: <值>
允许值: <长度> | <百分比> | auto
初始值: auto
适用于: 块级和替换元素
向下兼容: 否
宽度属性的初始值为“auto”,即为该元素的原有宽度(有就是元素自己的宽度)。百分比参考上级元素的宽度。不允许使用负的长度值。
14.高度
语法: height: <值>
允许值: <长度> | auto
初始值: auto
适用于: 块级和替换元素
向下兼容: 否
高度属性的初始值为“auto”,即为该元素的原有高度(有就是元素自己的高度,)。百分比参考上级元素的宽度。不允许使用负的长度值。
15.有关标签
table:表格标签,对整个表格样式的定义要放在table中; td:单元格标签,对单元格样式的定义要放在td中。
css滤镜
随着网页设计技术的发展,人们已经不满足于原有的一些HTML标记,而是希望能够为页面添加一些多媒体属性,例如滤镜的和渐变的效果。CSS技术的飞快发展使这些需求成为了现实。从现在开始我要为大家介绍一个新的CSS扩展部分:CSS滤镜属性(Filter Properties)。使用这种技术可以把可视化的滤镜和转换效果添加到一个标准的HTML元素上,例如图片、文本容器、以及其他一些对象。对于滤镜和渐变效果,前者是基础,因为后者就是滤镜效果的不断变化和演示更替。当滤镜和渐变效果结合到一个基本的SCRIPT小程序中后,网页设计者就可以拥有一个建立动态交互文档的强大工具。也就是CSS FILTER+ SCRIPT, 这就说明想要建立动态的文档还要一些SCRIPT (脚本语言)的基础。 可视化滤镜属性只能用在HTML控件元素上。所谓的HTML空间元素就是它们在页面上定义了一个矩形空间,浏览器的窗口可以显示这些空间。下面列出了HTML合法的控件和它们的说明。
备注:可惜只有IE4.0以上支持,如果是别的浏览器,那就.......
元素 说明 BODY 网页文档的主体元素,所有的可见范围都在<BODY>元素内 BUTTON 表单域的按钮,可以有“发送(submit)”、“重置(reset)”等形式 DIV 定义了网页上的一个区域,这个区域的高度、宽度或者绝对位置都是以知的 IMG 图片元素,通过指定“src"属性来指定图片的来源 INPUT 输入表单域 MARQUEE 移动字幕效果 SPAN 定义了网页上的一个区域,这个区域的高度、宽度或者绝对位置都是以知的 TABLE 表格 TD 表格数据单元格 TEXTAREA 文本区域 TFOOT 多行输入文本框 TH 表格标题单元格 THEAD 表格标题 TR 表格行
IE4.0以上支持的滤镜属性表
滤镜效果 描述 Alpha 设置透明度 Blru 建立模糊效果 Chroma 把指定的颜色设置为透明 DropShadow 建立一种偏移的影象轮廓,即投射阴影 FlipH 水平反转 FlipV 垂直反转 Glow 为对象的外边界增加光效 Grayscale 降低图片的彩色度 Invert 将色彩、饱和度以及亮度值完全反转建立底片效果 Light 在一个对象上进行灯光投影 Mask 为一个对象建立透明膜 Shadow 建立一个对象的固体轮廓,即阴影效果 Wave 在X轴和Y轴方向利用正弦波纹打乱图片 Xray 只显示对象的轮廓
1、Alpha 滤镜
语法:{FILTER:ALPHA(opacity=opacity,finishopacity=finishopacity,style=style,startx=startx, starty=starty,finishx=finishx,finishy=finishy)}
"Alpha"属性是把一个目标元素与背景混合。设计者可以指定数值来控制混合的程度。这种“与背景混合”通俗地说就是一个元素的透明度。通过指定坐标,可以指定点、线、面的透明度。他们的参数含义分别如下: “opacity"代表透明度水准。默认的范围是从0 到 100,他们其实是百分比的形式。也就是说,0代表完全透明,100代表完全不透明。”finishopacity"是一个可选参数,如果想要设置渐变的透明效果,就可以使用他们来指定结束时的透明度。范围也是0 到 100。“style" 参数指定了透明区域的形状特征。其中0代表统一形状、1代表线形、2代表放射状、3代表长方形。”STARTX“和”STARTY“代表渐变透明效果的开始X和Y坐标。”FINISHX“和”FINISHY“代表渐变透明效果结束X和Y 的坐标。
效果如下:
2、Blur 滤镜
语法:对于HTML:{ilter:blur(add=add,direction=direction,strength=strength)} 对于Script语言: [oblurfilter=] object.filters.blur 用手指在一幅尚未干透的油画上迅速划过时,画面就会变得模糊。”Blur"就是产生同样的模糊效果。 “ADD”参数是一个布尔判断“TRUE(默认)”或者“FALSE”。它指定图片是否被改变成印象派的模糊效果。模糊效果是按顺时针的方向进行的,“DIRECTION”参数用来设置模糊的方向。其中0度代表垂直向上,然后每45度为一个单位。它的默认值是向左的270度。“STRENGTH“值只能使用整数来指定,她代表有多少像素的宽度将受到模糊影响。默认是5个。对于网页上的字体,如果设置他的模糊”ADD=1“,那么这些字体的效果会非常好看的。如下: filter:blur(add=ture,direction=135,strength=10)
3、FlipH, FlipV 滤镜
语法:{filter:filph} ,{filter:filpv} 分别是水平反转和垂直反转,具体如下:
4、Chroma 滤镜
语法:{filter:chroma(color=color)} 使用”Chroma"属性可以设置一个对象中指定的颜色为透明色,参数COLOR即要透明的颜色。下面是兰色文字,然后用Chroma 滤镜过滤掉兰色,就成了下面的样子。 filter:chroma(color=blue)
滴水檐坊
5、DropShadow 滤镜
语法:{filter:dropshadow(color=color,offx=ofx,offy=offy,positive=positive)}
“DropShaow"顾名思义就是添加对象的阴影效果。其工作原理是建立一个偏移量,加上较深。"Color"代表投射阴影的颜色,"offx"和"offy"分别是X方向和Y方向阴影的饿偏移量。"Positive"参数是一个布尔值,如果为“TRUE(非0)”,那么就为任何的非透明像素建立可见的投影。如果为“FASLE(0)”,那么就为透明的像素部分建立透明效果
6、Glow 滤镜
语法:{filter:glow(color=color,strength)} 当对一个对象使用"glow"属性后,这个对象的边缘就会产生类似发光的效果。“COLOR”是指定发光的颜色,“STRENGTH”则是强度的表现,可以从1到255之间的任何整数来指定这个力度。 filter:glow(color=red,strength=10) 后的效果
7、Gray ,Invert,Xray 滤镜
语法:{filter:gray} ,{filter:invert},{filter:xray}
Gray滤镜是把一张图片变成灰度图;Invert滤镜是把对象的可视化属性全部翻转,包括色彩、饱和度、和亮度值;Xray滤镜是让对象反映出它的轮廓并把这些轮廓加亮,也就是所谓的“X”光片。
效果如下:
、Light 滤镜
语法:Filter{light}
这个属性模拟光源的投射效果。一旦为对象定义了“LIGHT"滤镜属性,那么就可以调用它的“方法(Method)"来设置或者改变属性。“LIGHT"可用的方法有:
AddAmbient 加入包围的光源 AddCone 加入锥形光源 AddPoint 加入点光源 Changcolor 改变光的颜色 Changstrength 改变光源的强度 Clear 清除所有的光源 MoveLight 移动光源 可以定义光源的虚拟位置,以及通过调整X轴和Y轴的数值来控制光源焦点的位置,还可以调整光源的形式(点光源或者锥形光源)指定光源是否模糊边界、光源的颜色、亮度等属性。如果动态的设置光源,可能回产生一些意想不到的效果。后面几页会有具体范例。
9、Mask 滤镜
语法:{filter:mask(color=color)}
使用"MASK"属性可以为对象建立一个覆盖于表面的膜,其效果就象戴者有色眼镜看物体一样。
10、Shadow 滤镜
语法:{filter:shadow(color=color,direction=direction)}
利用“Shadow”属性可以在指定的方向建立物体的投影,COLOR是投影色,DIRECTION是设置投影的方向。其中0度代表垂直向上,然后每45度为一个单位。它的默认值是向左的270度。
filter:shadow(color=red,direction=225) filter:shadow(color=blue,direction=225) filter:shadow(color=gray,direction=225) 效果分别如下:
11、Wave 滤镜 语法:{filter:wave(add=add,freq=freq,lightstrength=strength,phase=phase,strength=strength)} "wave" 属性把对象按垂直的波形样式打乱。默认是“TRUE(非0)”, “ADD”表示是否要把对象按照波形样式打乱,
“FREQ”是波纹的频率,也就是指定在对象上一共需要产生多少个完整的波纹,
“LIGHTSTRENGTH”参数可以对于波纹增强光影的效果,范围0----100,
“PHASE”参数用来设置正弦波的偏移量。
“STRENGTH”代表振幅大小。
|
posted @
2006-10-25 19:11 阿成 阅读(301) |
评论 (0) |
编辑 收藏
Java虚拟机的深入研究
作者:刘学超
1 Java技术与Java虚拟机
说起Java,人们首先想到的是Java编程语言,然而事实上,Java是一种技术,它由四方面组成: Java编程语言、Java类文件格式、Java虚拟机和Java应用程序接口(Java API)。它们的关系如下图所示:
图1 Java四个方面的关系
运行期环境代表着Java平台,开发人员编写Java代码(.java文件),然后将之编译成字节码(.class文件)。最后字节码被装入内存,一旦字节码进入虚拟机,它就会被解释器解释执行,或者是被即时代码发生器有选择的转换成机器码执行。从上图也可以看出Java平台由Java虚拟机和Java应用程序接口搭建,Java语言则是进入这个平台的通道,用Java语言编写并编译的程序可以运行在这个平台上。这个平台的结构如下图所示:
在Java平台的结构中, 可以看出,Java虚拟机(JVM) 处在核心的位置,是程序与底层操作系统和硬件无关的关键。它的下方是移植接口,移植接口由两部分组成:适配器和Java操作系统, 其中依赖于平台的部分称为适配器;JVM 通过移植接口在具体的平台和操作系统上实现;在JVM 的上方是Java的基本类库和扩展类库以及它们的API, 利用Java API编写的应用程序(application) 和小程序(Java applet) 可以在任何Java平台上运行而无需考虑底层平台, 就是因为有Java虚拟机(JVM)实现了程序与操作系统的分离,从而实现了Java 的平台无关性。
那么到底什么是Java虚拟机(JVM)呢?通常我们谈论JVM时,我们的意思可能是:
- 对JVM规范的的比较抽象的说明;
- 对JVM的具体实现;
- 在程序运行期间所生成的一个JVM实例。
对JVM规范的的抽象说明是一些概念的集合,它们已经在书《The Java Virtual Machine Specification》(《Java虚拟机规范》)中被详细地描述了;对JVM的具体实现要么是软件,要么是软件和硬件的组合,它已经被许多生产厂商所实现,并存在于多种平台之上;运行Java程序的任务由JVM的运行期实例单个承担。在本文中我们所讨论的Java虚拟机(JVM)主要针对第三种情况而言。它可以被看成一个想象中的机器,在实际的计算机上通过软件模拟来实现,有自己想象中的硬件,如处理器、堆栈、寄存器等,还有自己相应的指令系统。
JVM在它的生存周期中有一个明确的任务,那就是运行Java程序,因此当Java程序启动的时候,就产生JVM的一个实例;当程序运行结束的时候,该实例也跟着消失了。下面我们从JVM的体系结构和它的运行过程这两个方面来对它进行比较深入的研究。
2 Java虚拟机的体系结构
刚才已经提到,JVM可以由不同的厂商来实现。由于厂商的不同必然导致JVM在实现上的一些不同,然而JVM还是可以实现跨平台的特性,这就要归功于设计JVM时的体系结构了。
我们知道,一个JVM实例的行为不光是它自己的事,还涉及到它的子系统、存储区域、数据类型和指令这些部分,它们描述了JVM的一个抽象的内部体系结构,其目的不光规定实现JVM时它内部的体系结构,更重要的是提供了一种方式,用于严格定义实现时的外部行为。每个JVM都有两种机制,一个是装载具有合适名称的类(类或是接口),叫做类装载子系统;另外的一个负责执行包含在已装载的类或接口中的指令,叫做运行引擎。每个JVM又包括方法区、堆、Java栈、程序计数器和本地方法栈这五个部分,这几个部分和类装载机制与运行引擎机制一起组成的体系结构图为:
图3 JVM的体系结构
JVM的每个实例都有一个它自己的方法域和一个堆,运行于JVM内的所有的线程都共享这些区域;当虚拟机装载类文件的时候,它解析其中的二进制数据所包含的类信息,并把它们放到方法域中;当程序运行的时候,JVM把程序初始化的所有对象置于堆上;而每个线程创建的时候,都会拥有自己的程序计数器和Java栈,其中程序计数器中的值指向下一条即将被执行的指令,线程的Java栈则存储为该线程调用Java方法的状态;本地方法调用的状态被存储在本地方法栈,该方法栈依赖于具体的实现。
下面分别对这几个部分进行说明。
执行引擎处于JVM的核心位置,在Java虚拟机规范中,它的行为是由指令集所决定的。尽管对于每条指令,规范很详细地说明了当JVM执行字节码遇到指令时,它的实现应该做什么,但对于怎么做却言之甚少。Java虚拟机支持大约248个字节码。每个字节码执行一种基本的CPU运算,例如,把一个整数加到寄存器,子程序转移等。Java指令集相当于Java程序的汇编语言。
Java指令集中的指令包含一个单字节的操作符,用于指定要执行的操作,还有0个或多个操作数,提供操作所需的参数或数据。许多指令没有操作数,仅由一个单字节的操作符构成。
虚拟机的内层循环的执行过程如下:
do{
取一个操作符字节;
根据操作符的值执行一个动作;
}while(程序未结束)
由于指令系统的简单性,使得虚拟机执行的过程十分简单,从而有利于提高执行的效率。指令中操作数的数量和大小是由操作符决定的。如果操作数比一个字节大,那么它存储的顺序是高位字节优先。例如,一个16位的参数存放时占用两个字节,其值为:
第一个字节*256+第二个字节字节码。
指令流一般只是字节对齐的。指令tableswitch和lookup是例外,在这两条指令内部要求强制的4字节边界对齐。
对于本地方法接口,实现JVM并不要求一定要有它的支持,甚至可以完全没有。Sun公司实现Java本地接口(JNI)是出于可移植性的考虑,当然我们也可以设计出其它的本地接口来代替Sun公司的JNI。但是这些设计与实现是比较复杂的事情,需要确保垃圾回收器不会将那些正在被本地方法调用的对象释放掉。
Java的堆是一个运行时数据区,类的实例(对象)从中分配空间,它的管理是由垃圾回收来负责的:不给程序员显式释放对象的能力。Java不规定具体使用的垃圾回收算法,可以根据系统的需求使用各种各样的算法。
Java方法区与传统语言中的编译后代码或是Unix进程中的正文段类似。它保存方法代码(编译后的java代码)和符号表。在当前的Java实现中,方法代码不包括在垃圾回收堆中,但计划在将来的版本中实现。每个类文件包含了一个Java类或一个Java界面的编译后的代码。可以说类文件是Java语言的执行代码文件。为了保证类文件的平台无关性,Java虚拟机规范中对类文件的格式也作了详细的说明。其具体细节请参考Sun公司的Java虚拟机规范。
Java虚拟机的寄存器用于保存机器的运行状态,与微处理器中的某些专用寄存器类似。Java虚拟机的寄存器有四种:
- pc: Java程序计数器;
- optop: 指向操作数栈顶端的指针;
- frame: 指向当前执行方法的执行环境的指针;。
- vars: 指向当前执行方法的局部变量区第一个变量的指针。
在上述体系结构图中,我们所说的是第一种,即程序计数器,每个线程一旦被创建就拥有了自己的程序计数器。当线程执行Java方法的时候,它包含该线程正在被执行的指令的地址。但是若线程执行的是一个本地的方法,那么程序计数器的值就不会被定义。
Java虚拟机的栈有三个区域:局部变量区、运行环境区、操作数区。
局部变量区
每个Java方法使用一个固定大小的局部变量集。它们按照与vars寄存器的字偏移量来寻址。局部变量都是32位的。长整数和双精度浮点数占据了两个局部变量的空间,却按照第一个局部变量的索引来寻址。(例如,一个具有索引n的局部变量,如果是一个双精度浮点数,那么它实际占据了索引n和n+1所代表的存储空间)虚拟机规范并不要求在局部变量中的64位的值是64位对齐的。虚拟机提供了把局部变量中的值装载到操作数栈的指令,也提供了把操作数栈中的值写入局部变量的指令。
运行环境区
在运行环境中包含的信息用于动态链接,正常的方法返回以及异常捕捉。
动态链接
运行环境包括对指向当前类和当前方法的解释器符号表的指针,用于支持方法代码的动态链接。方法的class文件代码在引用要调用的方法和要访问的变量时使用符号。动态链接把符号形式的方法调用翻译成实际方法调用,装载必要的类以解释还没有定义的符号,并把变量访问翻译成与这些变量运行时的存储结构相应的偏移地址。动态链接方法和变量使得方法中使用的其它类的变化不会影响到本程序的代码。
正常的方法返回
如果当前方法正常地结束了,在执行了一条具有正确类型的返回指令时,调用的方法会得到一个返回值。执行环境在正常返回的情况下用于恢复调用者的寄存器,并把调用者的程序计数器增加一个恰当的数值,以跳过已执行过的方法调用指令,然后在调用者的执行环境中继续执行下去。
异常捕捉
异常情况在Java中被称作Error(错误)或Exception(异常),是Throwable类的子类,在程序中的原因是:①动态链接错,如无法找到所需的class文件。②运行时错,如对一个空指针的引用。程序使用了throw语句。
当异常发生时,Java虚拟机采取如下措施:
- 检查与当前方法相联系的catch子句表。每个catch子句包含其有效指令范围,能够处理的异常类型,以及处理异常的代码块地址。
- 与异常相匹配的catch子句应该符合下面的条件:造成异常的指令在其指令范围之内,发生的异常类型是其能处理的异常类型的子类型。如果找到了匹配的catch子句,那么系统转移到指定的异常处理块处执行;如果没有找到异常处理块,重复寻找匹配的catch子句的过程,直到当前方法的所有嵌套的catch子句都被检查过。
- 由于虚拟机从第一个匹配的catch子句处继续执行,所以catch子句表中的顺序是很重要的。因为Java代码是结构化的,因此总可以把某个方法的所有的异常处理器都按序排列到一个表中,对任意可能的程序计数器的值,都可以用线性的顺序找到合适的异常处理块,以处理在该程序计数器值下发生的异常情况。
- 如果找不到匹配的catch子句,那么当前方法得到一个"未截获异常"的结果并返回到当前方法的调用者,好像异常刚刚在其调用者中发生一样。如果在调用者中仍然没有找到相应的异常处理块,那么这种错误将被传播下去。如果错误被传播到最顶层,那么系统将调用一个缺省的异常处理块。
操作数栈区
机器指令只从操作数栈中取操作数,对它们进行操作,并把结果返回到栈中。选择栈结构的原因是:在只有少量寄存器或非通用寄存器的机器(如Intel486)上,也能够高效地模拟虚拟机的行为。操作数栈是32位的。它用于给方法传递参数,并从方法接收结果,也用于支持操作的参数,并保存操作的结果。例如,iadd指令将两个整数相加。相加的两个整数应该是操作数栈顶的两个字。这两个字是由先前的指令压进堆栈的。这两个整数将从堆栈弹出、相加,并把结果压回到操作数栈中。
每个原始数据类型都有专门的指令对它们进行必须的操作。每个操作数在栈中需要一个存储位置,除了long和double型,它们需要两个位置。操作数只能被适用于其类型的操作符所操作。例如,压入两个int类型的数,如果把它们当作是一个long类型的数则是非法的。在Sun的虚拟机实现中,这个限制由字节码验证器强制实行。但是,有少数操作(操作符dupe和swap),用于对运行时数据区进行操作时是不考虑类型的。
本地方法栈,当一个线程调用本地方法时,它就不再受到虚拟机关于结构和安全限制方面的约束,它既可以访问虚拟机的运行期数据区,也可以使用本地处理器以及任何类型的栈。例如,本地栈是一个C语言的栈,那么当C程序调用C函数时,函数的参数以某种顺序被压入栈,结果则返回给调用函数。在实现Java虚拟机时,本地方法接口使用的是C语言的模型栈,那么它的本地方法栈的调度与使用则完全与C语言的栈相同。
3 Java虚拟机的运行过程
上面对虚拟机的各个部分进行了比较详细的说明,下面通过一个具体的例子来分析它的运行过程。
虚拟机通过调用某个指定类的方法main启动,传递给main一个字符串数组参数,使指定的类被装载,同时链接该类所使用的其它的类型,并且初始化它们。例如对于程序:
class HelloApp
{
public static void main(String[] args)
{
System.out.println("Hello World!");
for (int i = 0; i < args.length; i++ )
{
System.out.println(args[i]);
}
}
}
编译后在命令行模式下键入: java HelloApp run virtual machine
将通过调用HelloApp的方法main来启动java虚拟机,传递给main一个包含三个字符串"run"、"virtual"、"machine"的数组。现在我们略述虚拟机在执行HelloApp时可能采取的步骤。
开始试图执行类HelloApp的main方法,发现该类并没有被装载,也就是说虚拟机当前不包含该类的二进制代表,于是虚拟机使用ClassLoader试图寻找这样的二进制代表。如果这个进程失败,则抛出一个异常。类被装载后同时在main方法被调用之前,必须对类HelloApp与其它类型进行链接然后初始化。链接包含三个阶段:检验,准备和解析。检验检查被装载的主类的符号和语义,准备则创建类或接口的静态域以及把这些域初始化为标准的默认值,解析负责检查主类对其它类或接口的符号引用,在这一步它是可选的。类的初始化是对类中声明的静态初始化函数和静态域的初始化构造方法的执行。一个类在初始化之前它的父类必须被初始化。整个过程如下:
图4:虚拟机的运行过程
4 结束语
本文通过对JVM的体系结构的深入研究以及一个Java程序执行时虚拟机的运行过程的详细分析,意在剖析清楚Java虚拟机的机理。
posted @
2006-10-15 15:39 阿成 阅读(333) |
评论 (0) |
编辑 收藏
struts的validator资料见:struts validator
附一个正则表达式的资料:
正则表达式
正则表达式(regular expression)描述了一种字符串匹配的模式,可以用来检查一个串是否含有某种子串、将匹配的子串做替换或者从某个串中取出符合某个条件的子串等。
列目录时, dir *.txt或ls *.txt中的*.txt就
不是一个正则表达式,因为这里*与正则式的*的含义是不同的。
为便于理解和记忆,先从一些概念入手,所有特殊字符或字符组合有一个总表在后面,最后一些例子供理解相应的概念。
正则表达式
是由普通字符(例如字符 a 到 z)以及特殊字符(称为元字符)组成的文字模式。正则表达式作为一个模板,将某个字符模式与所搜索的字符串进行匹配。
可以通过在一对分隔符之间放入表达式模式的各种组件来构造一个正则表达式,即/expression/
普通字符
由所有那些未显式指定为元字符的打印和非打印字符组成。这包括所有的大写和小写字母字符,所有数字,所有标点符号以及一些符号。
非打印字符
字符 | 含义 |
\cx | 匹配由x指明的控制字符。例如, \cM 匹配一个 Control-M 或回车符。x 的值必须为 A-Z 或 a-z 之一。否则,将 c 视为一个原义的 'c' 字符。 |
\f | 匹配一个换页符。等价于 \x0c 和 \cL。 |
\n | 匹配一个换行符。等价于 \x0a 和 \cJ。 |
\r | 匹配一个回车符。等价于 \x0d 和 \cM。 |
\s | 匹配任何空白字符,包括空格、制表符、换页符等等。等价于 [ \f\n\r\t\v]。 |
\S | 匹配任何非空白字符。等价于 [^ \f\n\r\t\v]。 |
\t | 匹配一个制表符。等价于 \x09 和 \cI。 |
\v | 匹配一个垂直制表符。等价于 \x0b 和 \cK。 |
特殊字符
所谓特殊字符,就是一些有特殊含义的字符,如上面说的"*.txt"中的*,简单的说就是表示任何字符串的意思。如果要查找文件名中有*的文件,则需要对*进行转义,即在其前加一个\。ls \*.txt。正则表达式有以下特殊字符。
特别字符 | 说明 |
$ | 匹配输入字符串的结尾位置。如果设置了 RegExp 对象的 Multiline 属性,则 $ 也匹配 '\n' 或 '\r'。要匹配 $ 字符本身,请使用 \$。 |
( ) | 标记一个子表达式的开始和结束位置。子表达式可以获取供以后使用。要匹配这些字符,请使用 \( 和 \)。 |
* | 匹配前面的子表达式零次或多次。要匹配 * 字符,请使用 \*。 |
+ | 匹配前面的子表达式一次或多次。要匹配 + 字符,请使用 \+。 |
. | 匹配除换行符 \n之外的任何单字符。要匹配 .,请使用 \。 |
[ | 标记一个中括号表达式的开始。要匹配 [,请使用 \[。 |
? | 匹配前面的子表达式零次或一次,或指明一个非贪婪限定符。要匹配 ? 字符,请使用 \?。 |
\ | 将下一个字符标记为或特殊字符、或原义字符、或向后引用、或八进制转义符。例如, 'n' 匹配字符 'n'。'\n' 匹配换行符。序列 '\\' 匹配 "\",而 '\(' 则匹配 "("。 |
^ | 匹配输入字符串的开始位置,除非在方括号表达式中使用,此时它表示不接受该字符集合。要匹配 ^ 字符本身,请使用 \^。 |
{ | 标记限定符表达式的开始。要匹配 {,请使用 \{。 |
| | 指明两项之间的一个选择。要匹配 |,请使用 \|。 |
构造正则表达式的方法和创建数学表达式的方法一样。也就是用多种元字符与操作符将小的表达式结合在一起来创建更大的表达式。正则表达式的组件可以是单个的字符、字符集合、字符范围、字符间的选择或者所有这些组件的任意组合。
限定符
限定符用来指定正则表达式的一个给定组件必须要出现多少次才能满足匹配。有*或+或?或{n}或{n,}或{n,m}共6种。
*、+和?限定符都是贪婪的,因为它们会尽可能多的匹配文字,只有在它们的后面加上一个?就可以实现非贪婪或最小匹配。
正则表达式的限定符有:
字符 | 描述 |
* | 匹配前面的子表达式零次或多次。例如,zo* 能匹配 "z" 以及 "zoo"。* 等价于{0,}。 |
+ | 匹配前面的子表达式一次或多次。例如,'zo+' 能匹配 "zo" 以及 "zoo",但不能匹配 "z"。+ 等价于 {1,}。 |
? | 匹配前面的子表达式零次或一次。例如,"do(es)?" 可以匹配 "do" 或 "does" 中的"do" 。? 等价于 {0,1}。 |
{n} | n 是一个非负整数。匹配确定的 n 次。例如,'o{2}' 不能匹配 "Bob" 中的 'o',但是能匹配 "food" 中的两个 o。 |
{n,} | n 是一个非负整数。至少匹配n 次。例如,'o{2,}' 不能匹配 "Bob" 中的 'o',但能匹配 "foooood" 中的所有 o。'o{1,}' 等价于 'o+'。'o{0,}' 则等价于 'o*'。 |
{n,m} | m 和 n 均为非负整数,其中n <= m。最少匹配 n 次且最多匹配 m 次。例如,"o{1,3}" 将匹配 "fooooood" 中的前三个 o。'o{0,1}' 等价于 'o?'。请注意在逗号和两个数之间不能有空格。 |
定位符
用来描述字符串或单词的边界,^和$分别指字符串的开始与结束,\b描述单词的前或后边界,\B表示非单词边界。
不能对定位符使用限定符。选择
用圆括号将所有选择项括起来,相邻的选择项之间用|分隔。但用圆括号会有一个副作用,是相关的匹配会被缓存,此时可用?:放在第一个选项前来消除这种副作用。
其中?:是非捕获元之一,还有两个非捕获元是?=和?!,这两个还有更多的含义,前者为正向预查,在任何开始匹配圆括号内的正则表达式模式的位置来匹配搜索字符串,后者为负向预查,在任何开始不匹配该正则表达式模式的位置来匹配搜索字符串。
后向引用
对一个正则表达式模式或部分模式两边添加圆括号将导致相关匹配存储到一个临时缓冲区中,所捕获的每个子匹配都按照在正则表达式模式中从左至右所遇到的内容存储。存储子匹配的缓冲区编号从 1 开始,连续编号直至最大 99 个子表达式。每个缓冲区都可以使用 '\n' 访问,其中 n 为一个标识特定缓冲区的一位或两位十进制数。
可以使用非捕获元字符 '?:', '?=', or '?!' 来忽略对相关匹配的保存。
各种操作符的运算优先级
相同优先级的从左到右进行运算,不同优先级的运算先高后低。各种操作符的优先级从高到低如下:
操作符 | 描述 |
\ | 转义符 |
(), (?:), (?=), [] | 圆括号和方括号 |
*, +, ?, {n}, {n,}, {n,m} | 限定符 |
^, $, \anymetacharacter | 位置和顺序 |
| | “或”操作 |
全部符号解释
字符 | 描述 |
\ | 将下一个字符标记为一个特殊字符、或一个原义字符、或一个 向后引用、或一个八进制转义符。例如,'n' 匹配字符 "n"。'\n' 匹配一个换行符。序列 '\\' 匹配 "\" 而 "\(" 则匹配 "("。 |
^ | 匹配输入字符串的开始位置。如果设置了 RegExp 对象的 Multiline 属性,^ 也匹配 '\n' 或 '\r' 之后的位置。 |
$ | 匹配输入字符串的结束位置。如果设置了RegExp 对象的 Multiline 属性,$ 也匹配 '\n' 或 '\r' 之前的位置。 |
* | 匹配前面的子表达式零次或多次。例如,zo* 能匹配 "z" 以及 "zoo"。* 等价于{0,}。 |
+ | 匹配前面的子表达式一次或多次。例如,'zo+' 能匹配 "zo" 以及 "zoo",但不能匹配 "z"。+ 等价于 {1,}。 |
? | 匹配前面的子表达式零次或一次。例如,"do(es)?" 可以匹配 "do" 或 "does" 中的"do" 。? 等价于 {0,1}。 |
{n} | n 是一个非负整数。匹配确定的 n 次。例如,'o{2}' 不能匹配 "Bob" 中的 'o',但是能匹配 "food" 中的两个 o。 |
{n,} | n 是一个非负整数。至少匹配n 次。例如,'o{2,}' 不能匹配 "Bob" 中的 'o',但能匹配 "foooood" 中的所有 o。'o{1,}' 等价于 'o+'。'o{0,}' 则等价于 'o*'。 |
{n,m} | m 和 n 均为非负整数,其中n <= m。最少匹配 n 次且最多匹配 m 次。例如,"o{1,3}" 将匹配 "fooooood" 中的前三个 o。'o{0,1}' 等价于 'o?'。请注意在逗号和两个数之间不能有空格。 |
? | 当该字符紧跟在任何一个其他限制符 (*, +, ?, {n}, {n,}, {n,m}) 后面时,匹配模式是非贪婪的。非贪婪模式尽可能少的匹配所搜索的字符串,而默认的贪婪模式则尽可能多的匹配所搜索的字符串。例如,对于字符串 "oooo",'o+?' 将匹配单个 "o",而 'o+' 将匹配所有 'o'。 |
. | 匹配除 "\n" 之外的任何单个字符。要匹配包括 '\n' 在内的任何字符,请使用象 '[.\n]' 的模式。 |
(pattern) | 匹配 pattern 并获取这一匹配。所获取的匹配可以从产生的 Matches 集合得到,在VBScript 中使用 SubMatches 集合,在JScript 中则使用 $0…$9 属性。要匹配圆括号字符,请使用 '\(' 或 '\)'。 |
(?:pattern) | 匹配 pattern 但不获取匹配结果,也就是说这是一个非获取匹配,不进行存储供以后使用。这在使用 "或" 字符 (|) 来组合一个模式的各个部分是很有用。例如, 'industr(?:y|ies) 就是一个比 'industry|industries' 更简略的表达式。 |
(?=pattern) | 正向预查,在任何匹配 pattern 的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。例如,'Windows (?=95|98|NT|2000)' 能匹配 "Windows 2000" 中的 "Windows" ,但不能匹配 "Windows 3.1" 中的 "Windows"。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始。 |
(?!pattern) | 负向预查,在任何不匹配 pattern 的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。例如'Windows (?!95|98|NT|2000)' 能匹配 "Windows 3.1" 中的 "Windows",但不能匹配 "Windows 2000" 中的 "Windows"。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始 |
x|y | 匹配 x 或 y。例如,'z|food' 能匹配 "z" 或 "food"。'(z|f)ood' 则匹配 "zood" 或 "food"。 |
[xyz] | 字符集合。匹配所包含的任意一个字符。例如, '[abc]' 可以匹配 "plain" 中的 'a'。 |
[^xyz] | 负值字符集合。匹配未包含的任意字符。例如, '[^abc]' 可以匹配 "plain" 中的'p'。 |
[a-z] | 字符范围。匹配指定范围内的任意字符。例如,'[a-z]' 可以匹配 'a' 到 'z' 范围内的任意小写字母字符。 |
[^a-z] | 负值字符范围。匹配任何不在指定范围内的任意字符。例如,'[^a-z]' 可以匹配任何不在 'a' 到 'z' 范围内的任意字符。 |
\b | 匹配一个单词边界,也就是指单词和空格间的位置。例如, 'er\b' 可以匹配"never" 中的 'er',但不能匹配 "verb" 中的 'er'。 |
\B | 匹配非单词边界。'er\B' 能匹配 "verb" 中的 'er',但不能匹配 "never" 中的 'er'。 |
\cx | 匹配由 x 指明的控制字符。例如, \cM 匹配一个 Control-M 或回车符。x 的值必须为 A-Z 或 a-z 之一。否则,将 c 视为一个原义的 'c' 字符。 |
\d | 匹配一个数字字符。等价于 [0-9]。 |
\D | 匹配一个非数字字符。等价于 [^0-9]。 |
\f | 匹配一个换页符。等价于 \x0c 和 \cL。 |
\n | 匹配一个换行符。等价于 \x0a 和 \cJ。 |
\r | 匹配一个回车符。等价于 \x0d 和 \cM。 |
\s | 匹配任何空白字符,包括空格、制表符、换页符等等。等价于 [ \f\n\r\t\v]。 |
\S | 匹配任何非空白字符。等价于 [^ \f\n\r\t\v]。 |
\t | 匹配一个制表符。等价于 \x09 和 \cI。 |
\v | 匹配一个垂直制表符。等价于 \x0b 和 \cK。 |
\w | 匹配包括下划线的任何单词字符。等价于'[A-Za-z0-9_]'。 |
\W | 匹配任何非单词字符。等价于 '[^A-Za-z0-9_]'。 |
\xn | 匹配 n,其中 n 为十六进制转义值。十六进制转义值必须为确定的两个数字长。例如,'\x41' 匹配 "A"。'\x041' 则等价于 '\x04' & "1"。正则表达式中可以使用 ASCII 编码。. |
\num | 匹配 num,其中 num 是一个正整数。对所获取的匹配的引用。例如,'(.)\1' 匹配两个连续的相同字符。 |
\n | 标识一个八进制转义值或一个向后引用。如果 \n 之前至少 n 个获取的子表达式,则 n 为向后引用。否则,如果 n 为八进制数字 (0-7),则 n 为一个八进制转义值。 |
\nm | 标识一个八进制转义值或一个向后引用。如果 \nm 之前至少有 nm 个获得子表达式,则 nm 为向后引用。如果 \nm 之前至少有 n 个获取,则 n 为一个后跟文字 m 的向后引用。如果前面的条件都不满足,若 n 和 m 均为八进制数字 (0-7),则 \nm 将匹配八进制转义值 nm。 |
\nml | 如果 n 为八进制数字 (0-3),且 m 和 l 均为八进制数字 (0-7),则匹配八进制转义值 nml。 |
\un | 匹配 n,其中 n 是一个用四个十六进制数字表示的 Unicode 字符。例如, \u00A9 匹配版权符号 (?)。 |
部分例子
正则表达式 | 说明 |
/\b([a-z]+) \1\b/gi | 一个单词连续出现的位置 |
/(\w+):\/\/([^/:]+)(:\d*)?([^# ]*)/ | 将一个URL解析为协议、域、端口及相对路径 |
/^(?:Chapter|Section) [1-9][0-9]{0,1}$/ | 定位章节的位置 |
/[-a-z]/ | A至z共26个字母再加一个-号。 |
/ter\b/ | 可匹配chapter,而不能terminal |
/\Bapt/ | 可匹配chapter,而不能aptitude |
/Windows(?=95 |98 |NT )/ | 可匹配Windows95或Windows98或WindowsNT,当找到一个匹配后,从Windows后面开始进行下一次的检索匹配。 |
posted @
2006-10-13 09:36 阿成 阅读(292) |
评论 (0) |
编辑 收藏
前两天晚上在经过一个十字路口的时候,不知中了什么邪,跑着横穿马路,正好被一个出租车撞上,
撞的偶pp上,那叫一个险。幸亏是撞的pp上,而且车速不算快。身上是些皮外伤。
所以奉劝大家宁等3小时,别抢1妙。
要是真有急事儿,非要抢那1妙,那就别犹豫,跑快点。
posted @
2006-10-09 08:57 阿成 阅读(324) |
评论 (0) |
编辑 收藏
真的很快,工作了感觉时间过得太快了。真是time fly啊
明天就101了,感觉刚过完51。
回家修养几天。
posted @
2006-09-30 17:06 阿成 阅读(260) |
评论 (0) |
编辑 收藏
现在我最缺什么?最需求在哪些方面改进?
最近一些小事让我逐渐意识到自己这工作一年多来一直没有重视或者说不敢的弱点。我开始问自己现在在社会上混,都需要些什么?我一一列举,作为程序员,我开始想到java、数据库、服务器、、、、、、,发现,都是一些技术方面的。光有这些行吗?肯定不行。开始想到了一些其他的东西,圆滑,这两个字没那么简单,里面有着一系列东西。我问自己,我作为一个写程序的技术人员,他妈的我需要干这个?我做到了吗?我没做做到。而且差的很远很远。说这些不是脑子一热,或者什么凭空想象,是所见所闻。而我得性格似乎与这个东西有隔阂,似乎不太容易做到,而且对它产生了烦感和误解。看着茫茫人海,各奔前程,我也逐渐开始理解了。如果你没有做到或像我一样或不理解,就仔细观察一下周围的环境,会明白的。经过反复的思考,决定该改正一些了。但我不会超越我自己的一些底线,我会对自己认准的朋友保持一颗真诚的心,不敢说已做到仗义,讲义气,但会一直为之努力。也希望每个人在圆滑的同时尽量保留自己的底线。我也会尽快的适应这个所谓的高科技时代。
posted @
2006-09-26 14:35 阿成 阅读(188) |
评论 (0) |
编辑 收藏
部门的经理挺好,周末组织部门同事去怀柔玩了一次,因为国庆、中秋快到了。
这次和上次去秦皇岛还没隔多长时间。
早上7点就起床了,吃完早饭,大家到齐之后就出发了。
先去的红螺寺,这原来是一个皇家寺院。里面的大佛听多。阿弥托佛、释迦摩尼、药师佛等等,
然后又看了500罗汉,据说里面总能找到一个与你想的。中午吃饭,点了好几条虹尊鱼,顿的,烤的都吃了,不过觉得一般。下午去了神堂裕,里面是一些小溪呀,山呀,在里面走走不错,回归大自然,净化一下,在工作呀、学习呀都能提高效率。
晚上回来后已经快7点了,接着腐败,找地儿吃饭。挺爽的。
十一之后逐渐变冷估计今年即便有活动,也是在市里了。下次出去估计的明年了。
posted @
2006-09-26 14:04 阿成 阅读(255) |
评论 (0) |
编辑 收藏
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<title></title>
<script language=javascript>
function fTxtKeyDown(obj)
{
if(event.keyCode == 32)
{
event.returnValue=false;
//空格键
var sRet;
try {
sRet = eval(obj.value);
}
catch(e) {
}
if (isNaN(sRet))
{
alert("计算式输入错误");
}
else
{
obj.value=sRet;
}
}
}
</script>
</head>
<body>
<font size="2">输入表达式(如:3*2-4)后,按空格键得到结果 try</font><br>
<br>
<input type=text onkeydown="fTxtKeyDown(this);" size="42">
</body>
</html>
posted @
2006-09-21 21:20 阿成 阅读(999) |
评论 (0) |
编辑 收藏
"^\\d+$" //非负整数(正整数 + 0)
"^[0-9]*[1-9][0-9]*$" //正整数
"^((-\\d+)|(0+))$" //非正整数(负整数 + 0)
"^-[0-9]*[1-9][0-9]*$" //负整数
"^-?\\d+$" //整数
"^\\d+(\\.\\d+)?$" //非负浮点数(正浮点数 + 0)
"^(([0-9]+\\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\\.[0-9]+)|([0-9]*[1-9][0-9]*))$" //正浮点数
"^((-\\d+(\\.\\d+)?)|(0+(\\.0+)?))$" //非正浮点数(负浮点数 + 0)
"^(-(([0-9]+\\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\\.[0-9]+)|([0-9]*[1-9][0-9]*)))$" //负浮点数
"^(-?\\d+)(\\.\\d+)?$" //浮点数
"^[A-Za-z]+$" //由26个英文字母组成的字符串
"^[A-Z]+$" //由26个英文字母的大写组成的字符串
"^[a-z]+$" //由26个英文字母的小写组成的字符串
"^[A-Za-z0-9]+$" //由数字和26个英文字母组成的字符串
"^\\w+$" //由数字、26个英文字母或者下划线组成的字符串
"^[\\w-]+(\\.[\\w-]+)*@[\\w-]+(\\.[\\w-]+)+$" //email地址
"^[a-zA-z]+://(\\w+(-\\w+)*)(\\.(\\w+(-\\w+)*))*(\\?\\S*)?$" //url
posted @
2006-09-20 16:57 阿成 阅读(323) |
评论 (0) |
编辑 收藏
<title>表单验证类 Validator v1.01</title>
<style>
body,td{font:normal 12px Verdana;color:#333333}
input,textarea,select,td{font:normal 12px Verdana;color:#333333;border:1px solid #999999;background:#ffffff}
table{border-collapse:collapse;}
td{padding:3px}
input{height:20;}
textarea{width:80%;height:50px;overfmin:auto;}
form{display:inline}
</style>
<table align="center">
<form name="theForm" id="demo" action="" method="get" onSubmit="return Validator.Validate(this,2)">
<tr>
<td>真实姓名:</td><td><input name="Name" dataType="Chinese" msg="真实姓名只允许中文"></td>
</tr>
<tr>
<td>英文名:</td><td><input name="Nick" dataType="English" require="false" msg="英文名只允许英文字母"></td>
</tr>
<tr>
<td>主页:</td><td><input name="Homepage" require="false" dataType="Url" msg="非法的Url"></td>
</tr>
<tr>
<td>密码:</td><td><input name="Password" dataType="SafeString" msg="密码不符合安全规则" type="password"></td>
</tr>
<tr>
<td>重复:</td><td><input name="Repeat" dataType="Repeat" to="Password" msg="两次输入的密码不一致" type="password"></td>
</tr>
<tr>
<td>信箱:</td><td><input name="Email" dataType="Email" msg="信箱格式不正确"></td>
</tr>
<tr>
<td>信箱:</td><td><input name="Email" dataType="Repeat" to="Email" msg="两次输入的信箱不一致"></td>
</tr>
<tr>
<td>QQ:</td><td><input name="QQ" require="false" dataType="QQ" msg="QQ号码不存在"></td>
</tr>
<tr>
<td>身份证:</td><td><input name="Card" dataType="IdCard" msg="身份证号码不正确"></td>
</tr>
<tr>
<td>年龄:</td><td><input name="Year" dataType="Range" msg="年龄必须在18~28之间" min="18" max="28"></td>
</tr>
<tr>
<td>年龄1:</td><td><input name="Year1" require="false" dataType="Compare" msg="年龄必须在18以上" to="18" operator="GreaterThanEqual"></td>
</tr>
<tr>
<td>电话:</td><td><input name="Phone" require="false" dataType="Phone" msg="电话号码不正确"></td>
</tr>
<tr>
<td>手机:</td><td><input name="Mobile" require="false" dataType="Mobile" msg="手机号码不正确"></td>
</tr>
<tr>
<td>生日:</td><td><input name="Birthday" dataType="Date" format="ymd" msg="生日日期不存在"></td>
</tr>
<tr>
<td>邮政编码:</td><td><input name="Zip" dataType="Custom" regexp="^[1-9]\d{5}$" msg="邮政编码不存在"></td>
</tr>
<tr>
<td>邮政编码:</td><td><input name="Zip1" dataType="Zip" msg="邮政编码不存在"></td>
</tr>
<tr>
<td>操作系统:</td><td><select name="Operation" dataType="Require" msg="未选择所用操作系统" ><option value="">选择您所用的操作系统</option><option value="Win98">Win98</option><option value="Win2k">Win2k</option><option value="WinXP">WinXP</option></select></td>
</tr>
<tr>
<td>所在省份:</td><td>广东<input name="Province" value="1" type="radio">陕西<input name="Province" value="2" type="radio">浙江<input name="Province" value="3" type="radio">江西<input name="Province" value="4" type="radio" dataType="Group" msg="必须选定一个省份" ></td>
</tr>
<tr>
<td>爱好:</td><td>运动<input name="Favorite" value="1" type="checkbox">上网<input name="Favorite" value="2" type="checkbox">听音乐<input name="Favorite" value="3" type="checkbox">看书<input name="Favorite" value="4" type="checkbox"" dataType="Group" min="2" max="3" msg="必须选择2~3种爱好"></td>
</tr>
<td>自我介绍:</td><td><textarea name="Description" dataType="Limit" max="10" msg="自我介绍内容必须在10个字之内">中文是一个字</textarea></td>
</tr>
<td>自传:</td><td><textarea name="History" dataType="LimitB" min="3" max="10" msg="自传内容必须在[3,10]个字节之内">中文是两个字节t</textarea></td>
</tr>
<tr>
<td colspan="2"><input name="Submit" type="submit" value="确定提交"><input onClick="Validator.Validate(document.getElementById('demo'))" value="检验模式1" type="button"><input onClick="Validator.Validate(document.getElementById('demo'),2)" value="检验模式2" type="button"><input onClick="Validator.Validate(document.getElementById('demo'),3)" value="检验模式3" type="button"></td>
</tr>
</form>
</table>
<script>
/*************************************************
Validator v1.01
code by 我佛山人
wfsr@cunite.com http://www.cunite.com*************************************************/
Validator = {
Require : /.+/,
Email : /^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/,
Phone : /^((\(\d{3}\))|(\d{3}\-))?(\(0\d{2,3}\)|0\d{2,3}-)?[1-9]\d{6,7}$/,
Mobile : /^((\(\d{3}\))|(\d{3}\-))?13\d{9}$/,
Url : /^http:\/\/[A-Za-z0-9]+\.[A-Za-z0-9]+[\/=\?%\-&_~`@[\]\':+!]*([^<>\"\"])*$/,
IdCard : /^\d{15}(\d{2}[A-Za-z0-9])?$/,
Currency : /^\d+(\.\d+)?$/,
Number : /^\d+$/,
Zip : /^[1-9]\d{5}$/,
QQ : /^[1-9]\d{4,8}$/,
Integer : /^[-\+]?\d+$/,
Double : /^[-\+]?\d+(\.\d+)?$/,
English : /^[A-Za-z]+$/,
Chinese : /^[\u0391-\uFFE5]+$/,
UnSafe : /^(([A-Z]*|[a-z]*|\d*|[-_\~!@#\$%\^&\*\.\(\)\[\]\{\}<>\?\\\/\'\"]*)|.{0,5})$|\s/,
IsSafe : function(str){return !this.UnSafe.test(str);},
SafeString : "this.IsSafe(value)",
Limit : "this.limit(value.length,getAttribute('min'), getAttribute('max'))",
LimitB : "this.limit(this.LenB(value), getAttribute('min'), getAttribute('max'))",
Date : "this.IsDate(value, getAttribute('min'), getAttribute('format'))",
Repeat : "value == document.getElementsByName(getAttribute('to'))[0].value",
Range : "getAttribute('min') < value && value < getAttribute('max')",
Compare : "this.compare(value,getAttribute('operator'),getAttribute('to'))",
Custom : "this.Exec(value, getAttribute('regexp'))",
Group : "this.MustChecked(getAttribute('name'), getAttribute('min'), getAttribute('max'))",
ErrorItem : [document.forms[0]],
ErrorMessage : ["以下原因导致提交失败:\t\t\t\t"],
Validate : function(theForm, mode){
var obj = theForm || event.srcElement;
var count = obj.elements.length;
this.ErrorMessage.length = 1;
this.ErrorItem.length = 1;
this.ErrorItem[0] = obj;
for(var i=0;i<count;i++){
with(obj.elements[i]){
var _dataType = getAttribute("dataType");
if(typeof(_dataType) == "object" || typeof(this[_dataType]) == "undefined") continue;
this.ClearState(obj.elements[i]);
if(getAttribute("require") == "false" && value == "") continue;
switch(_dataType){
case "Date" :
case "Repeat" :
case "Range" :
case "Compare" :
case "Custom" :
case "Group" :
case "Limit" :
case "LimitB" :
case "SafeString" :
if(!eval(this[_dataType])) {
this.AddError(i, getAttribute("msg"));
}
break;
default :
if(!this[_dataType].test(value)){
this.AddError(i, getAttribute("msg"));
}
break;
}
}
}
if(this.ErrorMessage.length > 1){
mode = mode || 1;
var errCount = this.ErrorItem.length;
switch(mode){
case 2 :
for(var i=1;i<errCount;i++)
this.ErrorItem[i].style.color = "red";
case 1 :
alert(this.ErrorMessage.join("\n"));
this.ErrorItem[1].focus();
break;
case 3 :
for(var i=1;i<errCount;i++){
try{
var span = document.createElement("SPAN");
span.id = "__ErrorMessagePanel";
span.style.color = "red";
this.ErrorItem[i].parentNode.appendChild(span);
span.innerHTML = this.ErrorMessage[i].replace(/\d+:/,"*");
}
catch(e){alert(e.description);}
}
this.ErrorItem[1].focus();
break;
default :
alert(this.ErrorMessage.join("\n"));
break;
}
return false;
}
return true;
},
limit : function(len,min, max){
min = min || 0;
max = max || Number.MAX_VALUE;
return min <= len && len <= max;
},
LenB : function(str){
return str.replace(/[^\x00-\xff]/g,"**").length;
},
ClearState : function(elem){
with(elem){
if(style.color == "red")
style.color = "";
var lastNode = parentNode.childNodes[parentNode.childNodes.length-1];
if(lastNode.id == "__ErrorMessagePanel")
parentNode.removeChild(lastNode);
}
},
AddError : function(index, str){
this.ErrorItem[this.ErrorItem.length] = this.ErrorItem[0].elements[index];
this.ErrorMessage[this.ErrorMessage.length] = this.ErrorMessage.length + ":" + str;
},
Exec : function(op, reg){
return new RegExp(reg,"g").test(op);
},
compare : function(op1,operator,op2){
switch (operator) {
case "NotEqual":
return (op1 != op2);
case "GreaterThan":
return (op1 > op2);
case "GreaterThanEqual":
return (op1 >= op2);
case "LessThan":
return (op1 < op2);
case "LessThanEqual":
return (op1 <= op2);
default:
return (op1 == op2);
}
},
MustChecked : function(name, min, max){
var groups = document.getElementsByName(name);
var hasChecked = 0;
min = min || 1;
max = max || groups.length;
for(var i=groups.length-1;i>=0;i--)
if(groups[i].checked) hasChecked++;
return min <= hasChecked && hasChecked <= max;
},
IsDate : function(op, formatString){
formatString = formatString || "ymd";
var m, year, month, day;
switch(formatString){
case "ymd" :
m = op.match(new RegExp("^((
\\d{4})|(\\d{2}))([-./])(\\d{1,2})\\4(\\d{1,2})$"));
if(m == null ) return false;
day = m[6];
month = m[5]--;
year = (m[2].length == 4) ? m[2] : GetFullYear(parseInt(m[3], 10));
break;
case "dmy" :
m = op.match(new RegExp("^(
\\d{1,2})([-./])(\\d{1,2})\\2((\\d{4})|(\\d{2}))$"));
if(m == null ) return false;
day = m[1];
month = m[3]--;
year = (m[5].length == 4) ? m[5] : GetFullYear(parseInt(m[6], 10));
break;
default :
break;
}
if(!parseInt(month)) return false;
month = month==12 ?0:month;
var date = new Date(year, month, day);
return (typeof(date) == "object" && year == date.getFullYear() && month == date.getMonth() && day == date.getDate());
function GetFullYear(y){return ((y<30 ? "20" : "19") + y)|0;}
}
}
</script>
posted @
2006-09-20 14:01 阿成 阅读(324) |
评论 (0) |
编辑 收藏
import
org.htmlparser.Node;
import
org.htmlparser.NodeFilter;
import
org.htmlparser.Parser;
import
org.htmlparser.filters.TagNameFilter;
import
org.htmlparser.tags.TableTag;
import
org.htmlparser.util.NodeList;
/**
* <br>
* 标题: <br>
* 功能概要: <br>
* 版权: cityyouth.cn (c) 2005 <br>
* 公司:上海城市青年网 <br>
* 创建时间:2005-12-21 <br>
* 修改时间: <br>
* 修改原因:
*
*
@author
张伟
*
@version
1.0
*/
public
class
TestYahoo {
public
static
void
testHtml() {
try
{
String sCurrentLine;
String sTotalString;
sCurrentLine
=
""
;
sTotalString
=
""
;
java.io.InputStream l_urlStream;
java.net.URL l_url
=
new
java.net.URL(
"
http://sports.sina.com.cn/iframe/nba/live/
"
);
java.net.HttpURLConnection l_connection
=
(java.net.HttpURLConnection) l_url
.openConnection();
l_connection.connect();
l_urlStream
=
l_connection.getInputStream();
java.io.BufferedReader l_reader
=
new
java.io.BufferedReader(
new
java.io.InputStreamReader(l_urlStream));
while
((sCurrentLine
=
l_reader.readLine())
!=
null
) {
sTotalString
+=
sCurrentLine;
}
System.out.println(sTotalString);
System.out.println(
"
====================
"
);
String testText
=
extractText(sTotalString);
System.out.println(testText);
}
catch
(Exception e) {
e.printStackTrace();
}
}
/**
* 抽取纯文本信息
*
*
@param
inputHtml
*
@return
*/
public
static
String extractText(String inputHtml)
throws
Exception {
StringBuffer text
=
new
StringBuffer();
Parser parser
=
Parser.createParser(
new
String(inputHtml.getBytes(),
"
8859_1
"
),
"
8859-1
"
);
//
遍历所有的节点
NodeList nodes
=
parser.extractAllNodesThatMatch(
new
NodeFilter() {
public
boolean
accept(Node node) {
return
true
;
}
});
Node node
=
nodes.elementAt(
0
);
text.append(
new
String(node.toPlainTextString().getBytes(
"
8859_1
"
)));
return
text.toString();
}
/**
* 读取文件的方式来分析内容. filePath也可以是一个Url.
*
*
@param
resource
* 文件/Url
*/
public
static
void
test5(String resource)
throws
Exception {
Parser myParser
=
new
Parser(resource);
//
设置编码
myParser.setEncoding(
"
GBK
"
);
String filterStr
=
"
table
"
;
NodeFilter filter
=
new
TagNameFilter(filterStr);
NodeList nodeList
=
myParser.extractAllNodesThatMatch(filter);
TableTag tabletag
=
(TableTag) nodeList.elementAt(
11
);
System.out.println(tabletag.toHtml());
System.out.println(
"
==============
"
);
}
/*
* public static void main(String[] args) { TestYahoo testYahoo = new
* TestYahoo(); testYahoo.testHtml(); }
*/
public
static
void
main(String[] args)
throws
Exception {
test5(
"
http://sports.yahoo.com/nba/scoreboard
"
);
}
}
posted @
2006-09-15 10:04 阿成 阅读(3951) |
评论 (0) |
编辑 收藏
摘要: 三种整合 Struts 应用程序与 Spring 的方式
...
阅读全文
posted @
2006-09-12 15:11 阿成 阅读(207) |
评论 (0) |
编辑 收藏
单一职责原则(SRP : Single Response Principle)
就一个类而言,应该仅有一个引起它变化的原因。
在这里,职责的定义是: “变化的原因”。对于何时遵循SRP有以下的考虑:
1.如果应用程序的变化会影响到类中某一种职责,那么就应该将它与另一种职责分开,这样做可以避免客户应用程序和类中的这两职责耦合在一起。
2.如果应用程序的变化总是会导致两个职责同时变化,那么就不必要分离它们。实际上,分离它们会引起不必要的复杂性。
从上可以得知:变化的轴线仅当变化实际发生时才具有真正的意义。如果没有征兆,那么去应用SRP,或者任何其它原则都是不明智。
实际应用:持久化(Persistence)实际开发中,考虑到业务规则是会频繁改变的,而持久化的方式却不会如此频繁的变化,并且变化的原因也是完全不同的。如果把业务规则和持久化方式绑定到一起,就会为以后的开发、维护造成麻烦。运用分层(layer)架构模式或者TDD开发方式可以很早分离这两个职责,特殊情况下,还可以使用FACADE或者PROXY模式对设计进行重构,分离这两个职责。
开闭原则(OCP : The Open-Close Principle)
描述:软件实体(类、模型、函数等等)应该是可以扩展的,但是不可修改。
遵循开闭原则设计出的模块具有两个主要的特征。它们是:
1. “对于扩展是开放的”(Open for extension)。
这意味着模块的行为是可以扩展的。当应用的需要改变时,我们可以对模块进行扩展,使其具有满足那些改变的新行为。换句话说,我们可以改变模块的功能。
2. “对于更改是封闭的”(Closed for modification)。
对模块行为进行扩展时,不必改动模块的源代码或者二进制代码。模块的二进制可执行版本,无论是可链接的库、DLL或者Java的.jar文件,都无需改动。
对于OCP,关键的是 抽象
模块可以操作一个抽象体。由于模块依赖于一个固定的抽象体,所以它对于更改可以是关闭的。同时,通过从这个抽象体派生,也可以扩展此模块的行为。
在许多方面,OCP都是面向对象设计的核心所在。但实际应用中,滥用OCP原则也是错误的。正确的做法是应该仅仅对程序中呈现出频繁变化的那些部分做出抽象。拒绝不成熟的抽象和抽象本身一样重要。
Liskov替换原则(LSP)
描述:子类型(subtype)必须能够替换掉它们的基类型(base type)。
此原则是Barbara Liskov首次在1988年写下的。所以就叫做Liskov替换原则。她如此写到:
“这里需要如下替换性质:若对每个类型S的对象o1,都存在一个类型T的对象o2,使得在所有针对T编写的程序P中,用o1替换o2后,程序P行为功能不变,则S是T的子类型。
LSP然我们得出一个非常重要的结论:一个模型,如果孤立的看,并不具有真正意义上的有效性。模型的有效性只能通过它的客户程序来表现。
在考虑一个特定设计是否恰当时,不能完全孤立的来看这个解决方案。必须要根据该设计的使用者所做出的合理假设来审视它。
有谁知道设计的使用者会做出什么样的合理假设呢?大多数这样的假设都很难预测。事实上,如果试图去预测所有这些假设,我们所得到的系统很可能会充满不必要的复杂性的臭味。因此,像所有其它原则一样了,通常最好的办法就是只预测那些最明显的对于LSP的违反情况,而推迟所有其它的预测,直到出现相关的脆弱性的臭味时,才去处理它们。
IS-A是关于行为的。
LSP清晰的指出,OOD中IS-A关系是就行为方式而言的,行为方式是可以进行合理假设的,是客户程序所依赖的。
基于契约设计
基于契约设计(DBC:Design By Contract)。使用DBC,类的编写者能够显式的规定针对该类的契约。客户代码的编写者可以通过该契约获悉可以依赖的行为方式。契约是通过为每个方法声明的前置条件(preconditions)和后置条件(postconditions)来指定的。要使一个方法得以执行,前置条件必须要为真。执行完毕后,该方法要保证后置条件为真。
在单元测试中指定契约
也可以通过编写单元测试的方式来指定契约。客户代码编写者会去查看这些单元测试,这样他们就可以知道对于要使用的类,应该做什么合理的假设。
启发式规则和习惯用法
1.派生类中的退化函数
在基类中实现了f()方法,在派生类中的函数f()就是退化的,派生类中的退化函数并不总表示为违反LSP,但是当存在这种情况时,还是值得注意一下的。
2.从派生类中抛出异常
在派生类的方法中添加了其基类不会抛出的异常。如果基类的使用者不期望这些异常,那么把它们添加到派生类的方法中就会导致不可替换性。此时要遵循LSP,要么就必须改变使用者的期望,要么派生类就不应该抛出这些异常。
总结:OCP是OOD中很多原则的核心。如果这个原则应用的有效,应用程序就会具有更多的可维护性、可重用性以及健壮性。LSP是使OCP成为可能的主要原则之一。正是子类型的可替换性才使得使用基类类型的模块在无需修改的情况下就可以扩展。这种可替换性必须是开发人员可以隐式依赖的。因此,如果没有显式的强制基类类型的契约,那么代码就必须良好的并且明显的表达出这一点。
术语IS-A的含义过于广泛以至于不能作为子类型的定义。子类型的正确定义是“可替换性”,这里的可替换性可以通过显式或者隐式的契约来定义。
Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=749737
posted @
2006-09-06 20:35 阿成 阅读(318) |
评论 (0) |
编辑 收藏
|
Abstract class 抽象类:抽象类是不允许实例化的类,因此一般它需要被进行扩展继承。
Abstract method 抽象方法:抽象方法即不包含任何功能代码的方法。
Access modifier 访问控制修饰符:访问控制修饰符用来修饰Java中类、以及类的方法和变量的访问控制属性。
Anonymous class 匿名类:当你需要创建和使用一个类,而又不需要给出它的名字.
Anonymous inner classes 匿名内部类:匿名内部类是没有类名的局部内部类。
API 应用程序接口:提供特定功能的一组相关的类和方法的集合。
Array 数组:存储一个或者多个相同数据类型的数据结构,使用下标来访问。
Automatic variables 自动变量:也称为方法局部变量method local variables,声明在方法体中的变量
AWT抽象窗口工具集:一个独立的API平台提供用户界面功能。
Base class 基类:即被扩展继承的类。
Blocked state 阻塞状态:当一个线程等待资源时即处于阻塞状态。阻塞状态不使用处理器资源
Call stack 调用堆栈:调用堆栈是一个方法列表,按调用顺序保存所有在运行期被调用的方法。
Casting 类型转换 :即一个类型到另一个类型的转换
char 字符:容纳单字符的一种基本数据类型。
Child class 子类:见继承类Derived class
Class 类:面向对象中的最基本、最重要的定义类型。
Class members 类成员:定义在类一级的变量,包括实例变量和静态变量。
Class methods 类方法:类方法通常是指的静态方法
Class variable 类变量:见静态变量Static variable
Collection 容器类:容器类可以看作是一种可以储存其他对象的对象
Collection interface 容器类接口:容器类接口定义了一个对所有容器类的公共接口。
Collections framework 容器类构架:接口、实现和算法三个元素构成了容器类的架构。
Constructor 构造函数:在对象创建或者实例化时候被调用的方法。
Containers容器:容器是一种特殊的组件,它可以容纳其他组件。
Declaration 声明:声明即是在源文件中描述类、接口、方法、包或者变量的语法。
Derived class 继承类:继承类是扩展继承某个类的类。
Encapsulation 封装性:封装性体现了面向对象程序设计的一个特性
Event classes 事件类:所有的事件类都定义在java.awt.event包中。
Event sources 事件源:产生事件的组件或对象称为事件源。
Exception 异常:异常是一种对象类型,异常还指应用中发生的一种非标准流程情况,即异常状态
Extensibility扩展性:扩展性指的是面向对象程序中,不需要重写代码和重新设计,能容易的 增强源设计的功能。
Finalizer 收尾:每个类都有一个特殊的方法finalizer,它不能被直接调用,而被JVM在适当的 时候调用,通常用来处理一些清理资源的工作,因此称为收尾机制。
Garbage collection 垃圾回收机制:当需要分配的内存空间不再使用的时候,JVM将调用垃圾 回收机制来回收内存空间。
Guarded region 监控区域:一段用来监控错误产生的代码。
Heap堆:Java中管理内存的结构称作堆。
Identifiers 标识符:即指定类、方法、变量的名字。注意Java是大小写敏感的语言。
Import statement 引入语法:引入语法允许你可以不使用某个类的全名就可以参考这个类。
Inheritance 继承:继承是面向对象程序设计的重要特点,它是一种处理方法,通过这一方法, 一个对象可以获得另一个对象的特征。
Inner classes 内部类:内部类与一般的类相似,只是它被声明在类的内部,或者甚至某个 类方法体中。
Instance 实例:类实例化以后成为一个对象。
Instance variable 实例变量
Interface 接口:接口定义的是一组方法或者一个公共接口,它必须通过类来实现。
Java source file Java源文件:Java源程序包含的是Java程序语言计算机指令。
Java Virtual Machine (JVM) Java虚拟机:解释和执行Java字节码的程序,其中Java字节码 由Java编译器生成。
javac Java编译器:Javac是Java编译程序的名称。
JVM Java虚拟机:见Java虚拟机
Keywords 关键字:即Java中的保留字,不能用作其他的标识符。
Layout managers 布局管理器:布局管理器是一些用来负责处理容器中的组件布局排列的类。
Local inner classes 局部内部类:在方法体中,或者甚至更小的语句块中定义的内部类。
Local variable 局部变量:在方法体中声明的变量
Member inner classes 成员内部类:定义在封装类中的没有指定static修饰符的内部类。
Members 成员:类中的元素,包括方法和变量。
Method 方法:完成特定功能的一段源代码,可以传递参数和返回结果,定义在类中。
Method local variables 方法局部变量:见自动变量Automatic variables
Modifier 修饰符:用来修饰类、方法或者变量行为的关键字。
Native methods 本地方法:本地方法是指使用依赖平台的语言编写的方法,它用来完成Java 无法处理的某些依赖于平台的功能。
Object 对象:一旦类实例化之后就成为对象。
Overloaded methods 名称重载方法:方法的名称重载是指同一个类中具有多个方法,使用相同 的名称而只是其参数列表不同。
Overridden methods 覆盖重载方法:方法的覆盖重载是指父类和子类使用的方法采用同样的 名称、参数列表和返回类型。
Package 包:包即是将一些类聚集在一起的一个实体。
Parent class 父类:被其他类继承的类。也见基类。
Private members 私有成员:私有成员只能在当前类被访问,其他任何类都不可以访问之。
Public members 公共成员:公共成员可以被任何类访问,而不管该类属于那个包。
Runtime exceptions 运行时间异常:运行时间异常是一种不能被你自己的程序处理的异常。 通常用来指示程序BUG。
Source file 源文件:源文件是包含你的Java代码的一个纯文本文件。
Stack trace 堆栈轨迹:如果你需要打印出某个时间的调用堆栈状态,你将产生一个堆栈轨迹。
Static inner classes 静态内部类:静态内部类是内部类最简单的形式,它于一般的类很相似, 除了被定义在了某个类的内部。
Static methods 静态方法:静态方法声明一个方法属于整个类,即它可以不需要实例化一个类 就可以通过类直接访问之。
Static variable 静态变量:也可以称作类变量。它类似于静态方法,也是可以不需要实例化类 就可以通过类直接访问。
Superclass 超类:被一个或多个类继承的类。
Synchronized methods 同步方法:同步方法是指明某个方法在某个时刻只能由一个线程访问。
Thread 线程:线程是一个程序内部的顺序控制流。
Time-slicing 时间片:调度安排线程执行的一种方案。
Variable access 变量访问控制:变量访问控制是指某个类读或者改变一个其他类中的变量的能力。
Visibility 可见性: 可见性体现了方法和实例变量对其他类和包的访问控制。
J2EE相关名词解释:
容器:充当中间件的角色
WEB容器:给处于其中的应用程序组件(JSP,SERVLET)提供一个环境,使JSP,SERVLET直接更容器中的环境变量接口交互,不必关注其它系统问题。主要有WEB服务器来实现。例如:TOMCAT,WEBLOGIC,WEBSPHERE等。该容器提供的接口严格遵守J2EE规范中的WEB APPLICATION 标准。我们把遵守以上标准的WEB服务器就叫做J2EE中的WEB容器。
EJB容器:Enterprise java bean 容器。更具有行业领域特色。他提供给运行在其中的组件EJB各种管理功能。只要满足J2EE规范的EJB放入该容器,马上就会被容器进行高效率的管理。并且可以通过现成的接口来获得系统级别的服务。例如邮件服务、事务管理。
WEB容器和EJB容器在原理上是大体相同的,更多的区别是被隔离的外界环境。WEB容器更多的是跟基于HTTP的请求打交道。而EJB容器不是。它是更多的跟数据库、其它服务打交道。但他们都是把与外界的交互实现从而减轻应用程序的负担。例如SERVLET不用关心HTTP的细节,直接引用环境变量session,request,response就行、EJB不用关心数据库连接速度、各种事务控制,直接由容器来完成。
RMI/IIOP:远程方法调用/internet对象请求中介协议,他们主要用于通过远程调用服务。例如,远程有一台计算机上运行一个程序,它提供股票分析服务,我们可以在本地计算机上实现对其直接调用。当然这是要通过一定的规范才能在异构的系统之间进行通信。RMI是JAVA特有的。
JNDI:JAVA命名目录服务。主要提供的功能是:提供一个目录系统,让其它各地的应用程序在其上面留下自己的索引,从而满足快速查找和定位分布式应用程序的功能。
JMS:JAVA消息服务。主要实现各个应用程序之间的通讯。包括点对点和广播。
JAVAMAIL:JAVA邮件服务。提供邮件的存储、传输功能。他是JAVA编程中实现邮件功能的核心。相当MS中的EXCHANGE开发包。
JTA:JAVA事务服务。提供各种分布式事务服务。应用程序只需调用其提供的接口即可。 JAF:JAVA安全认证框架。提供一些安全控制方面的框架。让开发者通过各种部署和自定义实现自己的个性安全控制策略。
EAI:企业应用集成。是一种概念,从而牵涉到好多技术。J2EE技术是一种很好的集成实现。
|
|
posted @
2006-09-05 10:11 阿成 阅读(316) |
评论 (0) |
编辑 收藏
ContentType
ContentType属性指定了MIME类型和JSP页面回应时的字符编码方式。MIME类型的默认值是“text/html”; 字符编码方式的默认值是“ISO-8859-1”. MIME类型和字符编码方式由分号隔开
pageEncoding
pageEncoding 在JSP标准的语法中,如果pageEncoding属性存在,那么JSP页面的字符编码方式就由pageEncoding决定,否则就由contentType属性中的charset决定,如果charset也不存在,JSP页面的字符编码方式就采用默认的ISO-8859-1。
posted @
2006-09-05 09:37 阿成 阅读(292) |
评论 (0) |
编辑 收藏
假设你的名字叫小不点,你住在一个大院子里,你的邻居有很多小伙伴,在门口传达室还有个看大门的李大爷,李大爷就是你的网关。当你想跟院子里的某个小伙伴玩,只要你在院子里大喊一声他的名字,他听到了就会回应你,并且跑出来跟你玩。
但是你不被允许走出大门,你想与外界发生的一切联系,都必须由门口的李大爷(网关)用电话帮助你联系。假如你想找你的同学小明聊天,小明家住在很远的另外一个院子里,他家的院子里也有一个看门的王大爷(小明的网关)。但是你不知道小明家的电话号码,不过你的班主任老师有一份你们班全体同学的名单和电话号码对照表,你的老师就是你的DNS服务器。于是你在家里拨通了门口李大爷的电话,有了下面的对话:
小不点:李大爷,我想找班主任查一下小明的电话号码行吗?
李大爷:好,你等着。(接着李大爷给你的班主任挂了一个电话,问清楚了小明的电话)问到了,他家的号码是211.99.99.99
小不点:太好了!李大爷,我想找小明,你再帮我联系一下小明吧。
李大爷:没问题。(接着李大爷向电话局发出了请求接通小明家电话的请求,最后一关当然是被转接到了小明家那个院子的王大爷那里,然后王大爷把电话给转到小明家)
就这样你和小明取得了联系。
至于DHCP服务器嘛,可以这样比喻:
你家院子里的居民越来越多了,传达室李大爷那里的电话交换机已经不能满足这么多居民的需求了,所以只好采用了一种新技术叫做DHCP,居民们开机的时候随机得到一个电话号码,每一次得到的号码都可能会不同。
你家门口的李大爷:就是你的网关
你的班主任:就是你的DNS服务器
传达室的电话交换机:就是你的DHCP服务器
同上,李大爷和王大爷之间的对话就叫做路由。
另:如果还有个小朋友叫做小暗,他住的院子看门的是孙大爷,因为小暗的院子刚盖好,孙大爷刚来不久,他没有李大爷和王大爷办公室的电话(李大爷和王大爷当然也没有他的电话),这时会有两种情况:
1、居委会的赵大妈告诉了孙大爷关于李、王两位大爷的电话(同时赵大妈也告诉了李、王关于孙的电话),这就叫静态设定路由
2、赵大妈病了,孙大爷自己到处打电话,见人就说:“我是小暗他们院子管电话的”,结果被李、王二位听到了,就记在了他们的通讯录上,然后李、王就给孙大爷回了个电话说:“我是小明(小不点)他们院子管电话的”,这就叫动态设定路由
然后有一天小不点要找小暗,结果自然是小不点给李大爷打电话说:“大爷,我找小暗”(这里省略了李大爷去查小暗电话的过程,假设他知道小暗的电话),李大爷一找通讯录:“哦,小暗的院子的电话是孙大爷管着的,要找小暗自然先要通知孙大爷,我可以通知王大爷让他去找孙大爷,也可以自己直接找孙,那当然是自己直接找孙方便了”,于是李大爷给孙大爷打了电话,然后孙大爷又把电话转到了小暗家。
这里李大爷的通讯录叫做路由表。
李大爷选择是自己直接找孙大爷还是让王大爷帮忙转接叫做路由选择。
李大爷之所以选择直接找孙大爷是有依据的,因为他直接找孙大爷就能一步到位,如果要王大爷转接就需要两步才能完成,这里的“步”叫做“跳数”,李大爷的选择遵循的是最少步骤(跳数)原则(如果他不遵守这个原则,小不点可能就会多等些时间才能找到小暗,最终结果可能导致李大爷因工作不力被炒鱿鱼,这叫做“延时太长,选路原则不合理,换了一个路由器”)
当然,事情总是变化的,小不点和小明吵架了,这些天小不点老是给小暗打电话,小明心里想:“操,他是不是在说我坏话啊?”于是小明决定偷听小不点和小暗的通话,但是他又不能出院子,怎么办呢?小明做了这样一个决定:
首先他告诉自己院里管电话的王大爷说:“你给李大爷打个电话说小暗搬到咱们院子了,以后凡是打给他的电话我来接”,王大爷没反映过来(毕竟年纪大了啊!)就给李大爷打了电话,说:“现在我来管理小暗的电话了,孙已经不管了”,结果李大爷就把他的通讯录改了,这叫做路由欺骗。
以后小不点再找小暗,李大爷就转给王大爷了(其实应该转给孙大爷的),王大爷收到了这个电话就转给了小明(因为他之前已经和小明说好了),小明收到这个电话就假装小暗和小不点通信。因为小明作贼心虚,害怕明天小不点和小暗见面后当面问他,于是通信断了之后,又自己以小不点的名义给小暗通了个电话复述了一遍刚才的话,有这就叫数据窃听。
再后来,小不点还是不断的和小暗联系,而零落了小明,小明心里嘀咕啊:“我不能总是这样以小暗的身份和小不点通话啊,外一有一天露馅了怎么办!”于是他想了一个更阴险的招数:“干脆我也不偷听你们的电话了,你小不点不是不给我打电话吗!那我让你也给小暗打不了,哼哼!”,他怎么做的呢?我们来看:
他联系了一批狐朋狗友,和他们串通好,每天固定一个时间大家一起给小暗院子传达室打电话,内容什么都有,只要传达室的孙爷爷接电话,就会听到“打雷啦,下雨收衣服啊!”、“人是人他妈生的,妖是妖他妈生的”、“你妈贵姓”等等,听的脑袋都大了,不听又不行,电话不停的响啊!终于有一天,孙爷爷忍不住了,大喊一声:“我受不了拉!!!!”,于是上吊自杀了!
这就是最简单的DDOS攻击,孙爷爷心理承受能力弱的现象叫做“数据报处理模块有BUG”,孙爷爷的自杀叫做“路由器瘫痪”。如果是我,就会微笑着和他们拉家常,例如告诉他们“我早就听了天气预报,衣服10分钟前已经收好了”或者“那你妈是人还是妖”或者“和你奶奶一个姓”等等,我这种健全的心理叫做“健壮的数据报处理,能够抵御任何攻击”
孙爷爷瘫了之后,小不点终于不再给小暗打电话了,因为无论他怎么打对方都是忙音,这种现象叫做“拒绝服务”,所以小明的做法还有一个名字叫做“拒绝服务攻击”。
小明终于安静了几天,...
几天后,小明的院子来了一个美丽的女孩,名字叫做小丽,小明很喜欢她(小小年纪玩什么早恋!)可是小丽有个很帅的男朋友,小明干瞪眼没办法。当然这里还是要遵循上面的原则:小丽是不能出院子的。那个男的想泡小丽自然只能打电话,于是小明又蠢蠢欲动了:
还记得王爷爷是院子的电话总管吗?他之所以能管理电话是因为他有一个通讯录,因为同一个院子可能有2个孩子都叫小明,靠名字无法区分,所以通讯录上每一行只有两项:
门牌 电话
一号门 1234567 (这个是小明的)
二号门 7654321 (这个是小丽的)
......
王爷爷记性不好,但这总不会错了吧(同一个院子不会有2个“二号门”吧)?每次打电话人家都要说出要找的电话号码,然后通过通讯录去院子里面敲门,比如人家说我找“1234567”,于是王爷爷一比较,哦,是一号门的,他就去敲一号门“听电话”,如果是找“7654321”,那他就找二号门“听电话”。
这里的电话号码就是传说中的“IP地址”
这里的门牌号就是传说中的网卡的’MAC‘地址(每一块网卡的MAC地址都是不一样的,这是网卡的制造商写死在网卡的芯片中的)
小明心里想“奶奶的,老子泡不到你也别想泡”,于是他打起了王爷爷通讯录的主意,经过细心的观察,周密的准备,他终于发现王爷爷有尿频的毛病(毕竟是老人啊...),终于在一个月黑风高的白天,王爷爷去上厕所了,小明偷偷的摸进传达室,小心翼翼的改了王爷爷的通讯录......
过了几天,小丽的男朋友又给小丽打来了电话,对方报的电话是“7654321”,王爷爷一看通讯录,靠:
门牌 电话
一号门 1234567 (这个是小明的)
一号门 7654321 (注意:这个原来是小丽的,但是被小明改了)
......
王爷爷不知道改了啊,于是就去找一号门的小明了,小明心里这个美啊,他以小丽父亲的口吻严厉的教训了那个男的和小丽之间不正当的男女关系,结果那个男的恭恭敬敬的挂了电话。当然小丽并不知道整个事情的发生...
这里小明的行为叫做“ARP欺骗”(因为在实际的网络上是通过发送ARP数据包来实现的,所以叫做“ARP欺骗”),王爷爷的通讯录叫做“ARP表”
这里要注意:王爷爷现在有两个通讯录了,一个是记录每个院子传达室电话的本本,叫做“路由表”,一个是现在说的记录院子里面详细信息的本本,叫做“ARP表”。
有句命言是“人们总是在追求完美的,尽管永远也做不到”(请记住这句话,因为这是一个大名人--也就是我,说的)
王爷爷的制度中有一条是这么写的“每个月要重新检查一下门牌号和电话的对应本(也就是ARP表)”,这个动作叫做“刷新ARP表”,每个月的时间限制叫做“刷新ARP表的周期”。这样小明为了让那个男的永远不能找到小丽,之后每个月都要偷偷改一次那个通讯录,不过这样也是不得不做的事啊!
补充一点,小明是很聪明的,如果通讯录(ARP表)被改成了这样:
门牌(MAC) 电话(IP)
一号门 1234567 (这个是小明的)
二号门 1234567 (注意:这个被小明改了,但是他一时头晕改错了)
......
就会是计算机就会弹出一个对话框提示“出现重复的IP地址”,最终会导致王爷爷不知所措,于是通知一号门和二号门,你们的电话重复了。这样小丽就知道有人在破坏她的好事,这个现象叫做“骗局被揭穿了”
小不点知道了小明偷听他和小暗的电话,于是就和小暗约定好了密码。小不点在家里把要说的加密了之后告诉小暗。土豆-〉星期三,地瓜-〉请客,笨蛋-〉小不点家。于是小不点告诉小暗:土豆笨蛋地瓜。小明听了???不懂。。。。郁闷了。。。这是加密。
除此之外,小丽也知道了小明改他家的电话号码了。于是王爷爷就登门一个一个把电话和门牌号记下来。并且藏起来不允许外人修改,只能自己有钥匙(密码)。这是ip地址和MAC地址绑定。当有人改了电话号码的时候,就得找王爷爷改。麻烦是麻烦了,但是安全了。不过小明偷偷的把王爷爷的钥匙偷配了一把(盗窃密码成功),于是他还可以修改。这样么,就这样了。
posted @
2006-09-04 20:16 阿成 阅读(214) |
评论 (0) |
编辑 收藏
版权所有,转载请声明出处
zhyiwww@163.com
在读我自己的认识之前
,
我们先来看一下
servet
的结构图
:
以下是我自己的一点浅见:
①
Servlet
在初始化的时候
,
是通过
init(ServletConfig config)
或
init()
来执行的。
ServletConfig
是一个接口,它怎样传递给他一格对象来进行初始化呢?其实,是这个对象是由
servlet
容器来实例化的,由容器产生一格
ServletConfig
的实现类的对象,然后传递给
Servlet
结论:
ServletConfig
由容器实例化
②
我们有些时候可能在
Servlet
初始化时给它一些固定的配置参数,那么这些参数是怎样传递到
Servlet
呢?
其实,我们在
web.xml
中给
servlet
配置启动参数,在容器对
servlet
进行初始化的时候,会收集你所配置的参数,记录在
ServletConfig
的实现类中,所以你才可以通过
ServletConfig
对象的
public String getInitParameter(String name);
或
public Enumeration getInitParameterNames();
方法来取得你已经配置好的参数,也就是说,你对
servlet
的配置都已经记录在
ServletConfig
对象中了。
结论:你对
Servlet
的配置,在
Servlet
的初始化时都由容器来收集并且记录到
ServletConfig
的实现类中。
③
我们来看一个
Servlet
的配置
<servlet>
<servlet-name>index</servlet-name>
<servlet-class>org.zy.pro.sw.servlet.IndexServlet</servlet-class>
<init-param>
<param-name>dbconfig</param-name>
<param-value>/WEB-INF/dbconfig.xml</param-value>
</init-param>
</servlet>
在此,我们实现对数据库的配置文件的加载。
当
Servlet
初始化完成后,我们可以通过
String dbconf=this.getServletConfig().getInitParameter("dbconfig")
来取得我们的配置的参数的值。
但是,我们仅能得到一个配置的字符串。之后我们可以通过配置文件取得我们的数据库的配置参数,然后对数据库进行初始化。
其实我们也可以通过传递一个类的名字串,然后再实例化。
<init-param>
<param-name>dbconfig</param-name>
<param-value>org.zy.util.db.DBUtil</param-value>
</init-param>
我们先取得配置参数:
String dbconf=this.getServletConfig().getInitParameter("dbconfig")
;
然后通过
Class.forName(dbconf).getInstance();
来实例化对象,就可以实现对数据库的调用了。
结论:在
web.xml
中对
Servlet
的初始化,只能传递字符串类型的数据
④
ServletContext
ServletContext
是负责和
Servlet
的上文和下文交互,上面和
Servlet
容器交互,下面和
Servlet
中的请求和相应进行交互。
在
ServletConfig
中,
public ServletContext getServletContext();
方法实现取得当前
ServletContext
的对象。
你可能要问,
ServletContext
是一个接口,那么你如何取得他的对象呢?
其实这个问题和
ServletConfig
相同,都是在
Servlet
进行初始化的时候产生的对象,是由容器来初始化的。
posted @
2006-09-04 17:25 阿成 阅读(352) |
评论 (0) |
编辑 收藏
Spring本身有ApplicationEvent和ApplicationListener,ApplicationContext可以发布ApplicationEvent,然后ApplicationListener监听event并做出相应动作。但是这里的ApplicationEvent有个陷阱,它的传播范围和当前的ApplicationContext的级别有关,并不是系统中所有的ApplicationListener都可以收到所有的Event。
假设当前系统为一个典型的Struts+Spring+Hibernate系统,那么系统中至少会有两个ApplicationContext存在,一个时root ApplicationContext,一个是Servlet的ApplicationContext。root ApplicationContext中包含你所有在webApplicationContext.xml中定义的bean,Servlet的ApplicationContext则包含有所有在action-servlet.xml中定义的bean,
需要注意的是root context中的bean是无法看到servlet context中的bean的。
而在servlet context中的ApplicationListener也无法收到root context发布的ApplicationEvent。
http://sweetriver.spaces.live.com/blog/cns!367370EB9A9B2807!129.entry
posted @
2006-09-04 09:10 阿成 阅读(549) |
评论 (0) |
编辑 收藏
一、Servlet和JSP概述
作 者 : 仙人掌工作室
1.1 Java Servlet及其特点
Servlet是Java技术对CGI编程的回答。Servlet程序在服务器端运行,动态地生成Web页面。与传统的CGI和许多其他类似CGI的技术相比,Java Servlet具有更高的效率,更容易使用,功能更强大,具有更好的可移植性,更节省投资(更重要的是, Servlet程序员收入要比Perl程序员高:-):
高效。
在传统的CGI中,每个请求都要启动一个新的进程,如果CGI程序本身的执行时间较短,启动进程所需要的开销很可能反而超过实际执行时间。而在Servlet中,每个请求由一个轻量级的Java线程处理(而不是重量级的操作系统进程)。
在传统CGI中,如果有N个并发的对同一CGI程序的请求,则该CGI程序的代码在内存中重复装载了N次;而对于Servlet,处理请求的是N个线程,只需要一份Servlet类代码。在性能优化方面,Servlet也比CGI有着更多的选择,比如缓冲以前的计算结果,保持数据库连接的活动,等等。
方便。
Servlet提供了大量的实用工具例程,例如自动地解析和解码HTML表单数据、读取和设置HTTP头、处理Cookie、跟踪会话状态等。
功能强大。
在Servlet中,许多使用传统CGI程序很难完成的任务都可以轻松地完成。例如,Servlet能够直接和Web服务器交互,而普通的CGI程序不能。Servlet还能够在各个程序之间共享数据,使得数据库连接池之类的功能很容易实现。
可移植性好。
Servlet用Java编写,Servlet API具有完善的标准。因此,为I-Planet Enterprise Server写的Servlet无需任何实质上的改动即可移植到Apache、Microsoft IIS或者WebStar。几乎所有的主流服务器都直接或通过插件支持Servlet。
节省投资。
不仅有许多廉价甚至免费的Web服务器可供个人或小规模网站使用,而且对于现有的服务器,如果它不支持Servlet的话,要加上这部分功能也往往是免费的(或只需要极少的投资)。
1.2 JSP及其特点
JavaServer Pages(JSP)是一种实现普通静态HTML和动态HTML混合编码的技术,有关JSP基础概念的说明请参见《JSP技术简介 》。
许多由CGI程序生成的页面大部分仍旧是静态HTML,动态内容只在页面中有限的几个部分出现。但是包括Servlet在内的大多数CGI技术及其变种,总是通过程序生成整个页面。JSP使得我们可以分别创建这两个部分。例如,下面就是一个简单的JSP页面:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<HEAD><TITLE>欢迎访问网上商店</TITLE></HEAD>
<BODY>
<H1>欢迎</H1>
<SMALL>欢迎,
<!-- 首次访问的用户名字为"New User" -->
<% out.println(Utils.getUserNameFromCookie(request)); %>
要设置帐号信息,请点击
<A HREF="Account-Settings.html">这里</A></SMALL>
<P>
页面的其余内容。.
</BODY></HTML>
下面是JSP和其他类似或相关技术的一个简单比较:
JSP和Active Server Pages(ASP)相比
Microsoft的ASP是一种和JSP类似的技术。JSP和ASP相比具有两方面的优点。首先,动态部分用Java编写,而不是VB Script或其他Microsoft语言,不仅功能更强大而且更易于使用。第二,JSP应用可以移植到其他操作系统和非Microsoft的Web服务器上。
JSP和纯Servlet相比
JSP并没有增加任何本质上不能用Servlet实现的功能。但是,在JSP中编写静态HTML更加方便,不必再用 println语句来输出每一行HTML代码。更重要的是,借助内容和外观的分离,页面制作中不同性质的任务可以方便地分开:比如,由页面设计专家进行HTML设计,同时留出供Servlet程序员插入动态内容的空间。
JSP和服务器端包含(Server-Side Include,SSI)相比
SSI是一种受到广泛支持的在静态HTML中引入外部代码的技术。JSP在这方面的支持更为完善,因为它可以用Servlet而不是独立的程序来生成动态内容。另外,SSI实际上只用于简单的包含,而不是面向那些能够处理表单数据、访问数据库的“真正的”程序。
JSP和JavaScript相比
JavaScript能够在客户端动态地生成HTML。虽然JavaScript很有用,但它只能处理以客户端环境为基础的动态信息。除了Cookie之外,HTTP状态和表单提交数据对JavaScript来说都是不可用的。另外,由于是在客户端运行,JavaScript不能访问服务器端资源,比如数据库、目录信息等等。
2.1 安装Servlet和JSP开发工具
要学习Servlet和JSP开发,首先你必须准备一个符合Java Servlet 2.1/2.2和JavaServer Pages1.0/1.1规范的开发环境。Sun提供免费的JavaServer Web Development Kit(JSWDK),可以从
http://java.sun.com/products/servlet/ 下载。
安装好JSWDK之后,你还要告诉javac,在编译文件的时候到哪里去寻找Servlet和JSP类。JSWDK安装指南对此有详细说明,但主要就是把servlet.jar和jsp.jar加入CLASSPATH。CLASSPATH是一个指示Java如何寻找类文件的环境变量,如果不设置CLASSPATH,Java在当前目录和标准系统库中寻找类;如果你自己设置了CLASSPATH,不要忘记包含当前目录(即在CLASSPATH中包含“.”)。
另外,为了避免和其他开发者安装到同一Web服务器上的Servlet产生命名冲突,最好把自己的Servlet放入包里面。此时,把包层次结构中的顶级目录也加入CLASSPATH会带来不少方便。请参见下文具体说明。
2.2 安装支持Servlet的Web服务器
除了开发工具之外,你还要安装一个支持Java Servlet的Web服务器,或者在现有的Web服务器上安装Servlet软件包。如果你使用的是最新的Web服务器或应用服务器,很可能它已经有了所有必需的软件。请查看Web服务器的文档,或访问
http://java.sun.com/products/servlet/industry.html 查看支持Servlet的服务器软件清单。
虽然最终运行Servlet的往往是商业级的服务器,但是开始学习的时候,用一个能够在台式机上运行的免费系统进行开发和测试也足够了。下面是几种当前最受欢迎的产品。
Apache Tomcat.
Tomcat是Servlet 2.2和JSP 1.1规范的官方参考实现。Tomcat既可以单独作为小型Servlet、JSP测试服务器,也可以集成到Apache Web服务器。直到2000年早期,Tomcat还是唯一的支持Servlet 2.2和JSP 1.1规范的服务器,但已经有许多其它服务器宣布提供这方面的支持。
Tomcat和Apache一样是免费的。不过,快速、稳定的Apache服务器安装和配置起来有点麻烦,Tomcat也有同样的缺点。和其他商业级Servlet引擎相比,配置Tomcat的工作量显然要多一点。具体请参见
http://jakarta.apache.org/ 。
JavaServer Web Development Kit (JSWDK).
JSWDK是Servlet 2.1和JSP 1.0的官方参考实现。把Servlet和JSP应用部署到正式运行它们的服务器之前,JSWDK可以单独作为小型的Servlet、JSP测试服务器。JSWDK也是免费的,而且具有很好的稳定性,但它的安装和配置也较为复杂。具体请参见
http://java.sun.com/products/servlet/download.html 。
Allaire JRun.
JRun是一个Servlet和JSP引擎,它可以集成到Netscape Enterprise或FastTrack Server、IIS、Microsoft Personal Web Server、版本较低的Apache、O'eilly的WebSite或者StarNine Web STAR。最多支持5个并发连接的限制版本是免费的,商业版本中不存在这个限制,而且增加了远程管理控制台之类的功能。具体请参见
http://www.allaire.com/products/jrun/ 。
New Atlanta 的ServletExec
ServletExec是一个快速的Servlet和JSP引擎,它可以集成到大多数流行的Web服务器,支持平台包括Solaris、Windows、MacOS、HP-UX和Linux。ServletExec可以免费下载和使用,但许多高级功能和管理工具只有在购买了许可之后才可以使用。New Atlanta还提供一个免费的Servlet调试器,该调试器可以在许多流行的Java IDE下工作。具体请参见
http://newatlanta.com/ 。
Gefion的LiteWebServer (LWS)
LWS是一个支持Servlet 2.2和JSP 1.1的免费小型Web服务器。 Gefion还有一个免费的WAICoolRunner插件,利用该插件可以为Netscape FastTrack和Enterprise Server增加Servlet 2.2和JSP 1.1支持。具体请参见
http://www.gefionsoftware.com/ 。
Sun的Java Web Server.
该服务器全部用Java写成,而且是首先提供Servlet 2.1和JSP 1.0规范完整支持的Web服务器之一。虽然Sun现在已转向Netscape/I-Planet Server,不再发展Java Web Server,但它仍旧是一个广受欢迎的Servlet、JSP学习平台。要得到免费试用版本,请访问
http://www.sun.com/software/jwebserver/try/ 。
3.1 Servlet基本结构
下面的代码显示了一个简单Servlet的基本结构。该Servlet处理的是GET请求,所谓的GET请求,如果你不熟悉HTTP,可以把它看成是当用户在浏览器地址栏输入URL、点击Web页面中的链接、提交没有指定METHOD的表单时浏览器所发出的请求。Servlet也可以很方便地处理POST请求。POST请求是提交那些指定了METHOD=“POST”的表单时所发出的请求,具体请参见稍后几节的讨论。
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class SomeServlet extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
// 使用“request”读取和请求有关的信息(比如Cookies)
// 和表单数据
// 使用“response”指定HTTP应答状态代码和应答头
// (比如指定内容类型,设置Cookie)
PrintWriter out = response.getWriter();
// 使用 "out"把应答内容发送到浏览器
}
}
如果某个类要成为Servlet,则它应该从HttpServlet 继承,根据数据是通过GET还是POST发送,覆盖doGet、doPost方法之一或全部。doGet和doPost方法都有两个参数,分别为HttpServletRequest 类型和HttpServletResponse 类型。HttpServletRequest提供访问有关请求的信息的方法,例如表单数据、HTTP请求头等等。HttpServletResponse除了提供用于指定HTTP应答状态(200,404等)、应答头(Content-Type,Set-Cookie等)的方法之外,最重要的是它提供了一个用于向客户端发送数据的PrintWriter 。对于简单的Servlet来说,它的大部分工作是通过println语句生成向客户端发送的页面。
注意doGet和doPost抛出两个异常,因此你必须在声明中包含它们。另外,你还必须导入java.io包(要用到PrintWriter等类)、javax.servlet包(要用到HttpServlet等类)以及javax.servlet.http包(要用到HttpServletRequest类和HttpServletResponse类)。
最后,doGet和doPost这两个方法是由service方法调用的,有时你可能需要直接覆盖service方法,比如Servlet要处理GET和POST两种请求时。
3.2 输出纯文本的简单Servlet
下面是一个输出纯文本的简单Servlet。
3.2.1 HelloWorld.java
package hall;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class HelloWorld extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
PrintWriter out = response.getWriter();
out.println("Hello World");
}
}
3.2.2 Servlet的编译和安装
不同的Web服务器上安装Servlet的具体细节可能不同,请参考Web服务器文档了解更权威的说明。假定使用Java Web Server(JWS)2.0,则Servlet应该安装到JWS安装目录的servlets子目录下。在本文中,为了避免同一服务器上不同用户的Servlet命名冲突,我们把所有Servlet都放入一个独立的包hall中;如果你和其他人共用一个服务器,而且该服务器没有“虚拟服务器”机制来避免这种命名冲突,那么最好也使用包。把Servlet放入了包hall之后,HelloWorld.java实际上是放在servlets目录的hall子目录下。
大多数其他服务器的配置方法也相似,除了JWS之外,本文的Servlet和JSP示例已经在BEA WebLogic和IBM WebSphere 3.0下经过测试。WebSphere具有优秀的虚拟服务器机制,因此,如果只是为了避免命名冲突的话并非一定要用包。
对于没有使用过包的初学者,下面我们介绍编译包里面的类的两种方法。
一种方法是设置CLASSPATH,使其指向实际存放Servlet的目录的上一级目录(Servlet主目录),然后在该目录中按正常的方式编译。例如,如果Servlet的主目录是C:\JavaWebServer\servlets,包的名字(即主目录下的子目录名字)是hall,在Windows下,编译过程如下:
DOS> set CLASSPATH=C:\JavaWebServer\servlets;%CLASSPATH%
DOS> cd C:\JavaWebServer\servlets\hall
DOS> javac YourServlet.java
第二种编译包里面的Servlet的方法是进入Servlet主目录,执行“javac directory\YourServlet.java”(Windows)或者“javac directory/YourServlet.java”(Unix)。例如,再次假定Servlet主目录是C:\JavaWebServer\servlets,包的名字是hall,在Windows中编译过程如下:
DOS> cd C:\JavaWebServer\servlets
DOS> javac hall\YourServlet.java
注意在Windows下,大多数JDK 1.1版本的javac要求目录名字后面加反斜杠(\)。JDK1.2已经改正这个问题,然而由于许多Web服务器仍旧使用JDK 1.1,因此大量的Servlet开发者仍旧在使用JDK 1.1。
最后,Javac还有一个高级选项用于支持源代码和.class文件的分开放置,即你可以用javac的“-d”选项把.class文件安装到Web服务器所要求的目录。
3.2.3 运行Servlet
在Java Web Server下,Servlet应该放到JWS安装目录的servlets子目录下,而调用Servlet的URL是
http://host/servlet/ServletName。注意子目录的名字是servlets(带“s”),而URL使用的是“servlet”。由于HelloWorld Servlet放入包hall,因此调用它的URL应该是
http://host/servlet/hall.HelloWorld。在其他的服务器上,安装和调用Servlet的方法可能略有不同。
大多数Web服务器还允许定义Servlet的别名,因此Servlet也可能用
http://host/any-path/any-file.html形式的URL调用。具体如何进行配置完全依赖于服务器类型,请参考服务器文档了解细节。
3.3 输出HTML的Servlet
大多数Servlet都输出HTML,而不象上例一样输出纯文本。要输出HTML还有两个额外的步骤要做:告诉浏览器接下来发送的是HTML;修改println语句构造出合法的HTML页面。
第一步通过设置Content-Type(内容类型)应答头完成。一般地,应答头可以通过HttpServletResponse的setHeader方法设置,但由于设置内容类型是一个很频繁的操作,因此Servlet API提供了一个专用的方法setContentType。注意设置应答头应该在通过PrintWriter发送内容之前进行。下面是一个实例:
HelloWWW.java
package hall;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class HelloWWW extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 " +
"Transitional//EN\">\n" +
"<HTML>\n" +
"<HEAD><TITLE>Hello WWW</TITLE></HEAD>\n" +
"<BODY>\n" +
"<H1>Hello WWW</H1>\n" +
"</BODY></HTML>");
}
}
3.4 几个HTML工具函数
通过println语句输出HTML并不方便,根本的解决方法是使用JavaServer Pages(JSP)。然而,对于标准的Servlet来说,由于Web页面中有两个部分(DOCTYPE和HEAD)一般不会改变,因此可以用工具函数来封装生成这些内容的代码。
虽然大多数主流浏览器都会忽略DOCTYPE行,但严格地说HTML规范是要求有DOCTYPE行的,它有助于HTML语法检查器根据所声明的HTML版本检查HTML文档合法性。在许多Web页面中,HEAD部分只包含<TITLE>。虽然许多有经验的编写者都会在HEAD中包含许多META标记和样式声明,但这里只考虑最简单的情况。
下面的Java方法只接受页面标题为参数,然后输出页面的DOCTYPE、HEAD、TITLE部分。清单如下:
ServletUtilities.java
package hall;
public class ServletUtilities {
public static final String DOCTYPE =
"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">";
public static String headWithTitle(String title) {
return(DOCTYPE + "\n" +
"<HTML>\n" +
"<HEAD><TITLE>" + title + "</TITLE></HEAD>\n");
}
// 其他工具函数的代码在本文后面介绍
}
HelloWWW2.java
下面是应用了ServletUtilities之后重写HelloWWW类得到的HelloWWW2:
package hall;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class HelloWWW2 extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println(ServletUtilities.headWithTitle("Hello WWW") +
"<BODY>\n" +
"<H1>Hello WWW</H1>\n" +
"</BODY></HTML>");
}
}
4.1 表单数据概述
如果你曾经使用过Web搜索引擎,或者浏览过在线书店、股票价格、机票信息,或许会留意到一些古怪的URL,比如“http://host/path?user=Marty+Hall&origin=bwi&dest=lax”。这个URL中位于问号后面的部分,即“user=Marty+Hall&origin=bwi&dest=lax”,就是表单数据,这是将Web页面数据发送给服务器程序的最常用方法。对于GET请求,表单数据附加到URL的问号后面(如上例所示);对于POST请求,表单数据用一个单独的行发送给服务器。
以前,从这种形式的数据提取出所需要的表单变量是CGI编程中最麻烦的事情之一。首先,GET请求和POST请求的数据提取方法不同:对于GET请求,通常要通过QUERY_STRING环境变量提取数据;对于POST请求,则一般通过标准输入提取数据。第二,程序员必须负责在“&”符号处截断变量名字-变量值对,再分离出变量名字(等号左边)和变量值(等号右边)。第三,必须对变量值进行URL反编码操作。因为发送数据的时候,字母和数字以原来的形式发送,但空格被转换成加号,其他字符被转换成“%XX”形式,其中XX是十六进制表示的字符ASCII(或者ISO Latin-1)编码值。例如,如果HTML表单中名为“users”的域值为“~hall, ~gates, and ~mcnealy”,则实际向服务器发送的数据为“users=%7Ehall%2C+%7Egates%2C+and+%7Emcnealy”。最后,即第四个导致解析表单数据非常困难的原因在于,变量值既可能被省略(如“param1=val1&param2=&param3=val3”),也有可能一个变量拥有一个以上的值,即同一个变量可能出现一次以上(如“param1=val1&param2=val2&param1=val3”)。
Java Servlet的好处之一就在于所有上述解析操作都能够自动完成。只需要简单地调用一下HttpServletRequest的getParameter方法、在调用参数中提供表单变量的名字(大小写敏感)即可,而且GET请求和POST请求的处理方法完全相同。
getParameter方法的返回值是一个字符串,它是参数中指定的变量名字第一次出现所对应的值经反编码得到得字符串(可以直接使用)。如果指定的表单变量存在,但没有值,getParameter返回空字符串;如果指定的表单变量不存在,则返回null。如果表单变量可能对应多个值,可以用getParameterValues来取代getParameter。getParameterValues能够返回一个字符串数组。
最后,虽然在实际应用中Servlet很可能只会用到那些已知名字的表单变量,但在调试环境中,获得完整的表单变量名字列表往往是很有用的,利用getParamerterNames方法可以方便地实现这一点。getParamerterNames返回的是一个Enumeration,其中的每一项都可以转换为调用getParameter的字符串。
4.2 实例:读取三个表单变量
下面是一个简单的例子,它读取三个表单变量param1、param2和param3,并以HTML列表的形式列出它们的值。请注意,虽然在发送应答内容之前必须指定应答类型(包括内容类型、状态以及其他HTTP头信息),但Servlet对何时读取请求内容却没有什么要求。
另外,我们也可以很容易地把Servlet做成既能处理GET请求,也能够处理POST请求,这只需要在doPost方法中调用doGet方法,或者覆盖service方法(service方法调用doGet、doPost、doHead等方法)。在实际编程中这是一种标准的方法,因为它只需要很少的额外工作,却能够增加客户端编码的灵活性。
如果你习惯用传统的CGI方法,通过标准输入读取POST数据,那么在Servlet中也有类似的方法,即在HttpServletRequest上调用getReader或者getInputStream,但这种方法对普通的表单变量来说太麻烦。然而,如果是要上载文件,或者POST数据是通过专门的客户程序而不是HTML表单发送,那么就要用到这种方法。
注意用第二种方法读取POST数据时,不能再用getParameter来读取这些数据。
ThreeParams.java
package hall;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.*;
public class ThreeParams extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
String title = "读取三个请求参数";
out.println(ServletUtilities.headWithTitle(title) +
"<BODY>\n" +
"<H1 ALIGN=CENTER>" + title + "</H1>\n" +
"<UL>\n" +
" <LI>param1: "
+ request.getParameter("param1") + "\n" +
" <LI>param2: "
+ request.getParameter("param2") + "\n" +
" <LI>param3: "
+ request.getParameter("param3") + "\n" +
"</UL>\n" +
"</BODY></HTML>");
}
public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
4.3 实例:输出所有的表单数据
下面这个例子寻找表单所发送的所有变量名字,并把它们放入表格中,没有值或者有多个值的变量都突出显示。
首先,程序通过HttpServletRequest的getParameterNames方法得到所有的变量名字,getParameterNames返回的是一个Enumeration。接下来,程序用循环遍历这个Enumeration,通过hasMoreElements确定何时结束循环,利用nextElement得到Enumeration中的各个项。由于nextElement返回的是一个Object,程序把它转换成字符串后再用这个字符串来调用getParameterValues。
getParameterValues返回一个字符串数组,如果这个数组只有一个元素且等于空字符串,说明这个表单变量没有值,Servlet以斜体形式输出“No Value”;如果数组元素个数大于1,说明这个表单变量有多个值,Servlet以HTML列表形式输出这些值;其他情况下Servlet直接把变量值放入表格。
ShowParameters.java
注意,ShowParameters.java用到了前面介绍过的ServletUtilities.java。
package hall;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.*;
public class ShowParameters extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
String title = "读取所有请求参数";
out.println(ServletUtilities.headWithTitle(title) +
"<BODY BGCOLOR=\"#FDF5E6\">\n" +
"<H1 ALIGN=CENTER>" + title + "</H1>\n" +
"<TABLE BORDER=1 ALIGN=CENTER>\n" +
"<TR BGCOLOR=\"#FFAD00\">\n" +
"<TH>参数名字<TH>参数值");
Enumeration paramNames = request.getParameterNames();
while(paramNames.hasMoreElements()) {
String paramName = (String)paramNames.nextElement();
out.println("<TR><TD>" + paramName + "\n<TD>");
String[] paramValues = request.getParameterValues(paramName);
if (paramValues.length == 1) {
String paramValue = paramValues[0];
if (paramValue.length() == 0)
out.print("<I>No Value</I>");
else
out.print(paramValue);
} else {
out.println("<UL>");
for(int i=0; i<paramValues.length; i++) {
out.println("<LI>" + paramValues[i]);
}
out.println("</UL>");
}
}
out.println("</TABLE>\n</BODY></HTML>");
}
public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
测试表单
下面是向上述Servlet发送数据的表单PostForm.html。就像所有包含密码输入域的表单一样,该表单用POST方法发送数据。我们可以看到,在Servlet中同时实现doGet和doPost这两种方法为表单制作带来了方便。
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<HEAD>
<TITLE>示例表单</TITLE>
</HEAD>
<BODY BGCOLOR="#FDF5E6">
<H1 ALIGN="CENTER">用POST方法发送数据的表单</H1>
<FORM ACTION="/servlet/hall.ShowParameters"
METHOD="POST">
Item Number:
<INPUT TYPE="TEXT" NAME="itemNum"><BR>
Quantity:
<INPUT TYPE="TEXT" NAME="quantity"><BR>
Price Each:
<INPUT TYPE="TEXT" NAME="price" VALUE="$"><BR>
<HR>
First Name:
<INPUT TYPE="TEXT" NAME="firstName"><BR>
Last Name:
<INPUT TYPE="TEXT" NAME="lastName"><BR>
Middle Initial:
<INPUT TYPE="TEXT" NAME="initial"><BR>
Shipping Address:
<TEXTAREA NAME="address" ROWS=3 COLS=40></TEXTAREA><BR>
Credit Card:<BR>
<INPUT TYPE="RADIO" NAME="cardType"
VALUE="Visa">Visa<BR>
<INPUT TYPE="RADIO" NAME="cardType"
VALUE="Master Card">Master Card<BR>
<INPUT TYPE="RADIO" NAME="cardType"
VALUE="Amex">American Express<BR>
<INPUT TYPE="RADIO" NAME="cardType"
VALUE="Discover">Discover<BR>
<INPUT TYPE="RADIO" NAME="cardType"
VALUE="Java SmartCard">Java SmartCard<BR>
Credit Card Number:
<INPUT TYPE="PASSWORD" NAME="cardNum"><BR>
Repeat Credit Card Number:
<INPUT TYPE="PASSWORD" NAME="cardNum"><BR><BR>
<CENTER>
<INPUT TYPE="SUBMIT" VALUE="Submit Order">
</CENTER>
</FORM>
</BODY>
</HTML>
5.1 HTTP请求头概述
HTTP客户程序(例如浏览器),向服务器发送请求的时候必须指明请求类型(一般是GET或者POST)。如有必要,客户程序还可以选择发送其他的请求头。大多数请求头并不是必需的,但Content-Length除外。对于POST请求来说Content-Length必须出现。
下面是一些最常见的请求头:
Accept:浏览器可接受的MIME类型。
Accept-Charset:浏览器可接受的字符集。
Accept-Encoding:浏览器能够进行解码的数据编码方式,比如gzip。Servlet能够向支持gzip的浏览器返回经gzip编码的HTML页面。许多情形下这可以减少5到10倍的下载时间。
Accept-Language:浏览器所希望的语言种类,当服务器能够提供一种以上的语言版本时要用到。
Authorization:授权信息,通常出现在对服务器发送的WWW-Authenticate头的应答中。
Connection:表示是否需要持久连接。如果Servlet看到这里的值为“Keep-Alive”,或者看到请求使用的是HTTP 1.1(HTTP 1.1默认进行持久连接),它就可以利用持久连接的优点,当页面包含多个元素时(例如Applet,图片),显著地减少下载所需要的时间。要实现这一点,Servlet需要在应答中发送一个Content-Length头,最简单的实现方法是:先把内容写入ByteArrayOutputStream,然后在正式写出内容之前计算它的大小。
Content-Length:表示请求消息正文的长度。
Cookie:这是最重要的请求头信息之一,参见后面《Cookie处理》一章中的讨论。
From:请求发送者的email地址,由一些特殊的Web客户程序使用,浏览器不会用到它。
Host:初始URL中的主机和端口。
If-Modified-Since:只有当所请求的内容在指定的日期之后又经过修改才返回它,否则返回304“Not Modified”应答。
Pragma:指定“no-cache”值表示服务器必须返回一个刷新后的文档,即使它是代理服务器而且已经有了页面的本地拷贝。
Referer:包含一个URL,用户从该URL代表的页面出发访问当前请求的页面。
User-Agent:浏览器类型,如果Servlet返回的内容与浏览器类型有关则该值非常有用。
UA-Pixels,UA-Color,UA-OS,UA-CPU:由某些版本的IE浏览器所发送的非标准的请求头,表示屏幕大小、颜色深度、操作系统和CPU类型。
有关HTTP头完整、详细的说明,请参见
http://www.w3.org/Protocols/ 的HTTP规范。
5.2 在Servlet中读取请求头
在Servlet中读取HTTP头是非常方便的,只需要调用一下HttpServletRequest的getHeader方法即可。如果客户请求中提供了指定的头信息,getHeader返回对应的字符串;否则,返回null。部分头信息经常要用到,它们有专用的访问方法:getCookies方法返回Cookie头的内容,经解析后存放在Cookie对象的数组中,请参见后面有关Cookie章节的讨论;getAuthType和getRemoteUser方法分别读取Authorization头中的一部分内容;getDateHeader和getIntHeader方法读取指定的头,然后返回日期值或整数值。
除了读取指定的头之外,利用getHeaderNames还可以得到请求中所有头名字的一个Enumeration对象。
最后,除了查看请求头信息之外,我们还可以从请求主命令行获得一些信息。getMethod方法返回请求方法,请求方法通常是GET或者POST,但也有可能是HEAD、PUT或者DELETE。getRequestURI方法返回URI(URI是URL的从主机和端口之后到表单数据之前的那一部分)。getRequestProtocol返回请求命令的第三部分,一般是“HTTP/1.0”或者“HTTP/1.1”。
5.3 实例:输出所有的请求头
下面的Servlet实例把所有接收到的请求头和它的值以表格的形式输出。另外,该Servlet还会输出主请求命令的三个部分:请求方法,URI,协议/版本。
ShowRequestHeaders.java
package hall;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.*;
public class ShowRequestHeaders extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
String title = "显示所有请求头";
out.println(ServletUtilities.headWithTitle(title) +
"<BODY BGCOLOR=\"#FDF5E6\">\n" +
"<H1 ALIGN=CENTER>" + title + "</H1>\n" +
"<B>Request Method: </B>" +
request.getMethod() + "<BR>\n" +
"<B>Request URI: </B>" +
request.getRequestURI() + "<BR>\n" +
"<B>Request Protocol: </B>" +
request.getProtocol() + "<BR><BR>\n" +
"<TABLE BORDER=1 ALIGN=CENTER>\n" +
"<TR BGCOLOR=\"#FFAD00\">\n" +
"<TH>Header Name<TH>Header Value");
Enumeration headerNames = request.getHeaderNames();
while(headerNames.hasMoreElements()) {
String headerName = (String)headerNames.nextElement();
out.println("<TR><TD>" + headerName);
out.println(" <TD>" + request.getHeader(headerName));
}
out.println("</TABLE>\n</BODY></HTML>");
}
public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
6.1 CGI变量概述
如果你是从传统的CGI编程转而学习Java Servlet,或许已经习惯了“CGI变量”这一概念。CGI变量汇集了各种有关请求的信息:
部分来自HTTP请求命令和请求头,例如Content-Length头;
部分来自Socket本身,例如主机的名字和IP地址;
也有部分与服务器安装配置有关,例如URL到实际路径的映射。
6.2 标准CGI变量的Servlet等价表示
下表假定request对象是提供给doGet和doPost方法的HttpServletRequest类型对象。 CGI变量 含义 从doGet或doPost访问
AUTH_TYPE 如果提供了Authorization头,这里指定了具体的模式(basic或者digest)。 request.getAuthType()
CONTENT_LENGTH 只用于POST请求,表示所发送数据的字节数。 严格地讲,等价的表达方式应该是String.valueOf(request.getContentLength())(返回一个字符串)。但更常见的是用request.getContentLength()返回含义相同的整数。
CONTENT_TYPE 如果指定的话,表示后面所跟数据的类型。 request.getContentType()
DOCUMENT_ROOT 与
http://host/对应的路径。 getServletContext().getRealPath("/")
注意低版本Servlet规范中的等价表达方式是request.getRealPath("/")。
HTTP_XXX_YYY 访问任意HTTP头。 request.getHeader("Xxx-Yyy")
PATH_INFO URL中的附加路径信息,即URL中Servlet路径之后、查询字符串之前的那部分。 request.getPathInfo()
PATH_TRANSLATED 映射到服务器实际路径之后的路径信息。 request.getPathTranslated()
QUERY_STRING 这是字符串形式的附加到URL后面的查询字符串,数据仍旧是URL编码的。在Servlet中很少需要用到未经解码的数据,一般使用getParameter访问各个参数。 request.getQueryString()
REMOTE_ADDR 发出请求的客户机的IP地址。 request.getRemoteAddr()
REMOTE_HOST 发出请求的客户机的完整的域名,如java.sun.com。如果不能确定该域名,则返回IP地址。 request.getRemoteHost()
REMOTE_USER 如果提供了Authorization头,则代表其用户部分。它代表发出请求的用户的名字。 request.getRemoteUser()
REQUEST_METHOD 请求类型。通常是GET或者POST。但偶尔也会出现HEAD,PUT, DELETE,OPTIONS,或者 TRACE. request.getMethod()
SCRIPT_NAME URL中调用Servlet的那一部分,不包含附加路径信息和查询字符串。 request.getServletPath()
SERVER_NAME Web服务器名字。 request.getServerName()
SERVER_PORT 服务器监听的端口。 严格地说,等价表达应该是返回字符串的String.valueOf(request.getServerPort())。但经常使用返回整数值的request.getServerPort()。
SERVER_PROTOCOL 请求命令中的协议名字和版本(即HTTP/1.0或HTTP/1.1)。 request.getProtocol()
SERVER_SOFTWARE Servlet引擎的名字和版本。 getServletContext().getServerInfo()
6.3 实例:读取CGI变量
下面这个Servlet创建一个表格,显示除了HTTP_XXX_YYY之外的所有CGI变量。HTTP_XXX_YYY是HTTP请求头信息,请参见上一节介绍。
ShowCGIVariables.java
package hall;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.*;
public class ShowCGIVariables extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
String[][] variables =
{ { "AUTH_TYPE", request.getAuthType() },
{ "CONTENT_LENGTH", String.valueOf(request.getContentLength()) },
{ "CONTENT_TYPE", request.getContentType() },
{ "DOCUMENT_ROOT", getServletContext().getRealPath("/") },
{ "PATH_INFO", request.getPathInfo() },
{ "PATH_TRANSLATED", request.getPathTranslated() },
{ "QUERY_STRING", request.getQueryString() },
{ "REMOTE_ADDR", request.getRemoteAddr() },
{ "REMOTE_HOST", request.getRemoteHost() },
{ "REMOTE_USER", request.getRemoteUser() },
{ "REQUEST_METHOD", request.getMethod() },
{ "SCRIPT_NAME", request.getServletPath() },
{ "SERVER_NAME", request.getServerName() },
{ "SERVER_PORT", String.valueOf(request.getServerPort()) },
{ "SERVER_PROTOCOL", request.getProtocol() },
{ "SERVER_SOFTWARE", getServletContext().getServerInfo() }
};
String title = "显示CGI变量";
out.println(ServletUtilities.headWithTitle(title) +
"<BODY BGCOLOR=\"#FDF5E6\">\n" +
"<H1 ALIGN=CENTER>" + title + "</H1>\n" +
"<TABLE BORDER=1 ALIGN=CENTER>\n" +
"<TR BGCOLOR=\"#FFAD00\">\n" +
"<TH>CGI Variable Name<TH>Value");
for(int i=0; i<variables.length; i++) {
String varName = variables[i][0];
String varValue = variables[i][1];
if (varValue == null)
varValue = "<I>Not specified</I>";
out.println("<TR><TD>" + varName + "<TD>" + varValue);
}
out.println("</TABLE></BODY></HTML>");
}
public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
7.1 状态代码概述
Web服务器响应浏览器或其他客户程序的请求时,其应答一般由以下几个部分组成:一个状态行,几个应答头,一个空行,内容文档。下面是一个最简单的应答:
HTTP/1.1 200 OK
Content-Type: text/plain
Hello World
状态行包含HTTP版本、状态代码、与状态代码对应的简短说明信息。在大多数情况下,除了Content-Type之外的所有应答头都是可选的。但Content-Type是必需的,它描述的是后面文档的MIME类型。虽然大多数应答都包含一个文档,但也有一些不包含,例如对HEAD请求的应答永远不会附带文档。有许多状态代码实际上用来标识一次失败的请求,这些应答也不包含文档(或只包含一个简短的错误信息说明)。
Servlet可以利用状态代码来实现许多功能。例如,可以把用户重定向到另一个网站;可以指示出后面的文档是图片、PDF文件或HTML文件;可以告诉用户必须提供密码才能访问文档;等等。这一部分我们将具体讨论各种状态代码的含义以及利用这些代码可以做些什么。
7.2 设置状态代码
如前所述,HTTP应答状态行包含HTTP版本、状态代码和对应的状态信息。由于状态信息直接和状态代码相关,而HTTP版本又由服务器确定,因此需要Servlet设置的只有一个状态代码。
Servlet设置状态代码一般使用HttpServletResponse的setStatus方法。setStatus方法的参数是一个整数(即状态代码),不过为了使得代码具有更好的可读性,可以用HttpServletResponse中定义的常量来避免直接使用整数。这些常量根据HTTP 1.1中的标准状态信息命名,所有的名字都加上了SC前缀(Status Code的缩写)并大写,同时把空格转换成了下划线。也就是说,与状态代码404对应的状态信息是“Not Found”,则HttpServletResponse中的对应常量名字为SC_NOT_FOUND。但有两个例外:和状态代码302对应的常量根据HTTP 1.0命名,而307没有对应的常量。
设置状态代码并非总是意味着不要再返回文档。例如,虽然大多数服务器返回404应答时会输出简单的“File Not Found”信息,但Servlet也可以定制这个应答。不过,定制应答时应当在通过PrintWriter发送任何内容之前先调用response.setStatus。
虽然设置状态代码一般使用的是response.setStauts(int)方法,但为了简单起见,HttpServletResponse为两种常见的情形提供了专用方法:sendError方法生成一个404应答,同时生成一个简短的HTML错误信息文档;sendRedirect方法生成一个302应答,同时在Location头中指示新文档的URL。
7.3 HTTP 1.1状态代码及其含义
下表显示了常见的HTTP 1.1状态代码以及它们对应的状态信息和含义。
应当谨慎地使用那些只有HTTP 1.1支持的状态代码,因为许多浏览器还只能够支持HTTP 1.0。如果你使用了HTTP 1.1特有的状态代码,最好能够检查一下请求的HTTP版本号(通过HttpServletRequest的getProtocol方法)。 状态代码 状态信息 含义
100 Continue 初始的请求已经接受,客户应当继续发送请求的其余部分。(HTTP 1.1新)
101 Switching Protocols 服务器将遵从客户的请求转换到另外一种协议(HTTP 1.1新)
200 OK 一切正常,对GET和POST请求的应答文档跟在后面。如果不用setStatus设置状态代码,Servlet默认使用202状态代码。
201 Created 服务器已经创建了文档,Location头给出了它的URL。
202 Accepted 已经接受请求,但处理尚未完成。
203 Non-Authoritative Information 文档已经正常地返回,但一些应答头可能不正确,因为使用的是文档的拷贝(HTTP 1.1新)。
204 No Content 没有新文档,浏览器应该继续显示原来的文档。如果用户定期地刷新页面,而Servlet可以确定用户文档足够新,这个状态代码是很有用的。
205 Reset Content 没有新的内容,但浏览器应该重置它所显示的内容。用来强制浏览器清除表单输入内容(HTTP 1.1新)。
206 Partial Content 客户发送了一个带有Range头的GET请求,服务器完成了它(HTTP 1.1新)。
300 Multiple Choices 客户请求的文档可以在多个位置找到,这些位置已经在返回的文档内列出。如果服务器要提出优先选择,则应该在Location应答头指明。
301 Moved Permanently 客户请求的文档在其他地方,新的URL在Location头中给出,浏览器应该自动地访问新的URL。
302 Found 类似于301,但新的URL应该被视为临时性的替代,而不是永久性的。注意,在HTTP1.0中对应的状态信息是“Moved Temporatily”,而HttpServletResponse中相应的常量是SC_MOVED_TEMPORARILY,而不是SC_FOUND。
出现该状态代码时,浏览器能够自动访问新的URL,因此它是一个很有用的状态代码。为此,Servlet提供了一个专用的方法,即sendRedirect。使用response.sendRedirect(url)比使用response.setStatus(response.SC_MOVED_TEMPORARILY)和response.setHeader("Location",url)更好。这是因为:
首先,代码更加简洁。
第二,使用sendRedirect,Servlet会自动构造一个包含新链接的页面(用于那些不能自动重定向的老式浏览器)。
最后,sendRedirect能够处理相对URL,自动把它们转换成绝对URL。
注意这个状态代码有时候可以和301替换使用。例如,如果浏览器错误地请求
http://host/~user(缺少了后面的斜杠),有的服务器返回301,有的则返回302。
严格地说,我们只能假定只有当原来的请求是GET时浏览器才会自动重定向。请参见307。
303 See Other 类似于301/302,不同之处在于,如果原来的请求是POST,Location头指定的重定向目标文档应该通过GET提取(HTTP 1.1新)。
304 Not Modified 客户端有缓冲的文档并发出了一个条件性的请求(一般是提供If-Modified-Since头表示客户只想比指定日期更新的文档)。服务器告诉客户,原来缓冲的文档还可以继续使用。
305 Use Proxy 客户请求的文档应该通过Location头所指明的代理服务器提取(HTTP 1.1新)。
307 Temporary Redirect 和302(Found)相同。许多浏览器会错误地响应302应答进行重定向,即使原来的请求是POST,即使它实际上只能在POST请求的应答是303时才能重定向。由于这个原因,HTTP 1.1新增了307,以便更加清除地区分几个状态代码:当出现303应答时,浏览器可以跟随重定向的GET和POST请求;如果是307应答,则浏览器只能跟随对GET请求的重定向。
注意,HttpServletResponse中没有为该状态代码提供相应的常量。(HTTP 1.1新)
400 Bad Request 请求出现语法错误。
401 Unauthorized 客户试图未经授权访问受密码保护的页面。应答中会包含一个WWW-Authenticate头,浏览器据此显示用户名字/密码对话框,然后在填写合适的Authorization头后再次发出请求。
403 Forbidden 资源不可用。服务器理解客户的请求,但拒绝处理它。通常由于服务器上文件或目录的权限设置导致。
404 Not Found 无法找到指定位置的资源。这也是一个常用的应答,HttpServletResponse专门提供了相应的方法:sendError(message)。
405 Method Not Allowed 请求方法(GET、POST、HEAD、DELETE、PUT、TRACE等)对指定的资源不适用。(HTTP 1.1新)
406 Not Acceptable 指定的资源已经找到,但它的MIME类型和客户在Accpet头中所指定的不兼容(HTTP 1.1新)。
407 Proxy Authentication Required 类似于401,表示客户必须先经过代理服务器的授权。(HTTP 1.1新)
408 Request Timeout 在服务器许可的等待时间内,客户一直没有发出任何请求。客户可以在以后重复同一请求。(HTTP 1.1新)
409 Conflict 通常和PUT请求有关。由于请求和资源的当前状态相冲突,因此请求不能成功。(HTTP 1.1新)
410 Gone 所请求的文档已经不再可用,而且服务器不知道应该重定向到哪一个地址。它和404的不同在于,返回407表示文档永久地离开了指定的位置,而404表示由于未知的原因文档不可用。(HTTP 1.1新)
411 Length Required 服务器不能处理请求,除非客户发送一个Content-Length头。(HTTP 1.1新)
412 Precondition Failed 请求头中指定的一些前提条件失败(HTTP 1.1新)。
413 Request Entity Too Large 目标文档的大小超过服务器当前愿意处理的大小。如果服务器认为自己能够稍后再处理该请求,则应该提供一个Retry-After头(HTTP 1.1新)。
414 Request URI Too Long URI太长(HTTP 1.1新)。
416 Requested Range Not Satisfiable 服务器不能满足客户在请求中指定的Range头。(HTTP 1.1新)
500 Internal Server Error 服务器遇到了意料不到的情况,不能完成客户的请求。
501 Not Implemented 服务器不支持实现请求所需要的功能。例如,客户发出了一个服务器不支持的PUT请求。
502 Bad Gateway 服务器作为网关或者代理时,为了完成请求访问下一个服务器,但该服务器返回了非法的应答。
503 Service Unavailable 服务器由于维护或者负载过重未能应答。例如,Servlet可能在数据库连接池已满的情况下返回503。服务器返回503时可以提供一个Retry-After头。
504 Gateway Timeout 由作为代理或网关的服务器使用,表示不能及时地从远程服务器获得应答。(HTTP 1.1新)
505 HTTP Version Not Supported 服务器不支持请求中所指明的HTTP版本。(HTTP 1.1新)
7.4 实例:访问多个搜索引擎
下面这个例子用到了除200之外的另外两个常见状态代码:302和404。302通过sendRedirect方法设置,404通过sendError方法设置。
在这个例子中,首先出现的HTML表单用来选择搜索引擎、搜索字符串、每页显示的搜索结果数量。表单提交后,Servlet提取这三个变量,按照所选择的搜索引擎的要求构造出包含这些变量的URL,然后把用户重定向到这个URL。如果用户不能正确地选择搜索引擎,或者利用其他表单发送了一个不认识的搜索引擎名字,则返回一个提示搜索引擎找不到的404页面。
SearchEngines.java
注意:这个Servlet要用到后面给出的SearchSpec类,SearchSpec的功能是构造适合不同搜索引擎的URL。
package hall;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.net.*;
public class SearchEngines extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
// getParameter自动解码URL编码的查询字符串。由于我们
// 要把查询字符串发送给另一个服务器,因此再次使用
// URLEncoder进行URL编码
String searchString =
URLEncoder.encode(request.getParameter("searchString"));
String numResults =
request.getParameter("numResults");
String searchEngine =
request.getParameter("searchEngine");
SearchSpec[] commonSpecs = SearchSpec.getCommonSpecs();
for(int i=0; i<commonSpecs.length; i++) {
SearchSpec searchSpec = commonSpecs[i];
if (searchSpec.getName().equals(searchEngine)) {
String url =
response.encodeURL(searchSpec.makeURL(searchString,
numResults));
response.sendRedirect(url);
return;
}
}
response.sendError(response.SC_NOT_FOUND,
"No recognized search engine specified.");
}
public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
SearchSpec.java
package hall;
class SearchSpec {
private String name, baseURL, numResultsSuffix;
private static SearchSpec[] commonSpecs =
{ new SearchSpec("google",
"
http://www.google.com/search?q=",
"&num="),
new SearchSpec("infoseek",
"
http://infoseek.go.com/Titles?qt=",
"&nh="),
new SearchSpec("lycos",
"
http://lycospro.lycos.com/cgi-bin/pursuit?query=",
"&maxhits="),
new SearchSpec("hotbot",
"
http://www.hotbot.com/?MT=",
"&DC=")
};
public SearchSpec(String name,
String baseURL,
String numResultsSuffix) {
this.name = name;
this.baseURL = baseURL;
this.numResultsSuffix = numResultsSuffix;
}
public String makeURL(String searchString, String numResults) {
return(baseURL + searchString + numResultsSuffix + numResults);
}
public String getName() {
return(name);
}
public static SearchSpec[] getCommonSpecs() {
return(commonSpecs);
}
}
SearchEngines.html
下面是调用上述Servlet的HTML表单。
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<HEAD>
<TITLE>访问多个搜索引擎</TITLE>
</HEAD>
<BODY BGCOLOR="#FDF5E6">
<FORM ACTION="/servlet/hall.SearchEngines">
<CENTER>
搜索关键字:
<INPUT TYPE="TEXT" NAME="searchString"><BR>
每页显示几个查询结果:
<INPUT TYPE="TEXT" NAME="numResults"
VALUE=10 SIZE=3><BR>
<INPUT TYPE="RADIO" NAME="searchEngine"
VALUE="google">
Google |
<INPUT TYPE="RADIO" NAME="searchEngine"
VALUE="infoseek">
Infoseek |
<INPUT TYPE="RADIO" NAME="searchEngine"
VALUE="lycos">
Lycos |
<INPUT TYPE="RADIO" NAME="searchEngine"
VALUE="hotbot">
HotBot
<BR>
<INPUT TYPE="SUBMIT" VALUE="Search">
</CENTER>
</FORM>
</BODY>
</HTML>
8.1 HTTP应答头概述
Web服务器的HTTP应答一般由以下几项构成:一个状态行,一个或多个应答头,一个空行,内容文档。设置HTTP应答头往往和设置状态行中的状态代码结合起来。例如,有好几个表示“文档位置已经改变”的状态代码都伴随着一个Location头,而401(Unauthorized)状态代码则必须伴随一个WWW-Authenticate头。
然而,即使在没有设置特殊含义的状态代码时,指定应答头也是很有用的。应答头可以用来完成:设置Cookie,指定修改日期,指示浏览器按照指定的间隔刷新页面,声明文档的长度以便利用持久HTTP连接,……等等许多其他任务。
设置应答头最常用的方法是HttpServletResponse的setHeader,该方法有两个参数,分别表示应答头的名字和值。和设置状态代码相似,设置应答头应该在发送任何文档内容之前进行。
setDateHeader方法和setIntHeadr方法专门用来设置包含日期和整数值的应答头,前者避免了把Java时间转换为GMT时间字符串的麻烦,后者则避免了把整数转换为字符串的麻烦。
HttpServletResponse还提供了许多设置常见应答头的简便方法,如下所示:
setContentType:设置Content-Type头。大多数Servlet都要用到这个方法。
setContentLength:设置Content-Length头。对于支持持久HTTP连接的浏览器来说,这个函数是很有用的。
addCookie:设置一个Cookie(Servlet API中没有setCookie方法,因为应答往往包含多个Set-Cookie头)。
另外,如上节介绍,sendRedirect方法设置状态代码302时也会设置Location头。
8.2 常见应答头及其含义
有关HTTP头详细和完整的说明,请参见
http://www.w3.org/Protocols/ 规范。
应答头 说明
Allow 服务器支持哪些请求方法(如GET、POST等)。
Content-Encoding 文档的编码(Encode)方法。只有在解码之后才可以得到Content-Type头指定的内容类型。利用gzip压缩文档能够显著地减少HTML文档的下载时间。Java的GZIPOutputStream可以很方便地进行gzip压缩,但只有Unix上的Netscape和Windows上的IE 4、IE 5才支持它。因此,Servlet应该通过查看Accept-Encoding头(即request.getHeader("Accept-Encoding"))检查浏览器是否支持gzip,为支持gzip的浏览器返回经gzip压缩的HTML页面,为其他浏览器返回普通页面。
Content-Length 表示内容长度。只有当浏览器使用持久HTTP连接时才需要这个数据。如果你想要利用持久连接的优势,可以把输出文档写入ByteArrayOutputStram,完成后查看其大小,然后把该值放入Content-Length头,最后通过byteArrayStream.writeTo(response.getOutputStream()发送内容。
Content-Type 表示后面的文档属于什么MIME类型。Servlet默认为text/plain,但通常需要显式地指定为text/html。由于经常要设置Content-Type,因此HttpServletResponse提供了一个专用的方法setContentTyep。
Date 当前的GMT时间。你可以用setDateHeader来设置这个头以避免转换时间格式的麻烦。
Expires 应该在什么时候认为文档已经过期,从而不再缓存它?
Last-Modified 文档的最后改动时间。客户可以通过If-Modified-Since请求头提供一个日期,该请求将被视为一个条件GET,只有改动时间迟于指定时间的文档才会返回,否则返回一个304(Not Modified)状态。Last-Modified也可用setDateHeader方法来设置。
Location 表示客户应当到哪里去提取文档。Location通常不是直接设置的,而是通过HttpServletResponse的sendRedirect方法,该方法同时设置状态代码为302。
Refresh 表示浏览器应该在多少时间之后刷新文档,以秒计。除了刷新当前文档之外,你还可以通过setHeader("Refresh", "5; URL=http://host/path")让浏览器读取指定的页面。
注意这种功能通常是通过设置HTML页面HEAD区的<META HTTP-EQUIV="Refresh" CONTENT="5;URL=http://host/path">实现,这是因为,自动刷新或重定向对于那些不能使用CGI或Servlet的HTML编写者十分重要。但是,对于Servlet来说,直接设置Refresh头更加方便。
注意Refresh的意义是“N秒之后刷新本页面或访问指定页面”,而不是“每隔N秒刷新本页面或访问指定页面”。因此,连续刷新要求每次都发送一个Refresh头,而发送204状态代码则可以阻止浏览器继续刷新,不管是使用Refresh头还是<META HTTP-EQUIV="Refresh" ...>。
注意Refresh头不属于HTTP 1.1正式规范的一部分,而是一个扩展,但Netscape和IE都支持它。
Server 服务器名字。Servlet一般不设置这个值,而是由Web服务器自己设置。
Set-Cookie 设置和页面关联的Cookie。Servlet不应使用response.setHeader("Set-Cookie", ...),而是应使用HttpServletResponse提供的专用方法addCookie。参见下文有关Cookie设置的讨论。
WWW-Authenticate 客户应该在Authorization头中提供什么类型的授权信息?在包含401(Unauthorized)状态行的应答中这个头是必需的。例如,response.setHeader("WWW-Authenticate", "BASIC realm=\"executives\"")。
注意Servlet一般不进行这方面的处理,而是让Web服务器的专门机制来控制受密码保护页面的访问(例如.htaccess)。
8.3 实例:内容改变时自动刷新页面
下面这个Servlet用来计算大素数。因为计算非常大的数字(例如500位)可能要花不少时间,所以Servlet将立即返回已经找到的结果,同时在后台继续计算。后台计算使用一个优先级较低的线程以避免过多地影响Web服务器的性能。如果计算还没有完成,Servlet通过发送Refresh头指示浏览器在几秒之后继续请求新的内容。
注意,本例除了说明HTTP应答头的用处之外,还显示了Servlet的另外两个很有价值的功能。首先,它表明Servlet能够处理多个并发的连接,每个都有自己的线程。Servlet维护了一份已有素数计算请求的Vector表,通过查找素数个数(素数列表的长度)和数字个数(每个素数的长度)将当前请求和已有请求相匹配,把所有这些请求同步到这个列表上。第二,本例证明,在Servlet中维持请求之间的状态信息是非常容易的。维持状态信息在传统的CGI编程中是一件很麻烦的事情。由于维持了状态信息,浏览器能够在刷新页面时访问到正在进行的计算过程,同时也使得Servlet能够保存一个有关最近请求结果的列表,当一个新的请求指定了和最近请求相同的参数时可以立即返回结果。
PrimeNumbers.java
注意,该Servlet要用到前面给出的ServletUtilities.java。另外还要用到:PrimeList.java,用于在后台线程中创建一个素数的Vector;Primes.java,用于随机生成BigInteger类型的大数字,检查它们是否是素数。(此处略去PrimeList.java和Primes.java的代码。)
package hall;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.*;
public class PrimeNumbers extends HttpServlet {
private static Vector primeListVector = new Vector();
private static int maxPrimeLists = 30;
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
int numPrimes = ServletUtilities.getIntParameter(request, "numPrimes", 50);
int numDigits = ServletUtilities.getIntParameter(request, "numDigits", 120);
PrimeList primeList = findPrimeList(primeListVector, numPrimes, numDigits);
if (primeList == null) {
primeList = new PrimeList(numPrimes, numDigits, true);
synchronized(primeListVector) {
if (primeListVector.size() >= maxPrimeLists)
primeListVector.removeElementAt(0);
primeListVector.addElement(primeList);
}
}
Vector currentPrimes = primeList.getPrimes();
int numCurrentPrimes = currentPrimes.size();
int numPrimesRemaining = (numPrimes - numCurrentPrimes);
boolean isLastResult = (numPrimesRemaining == 0);
if (!isLastResult) {
response.setHeader("Refresh", "5");
}
response.setContentType("text/html");
PrintWriter out = response.getWriter();
String title = "Some " + numDigits + "-Digit Prime Numbers";
out.println(ServletUtilities.headWithTitle(title) +
"<BODY BGCOLOR=\"#FDF5E6\">\n" +
"<H2 ALIGN=CENTER>" + title + "</H2>\n" +
"<H3>Primes found with " + numDigits +
" or more digits: " + numCurrentPrimes + ".</H3>");
if (isLastResult)
out.println("<B>Done searching.</B>");
else
out.println("<B>Still looking for " + numPrimesRemaining +
" more<BLINK>...</BLINK></B>");
out.println("<OL>");
for(int i=0; i<numCurrentPrimes; i++) {
out.println(" <LI>" + currentPrimes.elementAt(i));
}
out.println("</OL>");
out.println("</BODY></HTML>");
}
public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
// 检查是否存在同类型请求(已经完成,或者正在计算)。
// 如存在,则返回现有结果而不是启动新的后台线程。
private PrimeList findPrimeList(Vector primeListVector,
int numPrimes,
int numDigits) {
synchronized(primeListVector) {
for(int i=0; i<primeListVector.size(); i++) {
PrimeList primes = (PrimeList)primeListVector.elementAt(i);
if ((numPrimes == primes.numPrimes()) &&
(numDigits == primes.numDigits()))
return(primes);
}
return(null);
}
}
}
posted @
2006-09-01 17:20 阿成 阅读(612) |
评论 (0) |
编辑 收藏
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.com
、
www.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 @
2006-09-01 17:17 阿成 阅读(203) |
评论 (0) |
编辑 收藏
Java Servlet API说明文档(2.1a版)
译者前言:
近来在整理有关Servlet资料时发现,在网上竟然找不到一份中文的Java Servlet API的说明文档,而在有一本有关JSP的书后面附的Java Servlet API说明竟然不全,而这份文档的2.1a版在1998年的11月份就已定稿。所以我决定翻译一份中文的文档(其中一些与技术关系不大的部分已被略去),有兴趣的读者可以从http: //java.sun.com/products/servlet/2.1/servletspec-2.1.zip下载原文阅读。
Java Servlet API说明文档(2.1a版)
1998年11月
绪言
这是一份关于2.1版Java Servlet API的说明文档,作为对这本文档的补充,你可以到
http://java.sun.com/products/servlet/index.html下面下载Javadoc格式的文档。谁需要读这份文档
这份文档描述了Java Servlet API的最新版本2.1版。所以,这本书对于Servlet的开发者及servlet引擎的开发者同样适用。
Java Servlet API的组成
Java Servlet API由两个软件包组成:一个是对应HTTP的软件包,另一个是不对应HTTP的通用的软件包。这两个软件包的同时存在使得Java Servlet API能够适应将来的其他请求-响应的协议。
这份文档以及刚才提及的Javadoc格式的文档都描述了这两个软件包,Javadoc格式的文档还描述了你应该如何使用这两个软件包中的所有方法。
有关规范
你也许对下面的这些Internet规范感兴趣,这些规范将直接影响到Servlet API的发展和执行。你可以从http: //info.internet.isi.edu/7c/in-notes/rfc/.cache 找到下面提到的所有这些RFC规范。
RFC 1738 统一资源定位器(URL)
RFC 1808 相关统一资源定位器
RFC 1945 超文本传输协议--HTTP/1.0
RFC 2045 多用途Internet邮件扩展(多用途网际邮件扩充协议(MIME))第一部分:Internet信息体格式
RFC 2046 多用途Internet邮件扩展(多用途网际邮件扩充协议(MIME))第二部分:媒体类型
RFC 2047 多用途网际邮件扩充协议(MIME)(多用途Internet邮件扩展)第三部分:信息标题扩展用于非ASCII文本
RFC 2048 多用途Internet邮件扩展(多用途网际邮件扩充协议(MIME))第四部分: 注册步骤
RFC 2049 多用途Internet邮件扩展(多用途网际邮件扩充协议(MIME))第五部分:一致性标准和例子
RFC 2068 超文本传输协议 -- HTTP/1.1
RFC 2069 一个扩展HTTP:摘要访问鉴定
RFC 2109 HTTP状态管理机制
RFC 2145 HTTP 版本号的使用和解释
RFC 2324 超文本Coffee Pot控制协议 (HTCPCP/1.0)
万维网协会(
http://www.w3.org)管理着这些协议的规范和执行。/有关Java Servlets
JavaTM servlets 是一个不受平台约束的Java小程序,它可以被用来通过多种方法扩充一个Web服务器的功能。你可以把Servlet理解成Server上的 applets,它被编译成字节码,这样它就可以被动态地载入并用效地扩展主机的处理能力。
Servlet与applets不同的地方是,它不运行在Web浏览器或其他图形化的用户界面上。Servlet通过servlet引擎运行在Web服务器中,以执行请求和响应,请求、响应的典型范例是HTTP协议。
一个客户端程序,可以是一个Web浏览器,或者是非其他的可以连接上Internet的程序,它会访问Web服务器并发出请求。这个请求被运行在Web服务器上的Servlet引擎处理,并返回响应到Servlet。Servlet通过HTTP将这个响应转发到客户端。
在功能上,Servlet与CGI、NSAPI有点类似,但是,与他们不同的是:Servlet具有平台无关性。
Java Servlet概论
Servlet与其他普通的server扩展机制有以下进步:
因为它采用了不同的进程处理模式,所以它比CGI更快。
它使用了许多Web服务器都支持的标准的API。
它继承了Java的所有优势,包括易升级以及平台无关性。
它可以调用Java所提供的大量的API的功能模块。
这份文档说明了Java Servlet API的类和接口的方法。有关更多的信息,请参看下面的API说明。
Servlet的生命周期
一个Java servlet具有一个生命周期,这个生命周期定义了一个Servlet如何被载入并被初始化,如何接收请求并作出对请求的响应,如何被从服务中清除。Servlet的生命周期被javax.servlet.Servlet这个接口所定义。
所有的Java Servlet都会直接地或间接地执行javax.servlet.Servlet接口,这样它才能在一个Servlet引擎中运行。 Servlet引擎是Web 服务器按照Java Servlet API定制的扩展。Servlet引擎提供网络服务,能够理解MIME请求,并提供一个运行Servlet的容器。
javax.servlet.Servlet接口定义了在Servlet的生命周期中特定时间以及特定顺序被调用的方法。
Servlet的解析和载入
Servlet引擎解析并载入一个Servlet,这个过程可以发生在引擎启动时,需要一个Servlet去响应请求时,以及在此之间的任何时候。
Servlet引擎利用Java类载入工具载入一个Servlet,Servlet引擎可以从一个本地的文件系统、一个远程的文件系统以及网络载入Servlet。
Servlet的初始化
Servlet引擎载入Servlet后,Servlet引擎必须对Servlet进行初始化,在这一过程中,你可以读取一些固定存储的数据、初始化JDBC的连接以及建立与其他资源的连接。
在初始化过程中,javax.servlet.Servlet接口的init()方法提供了Servlet的初始化信息。这样,Servlet可以对自己进行配置。
init()方法获得了一个Servlet配置对象(ServletConfig)。这个对象在Servlet引擎中执行,并允许Servlet通过它获处相关参数。这个对象使得Servlet能够访问ServletContext对象。
Servlet处理请求
Servlet被初始化之后,它已经可以处理来自客户端的请求,每一个来自客户端的请求都被描述成一个ServletRequest对象,Servlet的响应被描述成一个ServletResponse对象。
当客户端发出请求时,Servlet引擎传递给Servlet一个ServletRequest对象和一个ServletResponse对象,这两个对象作为参数传递到service()方法中。
Servlet 也可以执行ServletRequest接口和ServletResponse接口。ServletRequest接口使得Servlet有权使用客户端发出的请求。Servlet可以通过ServletInputStream对象读取请求信息。
ServletResponse接口允许Servlet建立响应头和状态代码。通过执行这个接口,Servlet有权使用ServletOutputStream类来向客户端返回数据。
多线程和映射
在多线程的环境下,Servlet必须能处理许多同时发生的请求。例外的情况是这个Servlet执行了SingleThreadModel接口,如果是那样的话,Servlet只能同时处理一个请求。
Servlet依照Servlet引擎的映射来响应客户端的请求。一个映射对包括一个Servlet实例以及一个Servlet返回数据的URL,例如:HelloServlet with /hello/index.html。
然而,一个映射可能是由一个URL和许多Servlet实例组成,例如:一个分布式的Servlet引擎可能运行在不止一个的服务器中,这样的话,每一个服务器中都可能有一个Servlet实例,以平衡进程的载入。作为一个Servlet的开发者,你不能假定一个Servlet只有一个实例。
Servlet的卸载
Servlet引擎并不必需保证一个Servlet在任何时候或在服务开启的任何时候都被载入。Servlet引擎可以自由的在任何时候使用或清除一个Servlet。因此,我们不能依赖一个类或实例来存储重要的信息。
当Servlet引擎决定卸载一个Servlet时(例如,如果这个引擎被关闭或者需要让资源),这个引擎必须允许Servlet释放正在使用的资源并存储有关资料。为了完成以上工作,引擎会调用Servlet的destroy()方法。
在卸载一个Servlet之前,Servlet引擎必须等待所有的service()方法完成或超时结束(Servlet引擎会对超时作出定义)。当一个 Servlet被卸载时,引擎将不能给Servlet发送任何请求。引擎必须释放Servlet并完成无用存储单元的收集
Servlet映射技术
作为一个Servlet引擎的开发者,你必须对于如何映射客户端的请求到Servlet有大量的适应性。这份说明文档不规定映射如何发生。但是,你必须能够自由地运用下面的所有技术:
映射一个Servlet到一个URL
例如,你可以指定一个特殊的Servlet它仅被来自/feedback/index.html的请求调用。
映射一个Servlet到以一个指定的目录名开始的所有URL
例如,你可以映射一个Servlet到/catalog,这样来自/catalog/、 /catalog/garden和 /catalog/housewares/index.html的请求都会被映射到这个Servlet。但是来自/catalogtwo 或 /catalog.html的请求没被映射。
映射一个Servlet到所有以一个特定的字段结尾的所有URL
例如,你可以映射一个来自于所有以in.thtml结尾的请求到一个特定的Servlet。
映射一个Servlet到一个特殊的URL /servlet/servlet_name。
例如,如果你建立了一个名叫listattributes的Servlet,你可以通过使用/servlet/listattributes来访问这个Servlet。
通过类名调用Servlet
例如,如果Servlet引擎接收了来自/servlet/com.foo.servlet.MailServlet的请求,Servlet引擎会载入这个com.foo.servlet.MailServlet类,建立实例,并通过这个Servlet来处理请求。
Servlet环境
ServletContext 接口定义了一个Servlet环境对象,这个对象定义了一个在Servlet引擎上的Servlet的视图。通过使用这个对象,Servlet可以记录事件、得到资源并得到来自Servlet引擎的类(例如RequestDispatcher对象)。一个Servlet只能运行在一个Servlet环境中,但是不同的Servlet可以在Servlet引擎上有不同的视图。
如果Servlet引擎支持虚拟主机,每个虚拟主机有一个Servlet环境。一个Servlet环境不能在虚拟主机之间共享。
Servlet引擎能够允许一个Servlet环境有它自己的活动范围。
例如,一个Servlet环境是属于bank应用的,它将被映射到/bank目录下。在这种情况下,一个对getContext方法的调用会返回/bank的Servlet环境。
HTTP会话
HTTP是一个没有状态的协议。要建立一个有效的Web服务应用,你必须能够识别一个连续的来自远端的客户机的唯一的请求。随着时间的过去,发展了许多会话跟踪的技术,但是使用起来都比较麻烦。
Java Servlet API提供了一个简单的接口,通过这个接口,Servlet引擎可以有效地跟踪用户的会话。
建立Session
因为HTTP是一个请求-响应协议,一个会话在客户机加入之前会被认为是一个新的会话。加入的意思是返回会话跟踪信息到服务器中,指出会话已被建立。在客户端加入之前,我们不能判断下一个客户端请求是目前会话的一部分。
在下面的情况下,Session会被认为是新的Session。
客户端的Session在此之前还不知道
客户端选择不加入Session,例如,如果客户端拒绝接收来自服务器的cookie
作为一个Servlet的开发者,你必须决定你的Web应用是否处理客户机不加入或不能加入Session。服务器会在Web服务器或Servlet规定的时间内维持一个Session对象。当Session终止时,服务器会释放Session对象以及所有绑定在Session上的对象。
绑定对象到Session中
如果有助于你处理应用的数据需求,你也许需要绑定对象到Session中,你可以通过一个唯一的名字绑定任何的对象到Session中,这时,你需要使用 HttpSession对象。任何绑定到Session上的对象都可以被处理同一会话的Servlet调用。
有些对象可能需要你知道什么时候会被放置到Session中或从Session中移开。你可以通过使用HttpSessionBindingListener接口获得这些信息。当你的应用存储数据到Session中,或从Session中清除数据,Servlet都会通过HttpSessionBindingListener检杳什么类被绑定或被取消绑定。这个接口的方法会通报被绑定或被取消绑定的对象。
API对象的说明
这一部分包含了对Java Servlet API的全部类和接口的详细说明。这个说明与Javadoc API差不多,但是这份文档提供了更多的信息。
API包含了两个软件包,十二个接口和九个类。
软件包:javax.servlet
所包含的接口:RequestDispatcher;Servlet;ServletConfig;ServletContext;ServletRequest;ServletResponse;SingleThreadModel。
所包含的类:GenericServlet;ServletInputStream;ServletOutputStream;ServletException;UnavailableException。
一、RequestDispatcher接口:
定义:
public interface RequestDispatcher;
定义一个对象,从客户端接收请求,然后将它发给服务器的可用资源(例如Servlet、CGI、HTML文件、JSP文件)。Servlet引擎创建request dispatcher对象,用于封装由一个特定的URL定义的服务器资源。
这个接口是专用于封装Servlet的,但是一个Servlet引擎可以创建request dispatcher对象用于封装任何类型的资源。
request dispatcher对象是由Servlet引擎建立的,而不是由Servlet开发者建立的。
方法
1、forward
public void forward(ServletRequest request, ServletReponse response)
throws ServletException, IOException;
被用来从这个Servlet向其它服务器资源传递请求。当一个Servlet对响应作了初步的处理,并要求其它的对象对此作出响应时,可以使用这个方法。
当request对象被传递到目标对象时,请求的URL路径和其他路径参数会被调整为反映目标对象的目标URL路径。
如果已经通过响应返回了一个ServletOutputStream对象或PrintWriter对象,这个方法将不能使用,否则,这个方法会抛出一个IllegalStateException。
2、include
public void include(ServletRequest request, ServletResponse response)
throws ServletException, IOException
用来包括发送给其他服务器资源的响应的内容。本质上来说,这个方法反映了服务器端的内容。
请求对象传到目标对象后会反映调用请求的请求URL路径和路径信息。这个响应对象只能调用这个Servlet的ServletOutputStream对象和PrintWriter对象。
一个调用include的Servlet不能设置头域,如果这个Servlet调用了必须设置头域的方法(例如cookie),这个方法将不能保证正常使用。作为一个Servlet开发者,你必须妥善地解决那些可能直接存储头域的方法。例如,即使你使用会话跟踪,为了保证session的正常工作,你必须在一个调用include的Servlet之外开始你的session
二、Servlet接口。
定义
public interface Servlet
这个接口定义了一个Servlet:一个在Web服务器上继承了这个功能的Java类。
方法
1、init
public void init(ServletConfig config) throws ServletException;
Servlet引擎会在Servlet实例化之后,置入服务之前精确地调用init方法。在调用service方法之前,init方法必须成功退出。
如果init方法抛出一个ServletException,你不能将这个Servlet置入服务中,如果init方法在超时范围内没完成,我们也可以假定这个Servlet是不具备功能的,也不能置入服务中。
2、service
public void service(ServletRequest request, ServletResponse response)
throws ServletException, IOException;
Servlet引擎调用这个方法以允许Servlet响应请求。这个方法在Servlet未成功初始化之前无法调用。在Servlet被初始化之前,Servlet引擎能够封锁未决的请求。
在一个Servlet对象被卸载后,直到一个新的Servelt被初始化,Servlet引擎不能调用这个方法
3、destroy
public void destroy();
当一个Servlet被从服务中去除时,Servlet引擎调用这个方法。在这个对象的service方法所有线程未全部退出或者没被引擎认为发生超时操作时,destroy方法不能被调用。
4、getServletConfig
public ServletConfig getServletConfig();
返回一个ServletConfig对象,作为一个Servlet的开发者,你应该通过init方法存储ServletConfig对象以便这个方法能返回这个对象。为了你的便利,GenericServlet在执行这个接口时,已经这样做了。
5、getServletInfo
public String getServletInfo();
允许Servlet向主机的Servlet运行者提供有关它本身的信息。返回的字符串应该是纯文本格式而不应有任何标志(例如HTML,XML等)。
三、ServletConfig接口
定义
public interface ServletConfig
这个接口定义了一个对象,通过这个对象,Servlet引擎配置一个Servlet并且允许Servlet获得一个有关它的ServletContext接口的说明。每一个ServletConfig对象对应着一个唯一的Servlet。
方法
1、getInitParameter
public String getInitParameter(String name);
这个方法返回一个包含Servlet指定的初始化参数的String。如果这个参数不存在,返加空值。
2、getInitParameterNames
public Enumeration getInitParameterNames();
这个方法返回一个列表String对象,该对象包括Servlet的所有初始化参数名。如果Servlet没有初始化参数,getInitParameterNames返回一个空的列表。
3、getServletContext
public ServletContext getServletContext();
返回这个Servlet的ServletContext对象。
四、ServletContext接口
定义
public interface ServletContext
定义了一个Servlet的环境对象,通过这个对象,Servlet引擎向Servlet提供环境信息。
一个Servlet的环境对象必须至少与它所驻留的主机是一一对应的。在一个处理多个虚拟主机的Servlet引擎中(例如,使用了HTTP1.1的主机头域),每一个虚拟主机必须被视为一个单独的环境。此外,Servlet引擎还可以创建对应于一组Servlet的环境对象。
方法
1、getAttribute
public Object getAttribute(String name);
返回Servlet环境对象中指定的属性对象。如果该属性对象不存在,返回空值。这个方法允许访问有关这个Servlet引擎的在该接口的其他方法中尚未提供的附加信息。
2、getAttributeNames
public Enumeration getAttributeNames();
返回一个Servlet环境对象中可用的属性名的列表。
3、getContext
public ServletContext getContext(String uripath);
返回一个Servlet环境对象,这个对象包括了特定URI路径的Servlets和资源,如果该路径不存在,则返回一个空值。URI路径格式是/dir/dir/filename.ext。
为了安全,如果通过这个方法访问一个受限制的Servlet的环境对象,会返回一个空值。
4、getMajorVersion
public int getMajorVersion();
返回Servlet引擎支持的Servlet API的主版本号。例如对于2.1版,这个方法会返回一个整数2。
5、getMinorVersion
public int getMinorVersion();
返回Servlet引擎支持的Servlet API的次版本号。例如对于2.1版,这个方法会返回一个整数2。
6、getMimeType
public String getMimeType(String file);
返回指定文件的MIME类型,如果这种MIME类型未知,则返回一个空值。MIME类型是由Servlet引擎的配置决定的。
7、getRealPath
public String getRealPath(String path);
一个符合URL路径格式的指定的虚拟路径的格式是:/dir/dir/filename.ext。用这个方法,可以返回与一个符合该格式的虚拟路径相对应的真实路径的String。这个真实路径的格式应该适合于运行这个Servlet引擎的计算机(包括其相应的路径解析器)。
不管是什么原因,如果这一从虚拟路径转换成实际路径的过程不能执行,该方法将会返回一个空值。
8、getResource
public URL getResource(String uripath);
返回一个URL对象,该对象反映位于给定的URL地址(格式:/dir/dir/filename.ext)的Servlet环境对象已知的资源。无论 URLStreamHandlers对于访问给定的环境是不是必须的,Servlet引擎都必须执行。如果给定的路径的Servlet环境没有已知的资源,该方法会返回一个空值。
这个方法和java.lang.Class的getResource方法不完全相同。 java.lang.Class的getResource方法通过装载类来寻找资源。而这个方法允许服务器产生环境变量给任何资源的任何Servlet,而不必依赖于装载类、特定区域等等。
9、getResourceAsStream
public InputStream getResourceAsStream(String uripath);
返回一个InputStream对象,该对象引用指定的URL的Servlet环境对象的内容。如果没找到Servlet环境变量,就会返回空值,URL路径应该具有这种格式:/dir/dir/filename.ext。
这个方法是一个通过getResource方法获得URL对象的方便的途径。请注意,当你使用这个方法时,meta-information(例如内容长度、内容类型)会丢失。
10、getRequestDispatcher
public RequestDispatcher getRequestDispatcher(String uripath);
如果这个指定的路径下能够找到活动的资源(例如一个Servlet,JSP页面,CGI等等)就返回一个特定URL的RequestDispatcher 对象,否则,就返回一个空值,Servlet引擎负责用一个request dispatcher对象封装目标路径。这个 request dispatcher对象可以用来完全请求的传送。
11、getServerInfo
public String getServerInfo();
返回一个String对象,该对象至少包括Servlet引擎的名字和版本号。
12、log
public void log(String msg);
public void log(String msg, Throwable t);
public void log(Exception exception, String msg); // 这种用法将被取消
写指定的信息到一个Servlet环境对象的log文件中。被写入的log文件由Servlet引擎指定,但是通常这是一个事件log。当这个方法被一个异常调用时,log中将包括堆栈跟踪。
13、setAttribute
public void setAttribute(String name, Object o);
给予Servlet环境对象中你所指定的对象一个名称。
14、removeAttribute
public void removeAttribute(String name);
从指定的Servlet环境对象中删除一个属性。
注:以下几个方法将被取消
15、getServlet
public Servlet getServlet(String name) throws ServletException;
最初用来返回一个指定名称的Servlet,如果没找到就返回一个空值。如果这个Servlet能够返回,这就意味着它已经被初始化,而且已经可以接受 service请求。这是一个危险的方法。当调用这个方法时,可能并不知道Servlet的状态,这就可能导致有关服务器状态的问题。而允许一个 Servlet访问其他Servlet的这个方法也同样的危险。
现在这个方法返回一个空值,为了保持和以前版本的兼容性,现在这个方法还没有被取消。在以后的API版本中,该方法将被取消。
16、getServletNames
public Enumeration getServletNames();
最初用来返回一个String对象的列表,该列表表示了在这个Servlet环境下所有已知的Servlet对象名。这个列表总是包含这个Servlet自身。
基于与上一个方法同样的理由,这也是一个危险的方法。
现在这个方法返回一个空的列表。为了保持和以前版本的兼容性,现在这个方法还没有被取消。在以后的API版本中,该方法将被取消。
17、getServlets
public Enumeration getServlets();
最初用来返回在这个Servelet环境下所有已知的Servlet对象的列表。这个列表总是包含这个Servlet自身。
基于与getServlet方法同样的理由,这也是一个危险的方法。
现在这个方法返回一个空的列表。为了保持和以前版本的兼容性,现在这个方法还没有被取消。在以后的API版本中,该方法将被取消。
五、ServletRequest接口
定义
public interface ServletRequest
定义一个Servlet引擎产生的对象,通过这个对象,Servlet可以获得客户端请求的数据。这个对象通过读取请求体的数据提供包括参数的名称、值和属性以及输入流的所有数据。
方法
1、getAttribute
public Object getAttribute(String name);
返回请求中指定属性的值,如果这个属性不存在,就返回一个空值。这个方法允许访问一些不提供给这个接口中其他方法的请求信息以及其他Servlet放置在这个请求对象内的数据。
2、getAttributeNames
public Enumeration getAttributeNames();
返回包含在这个请求中的所有属性名的列表。
3、getCharacterEncoding
public String getCharacterEncoding();
返回请求中输入内容的字符编码类型,如果没有定义字符编码类型就返回空值。
4、getContentLength
public int getContentLength();
请求内容的长度,如果长度未知就返回-1。
5、getContentType
public String getContentType();
返回请求数据体的MIME类型,如果类型未知返回空值。
6、getInputStream
public ServletInputStream getInputStream() throws IOException;
返回一个输入流用来从请求体读取二进制数据。如果在此之前已经通过getReader方法获得了要读取的结果,这个方法会抛出一个IllegalStateException。
7、getParameter
public String getParameter(String name);
以一个String返回指定的参数的值,如果这个参数不存在返回空值。例如,在一个HTTP Servlet中,这个方法会返回一个指定的查询语句产生的参数的值或一个被提交的表单中的参数值。如果一个参数名对应着几个参数值,这个方法只能返回通过getParameterValues方法返回的数组中的第一个值。因此,如果这个参数有(或者可能有)多个值,你只能使用getParameterValues方法。
8、getParameterNames
public Enumeration getParameterNames();
返回所有参数名的String对象列表,如果没有输入参数,该方法返回一个空值。
9、getParameterValues
public String[] getParameterValues(String name);
通过一个String对象的数组返回指定参数的值,如果这个参数不存在,该方法返回一个空值。
10、getProtocol
public String getProtocol();
返回这个请求所用的协议,其形式是协议/主版本号.次版本号。例如对于一个HTTP1.0的请求,该方法返回HTTP/1.0。
11、getReader
public BufferedReader getReader() throws IOException;
这个方法返回一个buffered reader用来读取请求体的实体,其编码方式依照请求数据的编码方式。如果这个请求的输入流已经被getInputStream调用获得,这个方法会抛出一个IllegalStateException。
12、getRemoteAddr
public String getRemoteAddr();
返回发送请求者的IP地址。
13、getRemoteHost
public String getRemoteHost();
返回发送请求者的主机名称。如果引擎不能或者选择不解析主机名(为了改善性能),这个方法会直接返回IP地址。
14、getScheme
public String getScheme();
返回请求所使用的URL的模式。例如,对于一个HTTP请求,这个模式就是http。
15、getServerName
public String getServerName();
返回接收请求的服务器的主机名。
16、getServerPort
public int getServerPort();
返回接收请求的端口号。
17、setAttribute
public void setAttribute(String name, Object object);
这个方法在请求中添加一个属性,这个属性可以被其他可以访问这个请求对象的对象(例如一个嵌套的Servlet)使用。
注:以下方法将被取消
getRealPath
public String getRealPath(String path);
返回与虚拟路径相对应的真实路径,如果因为某种原因,这一过程不能进行,该方法将返回一个空值。
这个方法和ServletContext接口中的getRealPath方法重复。在2.1版中,ServletContext接口将阐明一个 Servlet所能用的所有的路径的映射。该方法执行的结果将会与ServletContext中getRealPath方法的结果完全一样。
六、ServletResponse接口
定义
public interface ServletResponse
定义一个Servlet引擎产生的对象,通过这个对象,Servlet对客户端的请求作出响应。这个响应应该是一个MIME实体,可能是一个HTML页、图象数据或其他MIME的格式。
方法
1、getCharacterEncoding
public String getCharacterEncoding();
返回MIME实体的字符编码。这个字符编码可以是指定的类型,也可以是与请求头域所反映的客户端所能接受的字符编码最匹配的类型。在HTTP协议中,这个信息被通过Accept-Charset传送到Servlet引擎。
有关字符编码和MIME的更多信息请参看RFC 2047。
2、getOutputStream
public ServletOutputStream getOutputStream() throws IOException;
返回一个记录二进制的响应数据的输出流。
如果这个响应对象已经调用getWriter,将会抛出IllegalStateException。
3、getWriter
public PrintWriter getWriter throws IOException;
这个方法返回一个PringWriter对象用来记录格式化的响应实体。如果要反映使用的字符编码,必须修改响应的MIME类型。在调用这个方法之前,必须设定响应的content类型。
如果没有提供这样的编码类型,会抛出一个UnsupportedEncodingException,如果这个响应对象已调用getOutputStream,会抛出一个getOutputStream。
4、setContentLength
public void setContentLength(int length);
设置响应的内容的长度,这个方法会覆盖以前对内容长度的设定。
为了保证成功地设定响应头的内容长度,在响应被提交到输出流之前必须调用这个方法。
5、setContentType
public void setContentType(String type);
这个方法用来设定响应的content类型。这个类型以后可能会在另外的一些情况下被隐式地修改,这里所说的另外的情况可能当服务器发现有必要的情况下对MIME的字符设置。
为了保证成功地设定响应头的content类型,在响应被提交到输出流之前必须调用这个方法。
七、SingleThreadModel接口
定义
public interface SingleThreadModel;
这是一个空接口,它指定了系统如何处理对同一个Servlet的调用。如果一个Servlet被这个接口指定,那么在这个Servlet中的service方法中将不会有两个线程被同时执行。
Servlet可以通过维持一个各自独立的Servlet实例池,或者通过只让Servlet的service中只有一个线程的方法来实现这个保证。
八、GenericServlet类
public abstract class GenericServlet implements Servlet,
ServletConfig, Serializable;
这个类的存在使得编写Servlet更加方便。它提供了一个简单的方案,这个方案用来执行有关Servlet生命周期的方法以及在初始化时对ServletConfig对象和ServletContext对象进行说明。
方法
1、destroy
public void destroy();
在这里destroy方法不做任何其他的工作。
2、getInitParameter
public String getInitParameter(String name);
这是一个简便的途径,它将会调用ServletConfig对象的同名的方法。
3、getInitParameterNames
public Enumeration getInitParameterNames();
这是一个简便的途径,它将会调用ServletConfig对象的同名的方法。
4、getServletConfig
public ServletConfig getServletConfig();
返回一个通过这个类的init方法产生的ServletConfig对象的说明。
5、getServletContext
public ServletContext getServletContext();
这是一个简便的途径,它将会调用ServletConfig对象的同名的方法。
6、getServletInfo
public String getServletInfo();
返回一个反映Servlet版本的String。
7、init
public void init() throws ServletException;
public void init(ServletConfig config) throws ServletException;
init(ServletConfig config)方法是一个对这个Servlet的生命周期进行初始化的简便的途径。
init()方法是用来让你对GenericServlet类进行扩充的,使用这个方法时,你不需要存储config对象,也不需要调用super.init(config)。
init(ServletConfig config)方法会存储config对象然后调用init()。如果你重载了这个方法,你必须调用super.init(config),这样GenericServlet类的其他方法才能正常工作。
8、log
public void log(String msg);
public void log(String msg, Throwable cause);
通过Servlet content对象将Servlet的类名和给定的信息写入log文件中。
9、service
public abstract void service(ServletRequest request, ServletResponse
response) throws ServletException, IOException;
这是一个抽象的方法,当你扩展这个类时,为了执行网络请求,你必须执行它。
九、ServletInputStream类
定义
public abstract class ServletInputStream extends InputStream
这个类定义了一个用来读取客户端的请求信息的输入流。这是一个Servlet引擎提供的抽象类。一个Servlet通过使用ServletRequest接口获得了对一个ServletInputStream对象的说明。
这个类的子类必须提供一个从InputStream接口读取有关信息的方法。
方法
1、readLine
public int readLine(byte[] b, int off, int len) throws IOException;
从输入流的指定的偏移量开始将指定长度的字节读入到指定的数组中。如果该行所有请求的内容都已被读取,这个读取的过程将结束。如果是遇到了新的一行,新的一行的首个字符也将被读入到数组中。
十、ServletOutputStream类
定义
public abstract class ServletOutputStream extends OutputStream
这是一个由Servlet引擎使用的抽象类。Servlet通过使用ServletResponse接口的使用获得了对一个这种类型的对象的说明。利用这个输出流可以将数据返回到客户端。
这个类的子类必须提供一个向OutputStream接口写入有关信息的方法。
在这个接口中,当一个刷新或关闭的方法被调用时。所有数据缓冲区的信息将会被发送到客户端,也就是说响应被提交了。请注意,关闭这种类型的对象时不一定要关闭隐含的socket流。
方法
1、print
public void print(String s) throws IOException;
public void print(boolean b) throws IOException;
public void print(char c) throws IOException;
public void print(int i) throws IOException;
public void print(long l) throws IOException;
public void print(float f) throws IOException;
public void print(double d) throws IOException;
输出变量到输出流中
2、println
public void println() throws IOException;
public void println(String s) throws IOException;
public void println(boolean b) throws IOException;
public void println(char c) throws IOException;
public void println(int i) throws IOException;
public void println(long l) throws IOException;
public void println(float f) throws IOException;
public void println(double d) throws IOException;
输出变量到输出流中,并增加一个回车换行符
十一、ServletException类
定义
public class ServletException extends Exception
当Servlet遇到问题时抛出的一个异常。
构造函数
public ServletException();
public ServletException(String message);
public ServletException(String message, Throwable cause);
public ServletException(Throwable cause);
构造一个新的ServletException,如果这个构造函数包括一个Throwable参数,这个Throwable对象将被作为可能抛出这个异常的原因。
方法
1、getRootCause
public Throwable getRootCause();
如果配置了抛出这个异常的原因,这个方法将返回这个原因,否则返回一个空值。
十二、UnavailableException类
定义
public class UnavailableException extends ServletException
不论一个Servlet是永久地还是临时地无效,都会抛出这个异常。Servlet会记录这个异常以及Servlet引擎所要采取的相应措施。
临时的无效是指Servlet在某一时间由于一个临时的问题而不能处理请求。例如,在另一个不同的应用层的服务(可能是数据库)无法使用。这个问题可能会自行纠正或者需要采取其他的纠正措施。
永久的无效是指除非管理员采取措施,这个Servlet将不能处理客户端的请求。例如,这个Servlet配置信息丢失或Servlet的状态被破坏。
Servlet 引擎可以安全地处理包括永久无效在内的这两种异常,但是对临时无效的正常处理可以使得Servlet引擎更健壮。特别的,这时对Servlet的请求只是被阻止(或者是被延期)一段时间,这显然要比在service自己重新启动前完全拒绝请求更为科学。
构造函数
public UnavailableException(Servlet servlet, String message);
public UnavailableException(int seconds, Servlet servlet,
String message);
构造一个包含指定的描述信息的新的异常。如果这个构造函数有一个关于秒数的参数,这将给出Servlet发生临时无效后,能够重新处理请求的估计时间。如果不包含这个参数,这意味着这个Servlet永久无效。
方法
1、getServlet
public Servlet getServlet();
返回报告无效的Servlet。这被Servlet引擎用来识别受到影响的Servlet。
2、getUnavailableSeconds
public int getUnavailableSeconds();
返回Servlet预期的无效时间,如果这个Servlet是永久无效,返回-1。
3、isPermanent
public boolean isPermanent();
如果这个Servlet永久无效,返回布尔值true,指示必须采取一些管理行动以使得这个Servlet可用。
软件包:javax.servlet.http
所包含的接口:HttpServletRequest;HttpServletResponse;HttpSession;HttpSessionBindingListener;HttpSessionContext。
所包含的类:Cookie;HttpServlet;HttpSessionBindingEvent;HttpUtils。
一、HttpServletRequest接口
定义\
public interface HttpServletRequest extends ServletRequest;
用来处理一个对Servlet的HTTP格式的请求信息。
方法
1、getAuthType
public String getAuthType();
返回这个请求的身份验证模式。
2、getCookies
public Cookie[] getCookies();
返回一个数组,该数组包含这个请求中当前的所有cookie。如果这个请求中没有cookie,返回一个空数组。
3、getDateHeader
public long getDateHeader(String name);
返回指定的请求头域的值,这个值被转换成一个反映自1970-1-1日(GMT)以来的精确到毫秒的长整数。
如果头域不能转换,抛出一个IllegalArgumentException。如果这个请求头域不存在,这个方法返回-1。
4、getHeader
public String getHeader(String name);
返回一个请求头域的值。(译者注:与上一个方法不同的是,该方法返回一个字符串)
如果这个请求头域不存在,这个方法返回-1。
5、getHeaderNames
public Enumeration getHeaderNames();
该方法返回一个String对象的列表,该列表反映请求的所有头域名。
有的引擎可能不允许通过这种方法访问头域,在这种情况下,这个方法返回一个空的列表。
6、getIntHeader
public int getIntHeader(String name);
返回指定的请求头域的值,这个值被转换成一个整数。
如果头域不能转换,抛出一个IllegalArgumentException。如果这个请求头域不存在,这个方法返回-1。
7、getMethod
public String getMethod();
返回这个请求使用的HTTP方法(例如:GET、POST、PUT)
8、getPathInfo
public String getPathInfo();
这个方法返回在这个请求的URL的Servlet路径之后的请求URL的额外的路径信息。如果这个请求URL包括一个查询字符串,在返回值内将不包括这个查询字符串。这个路径在返回之前必须经过URL解码。如果在这个请求的URL的Servlet路径之后没有路径信息。这个方法返回空值。
9、getPathTranslated
public String getPathTranslated();
这个方法获得这个请求的URL的Servlet路径之后的额外的路径信息,并将它转换成一个真实的路径。在进行转换前,这个请求的URL必须经过URL解码。如果在这个URL的Servlet路径之后没有附加路径信息。这个方法返回空值。
10、getQueryString
public String getQueryString();
返回这个请求URL所包含的查询字符串。一个查询字串符在一个URL中由一个“?”引出。如果没有查询字符串,这个方法返回空值。
11、getRemoteUser
public String getRemoteUser
返回作了请求的用户名,这个信息用来作HTTP用户论证。
如果在请求中没有用户名信息,这个方法返回空值。
12、getRequestedSessionId
public String getRequestedSessionId();
返回这个请求相应的session id。如果由于某种原因客户端提供的session id是无效的,这个session id将与在当前session中的session id不同,与此同时,将建立一个新的session。
如果这个请求没与一个session关联,这个方法返回空值。
13、getRequestURI
public String getRequestURI();
从HTTP请求的第一行返回请求的URL中定义被请求的资源的部分。如果有一个查询字符串存在,这个查询字符串将不包括在返回值当中。例如,一个请求通过 /catalog/books?id=1这样的URL路径访问,这个方法将返回/catalog/books。这个方法的返回值包括了Servlet路径和路径信息。
如果这个URL路径中的的一部分经过了URL编码,这个方法的返回值在返回之前必须经过解码。
14、getServletPath
public String getServletPath();
这个方法返回请求URL反映调用Servlet的部分。例如,一个Servlet被映射到/catalog/summer这个URL路径,而一个请求使用了/catalog/summer/casual这样的路径。所谓的反映调用Servlet的部分就是指/catalog/summer。
如果这个Servlet不是通过路径匹配来调用。这个方法将返回一个空值。
15、getSession
public HttpSession getSession();
public HttpSession getSession(boolean create);
返回与这个请求关联的当前的有效的session。如果调用这个方法时没带参数,那么在没有session与这个请求关联的情况下,将会新建一个session。如果调用这个方法时带入了一个布尔型的参数,只有当这个参数为真时,session才会被建立。
为了确保session能够被完全维持。Servlet开发者必须在响应被提交之前调用该方法。
如果带入的参数为假,而且没有session与这个请求关联。这个方法会返回空值。
16、isRequestedSessionIdValid
public boolean isRequestedSessionIdValid();
这个方法检查与此请求关联的session当前是不是有效。如果当前请求中使用的session无效,它将不能通过getSession方法返回。
17、isRequestedSessionIdFromCookie
public boolean isRequestedSessionIdFromCookie();
如果这个请求的session id是通过客户端的一个cookie提供的,该方法返回真,否则返回假。
18、isRequestedSessionIdFromURL
public boolean isRequestedSessionIdFromURL();
如果这个请求的session id是通过客户端的URL的一部分提供的,该方法返回真,否则返回假。请注意此方法与isRequestedSessionIdFromUrl在URL的拼写上不同。
以下方法将被取消\
19、isRequestedSessionIdFromUrl
public boolean isRequestedSessionIdFromUrl();
该方法被isRequestedSessionIdFromURL代替。
二、HttpServletResponse接口
定义\
public interface HttpServletResponse extends ServletResponse
描述一个返回到客户端的HTTP回应。这个接口允许Servlet程序员利用HTTP协议规定的头信息。
成员变量
public static final int SC_CONTINUE = 100;
public static final int SC_SWITCHING_PROTOCOLS = 101;
public static final int SC_OK = 200;
public static final int SC_CREATED = 201;
public static final int SC_ACCEPTED = 202;
public static final int SC_NON_AUTHORITATIVE_INFORMATION = 203;
public static final int SC_NO_CONTENT = 204;
public static final int SC_RESET_CONTENT = 205;
public static final int SC_PARTIAL_CONTENT = 206;
public static final int SC_MULTIPLE_CHOICES = 300;
public static final int SC_MOVED_PERMANENTLY = 301;
public static final int SC_MOVED_TEMPORARILY = 302;
public static final int SC_SEE_OTHER = 303;
public static final int SC_NOT_MODIFIED = 304;
public static final int SC_USE_PROXY = 305;
public static final int SC_BAD_REQUEST = 400;
public static final int SC_UNAUTHORIZED = 401;
public static final int SC_PAYMENT_REQUIRED = 402;
public static final int SC_FORBIDDEN = 403;
public static final int SC_NOT_FOUND = 404;
public static final int SC_METHOD_NOT_ALLOWED = 405;
public static final int SC_NOT_ACCEPTABLE = 406;
public static final int SC_PROXY_AUTHENTICATION_REQUIRED = 407;
public static final int SC_REQUEST_TIMEOUT = 408;
public static final int SC_CONFLICT = 409;
public static final int SC_GONE = 410;
public static final int SC_LENGTH_REQUIRED = 411;
public static final int SC_PRECONDITION_FAILED = 412;
public static final int SC_REQUEST_ENTITY_TOO_LARGE = 413;
public static final int SC_REQUEST_URI_TOO_LONG = 414;
public static final int SC_UNSUPPORTED_MEDIA_TYPE = 415;
public static final int SC_INTERNAL_SERVER_ERROR = 500;
public static final int SC_NOT_IMPLEMENTED = 501;
public static final int SC_BAD_GATEWAY = 502;
public static final int SC_SERVICE_UNAVAILABLE = 503;
public static final int SC_GATEWAY_TIMEOUT = 504;
public static final int SC_HTTP_VERSION_NOT_SUPPORTED = 505;
以上HTTP产状态码是由HTTP/1.1定义的。
方法
1、addCookie
public void addCookie(Cookie cookie);
在响应中增加一个指定的cookie。可多次调用该方法以定义多个cookie。为了设置适当的头域,该方法应该在响应被提交之前调用。
2、containsHeader
public boolean containsHeader(String name);
检查是否设置了指定的响应头。
3、encodeRedirectURL
public String encodeRedirectURL(String url);
对sendRedirect方法使用的指定URL进行编码。如果不需要编码,就直接返回这个URL。之所以提供这个附加的编码方法,是因为在 redirect的情况下,决定是否对URL进行编码的规则和一般情况有所不同。所给的URL必须是一个绝对URL。相对URL不能被接收,会抛出一个 IllegalArgumentException。
所有提供给sendRedirect方法的URL都应通过这个方法运行,这样才能确保会话跟踪能够在所有浏览器中正常运行。
4、encodeURL
public String encodeURL(String url);
对包含session ID的URL进行编码。如果不需要编码,就直接返回这个URL。Servlet引擎必须提供URL编码方法,因为在有些情况下,我们将不得不重写URL,例如,在响应对应的请求中包含一个有效的session,但是这个session不能被非URL的(例如cookie)的手段来维持。
所有提供给Servlet的URL都应通过这个方法运行,这样才能确保会话跟踪能够在所有浏览器中正常运行。
5、sendError
public void sendError(int statusCode) throws IOException;
public void sendError(int statusCode, String message) throws
IOException;
用给定的状态码发给客户端一个错误响应。如果提供了一个message参数,这将作为响应体的一部分被发出,否则,服务器会返回错误代码所对应的标准信息。
调用这个方法后,响应立即被提交。在调用这个方法后,Servlet不会再有更多的输出。
6、sendRedirect
public void sendRedirect(String location) throws IOException;
使用给定的路径,给客户端发出一个临时转向的响应(SC_MOVED_TEMPORARILY)。给定的路径必须是绝对URL。相对URL将不能被接收,会抛出一个IllegalArgumentException。
这个方法必须在响应被提交之前调用。调用这个方法后,响应立即被提交。在调用这个方法后,Servlet不会再有更多的输出。
7、setDateHeader
public void setDateHeader(String name, long date);
用一个给定的名称和日期值设置响应头,这里的日期值应该是反映自1970-1-1日(GMT)以来的精确到毫秒的长整数。如果响应头已经被设置,新的值将覆盖当前的值。
8、setHeader
public void setHeader(String name, String value);
用一个给定的名称和域设置响应头。如果响应头已经被设置,新的值将覆盖当前的值。
9、setIntHeader
public void setIntHeader(String name, int value);
用一个给定的名称和整形值设置响应头。如果响应头已经被设置,新的值将覆盖当前的值。
10、setStatus
public void setStatus(int statusCode);
这个方法设置了响应的状态码,如果状态码已经被设置,新的值将覆盖当前的值。
以下的几个方法将被取消\
11、encodeRedirectUrl
public String encodeRedirectUrl(String url);
该方法被encodeRedirectURL取代。
12、encodeUrl
public String encodeUrl(String url);
该方法被encodeURL取代。
13、setStatus
public void setStatus(int statusCode, String message);
这个方法设置了响应的状态码,如果状态码已经被设置,新的值将覆盖当前的值。如果提供了一个message,它也将会被作为响应体的一部分被发送。
三、HttpSession接口
定义\
public interface HttpSession
这个接口被Servlet引擎用来实现在HTTP客户端和HTTP会话两者的关联。这种关联可能在多外连接和请求中持续一段给定的时间。session用来在无状态的HTTP协议下越过多个请求页面来维持状态和识别用户。
一个session可以通过cookie或重写URL来维持。
方法
1、getCreationTime
public long getCreationTime();
返回建立session的时间,这个时间表示为自1970-1-1日(GMT)以来的毫秒数。
2、getId
public String getId();
返回分配给这个session的标识符。一个HTTP session的标识符是一个由服务器来建立和维持的唯一的字符串。
3、getLastAccessedTime
public long getLastAccessedTime();
返回客户端最后一次发出与这个session有关的请求的时间,如果这个session是新建立的,返回-1。这个时间表示为自1970-1-1日(GMT)以来的毫秒数。
4、getMaxInactiveInterval
public int getMaxInactiveInterval();
返加一个秒数,这个秒数表示客户端在不发出请求时,session被Servlet引擎维持的最长时间。在这个时间之后,Servlet引擎可能被Servlet引擎终止。如果这个session不会被终止,这个方法返回-1。
当session无效后再调用这个方法会抛出一个IllegalStateException。
5、getValue
public Object getValue(String name);
返回一个以给定的名字绑定到session上的对象。如果不存在这样的绑定,返回空值。
当session无效后再调用这个方法会抛出一个IllegalStateException。
6、getValueNames
public String[] getValueNames();
以一个数组返回绑定到session上的所有数据的名称。
当session无效后再调用这个方法会抛出一个IllegalStateException。
7、invalidate
public void invalidate();
这个方法会终止这个session。所有绑定在这个session上的数据都会被清除。并通过HttpSessionBindingListener接口的valueUnbound方法发出通告。
8、isNew
public boolean isNew();
返回一个布尔值以判断这个session是不是新的。如果一个session已经被服务器建立但是还没有收到相应的客户端的请求,这个session将被认为是新的。这意味着,这个客户端还没有加入会话或没有被会话公认。在他发出下一个请求时还不能返回适当的session认证信息。
当session无效后再调用这个方法会抛出一个IllegalStateException。
9、putValue
public void putValue(String name, Object value);
以给定的名字,绑定给定的对象到session中。已存在的同名的绑定会被重置。这时会调用HttpSessionBindingListener接口的valueBound方法。
当session无效后再调用这个方法会抛出一个IllegalStateException。
10、removeValue
public void removeValue(String name);
取消给定名字的对象在session上的绑定。如果未找到给定名字的绑定的对象,这个方法什么出不做。 这时会调用HttpSessionBindingListener接口的valueUnbound方法。
当session无效后再调用这个方法会抛出一个IllegalStateException。
11、setMaxInactiveInterval
public int setMaxInactiveInterval(int interval);
设置一个秒数,这个秒数表示客户端在不发出请求时,session被Servlet引擎维持的最长时间。
以下这个方法将被取消\
12、getSessionContext
public HttpSessionContext getSessionContext();
返回session在其中得以保持的环境变量。这个方法和其他所有HttpSessionContext的方法一样被取消了。
四、HttpSessionBindingListener接口
定义\
public interface HttpSessionBindingListener
这个对象被加入到HTTP的session中,执行这个接口会通告有没有什么对象被绑定到这个HTTP session中或被从这个HTTP session中取消绑定。
方法
1、valueBound
public void valueBound(HttpSessionBindingEvent event);
当一个对象被绑定到session中,调用此方法。HttpSession.putValue方法被调用时,Servlet引擎应该调用此方法。
2、valueUnbound
public void valueUnbound(HttpSessionBindingEvent event);
当一个对象被从session中取消绑定,调用此方法。HttpSession.removeValue方法被调用时,Servlet引擎应该调用此方法。
五、HttpSessionContext接口
定义\
此接口将被取消\
public interface HttpSessionContext
这个对象是与一组HTTP session关联的单一的实体。
这个接口由于安全的原因被取消,它出现在目前的版本中仅仅是为了兼容性的原因。这个接口的方法将模拟以前的版本的定义返回相应的值。
方法
1、getSession
public HttpSession getSession(String sessionId);
当初用来返回与这个session id相关的session。现在返回空值。
2、getIds
public Enumeration getIds();
当初用来返回这个环境下所有session id的列表。现在返回空的列表。
六、Cookie类\
定义\
public class Cookie implements Cloneable
这个类描述了一个cookie,有关cookie的定义你可以参照Netscape Communications Corporation的说明,也可以参照RFC 2109。
构造函数
public Cookie(String name, String value);
用一个name-value对定义一个cookie。这个name必须能被HTTP/1.1所接受。
以字符$开头的name被RFC 2109保留。
给定的name如果不能被HTTP/1.1所接受,该方法抛出一个IllegalArgumentException。
方法
1、getComment
public String getComment();
返回描述这个cookie目的的说明,如果未定义这个说明,返回空值。
2、getDomain
public String getDomain();
返回这个cookie可以出现的区域,如果未定义区域,返回空值。
3、getMaxAge
public int getMaxAge();
这个方法返回这个cookie指定的最长存活时期。如果未定义这个最长存活时期,该方法返回-1。
4、getName
public String getName();
该方法返回cookie名。
5、getPath
public String getPath();
返回这个cookie有效的所有URL路径的前缀,如果未定义,返回空值。
6、getSecure
public boolean getSecure();
如果这个cookie只通过安全通道传输返回真,否则返回假。
7、getValue
public String getValue();
该方法返回cookie的值。
8、getVersion
public int getVersion();
返回cookie的版本。版本1由RFC 2109解释。版本0由Netscape Communications Corporation的说明解释。新构造的cookie默认使用版本0。
9、setComment
public void setComment(String purpose);
如果一个用户将这个cookie提交给另一个用户,必须通过这个说明描述这个cookie的目的。版本0不支持这个属性。
10、setDomain
public void setDomain(String pattern);
这个方法设置cookie的有效域的属性。这个属性指定了cookie可以出现的区域。一个有效域以一个点开头(.foo.com),这意味着在指定的域名解析系统的区域中(可能是
http://www.foo.com但不是a.b.foo.com...乇4嫠闹骰?/a>
11、setMaxAge
public void setMaxAge(int expiry);
这个方法设定这个cookie的最长存活时期。在该存活时期之后,cookie会被终目。负数表示这个cookie不会生效,0将从客户端删除这个cookie。
12、setPath
public void setPath(String uri);
这个方法设置cookie的路径属性。客户端只能向以这个给定的路径String开头的路径返回cookie。
13、setSecure
public void setSecure(boolean flag);
指出这个cookie只能通过安全通道(例如HTTPS)发送。只有当产生这个cookie的服务器使用安全协议发送这个cookie值时才能这样设置。
14、setValue
public void setValue(String newValue);
设置这个cookie的值,对于二进制数据采用BASE64编码。
版本0不能使用空格、{}、()、=、,、“”、/、?、@、:以及;。
15、setVersion
public void setVersion(int v);
设置cookie的版本号
七、HttpServlet类\
定义\
public class HttpServlet extends GenericServlet implements
Serializable
这是一个抽象类,用来简化HTTP Servlet写作的过程。它是GenericServlet类的扩充,提供了一个处理HTTP协议的框架。
在这个类中的service方法支持例如GET、POST这样的标准的HTTP方法。这一支持过程是通过分配他们到适当的方法(例如doGet、doPost)来实现的。
方法
1、doDelete
protected void doDelete(HttpServletRequest request,
HttpServletResponse response) throws ServletException,
IOException;
被这个类的service方法调用,用来处理一个HTTP DELETE操作。这个操作允许客户端请求从服务器上删除URL。这一操作可能有负面影响,对此用户就负起责任。
这一方法的默认执行结果是返回一个HTTP BAD_REQUEST错误。当你要处理DELETE请求时,你必须重载这一方法。
2、doGet
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException,
IOException;
被这个类的service方法调用,用来处理一个HTTP GET操作。这个操作允许客户端简单地从一个HTTP服务器“获得”资源。对这个方法的重载将自动地支持HEAD方法。
GET操作应该是安全而且没有负面影响的。这个操作也应该可以安全地重复。
这一方法的默认执行结果是返回一个HTTP BAD_REQUEST错误。
3、doHead
protected void doHead(HttpServletRequest request,
HttpServletResponse response) throws ServletException,
IOException;
被这个类的service方法调用,用来处理一个HTTP HEAD操作。默认的情况是,这个操作会按照一个无条件的GET方法来执行,该操作不向客户端返回任何数据,而仅仅是返回包含内容长度的头信息。
与GET操作一样,这个操作应该是安全而且没有负面影响的。这个操作也应该可以安全地重复。
这个方法的默认执行结果是自动处理HTTP HEAD操作,这个方法不需要被一个子类执行。
4、doOptions
protected void doOptions(HttpServletRequest request,
HttpServletResponse response) throws ServletException,
IOException;
被这个类的service方法调用,用来处理一个HTTP OPTION操作。这个操作自动地决定支持哪一种HTTP方法。例如,一个Servlet写了一个HttpServlet的子类并重载了doGet方法,doOption会返回下面的头:
Allow: GET,HEAD,TRACE,OPTIONS
你一般不需要重载这个方法。
5、doPost
protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException,
IOException;
被这个类的service方法调用,用来处理一个HTTP POST操作。这个操作包含请求体的数据,Servlet应该按照他行事。
这个操作可能有负面影响。例如更新存储的数据或在线购物。
这一方法的默认执行结果是返回一个HTTP BAD_REQUEST错误。当你要处理POST操作时,你必须在HttpServlet的子类中重载这一方法。
6、doPut
protected void doPut(HttpServletRequest request,
HttpServletResponse response) throws ServletException,
IOException;
被这个类的service方法调用,用来处理一个HTTP PUT操作。这个操作类似于通过FTP发送文件。
这个操作可能有负面影响。例如更新存储的数据或在线购物。
这一方法的默认执行结果是返回一个HTTP BAD_REQUEST错误。当你要处理PUT操作时,你必须在HttpServlet的子类中重载这一方法。
7、doTrace
protected void doTrace(HttpServletRequest request,
HttpServletResponse response) throws ServletException,
IOException;
被这个类的service方法调用,用来处理一个HTTP TRACE操作。这个操作的默认执行结果是产生一个响应,这个响应包含一个反映trace请求中发送的所有头域的信息。
当你开发Servlet时,在多数情况下你需要重载这个方法。
8、getLastModified
protected long getLastModified(HttpServletRequest request);
返回这个请求实体的最后修改时间。为了支持GET操作,你必须重载这一方法,以精确地反映最后修改的时间。这将有助于浏览器和代理服务器减少装载服务器和网络资源,从而更加有效地工作。返回的数值是自1970-1-1日(GMT)以来的毫秒数。
默认的执行结果是返回一个负数,这标志着最后修改时间未知,它也不能被一个有条件的GET操作使用。
9、service
protected void service(HttpServletRequest request,
HttpServletResponse response) throws ServletException,
IOException;
public void service(ServletRequest request, ServletResponse response)
throws ServletException, IOException;
这是一个Servlet的HTTP-specific方案,它分配请求到这个类的支持这个请求的其他方法。
当你开发Servlet时,在多数情况下你不必重载这个方法。
八、HttpSessionBindingEvent类\
定义\
public class HttpSessionBindingEvent extends EventObject
这个事件是在监听到HttpSession发生绑定和取消绑定的情况时连通HttpSessionBindingListener的。这可能是一个session被终止或被认定无效的结果。
事件源是HttpSession.putValue或HttpSession.removeValue。
构造函数
public HttpSessionBindingEvent(HttpSession session, String name);
通过引起这个事件的Session和发生绑定或取消绑定的对象名构造一个新的HttpSessionBindingEvent。
方法
1、getName
public String getName();
返回发生绑定和取消绑定的对象的名字。
2、getSession
public HttpSession getSession();
返回发生绑定和取消绑定的session的名字。
九、HttpUtils类\
定义\
public class HttpUtils
收集HTTP Servlet使用的静态的有效的方法。
方法
1、getRequestURL
public static StringBuffer getRequestURL(HttpServletRequest
request);
在服务器上重建客户端用来建立请求的URL。这个方法反映了不同的协议(例如http和https)和端口,但不包含查询字符串。
这个方法返回一个StringBuffer而不是一个String,这样URL可以被Servlet开发者有效地修改。
2、parsePostData
public static Hashtable parsePostData(int len,
ServletInputstream in);
解析一个包含MIME类型application/x-www-form-urlencoded的数据的流,并创建一个具有关键值-数据对的 hash table。这里的关键值是字符串,数据是该字符串所对应的值的列表。一个关键值可以在POST的数据中出现一次或多次。这个关键值每出现一次,它的相应的值就被加入到hash table中的字符串所对应的值的列表中。
从POST数据读出的数据将经过URL解码,+将被转换为空格以十六进制传送的数据(例如%xx)将被转换成字符。
当POST数据无效时,该方法抛出一个IllegalArgumentException。
3、parseQueryString
public static Hashtable parseQueryString(String s);
解析一个查询字符串,并创建一个具有关键值-数据对的hash table。这里的数据是该字符串所对应的值的列表。一个关键值可以出现一次或多次。这个关键值每出现一次,它的相应的值就被加入到hash table中的字符串所对应的值的列表中。
从查询字符串读出的数据将经过URL解码,+将被转换为空格以十六进制传送的数据(例如%xx)将被转换成字符。
当查询字符串无效时,该方法抛出一个IllegalArgumentException。
术语表
bytecode
字节码:由Java编译器和Java解释程序生成的机器代码。
cookie
由Web服务器建立的数据,该数据存储在用户的计算机上,提供了一个Web站点跟踪用户的参数并存储在用户自己硬盘上的方法。
HTTP
超文本传输协议。一个请求响应协议用来连接WWW服务器向客户端浏览器传输HTML页面。
输入流对象
一个对象,由ServletInputStream类定义,被Servlet用来从客户端读取请求。
映射
由Servlet实例和Servlet返回数据的URL组成的一对,例如,HelloServlet和/hello/index.html。
输出流对象
一个对象,由ServletOutputStream class类定义,被Servlet用来向客户端返回数据。
request dispatcher object
由RequestDispatcher接口定义的一个对象,用来从客户端接收请求,并将其发送到Web服务器上可用的其他资源(例如Servlet、CGI、HTML文件或JSP文件)。
sandboxed servlet
在一个安全性约束下运行的Servlet。
servlet
一个小的,具有平台无关性的,没有图形用户界面的Java程序。它可以在许多方面扩充Web服务的功能。
servlet configuration object
ServletConfig接口定义的一个对象,用来配置一个Servlet。
servlet context object
ServletContext接口定义的一个对象。给予Servlet有关Servlet引擎的信息。
servlet引擎
由Web服务器提供商制作的一个环境,可以允许Servlet在具体的Web服务器上运行。
servlet请求对象
由ServletRequest接口定义的一个对象,允许Servlet获得用关客户端请求的数据。
servlet response object
由ServletResponse接口定义的一个对象,允许Servlet作出响应。
servlet runner
Java Servlet Developer’s Kit (JSDK)中的sun.servlet.http.HttpServer过程,它使得Servlet得以运行。
会话跟踪
在一个Web应用程序中,识别一个从同一个客户端发出的连续的唯一的请求的能力。
SSL
加密套接字协议层。一个安全协议,用来在Iternet上的客户端浏览器和服务器交换密钥和加密数据。
URI
统一资源标识。定义一个Internet地址,它是一个URL的超集。
URL
统一资源路径。这个地址定义了到达一个WWW上的文件的路线,通常由协议前缀、域名、目录名和文件名组成。
posted @
2006-09-01 17:06 阿成 阅读(231) |
评论 (0) |
编辑 收藏
从作用域范围来说,Servlet的作用域有ServletContext,HttpSession,ServletRequest.
Context范围:
ServletContextListener:
对一个应用进行全局监听.随应用启动而启动,随应用消失而消失主要有两个方法:
contextDestroyed(ServletContextEvent event)
在应用关闭的时候调用
contextInitialized(ServletContextEvent event)
在应用启动的时候调用
这个监听器主要用于一些随着应用启动而要完成的工作,也就是很多人说的我想在容器
启动的时候干..........
一般来说对"全局变量"初始化,如
public void contextInitialized(ServletContextEvent event){
ServletContex sc = event.getServletContext();
sc.setAttribute(name,value);
}
以后你就可以在任何servlet中getServletContext().getAttribute(name);
我最喜欢用它来做守护性工作,就是在contextInitialized(ServletContextEvent event)
方法中实现一个Timer,然后就让应用在每次启动的时候让这个Timer工作:
public void contextInitialized(ServletContextEvent event){
timer = new Timer();
timer.schedule(new TimerTask(){
public void run(){
//do any things
}
},0,时间间隔);
}
有人说Timer只能规定从现在开始的多长时间后,每隔多久做一次事或在什么时间做
一次事,那我想在每月1号或每天12点做一项工作如何做呢?
你只要设一个间隔,然后每次判断一下当时是不是那个时间段就行了啊,比如每月一号做,那你
时间间隔设为天,即24小时一个循环,然后在run方法中判断当时日期new Date().getDate()==1
就行了啊.如果是每天的12点,那你时间间隔设为小时,然后在run中判断new Date().getHour()
==12,再做某事就行了.
ServletContextAttributeListener:
这个监听器主要监听ServletContex对象在setAttribute()和removeAttribute()的事件,注意
也就是一个"全局变量"在被Add(第一次set),replace(对已有的变量重新赋值)和remove的时候.
分别调用下面三个方法:
public void attributeAdded(ServletContextAttributeEvent scab)这个方法不仅可以知道
哪些全局变量被加进来,而且可获取容器在启动时自动设置了哪些context变量:
public void attributeAdded(ServletContextAttributeEvent scab){
System.out.println(scab.getName());
}
public void attributeRemoved(ServletContextAttributeEvent scab)
public void attributeReplaced(ServletContextAttributeEvent scab)
Session范围:
HttpSessionListener:
这个监听器主要监听一个Session对象被生成和销毁时发生的事件.对应有两个方法:
public void sessionCreated(HttpSessionEvent se)
public void sessionDestroyed(HttpSessionEvent se)
一般来说,一个session对象被create时,可以说明有一个新客端进入.可以用来粗略统计在线人
数,注意这不是精确的,因为这个客户端可能立即就关闭了,但sessionDestroyed方法却会按一定
的策略很久以后才会发生.
HttpSessionAttributeListener:
和ServletContextAttributeListener一样,它监听一个session对象的Attribut被Add(一个特定
名称的Attribute每一次被设置),replace(已有名称的Attribute的值被重设)和remove时的事件.
对就的有三个方法.
public void attributeAdded(HttpSessionBindingEvent se)
public void attributeRemoved(HttpSessionBindingEvent se)
public void attributeReplaced(HttpSessionBindingEvent se)
上面的几个监听器的方法,都是在监听应用逻辑中servlet逻辑中发生了什么事,一般的来说.
我们只要完成逻辑功能,比如session.setAttribute("aaa","111");我只要把一个名为aaa的变量
放在session中以便以后我能获取它,我并不关心当session.setAttribute("aaa","111");发生时
我还要干什么.(当然有些时候要利用的),但对于下面这个监听器,你应该好好发解一下:
HttpSessionBindingListener:
上面的监听器都是作为一个独立的Listener在容器中控制事件的.而HttpSessionBindingListener
对在一对象中监听该对象的状态,实现了该接口的对象如果被作为value被add到一个session中或从
session中remove,它就会知道自己已经作为一个session对象或已经从session删除,这对于一些非
纯JAVA对象,生命周期长于session的对象,以及其它需要释放资源或改变状态的对象非常重要.
比如:
session.setAttribute("abcd","1111");
以后session.removeAttribute("abcd");因为abcd是一个字符中,你从session中remove后,它就会
自动被垃圾回收器回收,而如果是一个connection:(只是举例,你千万不要加connection往session
中加入)
session.setAttribute("abcd",conn);
以后session.removeAttribute("abcd");这时这个conn被从session中remove了,你已经无法获取它
的句柄,所以你根本没法关闭它.而在没有remove之前你根本不知道什么时候要被remove,你又无法
close(),那么这个connection对象就死了.另外还有一些对象可以在被加入一个session时要锁定
还要被remove时要解锁,应因你在程序中无法判断什么时候被remove(),add还好操作,我可以先加锁
再add,但remove就后你就找不到它的句柄了,根本没法解锁,所以这些操作只能在对象自身中实现.
也就是在对象被add时或remove时通知对象自己回调相应的方法:
MyConn extends Connection implements HttpSessionBindingListener{
public void valueBound(HttpSessionBindingEvent se){
this.initXXX();
}
public void valueUnbound(HttpSessionBindingEvent se){
this.close();
}
}
session.setAttribute("aaa",new MyConn());
这时如果调用session.removeAttribute("aaa"),则触发valueUnbound方法,就会自动关闭自己.
而其它的需要改变状态的对象了是一样.
posted @
2006-09-01 16:55 阿成 阅读(213) |
评论 (0) |
编辑 收藏
一、JSP工作原理
在一个JSP文件第一次被请求时,JSP引擎把该JSP文件转换成为一个servlet。而这个引擎本身也是一个servlet,在JSWDK或WEBLOGIC中,它就是JspServlet。 JSP引擎先把该JSP文件转换成一个Java源文件,在转换时如果发现jsp文件有任何语法错误,转换过程将中断,并向服务端和客户端输出出错信息;如果转换成功, JSP引擎用javac把该Java源文件编译成相应的class文件。然后创建一个该SERVLET的实例,该SERVLET的jspInit()方法被执行,jspInit()方法在servlet的生命周期中只被执行一次。然后jspService()方法被调用来处理客户端的请求。对每一个请求,JSP引擎创建一个新的线程来处理该请求。如果有多个客户端同时请求该JSP文件,则JSP引擎会创建多个线程。每个客户端请求对应一个线程。以多线程方式执行可大大降低对系统的资源需求,提高系统的并发量及响应时间.但应该注意多线程的编程限制,由于该servlet始终驻于内存,所以响应是非常快的。 如果.jsp文件被修改了,服务器将根据设置决定是否对该文件重新编译,如果需要重新编译,则将编译结果取代内存中的servlet,并继续上述处理过程。 虽然JSP效率很高,但在第一次调用时由于需要转换和编译而有一些轻微的延迟。 此外,如果在任何时候如果由于系统资源不足的原因,JSP引擎将以某种不确定的方式将servlet从内存中移去。当这种情况发生时jspDestroy()方法首先被调用, 然后servlet实例便被标记加入"垃圾收集"处理。 jspInit()及jspDestory()格式如下:可在jspInit()中进行一些初始化工作,如建立与数据库的连接,或建立网络连接,从配置文件中取一些参数等,在jspDestory()中释放相应的资源。
<%!
public void jspInit()
{
System.out.println("jspinit");
}
%>
<%!
public void jspDestory()
{
System.out.println("jspDestory");
}
%>
二、服务端的输出缓冲区
缺省情况下:服务端要输出到客户端的内容,不直接写到客户端,而是先写到一个输出缓冲区中.只有在下面三中情况下,才会把该缓冲区的内容输出到客户端上:
该JSP网页已完成信息的输出
输出缓冲区已满
JSP中调用了out.flush()或response.flushbuffer()
输出缓冲区的大小可以用:或response.setBufferSize()设置,如下:
设置输出缓冲区的大小为1KB。或response.setBufferSize(1);
设置输出缓冲区的大小为0,即不缓冲。或response.setBufferSize(0);
用response.getBufferSize()或out.getBufferSize()可取的输出缓冲区的大小,单位为字节. 用response.isCommitted()可检查看服务端是否已将数据输出到客户端. 如果返回值是TRUE则已将数据输出到客户端,是FALSE则还没有.
三、服务端输出重定向
有以下3种方法可以做到输出重定向:
RESPONSE.SETREDERECT("URL") 该方法通过修改HTTP协议的HEADER部分,对浏览器下达重定向指令的,使浏览器显示重定向网页的内容. response.sendRedirect("http://localhost:7001/index.html");
下面的方法也能改变HTTP HEADER属性,它的原理和 1 是一样的.
<%
response.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY);
String newLocn="/index.html";
response.setHeader("Location",newLocn);
% >
采用<JSP:FORWORD> 该方法是利用服务器端先将数据输出到缓冲区的机制,在把缓冲区的内容发送到客户端之前,原来的不发送,改为发送该页面的内容,如果在<JSP:FORWORD>之前有很多输出,前面的输出已使缓冲区满,将自动输出到客户端,那么该语句将不起作用,这一点应该特别注意. 如下面的例子中(1)会输出index.html的内容,2 不会输出index.html的内容,而是输出out.println("@@@@@@@@@@@@@@@@@"); 中的内容,并且在服务端会抛出:java.lang.IllegalStateException: Response already committed 异常,但客户端没有任何错误输出。
(1)
<%@page buffer="1kb"%>
<%
long i=0;
for(i=0;i<10;i++)
{
out.println("@@@@@@@@@@@@@@@@@");
}
%>
<jsp:forward page="./index.html" />
(2)
<%@page buffer="1kb"%>
<%
long i=0;
for(i=0;i<600;i++)
{
out.println("@@@@@@@@@@@@@@@@@");
}
%>
说明:
1. 方法(1),(2)可以使用变量表示重定向的地址;方法(3)不能使用变量表示重定向的地址。
String add="./index.html";
<jsp:forward page= add />
无法重定向到index.html中去
String add=http://localhost:7001/index.html
response.sendRedirect(add);
可以重定向到http://localhost:7001/index.html中去。
2. 采用方法(1),(2)request中的变量(通过request.setAttribute()保存到request中的值)不能在新的页面中采用,采用方法(3)能. 综上,我们应该采用(1),(2)重定向比较好.
四、JSP中正确应用类:
应该把类当成JAVA BEAN来用,不要在<% %> 中直接使用. 如下的代码(1)经过JSP引擎转化后会变为代码(2):
从中可看出如果把一个类在JSP当成JAVA BEAN 使用,JSP会根据它的作用范围把它保存到相应的内部对象中.
如作用范围为request,则把它保存到request对象中.并且只在第一次调用(对象的值为null)它时进行实例化. 而如果在<% %>中直接创建该类的一个对象,则每次调用JSP时,都要重新创建该对象,会影响性能.
代码(1)
<jsp:useBean id="test" scope="request" class="demo.com.testdemo">
</jsp:useBean>
<%
test.print("this is use java bean");
testdemo td= new testdemo();
td.print("this is use new");
%>
代码(2)
demo.com.testdemo test = (demo.com.testdemo)request.getAttribute("test");
if (test == null)
{
try
{
test = (demo.com.testdemo) java.beans.Beans.instantiate(getClass().getClassLoader(),"demo.com.testdemo");
}
catch (Exception _beanException)
{
throw new weblogic.utils.NestedRuntimeException("cannot instantiate 'demo.com.testdemo'",_beanException);
}
request.setAttribute("test", test);
out.print("\r\n");
}
out.print("\r\n\r\n\r\n");
test.print("this is use java bean");
testdemo td= new testdemo();
td.print("this is use new");
五、JSP的调试
JSP的调试比较麻烦,特别是当bean是在一个session中存在时,更加困难。得从好几个页面开始往里面走才行。通常是用out.println()或System.out.print()来打一大堆的信息来查问题。如果是用jbuilder做开发,它能直接调试JSP.不过更重要的是知道错误产生的原因及解决方法。下面对一些JSP编程常见错误进行分析。
(1).java.lang.NullPointerException异常
一般是对一个为NULL值的变量进行操作引起的.如下面的操作就会抛出
java.lang.NullPointerException
String a = null;
a.substring(0,1);
为避免这种异常最好在对变量操作之前检查看它是否为NULL值.如:
<% String ss=Session.getAttribute("NAME")
if isnull(ss)
{
}
else
{
}
%>
(2).JSP是用JAVA写的,所以它是大小写敏感的,用过其他编程语言的人最容易犯这个错误。另外在浏览器的地址栏中输入的访问JSP的地址也是区分大小写的.如http://localhost:7001/demo/t.jsp与http://localhost:7001/Demo/t.jsp是不一样的
(3).在jsp中判断字符串要使用compareTo方法,不要用==,因为在java中String变量不是一个简单的变量而是一个类实例,不同的方法会得到 不同的结果,如下所示:
String str1="ABCD";
String str2="ABCD"; (或 String str2="AB"+"CD";
if (str1==str2)
out.print("yes");
else
out.print("no");
结果是"yes"。
String str1,str2,str3;
str1="ABCD";
str2="AB";
str3=str2+"CD";
if (str1==str3)
out.print("yes");
else
out.print("no");
结果是"no"。
String str1=new String("ABCD");
String str2=new String("ABCD");
if (str1==str2)
out.print("yes");
else
out.print("no");
结果是"no"。
String str1=new String("ABCD");
String str2=new String("ABCD");
if (str1.compareTo(str2)==0)
out.print("yes");
else
out.print("no");
结果是"yes"。
(4)防止JSP或SERVLET中的输出被浏览器保存在缓冲区中:
浏览器在默认情况下会把浏览过的网页保存在缓冲区中,在调试时,一般不希望这样.把下面的脚本加入程序中,就可防止JSP或SERVLET中的输出被浏览器保存在缓冲区中
<%
response.setHeader("Cache-Control","no-store"); //HTTP 1.1
response.setHeader("Pragma","no-cache"); //HTTP 1.0
response.setDateHeader ("Expires", 0); //prevents caching at the proxy server
%>
在IE中也可通过设置实现:把/工具/INTERNET选项/常规/设置/的检察所存页面的较新版本,设为每次访问该页时都检查.
六、COOKIE
HTTP COOKIE实质是服务端与在客户端之间传送的普通HTTP头,可保存也可不保存在客户的硬盘上.如果保存,每一个文件大小不超过4K的文本文件.多个COOKIE可保存到同一个文件中. 如果从编程角度来看,在JSP中COOKIE就是JAVA提供的一个类.常用的方法如下所表示,因为客户端可能不接受COOKIE,所以建议不用它,改用SESSION等其他方式。
public class cookie
{
public String getDomain() //返回该COOKIE的有效域
public int getMaxAge() //返回该COOKIE的有效期,单位为秒
public String getName() //返回该COOKIE的名称
public String getPath() //返回该COOKIE的有效路径
public boolean getSecure() //返回该COOKIE的安全设置
public String getValue() //返回该COOKIE的值
public void setDomain(java.lang.String pattern) //设置该COOKIE的有效域
public void setMaxAge(int expiry) //设置该COOKIE的有效期,单位为秒
public void setPath(java.lang.String uri) //设置该COOKIE的有效路径
public void setSecure(boolean flag) //设置该COOKIE的安全设置
public void setValue(java.lang.String newValue) //设置该COOKIE的值
}
一个COOKIE包含以下五部分:
NAME/VALUE对,设置该COOKIE的名字及它保存的值
COOKIE通常和服务器相关,如果将域设为JAVA.SUN.COM,那么该COOKIE就和这个域相关,只对该网址起作用,当浏览该网址时,浏览器将把该COOKIE的内容发送给服务端,COOKIE是作为HTTP HEADER的一部分被发送的,如果没有设置域,那么COOKIE就只和创建该COOKIE的服务器相关.
路径用于指定服务器上可以使用该COOKIE的文件所在的路径,它只对该网址下的该路径下的应用起作用."/"表示服务器上所有目录都可以使用该COOKIE.
COOKIE都有一个有效期,有效期默认值为-1,这表示没有保存该COOKIE,当该浏览器退出时,该COOKIE立即失效.
安全选项true/false,如果设置为true,那么在服务端与在客户端之间传送该COOKIE的内容时,采用HTTPS协议.
如何检查一个客户端是否支持COOKIE的方法:
用下面的方法写一个COOKIE到客户端,并确认成功
try
{
Cookie c = new Cookie("mycookie","COOKIE TEST");
response.addCookie(c);
}
catch(Exception e)
{
System.out.println(e);
}
然后在一个新的JSP文件中:用下面的方法取客户端的COOKIE到cookies中, 如果cookies.length ==0,说明该客户端的浏览器不支持COOKIE
try
{
Cookie[] cookies = request.getCookies();
if(cookies.length ==0)
{
System.out.println("not support cookie");
}
}
catch(Exception e)
{
System.out.println(e);
}
七、JSP和SERVLET的区别:
SUN首先发展出SERVLET,其功能比较强劲,体系设计也很先进,只是,它输出HTML语句还是采用了老的CGI方式,是一句一句输出,所以,编写和修改HTML非常不方便。 后来SUN推出了类似于ASP的JSP,把JAVA代码嵌套到HTML语句中,这样,就大大简化和方便了网页的设计和修改。ASP,PHP,JSP都是嵌套型的SCRIPT语言。 一个分布式系统应分为三层:表示层,业务逻辑层,数据存取层,在J2EE体系结构中,SERVLET用来写业务逻辑层是很强大的,但是对于写表示层就很不方便。JSP则主要是为了方便写表示层而设计的。ENTITY BEAN实现数据存取层,SESSION BEAN实现业务逻辑层。如果是简单的应用系统,可采用JSP+BEANS的结构进行设计,JSP中应该仅仅存放与表示层有关的东西,也就是说,只放输出HTML网页的部份。而所有的数据计算,数据分析,数据库联结处理,统统是属于业务逻辑层,应该放在JAVA BEANS中。通过JSP调用JAVA BEANS,实现两层的整合。 实际上,微软的DNA技术,简单说,就是ASP+COM/DCOM技术。与JSP+BEANS完全类似,所有的表示层由ASP完成,所有的业务逻辑由COM/DCOM完成。 为什么要采用这些组件技术呢?因为单纯的ASP/JSP语言是非常低效率执行的,如果出现大量用户点击,纯SCRIPT语言很快就到达了他的功能上限,而组件技术就能大幅度提高功能上限,加快执行速度。另外一方面,纯SCRIPT语言将表示层和业务逻辑层混在一起,造成修改不方便,并且代码不能重复利用,采用组件技术就只改组件就可以了。 对于复杂的应用,应该采用ENTITY BEAN实现数据存取层,SESSION BEAN实现业务逻辑层,用JSP来调用SESSION BEAN,由SESSION BEAN调用ENTITY BEAN。即采用JSP+EJB来构建一个复杂的分布式系统。它比JSP+BEAN具有更高的吞吐量,可靠性,安全性。综上所述,对简单应用,可采用JSP+BAEN,对复杂的应用系统,应采用JSP+EJB,SERVLET变的无足轻重。用JSP完全可替代它。
posted @
2006-09-01 16:53 阿成 阅读(293) |
评论 (0) |
编辑 收藏
request.getSession() will return the current session and if one does not exist, a new session will be cretaed.
request.getSession(true) will return the current session if one exists, if one doesn't exits a new one will be created.
So there is actually no difference between the two methods.
HOWEVER, if you use request.getSession(false), it will return the current session if one exists and if one DOES NOT exist a new one will NOT be cretaed.
posted @
2006-09-01 13:59 阿成 阅读(673) |
评论 (0) |
编辑 收藏
转载-
http://blog.csdn.net/ufoer23/archive/2005/02/24/299619.aspx2005 年 1 月
Hibernate 是一个流行的开源对象关系映射工具,单元测试和持续集成的重要性也得到了广泛的推广和认同,在采用了Hibernate的项目中如何保证测试的自动化和持续性呢?本文讨论了Hibernate加载其配置文件hibernate.properties和hibernate.cfg.xml的过程,以及怎么样将hibernate提供的配置文件的访问方法灵活运用到单元测试中。
1 介绍
Hibernate 是一个流行的开源对象关系映射工具,单元测试和持续集成的重要性也得到了广泛的推广和认同,在采用了Hibernate的项目中如何保证测试的自动化和持续性呢?本文讨论了Hibernate加载其配置文件hibernate.properties和hibernate.cfg.xml的过程,以及怎么样将hibernate提供的配置文件的访问方法灵活运用到单元测试中。注意:本文以hibernate2.1作为讨论的基础,不保证本文的观点适合于其他版本。
2 读者
Java开发人员,要求熟悉JUnit和掌握Hibernate的基础知识
3 内容
3.1.准备
对于hibernate的初学者来说,第一次使用hibernate的经验通常是:
1, 安装配置好Hibernate,我们后面将%HIBERNATE_HOME%作为对Hibernate安装目录的引用,
2, 开始创建好自己的第一个例子,例如hibernate手册里面的类Cat,
3, 配置好hbm映射文件(例如Cat.hbm.xml,本文不讨论这个文件内配置项的含义)和数据库(如hsqldb),
4, 在项目的classpath路径下添加一个hibernate.cfg.xml文件,如下(第一次使用hibernate最常见的配置内容):
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration
PUBLIC "-//Hibernate/Hibernate Configuration DTD//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-2.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="connection.url">jdbc:hsqldb:hsql://localhost</property>
<property name="connection.driver_class">org.hsqldb.jdbcDriver</property>
<property name="connection.username">sa</property>
<property name="connection.password"></property>
<property name="dialect">net.sf.hibernate.dialect.HSQLDialect</property>
<property name="hibernate.show_sql">false</property>
<mapping resource="Cat.hbm.xml"/>
</session-factory>
</hibernate-configuration>
|
5, 然后还需要提供一个类来测试一下创建,更新,删除和查询Cat,对于熟悉JUnit的开发人员,可以创建一个单元测试类来进行测试,如下:
import junit.framework.TestCase;
import net.sf.hibernate.HibernateException;
import net.sf.hibernate.Session;
import net.sf.hibernate.Transaction;
import net.sf.hibernate.cfg.Configuration;
public class CatTest extends TestCase {
private Session session;
private Transaction tx;
protected void setUp() throws Exception {
Configuration cfg = new Configuration().configure();////注意这一行,这是本文重点讨论研究的地方。
session = cfg.buildSessionFactory().openSession();
tx = session.beginTransaction();
}
protected void tearDown() throws Exception {
tx.commit();
session.close();
}
public void testCreate() {
//请在此方法内添加相关的代码,本文不讨论怎么样使用Hibernate API。
}
public void testUpdate() {
//请在此方法内添加相关的代码,本文不讨论怎么样使用Hibernate API。
}
public void testDelete() {
//请在此方法内添加相关的代码,本文不讨论怎么样使用Hibernate API。
}
public void testQuery() {
//请在此方法内添加相关的代码,本文不讨论怎么样使用Hibernate API。
}
}
|
3.2 new Configuration()都做了什么?
对于第一次使用hibernate的新手来说,下面的这段代码可以说是最常见的使用Configuration方式。
Configuration cfg = new Configuration().configure();
|
Configuration是hibernate的入口,在新建一个Configuration的实例的时候,hibernate会在classpath里面查找hibernate.properties文件,如果该文件存在,则将该文件的内容加载到一个Properties的实例GLOBAL_PROPERTIES里面,如果不存在,将打印信息
hibernate.properties not found
|
然后是将所有系统环境变量(System.getProperties())也添加到GLOBAL_PROPERTIES里面(注1)。如果hibernate.properties文件存在,系统还会验证一下这个文件配置的有效性,对于一些已经不支持的配置参数,系统将打印警告信息。
3.3. configure()在做什么?
new Configuration()讨论至此,下面讨论configure()方法。
configure()方法默认会在classpath下面寻找hibernate.cfg.xml文件,如果没有找到该文件,系统会打印如下信息并抛出HibernateException异常。
hibernate.cfg.xml not found
|
如果找到该文件,configure()方法会首先访问< session-factory >,并获取该元素的name属性,如果非空,将用这个配置的值来覆盖hibernate.properties的hibernate.session_factory_name的配置的值,从这里我们可以看出,hibernate.cfg.xml里面的配置信息可以覆盖hibernate.properties的配置信息。
接着configure()方法访问<session-factory>的子元素,首先将使用所有的<property>元素配置的信息(注2),如前面我们使用的配置文件
<property name="connection.url">jdbc:hsqldb:hsql://localhost</property>
<property name="connection.driver_class">org.hsqldb.jdbcDriver</property>
<property name="connection.username">sa</property>
<property name="connection.password"></property>
<property name="dialect">net.sf.hibernate.dialect.HSQLDialect</property>
|
会覆盖hibernate.properties里面对应的配置,hibernate2.1发布包里面自带的hibernate.properties文件(位于%HIBERNATE_HOME%/etc下面)里面的值,如下:
hibernate.dialect net.sf.hibernate.dialect.HSQLDialect
hibernate.connection.driver_class org.hsqldb.jdbcDriver
hibernate.connection.username sa
hibernate.connection.password
hibernate.connection.url jdbc:hsqldb:hsql://localhost
|
然后configure()会顺序访问以下几个元素的内容
<mapping>
<jcs-class-cache>
<jcs-collection-cache>
<collection-cache>
|
其中<mapping>是必不可少的,必须通过配置<mapping>,configure()才能访问到我们定义的java对象和关系数据库表的映射文件(hbm.xml),例如:
<mapping resource="Cat.hbm.xml"/>
|
通过以上的分析,我们对hibernate配置文件hibernate.properties和hibernate.cfg.xml的默认的加载过程就比较清楚了。
3.4 Configuration的其他用法
Configuration的configure ()方法还支持带参数的访问方式,你可以指定hbm.xml文件的位置,而不是使用默认的classpath下面的hibernate.cfg.xml这种方式,例如:
Configuration cfg = new Configuration().configure("myexample.xml");
|
同时Configuration还提供了一系列方法用来定制hibernate的加载配置文件的过程,让你的应用更加灵活,常用的是以下几种:
addProperties(Element)
addProperties(Properties)
setProperties(Properties)
setProperty(String, String)
|
通过以上几个方法,除了使用默认的hibernate.properties文件,你还可以提供多个.properties配置文件,使用Hibernate的时候根据不同的情况使用不同的配置文件,例如:
Properties properties = Properties.load("my.properties");
Configuration config = new Configuration().setProperties(properties).configure();
|
除了指定.properties文件之外,还可以指定.hbm.xml文件,下面列出几个常用的方法:
addClass(Class)
addFile(File)
addFile(String)
addURL(URL)
|
前面我们已经讲了,configure()方法默认是通过访问hibernate.cfg.xml的<mapping>元素来加载我们提供的.hbm.xml文件,上面列出的方法可以直接指定hbm.xml文件,例如addClass()方法可以直接通过指定class来加载对应的映射文件,hibernate会将提供的class的全名(包括package)自动转化为文件路径,如net.sf.hibernate.examples.quickstart.Cat.class对应了net/sf/hibernate/examples/quickstart/Cat.hbm.xml,还可以用addFile方法直接指定映射文件。
例一:
Configuration config = new Configuration().addClass(Cat.class);
|
例二:
Configuration config = new Configuration().addURL(Configuration.class.getResource ("Cat.hbm.xml"));
|
例三:
Configuration config = new Configuration().addFile("Cat.hbm.xml");
|
3.5 总结
Configuration提供的这些方法的好处如下:
1. 一个应用中往往有很多.hbm.xml映射文件,开发的过程中如果只是为了测试某个或几个Java PO(Persistence Object),我们没有必要把所有的.hbm.xml都加载到内存,这样可以通过addClass或者addFile直接,显得非常灵活。
2. 学习Hibernate的过程中,往往需要通过练习来体会Hibernate提供的各种特征,而很多特征是需要修改配置文件的,如果要观察相同的代码在不同的特征下的表现,就需要手工改配置文件,这样太麻烦了,而且容易出错,我们可以提供多个配置文件,每个配置文件针对需要的特征而配置,这样我们在调用程序的时候,把不同的配置文件作为参数传递进去,而程序代码里面使用setProperties和addFile指定传入的配置文件参数就可以了。
3. 在单元测试中,特别是在集成测试里面,整个过程是自动化的,我们不能手工干预测试过程,往往需要准备多个配置文件针对不同的测试案例,这个时候setProperties和addFile方法就显得特别有用了,在不同的测试案例中用这些方法来指定相应的配置文件,这样就可以做到自动化测试,保证了持续性。
3.6 应用举例
在刚开始学习hibernate的时候,对于hibernate的hbm映射文件里的各种配置参数没有一个感性的认识,例如inverse="true",lazy="true"这样的配置参数,不通过实践是无法体会到其作用的,传统的方法就是每需要测试一种参数的效果就更改相应的配置文件,然后运行测试来观察结果,如果能够灵活的运用Configuration提供的定制配置的方法,我们可以提供多个配置文件,每个配置文件里面有不同的配置参数,配合相应的测试案例就方便多了。
例如针对ono-to-many和many-to-one的双向关联的映射关系,我们想测试在one-to-many一方使用inverse="false"和inverse="true"的不同效果,假设已经正确的配置好了hibernate.properties,那么还需要提供两个不同的hbm.xml文件,假设分别名为bidirect.inverse.false.hbm.xml和bidirect.inverse.true.hbm.xml。
然后需要写两个不同的测试案例,分别针对两个不同的配置文件进行测试就可以了,这样的好处是,不用针对不同的测试案例修改配置文件,特别是在集成测试的时候,一切都是自动化的,如果每测试一个案例就需要手工去更改配置文件,这肯定是一个失败的测试。
代码模板如下:
FalseInverseTest.java文件
import junit.framework.TestCase;
import net.sf.hibernate.HibernateException;
import net.sf.hibernate.Session;
import net.sf.hibernate.Transaction;
import net.sf.hibernate.cfg.Configuration;
/**
* test false inverse
*/
public class FalseInverseTest extends TestCase {
private Session session;
private Transaction tx;
protected void setUp() throws Exception {
Configuration cfg = new Configuration().addFile("bidirect.inverse.false.hbm.xml");
session = cfg.buildSessionFactory().openSession();
tx = session.beginTransaction();
}
protected void tearDown() throws Exception {
tx.commit();
session.close();
}
public void testLogic() {
//在此编写测试代码
}
}
|
TrueInverseTest.java文件
import junit.framework.TestCase;
import net.sf.hibernate.HibernateException;
import net.sf.hibernate.Session;
import net.sf.hibernate.Transaction;
import net.sf.hibernate.cfg.Configuration;
/**
* test true inverse
*/
public class TrueInverseTest extends TestCase {
private Session session;
private Transaction tx;
protected void setUp() throws Exception {
Configuration cfg = new Configuration().addFile("bidirect.inverse.true.hbm.xml");
session = cfg.buildSessionFactory().openSession();
tx = session.beginTransaction();
}
protected void tearDown() throws Exception {
tx.commit();
session.close();
}
public void testLogic() {
//在此编写测试代码
}
}
|
结束语
通过对Hibernate默认的配置文件的加载顺序和Hibernate提供的加载配置文件的方法的讨论,我们对在使用到Hibernate的项目的单元测试中使用多个Hibernate配置文件有了比较清楚的认识。
持续集成中的测试的特征是自动化和持续性,不能手工干预其过程,在使用到Hibernate的项目如果要实现持续集成,就要为不同的测试案例提供不同的配置文件,而不是针对不同的测试案例进行手工调整,因此,在使用到Hibernate的项目中灵活的运用多配置文件,可以提高测试的效率,保证自动化和持续性。
注1:有关的代码请参考Environment类的static{}。
注2:如果在hibernate.cfg.xml的<property/>配置的name没有以hibernate开头,那么configure()内部会自动在前面添加hibernate,例如connection.url,hibernate会自动将其转化为hibernate.connection.url。
参考资料
posted @
2006-08-31 09:47 阿成 阅读(217) |
评论 (0) |
编辑 收藏
posted @
2006-08-29 09:56 阿成 阅读(248) |
评论 (0) |
编辑 收藏
简化Spring(1)--配置文件
简化Spring(2)--Model层
简化Spring(3)--Controller层
简化Spring(4)--View层
posted @
2006-08-29 08:58 阿成 阅读(220) |
评论 (0) |
编辑 收藏
摘要: 1.
简介
1.1
结构
DECLARE --
定义
BEG...
阅读全文
posted @
2006-08-29 08:52 阿成 阅读(349) |
评论 (0) |
编辑 收藏
JDK
java.lang.*;
java.io.*;
java.util.*;
java.sql.*;
posted @
2006-08-25 14:41 阿成 阅读(210) |
评论 (0) |
编辑 收藏
最近知道一个大学同学10月中旬结婚,
一个初中同学10月底结婚,好快呀,咱哥们的个人问题还没普呢!
posted @
2006-08-25 10:35 阿成 阅读(208) |
评论 (0) |
编辑 收藏
1、I18N -
Internationalization的简称,国际化的意思,因为该单词的首字母I与尾字母N中间隔着18个字母,由此得名。够忽悠的。
2、PM
公司里的PM:Project Management 项目管理
论坛里的PM:Private Message 站内短消息
化学里的PM:Polymethyl Methacrylate 聚甲基丙烯酸甲酯
时间里的PM:prior to midnight 下午
拼音里的PM:拍马、拍卖
posted @
2006-08-23 20:26 阿成 阅读(215) |
评论 (0) |
编辑 收藏
http://www.mqxx.com/wgl/music/music2/13.mp3
罗大佑那首广为传唱的《童年》的英文版:回到那懵懂童年,会和邻居家的孩子在小小的游戏里一争高下;回到那青涩过往,烛光晚会、海边沙滩就这样送出了自己的初吻;回到那刚出茅庐的岁月,初尝生活的艰辛,爸爸告诫说慢慢来……直到有了自己的小家、有了孩子,“多么想念我的家乡,多么想念那过去的日子”,可这一切已经过去了……相同的旋律,不同的语言,别有一番风味,很亲切的。
Days On My Past
I recall when I was young. Oh I will play and always having fun
With the neighbours next to me and we'll play until the setting sun
Try to be the best among the others in a game call the "spider battle"
It doesn't matter who is the best now. Those were the days of my past
A few years later when I got to school and was late for lesson all the time
Always day dreaming in the class, till I don't even know the lesson's done
Then my teacher always tell me never ever be lazy again
What can I do now? What can I say now? Those were the days of my past
As the days go on and on I grew up and had my first love
Candel light and sandy beach finally give away my first kiss
Mother said I was too young to fall in love and then I will one day regret
So love was over, but I do miss her. Those were the days of my past
Just when I left my high school and got my first job as salesmen
Working hard all day and night. No one there to lend a helping hand
Dady told me not to worry and said that l should go on step by step
What can I say now? What can I do now? Those were the days of my past
Then once day I settled down with the only one I really love
Got a small family with two kids that is what I'm always hoping for
But I still remember having fun with all my friends when I was young
I miss my home town. I miss my old friends, those the days of my past
Oh I miss my home town I miss my old friends. When will I see them again?
重点词汇
setting n. (太阳)落山 | spider n. 蜘蛛,设圈套者 |
give away v. 送掉,分发 | regret v. 感到遗憾,后悔,惋惜,哀悼 |
step by step adv. 逐步地 | settle down v. 定居,安定下来 |
歌曲名:童年
词/曲:罗大佑
编曲:山崎稔
池塘边的榕树上 知了在声声叫着夏天
操场边的秋千上 只有蝴蝶停在上面
黑板上老师的粉笔 还在拼命唧唧喳喳写个不停
等待着下课 等待着放学 等待游戏的童年
福利社里面什么都有 就是口袋里没有半毛钱
诸葛四郎和魔鬼党 到底谁抢到那支宝剑
隔壁班的那个女孩 怎么还没经过我的窗前
嘴里的零食 手里的漫画 心里初恋的童年
总是要等到睡觉前 才知道功课只做了一点点
总是要等到考试以后 才知道该念的书都没有念
一寸光阴一寸金 老师说过寸金难买寸光阴
一天又一天 一年又一年 迷迷糊糊的童年
没有人知道为什么 太阳总下到山的那一边
没有人能够告诉我 山里面有没有住着神仙
多少的日子里 总是一个人面对着天空发呆
就这么好奇 就这么幻想 这么孤单的童年
阳光下蜻蜓飞过来 一片片绿油油的稻田
水彩蜡笔和万花筒 画不出天边那一道彩虹
什么时候才能像高年级的同学有张成熟与长大的脸
盼望着假期 盼望着明天 盼望长大的童年
一天又一天 一年又一年 盼望长大的童年
posted @
2006-08-23 20:12 阿成 阅读(378) |
评论 (0) |
编辑 收藏