2007年4月6日
北京英语角(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 阿成 阅读(2110) |
评论 (6) |
编辑 收藏
1.未到火候,千万别说。有些男孩才跟人家约过几次,就提出要建立“更进一步”的关系,这十有八九要坏汤。爱情上的事妙就妙在一切尽在不言中。说清楚了,话挑明了,反而不美。本来人家肯单独出来跟你说话,这说明女孩对你不无好感。但是这种好感有时连她们自己都说不清,你却急于挑明,破坏了这种朦胧美,那就别怪人家敬而远之以致退避三舍了。看看周围那些还没开始就夭折的爱情,许多都是由男孩没有掌握好火候造成的。
2.收起拳头是为了更好的出击。被女孩拒绝了怎么办呢?有人说应该穷追猛打、坚持不懈。其实不然。除非你是一个啥也不在乎的人,否则你很难承受女孩的白眼和同伴们嘲弄的目光。倒不如偃旗息鼓、暂时撤退。这不仅保护了你的尊严而且还留了条后路,从而为日后反攻创造机会。就让她在人群里闯荡。等她碰得头破血流时,你再去找她,她才能认识到原来你才是最爱她的人。即使实在割舍不下,要去找她,也应注意方式方法。千万别让她觉得你讨厌。而这一点往往是很难做到的。
3.不要太露骨。要学会不声不响地关心她,用你的诚实和善意对待她。只有这样你才能在一大帮围着她呱呱乱叫的男孩当中引起她的注意。记住,只有特别的你才会引起她特别的关注。
4.非请勿入。一个老是往女孩寝室跑的男孩是不会引起女孩太多的好感的。有些学生会干部借口工作常往女生寝室跑,去了后就老赖在那不走,结果给人家带来了诸多不便,效果只会适得其反。产生这种结果的根本原因还是因为太露骨。
5.战略上藐视...,战术上重视...。有时你喜爱的女孩会和你有些接触,比如谈谈话、聊聊天、一起工作等等。你最好能以平常的心态看待这些事情,不要背上包袱、患得患失。例如不要太在意她无意中说的话,有些男孩容易自作多情,源出于此。但对每一次这样的机会则应引起足够的重视,例如初次谈话要注意掌握好分寸,最好不要涉及情爱范畴,不妨说说小时候的事。你如果只奔主体那就要糟!女孩非得象警惕狼一样地警惕你。
6.马屁最好少拍。你夸她长得漂亮,如果她真的漂亮,那么你不过是第七百个夸她漂亮的人,而她映象最深的恐怕是第一个这样夸她的人;如果她相貌普通,你这样夸她能产生什么样的结果谁也说不准。要是让她发现你言不由衷,那你就死定了。记住,哄女孩只有在女孩成为你的女朋友之后才能哄,还没追到手就开始哄是没有掌握好火候的表现。
7.少来点大男子主义。有个男孩好不容易请得他倾慕已久的女孩去吃饭,花了他半个月生活费。后来他去付账时发现女孩已经替他付了,他就要还她钱。女孩不愿意。他觉得女孩剥了他的面子,大为光火。女孩气得哭了。本来女孩替他付账这说明她对他有好感,他不思讨好反而发脾气,如此就难怪他要打光棍了。几乎没有一个女孩会对那些不尊重女性的男孩有好感,切记!切记!。
8.团结大多数。几乎每个女孩都有一两个最知心的女友,当其他道路都不通时,她们是你通往胜利的成功之路。你可以通过她们了解女孩到底对你有没有意思,还可以通过她们传达你的意思。这比你直接说要好得多。可千万别忽视这一支生力军,我不止一次地看到男孩通过这个方法大功告成的。
最后要提醒大家的是,爱情从来都没有固定的公式。别人成功的方法也许正是你的败着;别人失败的方法也许恰是你的妙着。我这里只提一点原则性的东西,切不可生搬硬套。如果因此而坏了你的好事切莫来找我。女孩要是不喜欢你,就是玉皇大帝也没有法子。还是古人说的好:不战而屈人之兵,上之上策也。
posted @
2007-04-23 19:12 阿成 阅读(492) |
评论 (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 阿成 阅读(2404) |
评论 (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 阿成 阅读(471) |
评论 (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 阿成 阅读(431) |
评论 (0) |
编辑 收藏