爪哇东南的自留地

学习探讨开源和web开发

导航

<2006年9月>
272829303112
3456789
10111213141516
17181920212223
24252627282930
1234567

统计

常用链接

留言簿(1)

随笔分类

随笔档案

相册

收藏夹

life

technique

搜索

最新评论

阅读排行榜

评论排行榜

2006年9月13日 #

数码摄影中的光及用光技巧

     摘要: 大家都知道,摄影是光的艺术!没有光线,就没有摄影;用光不当,就得不到一幅满意的照片。因此,光是摄影的基础,有了它,感光材料才有感光的可能;有了它,物体才能显现出形态、色彩和质感。     布光,则是摄影中的一项创造性工作,它体现着一个摄影师的个性和风格,也关系到一幅作品的成败。     从某种意义上讲,不管是摄...  阅读全文

posted @ 2006-10-02 14:32 ericli 阅读(1540) | 评论 (2)编辑 收藏

数码摄影入门之六 名家名言

根据
美国摄影家维利·奎克的看法,几乎所有的人都能够通过应用一些基本法则,达到具有相当水平的摄影构图能力。为此,他提出了一些简单的方法。他认为,依据这些方法,任何人都能在短时间内学会拍出较好的照片。

有些人或许对构图有天赋。不过,根据美国摄影家维利·奎克的看法,几乎所有的人都能够通过应用一些基本法则,达到具有相当水平的摄影构图能力。为此,他提出了一些简单的方法。他认为,依据这些方法,任何人都能在短时间内学会拍出较好的照片。 

维利·奎克指出,摄影者最易犯的错误是拍摄点取得过高,与被摄体离得太远,致使画面出现各种各样与拍摄目的毫无关系的东西。

他说:“请记住,画家是 把东西画进画面,而摄影者则是从画面去掉一些东西。” 他说:“一张照片应该只有一个趣味中心,画面上不能有无用的东西。如果你对某个物体是否有助于画面抱有怀疑,你就应尽可能地把它放弃掉。” 这里就是人们常说的摄影是“减法的艺术”

奎克认为:“这并不是说,趣味中心非得准确地置于交叉点上不可。正因为如此,实际上趣味中心常出现在交叉点附近。” 

奎克指出:“每一幅照片都应保持一定的平衡。这就是说,对主要被摄体的安排,不能使画、面出现向分量大的一边倾倒。但平衡并不意味着将两个同样大小和同等形状的东西置于对称的位置。这里的关系,只要你看一下在玩跷跷板的孩子就知道了。跷跷板两边的孩子,小些的一个必定坐在离中央远些的地方,而大的孩子肯定坐在靠近中央的地方。” 

奎克认为:“把突出的线条安排,指向趣味中心,是拍摄一幅悦目照片的另一个要素。这些线条被称为主导线,有效地利用主导线,可以创造出惊人的照片。把主导线安排成对角线,会产生有力的动感。 

“线条常常在照片中提供一个边框。这可以通过选择视点来进行,例如在树下通过树枝框住画面上的被摄主体。此时通常使用小光圈,以使整幅照片清晰。这样做,也有可能使边框过于突出,但安排适当,会得到好照片。 
“在一幅照片中,地平线的位置会给人以强烈的印象。拍摄时,地平线要尽量避免处在照片的等比线上,因为这样做会把照片均分为两半,给人以呆板的感觉。 

地平线处在画面下方,会给人以宁静的感觉,而处于上方,给人的感觉则是活泼、有力的。 “此外,横幅画面可以产生安宁、平静的感觉,而竖直画面则会产生动态效果。” 

奎克认为,以上方法如果运用得好,你的照片将会出现明显的改观,而且随着时间的推移,你很快就会学会鉴赏构图,从而为你增添摄影的乐趣。 

达柯 认为,有时“如果线条与视觉产生了共鸣,一簇线条本身就能成为一个主体”。

美国摄影家L.小雅各希斯认为:“构图是从摄影者的心灵的眼睛做起的。构图的过程也被称为‘预见’,就是在未拍摄某一物体之前或正在拍摄的时候,就能在脑海中形成一个图像或印象。通过经常析自己和别人的作品的构图,就会使自己的这种预见本领更加娴熟,变成一种本能。”这就是我常说的用脑子拍照。

线条构成画面

奥地利摄影家伊涅斯特·哈斯对此也有同感,对于构图,他己达到手中无剑,剑在心中的境界,他认为、“构图在于平衡,每个人对平衡的处理都各有不同。”正如同武功达到最高境界时,已没有招式名称,只凭自己的功力去化解。他认为:“你越能忘记你的器材,就越能集中你的题材和构图,这时相机只是你眼睛的延续,再没有其他意义。” 小雅各布斯还认为:“构图的最基本的因素是线条、形式、质感以及这些因素之间的空间。当然色彩同样也是不可忽略的因素。会聚的线条一般能说明透视关系,但并不是所有的照片都非要表现出透视的深度不可。许多杰出的作品都是平面图案。在取景器中的某个景物或人物的肖像,都是根据摄影者个人的感受进行安排的。就是所谓摄影里所有的控制是服务于你想表达的情绪。

色彩的平衡很重要

“在大多数情况下,每幅照片中都有一个或一组形状或形式起主导作用,而照片中的色彩、体积、位置和其他形状等,都是为主导因素服务的。……构图中的对比,是指大与小、明与暗、近与远、主动与被动、平滑与粗糙、色彩的浓艳与轻淡等等的对比。要多利用这些相互对立的因素,通过它门使主体影响整个构图。例如,恰当地利用对比法则,照片就会具有强大的魅力。” 

摄影家瓦尔特·德·格鲁伊特对摄影构图作阐述。他认为:“每一种构图都是以排列次序为基础的,就像我们从哥士达心理学和信息学原理中所了解到的那样,构图可以用多种方法获得。它产生于相似事物的组合以及对相反事物的强调。排列次序并不是千篇一律的,形态和色彩的疏密和对比也会产生排列次序。排列次序是以一种美学上的均衡为基础的,而均衡则从复杂、矛盾和动态之中造就和谐。”

线条的引导作用,所有的线条汇聚到中心主体

他说:“任何人,只要懂得把照片分成单个的构图成分,借以得出它在美学上的一致性和合理性来审查照片的具体效果,就都会成功地创作出好的作品。" 

"要获得令人满意的照片构图,须经常分析照片。” 他提出的分析照片构图的最重要标准是: 

1.人物和环境的关系以及反差情况。 2.照片所传递的信息的价值以及类似事物。 3.照明和纵深。4.突出的线条和照片的画幅。

对于如何获得较好的摄影构图,格鲁伊特提出了以下重要建议:

a.画面所提供的信息不能造成视觉上的混乱。 

b.人物和环境的关系要有助于传达照片的意图。 

c.应当避免由于人物和环境之间的含糊关系而可能产生的错觉。

d.明与暗的关系或者彩色对比的关系是非常重要的。 

e.除了人物和环境在形式上的关系之外,对人物和环境的心理上的权衡也是 十分重要的,因为每个视觉印象都立即作出喜欢或不喜欢的判断。无偏好的估价意味着根本没有反应。 

茉莉mm的“光绘”

f.表现与我们熟悉的物体相类似的东西,使人容易辨认,从而能比较迅速地予以理解。因此,重复内容是必要的。

g.照片的复杂程度一定不能太低(感官刺激不够),也不能太高(感官刺激过分) 

h.每个人对每幅照片的美学评价总是不一样的,而且这种评价是受感情支配的。它在很大程度上取决于观众的认识,他的经历和他的敏感性。 

i.形式主义和时髦风尚是不能持久的。这种缺乏独创性的缺点,是不可能用技术补偿的。 

j.照明、透视、重叠和影纹的层次变化,有助于在二维空间的平面上体现出明显的纵深感。

k.不寻常的透视效果,有助于使照片生动活泼。 

l.有意识地使用突出的方向线和选择适合主体的画幅,会加强照片的效果。

更多名家名言和作者发表的个人看法,请访问:

posted @ 2006-09-27 13:49 ericli 阅读(251) | 评论 (0)编辑 收藏

一些摄影方面的术语

CCD:
中文译为"电子耦合组件"(charged coupled device),它就像传统相机的底片一样,是感应光线的电路装置,可以将它想象成一颗颗微小的感应粒子,铺满在光学镜头后方,当光线与图像从镜头透过、投射到ccd表面时,ccd就会产生电流,将感应到的内容转换成数码资料储存起来。ccd的尺寸其实是说感光器件的面积大小,ccd像素数目越多、单一像素尺寸越大,捕获的光子越多,感光性能越好,信噪比越低,收集到的图像就会越清晰。因此,尽管ccd数目并不是决定图像品质的唯一重点,我们仍然可以把它当成相机等级的重要判准之一。

CMOS: 
互补性氧化金属半导体CMOS(Complementary Metal-Oxide Semiconductor)和CCD一样同为在数码相机中可记录光线变化的半导体。CMOS的制造技术和一般计算机芯片没什么差别,主要是利用硅和锗这两种元素所做成的半导体,使其在CMOS上共存着带N(带–电) 和 P(带+电)级的半导体,这两个互补效应所产生的电流即可被处理芯片纪录和解读成影像。同样,CMOS的尺寸大小影响感光性能的效果,面积越大感光性能越好。CMOS的缺点就是太容易出现杂点, 这主要是因为早期的设计使CMOS在处理快速变化的影像时,由于电流变化过于频繁而会产生过热的现象。
光学变焦倍数:光学变焦是依靠光学镜头结构来实现变焦,变焦方式与35mm相机差不多,就是通过摄像头的镜片移动来放大与缩小需要拍摄的景物,光学变焦倍数越大,能拍摄的景物就越远。如今的数码相机的光学变焦倍数大多在2倍-5倍之间,也有一些码相机拥有10倍的光学变焦效果。而家用摄像机的光学变焦倍数在10倍-25倍,能比较清楚地拍到70米外的东西。

光圈:
光圈英文名称为Aperture,光圈是一个用来控制光线透过镜头,进入机身内感光面的光量的装置,也是相机一个极其重要的指标参数,它通常是在镜头内。它的大小决定着通过镜头进入感光元件的光线的多少。表达光圈大小我们是用F值。光圈F值 = 镜头的焦距 / 镜头口径的直径从以上的公式可知要达到相同的光圈F值,长焦距镜头的口径要比短焦距镜头的口径大。

    我们平时所说的光圈值F2.8、F8、F16等是光圈“系数”,是相对光圈,并非光圈的物理孔径,与光圈的物理孔径及镜头到感光器件(胶片或CCD或CMOS)的距离有关。

    当光圈物理孔径不变时,镜头中心与感光器件距离愈远,F数愈小,反之,镜头中心与感光器件距离愈近,通过光孔到达感光器件的光密度愈高,F数就愈大。完整的光圈值系列如下: F1, F1.4, F2, F2.8, F4, F5.6, F8, F11, F16, F22, F32, F44, F64。

噪点:
噪点产生的原因:

1、长时间曝光产生的图像噪音

    这种现像主要大部分出现在拍摄夜景,在图像的黑暗的夜空中,出线了一些孤立的亮点。可以说其原因是由于CCD无法处理较慢的快门速度所带来的巨大的工作量,致使一些特定的像素失去控制而造成的。为了防止产生这种图像噪音,部分数码相机中配备了被称为"降噪"的功能。

    如果使用降噪功能,在记录图像之前就会利用数字处理方法来消除图像噪音,因此在保存完毕以前就需要花费一点额外的时间。

2、用JPEG格式对图像压缩而产生的图像噪音

    由于JPEG格式的图像在缩小图像尺寸后图像仍显得很自然,因此就可以利用特殊的方法来减小图像数据。此时,它就会以上下左右8×8个像素为一个单位进行处理。因此尤其是在8×8个像素边缘的位置就会与下一个8×8个像素单位发生不自然的结合。

    由JPEG格式压缩而产生的图像噪音也被称为马赛克噪音(Block Noise),压缩率越高,图像噪音就越明显。

    虽然把图像缩小后这种噪音也会变得看不出来,但放大打印后,一进行色彩补偿就表现得非常明显。这种图像噪音可以通过利用尽可能高的画质或者利用JPEG格式以外的方法来记录图像而得以解决。

3、模糊过滤造成的图像噪音

    模糊过滤造成的图像噪音和JPEG一样,在对图像进行处理时造成的图像噪音。有时是在数码相机内部处理过程中产生的,有时是利用图像润色软件进行处理时产生的。对于尺寸较小的图像,为了使图像显得更清晰而强调其色彩边缘时就会产生图像噪音。

    所谓的清晰处理就是指数码相机具有的强调图像色彩边缘的功能和图像编辑软件的“模糊过滤(Unsharp Mask)”功能。在不同款式的数码相机中也有一些相机会对整个图像进行色彩边缘的强调。而处理以后就会在原来的边缘外侧出现其他颜色的色线。

   如果将图像尺寸缩小以后用于因特网的话,图像不是总觉得会变得模糊不清吗?此时如果利用“模糊过滤”功能对图像进行清晰处理,图像看起来效果就会好一些。不过由于产生了图像噪音,在进行第二次或第三次处理时,这种图像噪音就显得很麻烦。切忌不要因为处理过度而使图像显得过于粗糙。 

    这里值得一题的是光圈F值愈小,在同一单位时间内的进光量便愈多,而且上一级的进光量刚是下一级的一倍,例如光圈从F8调整到F5.6,进光量便多一倍,我们也说光圈开大了一级。多数非专业数码相机镜头的焦距短、物理口径很小,F8时光圈的物理孔径已经很小了,继续缩小就会发生衍射之类的光学现象,影响成像。所以一般非专业数码相机的最小光圈都在F8至F11,而专业型数码相机感光器件面积大,镜头距感光器件距离远,光圈值可以很小。对于消费型数码相机而言,光圈F值常常介于F2.8 - F16。此外许多数码相机在调整光圈时,可以做1/3级的调整。

posted @ 2006-09-25 15:00 ericli 阅读(253) | 评论 (0)编辑 收藏

SQL优化原则


 SQL优化原则
 
1、使用索引来更快地遍历表。
缺省情况下建立的索引是非群集索引,但有时它并不是最佳的。在非群集索引下,数据在物理上随机存放在数据页上。合理的索引设计要建立在
对各种查询的分析和预测上。一般来说:
a.有大量重复值、且经常有范围查询( > ,< ,> =,< =)和order by、group by发生的列,可考
虑建立群集索引;
b.经常同时存取多列,且每列都含有重复值可考虑建立组合索引;
c.组合索引要尽量使关键查询形成索引覆盖,其前导列一定是使用最频繁的列。索引虽有助于提高性能但不是索引越多越好,恰好相反过多的索引会导致系统低效。用户在表中每加进一个索引,维护索引集合就要做相应的更新工作。
2、在海量查询时尽量少用格式转换。
3、ORDER BY和GROPU BY使用ORDER BY和GROUP BY短语,任何一种索引都有助于SELECT的性能提高。
7、任何对列的操作都将导致表扫描,它包括数据库函数、计算表达式等等,查询时要尽可能将操作移至等号右边。
4、IN、OR子句常会使用工作表,使索引失效。如果不产生大量重复值,可以考虑把子句拆开。拆开的子句中应该包含索引。

Sql的优化原则2:
1、只要能满足你的需求,应尽可能使用更小的数据类型:例如使用MEDIUMINT代替INT
2、尽量把所有的列设置为NOT NULL,如果你要保存NULL,手动去设置它,而不是把它设为默认值。
3、尽量少用VARCHAR、TEXT、BLOB类型
4、如果你的数据只有你所知的少量的几个。最好使用ENUM类型
5、正如graymice所讲的那样,建立索引。
以下是我做的一个实验,可以发现索引能极大地提高查询的效率:

我有一个会员信息表users,里边有37365条用户记录:

在不加索引的时候进行查询:
sql语句A:
select * from users where username like ’%许%’;
在Mysql-Front中的8次查询时长为:1.40,0.54,0.54,0.54,0.53,0.55,0.54 共找到960条记录

sql语句B:
select * from users where username like ’许%’;
在Mysql-Front中的8次查询时长为:0.53,0.53,0.53,0.54,0.53,0.53,0.54,0.54 共找到836条记录

sql语句C:
select * from users where username like ’%许’;
在Mysql-Front中的8次查询时长为:0.51,0.51,0.52,0.52,0.51,0.51,0.52,0.51 共找到7条记录

为username列添加索引:
create index usernameindex on users(username(6));

再次查询:
sql语句A:
select * from users where username like ’%许%’;
在Mysql-Front中的8次查询时长为:0.35,0.34,0.34,0.35,0.34,0.34,0.35,0.34 共找到960条记录

sql语句B:
select * from users where username like ’许%’;
在Mysql-Front中的8次查询时长为:0.06,0.07,0.07,0.07,0.07,0.07,0.06,0.06 共找到836条记录

sql语句C:
select * from users where username like ’%许’;
在Mysql-Front中的8次查询时长为:0.32,0.31,0.31,0.32,0.31,0.32,0.31,0.31 共找到7条记录

在实验过程中,我没有另开任何程序,以上的数据说明在单表查询中,建立索引的可以极大地提高查询速度。
另外要说的是如果建立了索引,对于like ’许%’类型的查询,速度提升是最明显的。因此,我们在写sql语句的时候也尽量采用这种方式查询。

对于多表查询我们的优化原则是:

尽量将索引建立在:left join on/right join on ... +条件,的条件语句中所涉及的字段上。

多表查询比单表查询更能体现索引的优势。

6、索引的建立原则:
如果一列的中数据的前缀重复值很少,我们最好就只索引这个前缀。Mysql支持这种索引。我在上面用到的索引方法就是对username最左边的6个字符进行索引。索引越短,占用的

磁盘空间越少,在检索过程中花的时间也越少。这方法可以对最多左255个字符进行索引。

在很多场合,我们可以给建立多列数据建立索引。

索引应该建立在查询条件中进行比较的字段上,而不是建立在我们要找出来并且显示的字段上

7、限制索引的使用的避归。
7.1  IN、OR子句常会使用工作表,使索引失效。
如果不产生大量重复值,可以考虑把子句拆开。拆开的子句中应该包含索引。这句话怎么理解决,请举个例子

例子如下:
如果在fields1和fields2上同时建立了索引,fields1为主索引
以下sql会用到索引
select * from tablename1 where fields1=’value1’ and fields2=’value2’
以下sql不会用到索引
select * from tablename1 where fields1=’value1’ or fields2=’value2’
7.2 使用IS NULL 或IS NOT NULL
         使用IS NULL 或IS NOT NULL同样会限制索引的使用。因为NULL值并没有被定义。在SQL语句中使用NULL会有很多的麻烦。因此建议开     发人员在建表时,把需要索引的列设成NOT NULL。如果被索引的列在某些行中存在NULL值,就不会使用这个索引(除非索引是一个位图索引,关于位图索引在稍后在详细讨论)。

7.3 使用函数
如果不使用基于函数的索引,那么在SQL语句的WHERE子句中对存在索引的列使用函数时,会使优化器忽略掉这些索引。下面的查询不会使用索引(只要它不是基于函数的索引)
   select empno,ename,deptno
          from   emp
          where  trunc(hiredate)='01-MAY-81';
          把上面的语句改成下面的语句,这样就可以通过索引进行查找。
          select empno,ename,deptno
          from   emp
          where  hiredate<(to_date('01-MAY-81')+0.9999);

7.4 比较不匹配的数据类型
比较不匹配的数据类型也是比较难于发现的性能问题之一。注意下面查询的例子,account_number是一个VARCHAR2类型,在account_number字段上有索引。下面的语句将执行全表扫描。
         select bank_name,address,city,state,zip
         from   banks
         where  account_number = 990354;
         Oracle可以自动把where子句变成to_number(account_number)=990354,这样就限制了索引的使用,改成下面的查询就可以使用索引:
         select bank_name,address,city,state,zip
         from   banks
         where  account_number ='990354';
     特别注意:不匹配的数据类型之间比较会让Oracle自动限制索引的使用,即便对这个查询执行Explain Plan也不能让您明白为什么做了一               次“全表扫描”。

补充:
1.索引带来查询上的速度的大大提升,但索引也占用了额外的硬盘空间(当然现在一般硬盘空间不成问题),而且往表中插入新记录时索引也要随着更新这也需要一定时间.
有些表如果经常insert,而较少select,就不用加索引了.不然每次写入数据都要重新改写索引,花费时间;
这个视实际情况而定,通常情况下索引是必需的.
2.我在对查询效率有怀疑的时候,一般是直接用Mysql的Explain来跟踪查询情况.
你用Mysql-Front是通过时长来比较,我觉得如果从查询时扫描字段的次数来比较更精确一些.

 

 

posted @ 2006-09-13 20:25 ericli 阅读(495) | 评论 (3)编辑 收藏

取当前日期 到 前3个月的sql记录 怎么写

select * from t where 日期字段名字>DATEADD ( mm, -3, getdate())

//t 是你的表名

 

 


现在程序里得到当前时间和3个月前的时间
Date nowDate=new Date();
Date oldDate=new Date();
oldDate.setMonth(oldDate.getMonth()-3);

select * from tab where 日期字段>oldDate and 日期字段<newDate

posted @ 2006-09-13 20:24 ericli 阅读(1930) | 评论 (0)编辑 收藏

Struts初始化

我在几个月前曾经发表过一个帖子,就是和大家一起学习struts源代码。成为一名合格的程序员,阅读大量的优秀程序是必不可少的。只看书是不会让你水平有很大提高的,要多看多写。
本来是打算等下面几篇文章写好后一起发布的,这样大家可能才能看得明白些,但是根据我现在的状况,估计还要一、两个月。所以,为了防止在struts源代码发生过大变化后我的文章就没有太大价值了,所以就提前发表了,霍霍~~~
我的email为:mariah_fan@hotmail.com,有什么不对的地方请大家指正:)
struts作为J2EE的MVC框架已经取得了很大的成功,下面将分几篇文章说明struts源程序的结构。
第一篇  struts的初始化

struts 的核心类是org.apache.struts.action.ActionServlet,这个类将会在struts第一次使用时,
作为servlet初始化并存入tomcat容器。很显然的,初始化将会调用init方法初始化相应的数据。

一、initInternal()方法:
    通过调用MessageResources.getMessageResources(internalName)方法生成一个
    MessageResources类,getMessageResources是通过调用MessageResourcesFactory.
    createResources(config)来实现的。至于MessageResourcesFactory是一个abstract类,任何
    继承自它的类都要实现createResources方法,生成MessageResources对象。整个程序生成
    MessageResourcesFactory使用了如下技巧:
    MessageResourcesFactory.factoryClass = factoryClass;
    MessageResourcesFactory.clazz = null;
    首先会通过factoryClass来定义一个类全名,然后通过ClassLoader.loadClass
    (factoryClass)方法来生成这个类,并赋给clazz,然后通过newInstance来生成一个对象。
    在本程序中,生成MessageResources对象实际就是对如下属性进行了初始化:
    this.factory = factory;("org.apache.struts.util.PropertyMessageResourcesFactory")
    this.config = config;("org.apache.struts.action.ActionResources")
    this.returnNull = returnNull;(true/false)

    对于MessageResources类的作用是根据不同的Locate来格式化相应的string。或者把你需要改变
    的string存放到数组中,然后通过getMessage(Locale locale, String key, Object args[])
    方法来格式化。然后把格式好的string存放到HashMap里,这样就可以为以后重用。这里的key是
    使用的locale.toString() + "." + key

    在PropertyMessageResources中的loadLocale方法用来读取resource的初始化信息。首先它会
    通过一个HashMap检测这个localKey相关的message是否已经被初始化了,如果被初始化过就跳
    出,检测的方法是locales.get(localeKey) != null。
    然后会读取如下一个文件:
    org/apache/struts/action/ActionResources_(localKey).properties,然后进行如下操作:
    Properties props = new Properties();
    ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
    is = classLoader.getResourceAsStream(name);
    props.load(is);
    Iterator names = props.keySet().iterator();
        while (names.hasNext()) {
            String key = (String) names.next();
            if (log.isTraceEnabled()) {
                log.trace("  Saving message key '" + messageKey(localeKey, key));
            }
            messages.put(messageKey(localeKey, key), props.getProperty(key));
    }

    PropertyMessageResources 就是通过上面的loadLocale方法查找与Locale locale, String key
    相对对应的Message.查找的次序如下locale.toString(),然后是
    localeKey = localeKey.substring(0, underscore),然后是defaultLocale,然后是key。

    最后,resource类的结构如下:
    PropertyMessageResources extends MessageResources
    PropertyMessageResourcesFactory extends MessageResourcesFactory

二、initOther()方法:
    从servlet中获取config和debug两个参数,然后初始化ConvertUtils对象。由于
    ConvertUtils.deregister()的初始化,所有的Converter都是有初始值的,所以这里Struts自己
    把这些初始值设置为null,即转换出错的时候返回null,而不是初始值。使用ConvertUtils类的
    原因是由于从form传输过来的都是String类型的值,所以我们要把它们转换成相应的类型。

    提到几个技巧:
    *public boolean isIndexed() {
         if (type == null) {
             return (false);
         //技巧一:判断是否是一个Array类的方法
         } else if (type.isArray()) {
             return (true);
         //技巧二:判断type是否是List的一个父类或者父接口,或者与List为同一个类
         //要注意如果List是另一个primitive的TYPE类,那么type必须也是这个类才会
         //返回true,否则都是false。注意long.TYPE与Long.class是不同的
         } else if (List.class.isAssignableFrom(type)) {
             return (true);
         } else {
            return (false);
         }
     }

    *//componentType为Array类所存储的元素的类别
     Class componentType = indexedProperty.getClass().getComponentType();
     //生成一个新的Array
     Object newArray = Array.newInstance(componentType, (index + 1));
     System.arraycopy(indexedProperty, 0, newArray, 0, length);
     indexedProperty = newArray;
     set(name, indexedProperty);
     int newLength = Array.getLength(indexedProperty);
     for (int i = length; i < newLength; i++) {
        Array.set(indexedProperty, i, createProperty(name+"["+i+"]", componentType));
     }

三、initServlet()方法:
    这个方法主要是通过digester类解析web.xml,对String servletMapping属性进行初始化。对于
    digester说明如下:这是一个基于DOM的SAX实现的类,它是事件触发的,根据xml文件的结构,
    每次读到一个节点元素就会触发一个事件。

    InputStream input = getServletContext().getResourceAsStream("/WEB-INF/web.xml");
    这是一个比较少见的方法。首先通过this.servletName = getServletConfig().
    getServletName()获取servlet的名称,然后根据
    if (servletName.equals(this.servletName)) {
        this.servletMapping = urlPattern;
    }
    来判断当前读到的servlet名称是否是我们运行的servlet的名称,如果是,就把url-pattern作为
    我们的servletMapping。

四、getServletContext().setAttribute(Globals.ACTION_SERVLET_KEY, this)
    把自己存储到servletContext中,属性名为Globals.ACTION_SERVLET_KEY。

五、ModuleConfig moduleConfig = initModuleConfig("", config)
    这个方法使用由initOther()方法获取的config值为要解析的xml路径,用来初始化ModuleConfig。
    它首先采用与生成MessageResourcesFactory同样的方法产生一个MessageResourcesFactory对象:
    MessageResourcesFactory为一个抽象类,每一个继承它的类都要实现
    createModuleConfig(String prefix)方法。本程序使用的缺省的MessageResourcesFactory类为
    org.apache.struts.config.impl.DefaultModuleConfigFactory,它
    的createModuleConfig(String prefix)方法会生成一个ModuleConfigImpl类。

    ModuleConfigImpl类相当于一个JavaBean,用来存放一个web模块运行时所需要的配置信息。当
    然,一个web模块可以拥有多个ModuleConfig,但是缺省的是prefix长度为0的ModuleConifg。它
    的每个属性几乎都是由HashMap组成的,它通过一个configured布尔值来描述当前的ModuleConfig
    是否已经被初始化完毕,在每存放一个属性的时候都会监测这个值。如果初始化完毕而还要改变
    里面的属性值,则会报出IllegalStateException("Configuration is frozen")异常,现在对它
    的属性简单说明如下:
    * protected HashMap actionConfigs:
      这个HashMap用来存储ActionConfig对象。
    * protected HashMap dataSources
      这个HashMap用来存储DataSourceConfig对象。
    * protected HashMap exceptions
      这个HashMap用来存储ExceptionConfig对象。
    * protected HashMap formBeans
      这个HashMap用来存储FormBeanConfig对象。
    * protected HashMap forwards
      这个HashMap用来存储ForwardConfig对象。
    * protected HashMap messageResources
      这个HashMap用来存储MessageResourcesConfig对象。
    * protected ArrayList plugIns
      这个HashMap用来存储PlugInConfig对象。
    * protected ControllerConfig controllerConfig
      ControllerConfig类
    * protected boolean configured
      标志这个ModuleConfig是(true)否(false)配置完成。
    * protected String prefix
      用来标志和区分ModuleConfig类,同时在使用上面的config类初始化相应的资源以后,也是通
      过这个prefix来区分所属的不同的web模块。
    * protected String actionMappingClass = "org.apache.struts.action.ActionMapping"
      ActionMapping类名,缺省为org.apache.struts.action.ActionMapping。

    初始化ModuleConfig的方法如下:
    首先是使用getServletConfig().getInitParameter("mapping")来获取设定的ActionMapping类
    名,然后通过initConfigDigester()方法来生成一个digester。最后用","分隔config,对每一
    块调用parseModuleConfigFile(prefix, paths, config, digester, path)方法解析。注意,这
    个方法实际上只有两个参数是有意义的:path为我们要解析的xml文件,config用来初始化完成
    后保存到servletContext中。

    如果ModuleConfig中存放的FormBeanConfig为Dydamic类型,那么就调用
    DynaActionFormClass.createDynaActionFormClass(FormBeanConfig)初始化
    DynaActionFormClass,并存放到DynaActionFormClass.dynaClasses 的 static HashMap中。这
    里的key为FormBeanConfig.getName() + moduleConfig.getPrefix()。
   
    如果当前的ModuleConfig为缺省的ModuleConfig,那么将会调用如下几个方法:
    defaultControllerConfig(config)
    defaultMessageResourcesConfig(config)
    defaultFormBeansConfig(config)
    defaultForwardsConfig(config)
    defaultMappingsConfig(config)
    在struts1.1以后,这个特例将会被废弃:

    defaultControllerConfig(config)为ControllerConfig通过getInitParameter(s)方法初始化如
    下几个属性:bufferSize,content,locale(true/false),maxFileSize,nocache(true/false)
    ,multipartClass,tempDir。

    defaultMessageResourcesConfig(config)为MessageResourcesConfig通过getInitParameter(s)
    方法初始化如下几个属性:application,factory,null(true/false)。

    其它的几个方法就是获取不同的对象,然后把它们相应的存储到servlet中。关心如下:
    ActionFormBeans=>FormBeanConfig,ActionForwards=>ForwardConfig,
    ActionMappings=>ActionConfig。

六、initModuleMessageResources(ModuleConfig config)
    通过存储在ModuleConfig中的MessageResourcesConfig对象,逐个初始化MessageResource,
    然后再把初始化好的MessageResources存放到ServletContext中,attributeName为
    MessageResourcesConfig.getKey() + ModuleConfig.getPrefix()。

七、initModuleDataSources(ModuleConfig config)
    通过存储在ModuleConfig中的DataSourceConfig对象,逐个初始化DataSource。然后对于每一个
    DateSource通过BeanUtils.populate(ds, dscs[i].getProperties())方法初始化其属性。再把初
    始化好的DateSource存放到ServletContext中,attributeName为
    DataSourceConfig.getKey() + ModuleConfig.getPrefix()。同时也存放到名位dataSources的
    FastHashMap中,key为DataSourceConfig.getKey()。

    这里还会根据生成的DateSource对象是否是GenericDataSource类型,如果是则调用
    GenericDataSource.open()方法。GenericDataSource是一个非常简单的数据库连接池,它的
    open()方法用来初始化连接池,生成最小数目的GenericConnection,这里的open()方法根据
    String driver变量是否为null来判断是否已经被初始化过。需要仔细说明的是getConnection()
    方法,它首先从连接池中取出GenericConnection对象,然后检查其是否是可链接的,如果是就
    返回,否则继续取出,同时activeCount-1。如果没有取到,则会检查当前可使用的
    GenericConnection是否达到最大值(activeCount < maxCount),如果没有,调用
    createConnection()方法声成一个新的GenericConnection,然后检查其是否是可链接,如果可以
    则返回。returnConnection(GenericConnection conn)方法则是通过把GenericConnection放回到
    连接池,然后activeCount-1。

    这个方法中使用到了ServletContextWriter类,DateSource的log信息就通过这个类写入。对这个
    类说明如下:
    它继承自PrintWriter,而PrintWriter又继承自Writer。Writer类所作的事情就是在同步的情况下
    调用abstract方法:abstract public void write(char cbuf[], int off, int len),这个方法
    将会根据调用者的需要由调用者实现。
    PrintWriter则首先通过ensureOpen()方法检验这个类中是否有写入的对象(Writer类或其子类),
    如果有则根据不同的情况调用这个写入对象的write方法(out.write(....))。这个类的print(...)
    方法就是据不同的情况调用相应的write(...)方法。而println(...)与之的区别就是每次多写入一
    个换行字符串。还有一个区别是println(...)会根据是否需要autoflush进行flush,而write(...)
    方法不会。
    ServletContextWriter类的作用是把字符写入ServletContext中。ServletContextWriter类方法中
    真正实现了write方法:
    public void write(char c) {
        if (c == '\n')
            flush();
        else if (c != '\r')
            buffer.append(c);
    }
    public void flush() {
        if (buffer.length() > 0) {
            context.log(buffer.toString());
            buffer.setLength(0);
        }
    }

八、initModulePlugIns(moduleConfig)
    通过存储在ModuleConfig中的PlugInConfig对象,逐个初始化PlugIn对象,存放到一个数组中,
    然后再把这个数组存放到ServletContext中,attributeName为
    Globals.PLUG_INS_KEY + ModuleConfig.getPrefix()。

    对每一个生成的PlugIn对象通过
    BeanUtils.populate(plugIns[i], plugInConfigs[i].getProperties())方法初始化其属性。然后
    再把PlugInConfig对象存放到由其生成的PlugIn对象中。

    最后,通过plugIns[i].init(this, (ModuleConfig) config)初始化这个plugIn对象。

九、初始化结束
    完成了这个初始化以后,会调用ModuleConfig.freeze()令这个ModuleConfig变得不可改变。然后
    会遍历ServletConfig中的initParameterNames,如果有以"config/"开头的,则通过这个parameter
    的值继续初始化其它的ModuleConfig,且这个ModuleConfig的prefix为"config/"后的字符串。
   
    同样调用如下方法:
    initModuleMessageResources(moduleConfig);
    initModuleDataSources(moduleConfig);
    initModulePlugIns(moduleConfig);
    moduleConfig.freeze();

    最后调用destroyConfigDigester()释放内存。

posted @ 2006-09-13 20:24 ericli 阅读(357) | 评论 (0)编辑 收藏

Struts1.1源码解析

Struts1.1b3部分源代码分析.
作者:     文章来源:
访问次数: 次    加入时间:2006-05-12
 
Struts1.1部分源代码分析
一:说明
本文针对Struts1.1b3做分析,主要希望通过对源代码的分析阐述Struts1.1的工作方式。
本文不适合初学者参考,适合具有一定基于Struts开发的程序员参考。
下面的描述;里面将会对ActionServlet,RequestProcessor,ModuleConfig等几个类做一些
说明。以注释源代码的方式,说明取工作流程。
特别申明:Struts1.1代码版权属于Apache遵循The Apache Software License, Version 1.1.
本文版权属于孤魂一笑个人所有,任何个人或组织希望转载,请与我联系。并获得我的授权
方可转载。

二:ActionServlet分析
我们先来看一下使用Struts的配置文件。


action
org.apache.struts.action.ActionServlet


definitions-config
/WEB-INF/tiles-defs.xml,/WEB-INF/tiles-tests-defs.xml,/WEB-INF/tiles-tutorial-defs.xml,
/WEB-INF/tiles-examples-defs.xml


definitions-debug
0


definitions-parser-details
0


definitions-parser-validate
true

 

config
/WEB-INF/struts-config.xml

 

config/examples
/WEB-INF/struts-examples-config.xml

 

config/test
/WEB-INF/struts-tests-config.xml

 

config/tutorial
/WEB-INF/struts-tutorial-config.xml


validate
true


debug
2


detail
2

 

application
org.apache.struts.webapp.tiles.dev1-1.ApplicationResources


2

 


action
*.do

 

接下来我们来看一下ActionServlet的具体使用
javax.servlet.http.HttpServlet
|
|-->org.apache.struts.action.ActionServlet
所以本质上ActionServlet是一个普通的servlet,负责处理.do为后缀的Http请求.
servlet在执行doGet(),doPost(),之前先调用init(),
以下我们先分析一下init()方法
/**
* Initialize this servlet. Most of the processing has been factored into
* support methods so that you can override particular functionality at a
* fairly granular level.

* servlet初始化操作,注意初始化顺序
* @exception ServletException if we cannot configure ourselves correctly
*/
public void init() throws ServletException {
//注意初始化的顺序
//Initialize our internal MessageResources bundle
initInternal();
//Initialize other global characteristics of the controller servlet
//处理一些全局变量的设置如:debug,detail等
initOther();
//Initialize the servlet mapping under which our controller servlet
//is being accessed. This will be used in the &html:form>
//tag to generate correct destination URLs for form submissions
//主要是注册DTD文件以及解析web.xml关于ActionServlet的配置。如后缀名等.
// Configure the processing rules that we need
// digester.addCallMethod("web-app/servlet-mapping",
// "addServletMapping", 2);
// digester.addCallParam("web-app/servlet-mapping/servlet-name", 0);
// digester.addCallParam("web-app/servlet-mapping/url-pattern", 1);
//initServlet()的上面一段将把Struts默认的后缀名从web.xml中解析得到
//也就是web.xml中的如下配置:
//
//action
//*.do
//默认以.do结尾的请求都将由Struts来处理,你可以自己修改
//
initServlet();

// Initialize modules as needed
//在Attribute中保存类实例
getServletContext().setAttribute(Globals.ACTION_SERVLET_KEY, this);
//根据配置文件生成ModuleConfig,这是很重要的一步.下面会专门分析
//在tiles的配置中先解析注释为"Mark 0"的一个配置文件:/WEB-INF/struts-config.xml
//使用initModuleConfig方法解析XML文件.
//参数为prefix:"",paths:"/WEB-INF/struts-config.xml"
ModuleConfig moduleConfig = initModuleConfig("", config);
//初始化Message
initModuleMessageResources(moduleConfig);
//初始化JDBC DataSource
initModuleDataSources(moduleConfig);
//初始化PlunIn
initModulePlugIns(moduleConfig);

moduleConfig.freeze();
//在Struts1.1以后可以使用多个配置文件,在解析完默认的配置文件也就是上面提到的
//注释为"Mark 0"的一个配置文件:/WEB-INF/struts-config.xml后解析其他的配置文件
Enumeration names = getServletConfig().getInitParameterNames();
//依次解析注释为"Mark 1"、"Mark 2"、"Mark 3"对应配置文件
while (names.hasMoreElements()) {
//每一个配置文件的文件名
String name = (String) names.nextElement();
if (!name.startsWith("config/")) {
continue;
}
//
String prefix = name.substring(6);
moduleConfig = initModuleConfig
(prefix, getServletConfig().getInitParameter(name));

initModuleMessageResources(moduleConfig);
initModuleDataSources(moduleConfig);
initModulePlugIns(moduleConfig);
moduleConfig.freeze();
}
destroyConfigDigester();

}
/**
* 此方法使用Digester解析XML,关于使用Digester的介绍参看我的另外一篇文章
*
Initialize the application configuration information for the
* specified module.


*
* @param prefix Module prefix for this module
* @param paths Comma-separated list of context-relative resource path(s)
* for this modules's configuration resource(s)
*
* @exception ServletException if initialization cannot be performed
* @since Struts 1.1
*/
protected ModuleConfig initModuleConfig
(String prefix, String paths) throws ServletException {

if (log.isDebugEnabled()) {
log.debug("Initializing module path '" + prefix +
"' configuration from '" + paths + "'");
}

// Parse the configuration for this module
ModuleConfig config = null;
InputStream input = null;
String mapping = null;
try {
//工厂方法创建ModuleConfig标记为:prefix
ModuleConfigFactory factoryObject =
ModuleConfigFactory.createFactory();
config = factoryObject.createModuleConfig(prefix);

// Support for module-wide ActionMapping type override
mapping = getServletConfig().getInitParameter("mapping");
if (mapping != null) {
config.setActionMappingClass(mapping);
}

// Configure the Digester instance we will use
//得到解析XML的digester
Digester digester = initConfigDigester();

// Process each specified resource path
while (paths.length() > 0) {
//开始解析指定路径文件名的文件
digester.push(config);
String path = null;
//文件是否为多个并且使用","分割
int comma = paths.indexOf(',');
//存在多个配置文件
if (comma >= 0) {
//先解析第一个
path = paths.substring(0, comma).trim();
//paths存放剩余的文件名下次再处理
paths = paths.substring(comma + 1);
} else {
//当前只存在一个
path = paths.trim();
//没有剩余,下次循环根据上面一句将会在唯一的循环退出点"point break"
//退出循环
paths = "";
}
//全部解析完成跳出循环
//point break
if (path.length() < 1) {
break;
}
//根据文件名取文件
URL url = getServletContext().getResource(path);
InputSource is = new InputSource(url.toExternalForm());
input = getServletContext().getResourceAsStream(path);
is.setByteStream(input);
digester.parse(is);
//全局变量的形式把解析生成的ModuleConfig实例保存起来
//如"Mark 1"处标记的"config/examples",
//key为:"org.apache.struts.action.MODULEexamples"
//Globals.MODULE_KEY值为:org.apache.struts.action.MODULE
getServletContext().setAttribute
(Globals.MODULE_KEY + prefix, config);
input.close();
}

} catch (Throwable t) {
log.error(internal.getMessage("configParse", paths), t);
throw new UnavailableException
(internal.getMessage("configParse", paths));
} finally {
if (input != null) {
try {
input.close();
} catch (IOException e) {
;
}
}
}

// Force creation and registration of DynaActionFormClass instances
// for all dynamic form beans we wil be using
//根据ModuleConfig实例得到配置的FormBean的配置
//注意:因为在Struts整个运行当中FormBean的实例要在Action的实例创建之前先创建
//因为Action执行perform(1.1以前),execute(1.1)需要使用到FormBean
FormBeanConfig fbs[] = config.findFormBeanConfigs();
for (int i = 0; i < fbs.length; i++) {
if (fbs[i].getDynamic()) {
DynaActionFormClass.createDynaActionFormClass(fbs[i]);
}
}

// Special handling for the default module (for
// backwards compatibility only, will be removed later)
//下面是生成一些实例
if (prefix.length() < 1) {
defaultControllerConfig(config);
defaultMessageResourcesConfig(config);
defaultFormBeansConfig(config);
defaultForwardsConfig(config);
defaultMappingsConfig(config);
}

// Return the completed configuration object
//config.freeze(); // Now done after plugins init
return (config);

}

到此初始化工作基本结束,下面将处理具体的Http请求。在Servlet协议中所有的post方法将调用
以下方法来处理
public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {}
get方法也调用类似的方法来处理

在Struts中post,get方法都调用同一个方法
process(request, response);来处理具体的请求
如下:
/**
* 对每一个提交过来的Action进行处理
* Perform the standard request processing for this request, and create
* the corresponding response.
*
* @param request The servlet request we are processing
* @param response The servlet response we are creating
*
* @exception IOException if an input/output error occurs
* @exception ServletException if a servlet exception is thrown
*/
protected void process(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
//设置或删除Attribute
RequestUtils.selectModule(request, getServletContext());
//具体的处理交给RequestProcessor去处理HttpRequest,HttpResponse
//这是一个很典型的设计模式
//下面我们将详细来分析RequestProcessor,很容易理解Struts的运行方式
getRequestProcessor(getModuleConfig(request)).process(request, response);
}

三:RequestProcessor分析
通过前面的分析我们知道Struts中对HttpRequest,HttpResponse的处理都交给RequestProcessor
的process()方法来处理。下面我们来看看process方法
/**
*
Process an HttpServletRequest and create the
* corresponding HttpServletResponse.


*
* @param request The servlet request we are processing
* @param response The servlet response we are creating
*
* @exception IOException if an input/output error occurs
* @exception ServletException if a processing exception occurs
*/
public void process(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {

// Wrap multipart requests with a special wrapper
//如果是upload,返回一个MultipartRequestWrapper()
request = processMultipart(request);

// Identify the path component we will use to select a mapping
//对request进行分析得到提交过来的Form Action
String path = processPath(request, response);
if (path == null) {
return;
}
if (log.isInfoEnabled()) {
log.info("Processing a '" + request.getMethod() +
"' for path '" + path + "'");
}

// Select a Locale for the current user if requested
//本地化处理
processLocale(request, response);

// Set the content type and no-caching headers if requested
processContent(request, response);
//设置Cache不保存
processNoCache(request, response);

// General purpose preprocessing hook
if (!processPreprocess(request, response)) {
return;
}

// Identify the mapping for this request
//得到path以后,根据配置文件(struts-config.xml)的相关配置来得到一个
//ActionMapping的实例
//ActionMapping继承ActionConfig
//仔细看一下ActionMapping的代码就能发现:
//下面的一段将解析影射一个ActionMapping的实例
//在ActionServlet在初始化的时候实际上已经把所有的ActionMapping的实例
//都已经创建好了。processMapping方法实际上是从Attribute中去得到已经
//保存好的ActionMapping的实例,可以理解为在Tomcat启动的时候已经
//把所有的ActionMapping保存在Attribute里面。所以在Tomcat启动的时候
//比较慢,如果Struts-config.xml有问题启动就会出错。
/***

type="org.apache.struts.webapp.tiles.test.TestActionTileAction">

 

**/
//****注意得到创建实例的顺序
//ActionMapping实例已经创建好了,现在从Atribute中取到
ActionMapping mapping = processMapping(request, response, path);
if (mapping == null) {
return;
}

// Check for any role required to perform this action
if (!processRoles(request, response, mapping)) {
return;
}

// Process any ActionForm bean related to this request
//根据mapping得到ActionForm的实例。
//同名ActionForm在系统中只会创建一次。
ActionForm form = processActionForm(request, response, mapping);
//压栈
processPopulate(request, response, form, mapping);
//处理校验,调用ActionForm的validate方法
//假如出错将会返回到前一页面
//也就是说在Action还没有创建之前就将做校验
if (!processValidate(request, response, form, mapping)) {
return;
}

// Process a forward or include specified by this mapping
if (!processForward(request, response, mapping)) {
return;
}
if (!processInclude(request, response, mapping)) {
return;
}

// Create or acquire the Action instance to process this request
//在得到ActionMapping、ActionForm的实例后接下来得到Action实例
//实例如果已经创建从Map里面去取如果没有创建一个并保存在Map里面
//对保存Action实例的Map 实现线程同步
Action action = processActionCreate(request, response, mapping);
if (action == null) {
return;
}

// Call the Action instance itself
//在ActionMapping、ActionForm、Action实例创建好以后
//调用Action的execute()方法得到一个ActionForward
//因为所有的可执行Action都必须有override Action的execute()/perform()方法
ActionForward forward =
processActionPerform(request, response,
action, form, mapping);

// Process the returned ActionForward instance
//Action已经正常执行,执行结束后将返回到另外一个页面
processActionForward(request, response, forward);

}

仔细阅读上面的代码,要特别注意ActionMapping、ActionForm、Action的实例是依次创建的。
创建完以后才去执行Action的execute()方法。为什么要依次创建ActionMapping、ActionForm
、Action??????


四:ModuleConfig分析
现在我们很有必要了解一下ModuleConfig这个类,因为太多地方用到了。
实际上ModuleConfig是一个接口有一个实现。org.apache.struts.config.impl.ModuleConfigImpl
具体实现我就没有不要去分析了。我们来看看这个接口。

package org.apache.struts.config;

/**
*
The collection of static configuration information that describes a
* Struts-based module. Multiple modules are identified by
* a prefix at the beginning of the context
* relative portion of the request URI. If no module prefix can be
* matched, the default configuration (with a prefix equal to a zero-length
* string) is selected, which is elegantly backwards compatible with the
* previous Struts behavior that only supported one module.


*
* @author Rob Leland
* @version $Revision: 1.2 $ $Date: 2002/12/22 05:31:14 $
* @since Struts 1.1
*/
public interface ModuleConfig {
/**
* Has this module been completely configured yet. Once this flag
* has been set, any attempt to modify the configuration will return an
* IllegalStateException.
*/
boolean getConfigured();

/**
* The controller configuration object for this module.
*/
ControllerConfig getControllerConfig();
/**
* The controller configuration object for this module.
* @param cc The controller configuration object for this module.
*/

void setControllerConfig(ControllerConfig cc);

/**
* The prefix of the context-relative portion of the request URI, used to
* select this configuration versus others supported by the controller
* servlet. A configuration with a prefix of a zero-length String is the
* default configuration for this web module.
*/
String getPrefix();

/**
* The prefix of the context-relative portion of the request URI, used to
* select this configuration versus others supported by the controller
* servlet. A configuration with a prefix of a zero-length String is the
* default configuration for this web module.
*/
public void setPrefix(String prefix);
/**
* The default class name to be used when creating action mapping
* instances.
*/
String getActionMappingClass();
/**
* The default class name to be used when creating action mapping
* instances.
* @param actionMappingClass default class name to be used when creating action mapping
* instances.
*/

void setActionMappingClass(String actionMappingClass);

/**
* Add a new ActionConfig instance to the set associated
* with this module.
*
* @param config The new configuration instance to be added
*
* @exception java.lang.IllegalStateException if this module configuration
* has been frozen
*/
void addActionConfig(ActionConfig config);

/**
* Add a new DataSourceConfig instance to the set associated
* with this module.
*
* @param config The new configuration instance to be added
*
* @exception java.lang.IllegalStateException if this module configuration
* has been frozen
*/
void addDataSourceConfig(DataSourceConfig config);

/**
* Add a new ExceptionConfig instance to the set associated
* with this module.
*
* @param config The new configuration instance to be added
*
* @exception java.lang.IllegalStateException if this module configuration
* has been frozen
*/
void addExceptionConfig(ExceptionConfig config);

/**
* Add a new FormBeanConfig instance to the set associated
* with this module.
*
* @param config The new configuration instance to be added
*
* @exception java.lang.IllegalStateException if this module configuration
* has been frozen
*/
void addFormBeanConfig(FormBeanConfig config);

/**
* Add a new ForwardConfig instance to the set of global
* forwards associated with this module.
*
* @param config The new configuration instance to be added
*
* @exception java.lang.IllegalStateException if this module configuration
* has been frozen
*/
void addForwardConfig(ForwardConfig config);

/**
* Add a new MessageResourcesConfig instance to the set
* associated with this module.
*
* @param config The new configuration instance to be added
*
* @exception IllegalStateException if this module configuration
* has been frozen
*/
void addMessageResourcesConfig(MessageResourcesConfig config);

/**
* Add a newly configured {@link org.apache.struts.config.PlugInConfig} instance to the set of
* plug-in Actions for this module.
*
* @param plugInConfig The new configuration instance to be added
*/
void addPlugInConfig(PlugInConfig plugInConfig);

/**
* Return the action configuration for the specified path, if any;
* otherwise return null.
*
* @param path Path of the action configuration to return
*/
ActionConfig findActionConfig(String path);

/**
* Return the action configurations for this module. If there are
* none, a zero-length array is returned.
*/
ActionConfig[] findActionConfigs();

/**
* Return the data source configuration for the specified key, if any;
* otherwise return null.
*
* @param key Key of the data source configuration to return
*/
DataSourceConfig findDataSourceConfig(String key);

/**
* Return the data source configurations for this module. If there
* are none, a zero-length array is returned.
*/
DataSourceConfig[] findDataSourceConfigs();

/**
* Return the exception configuration for the specified type, if any;
* otherwise return null.
*
* @param type Exception class name to find a configuration for
*/
ExceptionConfig findExceptionConfig(String type);

/**
* Return the exception configurations for this module. If there
* are none, a zero-length array is returned.
*/
ExceptionConfig[] findExceptionConfigs();

/**
* Return the form bean configuration for the specified key, if any;
* otherwise return null.
*
* @param name Name of the form bean configuration to return
*/
FormBeanConfig findFormBeanConfig(String name);

/**
* Return the form bean configurations for this module. If there
* are none, a zero-length array is returned.
*/
FormBeanConfig[] findFormBeanConfigs();

/**
* Return the forward configuration for the specified key, if any;
* otherwise return null.
*
* @param name Name of the forward configuration to return
*/
ForwardConfig findForwardConfig(String name);

/**
* Return the form bean configurations for this module. If there
* are none, a zero-length array is returned.
*/
ForwardConfig[] findForwardConfigs();

/**
* Return the message resources configuration for the specified key,
* if any; otherwise return null.
*
* @param key Key of the data source configuration to return
*/
MessageResourcesConfig findMessageResourcesConfig(String key);

/**
* Return the message resources configurations for this module.
* If there are none, a zero-length array is returned.
*/
MessageResourcesConfig[] findMessageResourcesConfigs();

/**
* Return the configured plug-in actions for this module. If there
* are none, a zero-length array is returned.
*/
PlugInConfig[] findPlugInConfigs();

/**
* Freeze the configuration of this module. After this method
* returns, any attempt to modify the configuration will return
* an IllegalStateException.
*/
void freeze();

/**
* Remove the specified action configuration instance.
*
* @param config ActionConfig instance to be removed
*
* @exception java.lang.IllegalStateException if this module configuration
* has been frozen
*/
void removeActionConfig(ActionConfig config);

/**
* Remove the specified exception configuration instance.
*
* @param config ActionConfig instance to be removed
*
* @exception java.lang.IllegalStateException if this module configuration
* has been frozen
*/
void removeExceptionConfig(ExceptionConfig config);

/**
* Remove the specified data source configuration instance.
*
* @param config DataSourceConfig instance to be removed
*
* @exception java.lang.IllegalStateException if this module configuration
* has been frozen
*/
void removeDataSourceConfig(DataSourceConfig config);

/**
* Remove the specified form bean configuration instance.
*
* @param config FormBeanConfig instance to be removed
*
* @exception java.lang.IllegalStateException if this module configuration
* has been frozen
*/
void removeFormBeanConfig(FormBeanConfig config);

/**
* Remove the specified forward configuration instance.
*
* @param config ForwardConfig instance to be removed
*
* @exception java.lang.IllegalStateException if this module configuration
* has been frozen
*/
void removeForwardConfig(ForwardConfig config);

/**
* Remove the specified message resources configuration instance.
*
* @param config MessageResourcesConfig instance to be removed
*
* @exception java.lang.IllegalStateException if this module configuration
* has been frozen
*/
void removeMessageResourcesConfig(MessageResourcesConfig config);
}


上面的注释已经非常清晰了。我就不去浪费大家的时间了,再想仔细看就去看他的实现。
其实主要是对HashMap()的处理。

五:ActionMapping分析
最后我们实现很有必要看一下ActionMapping,因为你如果想清楚的了解Struts-config.xml
这个配置文件的作用,你应该要知道ActionMapping
前面已经说过ActionMapping继承ActionConfig
以下就是ActionMapping加的四个方法。
/**
*
Find and return the ExceptionConfig instance defining
* how exceptions of the specified type should be handled. This is
* performed by checking local and then global configurations for the
* specified exception's class, and then looking up the superclass chain
* (again checking local and then global configurations). If no handler
* configuration can be found, return null.


*
* @param type Exception class for which to find a handler
* @since Struts 1.1
*/
public ExceptionConfig findException(Class type) {

}


/**
*
Find and return the ForwardConfig instance defining
* how forwarding to the specified logical name should be handled. This is
* performed by checking local and then global configurations for the
* specified forwarding configuration. If no forwarding configuration
* can be found, return null.


*
* @param name Logical name of the forwarding instance to be returned
*/
public ActionForward findForward(String name) {

}


/**
*
Return the logical names of all locally defined forwards for this
* mapping. If there are no such forwards, a zero-length array
* is returned.
*/
public String[] findForwards() {
}


/**
*

Create (if necessary) and return an {@link ActionForward} that
* corresponds to the input property of this Action.
*
* @since Struts 1.1b2
*/
public ActionForward getInputForward() {

}

还是看以下他的基类ActionConfig吧
public class ActionConfig implements Serializable {

/**
* Has configuration of this component been completed?
*/
protected boolean configured = false;

/**
* The set of exception handling configurations for this
* action, if any, keyed by the type property.
*/
protected HashMap exceptions = new HashMap();

/**
* The set of local forward configurations for this action, if any,
* keyed by the name property.
*/
protected HashMap forwards = new HashMap();

/**
* The module configuration with which we are associated.
*/
protected ModuleConfig moduleConfig = null;

/**
* The request-scope or session-scope attribute name under which our
* form bean is accessed, if it is different from the form bean's
* specified name.
*/
protected String attribute = null;

/**
* Context-relative path of the web application resource that will process
* this request via RequestDispatcher.forward(), instead of instantiating
* and calling the Action class specified by "type".
* Exactly one of forward, include, or
* type must be specified.
*/
protected String forward = null;

/**
* Context-relative path of the web application resource that will process
* this request via RequestDispatcher.include(), instead of instantiating
* and calling the Action class specified by "type".
* Exactly one of forward, include, or
* type must be specified.
*/
protected String include = null;

/**
* Context-relative path of the input form to which control should be
* returned if a validation error is encountered. Required if "name"
* is specified and the input bean returns validation errors.
*/
protected String input = null;

/**
* Fully qualified Java class name of the
* MultipartRequestHandler implementation class used to
* process multi-part request data for this Action.
*/
protected String multipartClass = null;

/**
* Name of the form bean, if any, associated with this Action.
*/
protected String name = null;

/**
* General purpose configuration parameter that can be used to pass
* extra iunformation to the Action instance selected by this Action.
* Struts does not itself use this value in any way.
*/
protected String parameter = null;

/**
* Context-relative path of the submitted request, starting with a
* slash ("/") character, and omitting any filename extension if
* extension mapping is being used.
*/
protected String path = null;

/**
* Prefix used to match request parameter names to form ben property
* names, if any.
*/
protected String prefix = null;

/**
* Comma-delimited list of security role names allowed to request
* this Action.
*/
protected String roles = null;

/**
* Identifier of the scope ("request" or "session") within which
* our form bean is accessed, if any.
*/
protected String scope = "session";

/**
* Suffix used to match request parameter names to form bean property
* names, if any.
*/
protected String suffix = null;

/**
* Fully qualified Java class name of the Action class
* to be used to process requests for this mapping if the
* forward and include properties are not set.
* Exactly one of forward, include, or
* type must be specified.
*/
protected String type = null;

/**
* Should the validate() method of the form bean associated
* with this action be called?
*/
protected boolean validate = true;
}

其实ActionConfig是一个很典型的ValueObject.所以其他的get/set方法我就不写出来了。
看这个代码一定要和struts-config.xml一起来看,根据struts-config.xml去找找
每一段配置文件最终要生成一个ActionConfig,他们之间的对应关系。
如果你想扩展Struts,ActionMapping估计你一定要修改。还有ActionServlet你也要修改。

六:结束语
分析了一些代码下面做一些概述。先来整体的了解一下Struts的工作流程.
在实现一个基于Struts的运用之前我们首先是做环境设置,Struts正常工作需要至少两个
配置文件web.xml,struts-config.xml.
web.xml告诉App Server所有以.do结尾的请求最终提交给ActionServlet去处理。
2就规定ActionServlet是在App Server启动的时候
创建的并且一直存在。
ActionServlet在创建的时候会做如下的工作:
保存一些后面需要使用的实例在Attribute(内存)里面。
根据web.xml的配置解析struts-config.xml文件。
根据struts-config.xml的配置生成ActionMapping实例并且保存。

ActionServlet在生命周期就一直等待Http 请求
每一个.do结尾的Http 请求都由ActionServlet先截获然后根据请求路径得到具体调用那
一个Action去处理,在这之前生成、处理ActionForm。具体知道那一个Action去处理请求
后调用Action的execute()/perform()处理完成,返回。


 

posted @ 2006-09-13 20:23 ericli 阅读(1381) | 评论 (0)编辑 收藏

Toad中出现问题

 toad连接出现问题:
可能是一些环境变量被修改了,比如昨天我碰到的问题
d:\oracle\ora92\BIN 被修改了,在path 里面将其配置好,就可以了
正常的配置信息如下:
 (Oracle Root)
  ORACLE_HOME = d:\oracle\ora92
  ORACLE_SID =
  NLS_LANG = NA
  SQLPATH =
  d:\oracle\ora92\BIN is in system PATH
  d:\oracle\ora92\BIN does exist
  Client dll found oci.dll
  oci.dll version: 9.2.0.1.0  
  (Oracle Root) is valid
HOME0
  ORACLE_HOME_NAME = OraHome92
  ORACLE_HOME = d:\oracle\ora92
  ORACLE_SID = NHOALG
  NLS_LANG = SIMPLIFIED CHINESE_CHINA.ZHS16GBK
  SQLPATH = d:\oracle\ora92\dbs
  d:\oracle\ora92\BIN is in system PATH
  d:\oracle\ora92\BIN does exist
  Client dll found oci.dll
  oci.dll version: 9.2.0.1.0
  HOME0 is valid
$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
同事遇到问题,toad安装好一次后,就打不开了
是版本的问题,换了一个版本就好了
7.6--8.0
 

posted @ 2006-09-13 20:21 ericli 阅读(661) | 评论 (0)编辑 收藏

Eclipse ShortCut

★ Eclipse 3.0 RC2 熱鍵表 (中英對照版,Default) ★

Author:sungo
Date:2004/06/16 (三)


◎閱讀說明:冒號 ( : )後的藍色標註熱鍵為Eclipse預設的標準熱鍵,空
白即表示Eclipse並未對該項目設熱鍵;紅色標註選項表示常用或重要熱
鍵;淡粉紅色標註選項,表示可以啟動該熱鍵的範圍。

◎熱鍵修改處:
Window->Preference->Workbench->Keys

◎FAQ,如果沒設熱鍵的功能要如何啟動?
方法一.自己增設熱鍵。
方法二.熱鍵設定裡的選項,其實功能表大部分都有,可以由功能表中直接點選。

◎給所有在看這篇文章的朋友:
T55555為了JavaWorld週年慶把DG2拿出來了,為了效法他的精神,所以我決定把
我的第一次也貢獻出來,加入JavaWorld這半年來我第一次打這麼長的文章...XD
。本來打算3.0 Release出來後再整理熱鍵表,但3.0 RC系列架構已經趨於平穩,
Release出來也不會有多大的變動,所以就先整理了。熱鍵表所列的功能,只要您
全部實際操作過一遍,將會為您在操作Eclipse上,帶來莫大的助益。

-------------------------------------------------------------------------------------------------------------

Compare - 比較
Copy All from Left to Right (由左向右複製全部)
Copy All from Right to Left (由右向左複製全部)
Copy from Left to Right (由左向右複製)
Copy from Right to Left (由右向左複製)
Select Next Change (選取下一個變更)
Select Previous Change (選取上一個變更)

CVS - Concurrent Versions System (版本控制系統)
Checkout from CVS (由CVS檢查)

Edit - 編輯
Add Bookmark (新增書籤)
Add Task (新增作業)
Collapse (程式碼折疊) : Ctrl+鍵盤右方數字區的'-' <Editing Text>
Content Assist (內容輔助 - Code Templetes啟動): Alt+/ 或 Ctrl+Space <In Dialogs and Windows>
Context Information (內容資訊) : Alt+? 或 ALT+Shift+?或 Ctrl+Shift+Space <In Windows>
Copy (複製選取文字到OS剪貼簿) : Ctrl+C <In Dialogs and Windows>
Custom Encoding (自訂編碼)
Cut (剪下選取文字並加至OS剪貼簿) : Ctrl+X <In Dialogs and Windows>
Default Encoding (使用預設編碼)
Delete (刪除選取的文字) : Delete <In Windows>
Expand (程式碼展開) : Ctrl+鍵盤右方數字區的'+' <Editing Text>
Expand All (程式碼全部展開) : Ctrl+鍵盤右方數字區的'*' <Editing Text>
Find and Replace (尋找並取代文字) : Ctrl+F <In Windows>
Find Next (尋找下一個項目) : Ctrl+K <Editing Text>
Find Previous (尋找上一個項目) : Ctrl+Shift+K <Editing Text>
Incremental Find (漸進式尋找) : Ctrl+J <Editing Text>
Incremental Find Reverse (逆向漸進式尋找) : Ctrl+Shift+J <Editing Text>
ISO-8859-1 Encoding (將編碼變更為 ISO-8859-1)
Paste (從OS剪貼簿貼上文字) : Ctrl+V <In Dialogs and Windows>
Quick Diff Toggle (QuickDiff 開啟/關閉) : Ctrl+Shift+Q <Editing Text>
Quick Fix (快速套件修正) : Ctrl+1 <Editing Java Source>
Redo (重做上一次作業) : Ctrl+Y <In Windows>
Restore Last Selection (恢復最後選取) : Alt+Shift+方向鍵'↓' <Editing Java Source>
Revert to Saved (回復至已儲存)
Select All (選擇全部文字) : Ctrl+A <In Dialogs and Windows>
Select Enclosing Element : (選取含括的元素) Alt+Shift+方向鍵'↑' <Editing Java Source>
Select Next Element (選取下一個元素) : Alt+Shift+方向鍵'→' <Editing Java Source>
Select Previous Element (選取上一個元素) : Alt+Shift+方向鍵'←' <Editing Java Source>
Shift Left (將該行文字往左移動一個縮排)
Shift Right (將該行文字往右移動一個縮排)
Show Tooltip Description (在游標位置顯示元素的工具提示說明) : F2 <Editing Java Source>
System Encoding (將編碼變更為系統編碼)
Toggle Folding (程式碼折疊、展開功能 開啟/關閉) : Ctrl+鍵盤右方數字區的'/' <Editing Text>
Toggle Insert Model (Smart Insert模式 開啟/關閉) : Ctrl+Shift+Insert <Editing Text>
Undo (復原前次作業) : Ctrl+Z <In Windows>
US ASCII Encoding (將編碼變更為 ASCII)
UTF-16BE Encoding (將編碼變更為 UTF-16BE)
UTF-16 Encoding (將編碼變更為 UTF-16)
UTF-16LE Encoding (將編碼變更為 UTF-16LE)
UTF-8 Encoding (將編碼變更為 UTF-8)

File - 檔案
Close (關閉檔案) : Ctrl+W 或 Ctrl+F4 <In Windows>
Close All (關閉全部檔案) : Ctrl+Shift+W 或 Ctrl+Shift+F4 <In Windows>
Exit (關閉螢幕最上層視窗) : Alt+F4 <In Dialogs and Windows>   0
(註:3.0RC2並沒預設這組熱鍵,但仍然可以使用。)

Export (開啟匯出功能表)
Import (開啟匯入功能表)
Move (移動檔案)
New (開啟檔案建立精靈Wizard) : Ctrl+N <In Windows>
New menu (顯示File->New選單) : ALT+Shift+N <In Windows>
Open External File (開啟外部檔案)
Open Workspace (開啟Workspace設定視窗)
Print (列印檔案) : Ctrl+P <In Windows>
Properties (org.eclipse.ui.file.properties) (顯示檔案內容) : Alt+Enter <In Windows>
Refresh (重新整理) : F5 <In Windows>
Rename (重新命名) : F2 <In Windows>
Revert (回復至已儲存)
Save (儲存檔案) : Ctrl+S <In Windows>
Save All (儲存全部檔案) : Ctrl+Shift+S <In Windows>
Save As (另存新檔)

Help - 說明
About (開啟關於對話框)
Help Contents (開啟Eclipse說明文件)
Tips and Tricks (開啟要訣和技巧說明頁面)
Weclome (開啟歡迎使用頁面)

Navigate - 導覽
Back (向後導覽)
Backward History (在編輯器導覽歷程中向後移動) : Alt+方向鍵'←' <In Windows>
Forward (向前導覽)
Forward History (在編輯器導覽歷程中向前移動) : Alt+方向鍵'→' <In Windows>
Go Into (進入選取的目錄內)
Go to Line (移至某一行文字) : Ctrl+L <Editing Text>
Go to Matching Bracket (將游標移至對稱的方括弧) : Ctrl+Shift+P <Editing Java Source>
Go to Next Member (移至下一個Member) : Ctrl+Shift+方向鍵'↓' <Editing Java Source>
Go to Package (移至套件,用於Package Explorer)
Go to Previous Member (移至上一個Member) : Ctrl+Shift+方向鍵'↑' <Editing Java Source>
Go to Resource (移至資源)
Go to Type (移至類別,用於Package Explorer)
Last Edit Loaction (移至前次編輯位置) : Ctrl+Q <In Windows>
Next (移至下一個標示元素) : Ctrl+. <In Windows>
(註:元素類型可由Toolbar的Next Annotation小倒三角圖示設定。)

Open Call Hierarchy (開啟Call Hierarchy視窗) : Ctrl+Alt+H <In Windows>
Open Declaration (瀏覽所選取的Java元素) : F3 <In Windows> (註:效果等於Ctrl+滑鼠左鍵點選。)
Open External Javadoc (開啟外部Javadoc) : Shift+F2 <In Windows>
Open Resource (開啟資源選取對話方塊) : Ctrl+Shift+R <In Windows>
Open Structure (顯示所選元素的結構) : Ctrl+F3 <Editing Java Source>
Open Super Implementation (開啟super類型中的實作)
Open Type (開啟類別) : Ctrl+Shift+T <In Windows>
Open Type Hierarchy (開啟類別階層顯示視窗) : F4 <In Windows>
Open Type in Hierarchy (在類別階層的視圖中開啟類別) : Ctrl+Shift+H <In Windows>
Previous (移至上一個標示元素) : Ctrl+, <In Windows>
(註:元素類型可由Toolbar的Previous Annotation小倒三角圖示設定。)

Quick Hierarchy (以浮動方式將類別階層視窗顯示在編輯器上) : Ctrl+T <Editing Java Source>
Quick Outline (以浮動方式將概要視窗顯示在編輯器上) : Ctrl+O <Editing Java Source>
Show In menu (顯示當前Show In功能表可用的選項) : Alt+Shift+W <In Windows>
Show in Package Explorer (在套件瀏覽器中顯示所選元素)
Up (導覽上一層)

Project - 專案
Build All (建置所有專案) : Ctrl+B <In Windows>
Build Clean (清除專案)
Build Project (建置專案)
Close Project (關閉專案)
Generate Javadoc (產生Javadoc)
Open Project (開啟專案)
Properties (org.eclipse.ui.project.properties) (開啟專案屬性設定視窗)
Rebuild All (重新建置所有專案)
Rebuild Project (重新建置專案)
Repeat Working Set Build (重複建置所設定的專案) (註:Build Automatically 關閉時才可使用。)

Refactor - Java (重構 - Java)
Change Method Signature (變更方法簽章) : Alt+Shift+C <In Windows>
Convert Anonymous Class to Nested (將匿名類別轉換成巢狀類別)
Convert Local Variable to Field (將區域變數轉換成欄位) : Alt+Shift+F <Editing Java Source>
Encapsulate Field (封裝欄位:為欄位建立getting和setting方法,並只用這些方法來存取欄位)
Extract Constant (擷取成常數:將數字擷取至新的Static欄位,並使用新的Static欄位)

註:<-START->

如下段程式碼經由Extract Constant重構數字10後...
12345
 public class Test {
    public static void main(String[] args) {
        int x = 10;
    }
}

 
會變成下列程式碼:(常數欄位名稱,在重構時可以自由設定。)123456
 public class Test {
    public static final int TEN = 10;
    public static void main(String[] args) {
        int x = TEN;
    }
}

 
註:<-END->

Extract Interface (擷取成介面:將原類別內的方法擷取至新介面中,原類別將實作該介面)
(註:新介面會存成一個新的*.java檔。若是在interface中執行此功能,舊介面則會繼承新介面。)
Extract Local Variable (擷取成區域變數:將數字擷取為新的區域變數) : Alt+Shift+L <In Windows>
Extract Method (擷取成方法:將選取的表示式擷取成一個新方法) : Alt+Shift+M <In Windows>
Generalize Type (將reference的類別型態更改可以取代的Superclass)
Inline (列入常數、區域變數或方法) : Alt+Shift+I <In Windows>
Introduce Factory (採用代理:利用方法來取得建構子)

註:<-START->

如下段程式碼經由Introduce Factory重構Test建構子後...
123456789
 public class Test {
    private int number;
    Test(int x) {
        number = x;
    }
    public static void main(String[] args) {
        System.out.println(new Test(5).number);
    }
}

 
會變成下列程式碼:123456789101112
 public class Test {
    private int number;
    public Test(int x) {
        number = x;
    }
    public static void main(String[] args) {
        System.out.println(createTest(5).number);
    }
    public static Test createTest(int x) {
        return new Test(x);
    }
}

 
註:<-END->

Introduce Parameter (採用參數:將區域變數改成參數方式傳遞)

註:<-START->

如下段程式碼經由Introduce Parameter重構數字5後...
12345
 public class Test {
    public static void main(String[] args) {
        int x = 5;
    }
}

 
會變成下列程式碼:12345
 public class Test {
    public static void main(String[] args, int five) {
        int x = five;
    }
}

 
註:<-END->

Move Member Type to New File (將類別成員移至一個新檔)
Move - Refactoring (移動 - 重構:將所選取的元素移至新位置) : Alt+Shift+V <In Windows>
Pull Up (將類別成員<欄位與方法>上推到類別的父類別中)
Push Down (將類別成員(欄位與方法)下推到類別的子類別中)
Redo - Refactoring (重做 - 重構:重做前次的重構) : Alt+Shift+Y <In Windows>
Refactor Quick Menu (在編輯區顯示重構的快速功能表) : Alt+Shift+T <In Windows>
Rename - Refactoring (更名 - 重構:將所選取的元素重新命名,類別名稱、區域變數名稱等...) :
Alt+Shift+R <In Windows>
Undo - Refactoring (復原 - 重構:復原前次的重構) : Alt+Shift+Z <In Windows>
Use Supertype Where Possible (啟動「適當時使用父類別」重構對話框。)

◎補充命名規則:(重構時不照此規則命名時,Eclipse將會警示,
雖然可以不管警示,但這是個好風格。)

1.介面(interface)名稱:開頭大寫。
2.區域變數(Local Variable)名稱:開頭小寫 。
3.常數欄位(public static final int...)名稱:全部大寫。

Run/Debug - 執行/除錯
Add Class Load Breakpoint (新增一個類別載入中斷點))
Add Java Exception Breakpoint (新增一個Java異常狀況中斷點)
Debug... (開啟除錯啟動配置對話框)
Debug Last Launched (開啟最近一次啟動作業的除錯模式) : F11 <In Windows>
Display (org.eclipse.jdt.debug.ui.commands.Display) (顯示) : Ctrl+Shift+D <In Windows>
(註:當執行緒暫停時,此指令會使用「顯示」檢視畫面,顯示在該執行緒之堆疊框或變數的環境定義下
,評估所選表示式的結果。)

Execute (執行:開啟執行啟動配置對話框) : Ctrl+U <In Windows>
Inspect (觀察): Ctrl+Shift+I <In Windows>
(註:當執行緒暫停時,此指令會使用「表示式」檢視畫面,顯示在該執行緒之堆疊框或變數的環境定義
下,視察所選表示式或變數的結果。)

Profile Last Launched (最近一次作業的概況)
Resume (繼續執行緒的執行) : F8 <Debugging>
Run Last Launched (在執行模式下迅速執行最近一次的啟動作業): Ctrl+F11 <In Windows>
Run Last Launched External Tool (執行前一次啟動的外部工具)
Run to Line (執行至指定行,可在沒有設定中斷點之處暫停執行) : Ctrl+R <Debugging>
Skip All Breakpoints (在不清除中斷點的情況下,執行時跳過所有的中斷點)
Step Into (進入副程序) : F5 <Debugging>
Step Into Selection (進入目前所選的陳述式進行副程序除錯) : Ctrl+F5 <Debugging>
Step Over (跳過副程序) : F6 <Debugging>
Step Return (跳出副程序): F7 <Debugging>
Suspend (暫停執行緒)
Terminate (終止執行緒)
Toggle Breakpoint (新增/移除,中斷點) : Ctrl+Shift+B <In Windows>
Toggle Method Breakpoint (新增/移除,方法中斷點)
Toggle Step Filters (以過濾條件逐步除錯) : Shift+F5 <In Windows>
Toggle Watchpoint (新增/移除,欄位的監視點)

Search - 搜索
Declaration in Hierarchy (在它的階層中搜尋所選元素的宣告)
Declaration in Project (在專案中搜尋所選元素的宣告)
Declaration in Working Set (在工作集中搜尋所選元素的宣告)
Declaration in Workspace (在工作區中搜尋所選元素的宣告) : Ctrl+G <In Windows>
Exception Occurrences (搜索例外事件)
File Search (開啟搜尋對話框的檔案搜尋頁面)
Implementors in Project (在專案中搜尋所選介面的實作者)
Implementors in Working Set (在工作集中搜尋所選介面的實作者)
Implementors in Workspace (在工作區中搜尋所選介面的實作者)
Occurrences in File (在它的宣告檔案中搜尋所選元素的所有出現處) : Ctrl+Shift+U <In Windows>
Open Search Dialog (開啟搜尋對話框) : Ctrl+H <In Windows>
Read Access in Hierarchy (在它的階層中搜尋所選元素的讀取參考)
Read Access in Project (在專案中搜尋所選元素的讀取參考)
Read Access in Working Set (在工作集中搜尋所選元素的讀取參考)
Read Access in Workspace (在工作區中搜尋所選元素的讀取參考)
References in Hierarchy (在它的階層中搜尋所選元素的參考)
References in Project (在專案中搜尋所選元素的參考)
References in Working Set (在工作集中搜尋所選元素的參考)
References in Workspace (在工作區中搜尋所選元素的參考) : Ctrl+Shift+G <In Windows>
Referring Tests (查詢測試)
Write Access in Hierarchy (在它的階層中搜尋所選元素的寫入參考)
Write Access in Project (在專案中搜尋所選元素的寫入參考)
Write Access in Working Set (在工作集中搜尋所選元素的寫入參考)
Write Access in Workspace (在工作區中搜尋所選元素的寫入參考)

Source - 程式碼
Add Block Comment (將選取的字行以多行註解/**/包起來) : Ctrl+Shift+/ <Editing Java Source>
Add Constructors from Superclass (從父類別增加一個建構子)
Add Import (為目前所選的類別參考建立一項匯入宣告) : Ctrl+Shift+M <Editing Java Source>
Add Javadoc Comment (新增Javadoc註解) : Alt+Shift+J <In Windows>
Comment (註解)
Externalize Strings (開啟「外部化字串」精靈)
Find Strings to Externalize (尋找要外部化的字串)
Format (程式碼自動排版) : Ctrl+Shift+F <Editing Java Source>
Format Element (格式化元素)
Generate Constructor using Fields (使用欄位來產生建構子)

註:<-START->

如下段程式碼,選取欄位名稱money並經由Generate Constructor using Fields指令後...
12345
 public class Test {
    int money;
    public static void main(String[] args) {
    }
}

 
會變成下列程式碼:12345678
 public class Test {
    int money;
    public Test(int money) {
        this.money = money;
    }
    public static void main(String[] args) {
    }
}

 
註:<-END->

Generate Delegate Methods (開啟「產生委派方法」對話框,可新增類型欄位的委派方法)
Generate Getters and Setters (開啟「產生Getter與Setter」對話框,可以為欄位自動
建立Getter和Setter方法)
Indent Line (單行縮排,其會遵照Formatter設定的格式) : Ctrl+I <Editing Java Source>
Mark Occurrences (標註事件): Alt+Shift+O <Editing Java Source>
Organize Imports (組織匯入) : Ctrl+Shift+O <In Windows>
(註:這是個很方便的功能,簡易說明一下使用時機:假設我們在程式中打,JFrame src;而並未
import任何swing類別,此時Eclipse便會出現錯誤警示,用紅底線將JFrame標起來,此時只要按
下Ctrl+Shift+O,Eclipse便會自動將:import javax.swing.JFrame;加到程式碼中了。)

Override/Implement Methods (開啟「覆寫/實作方法」對話框,可覆寫或實作現行類別中的方法)
Remove Block Comment (移除多行註解/**/) : Ctrl+Shift+\ <Editing Java Source>
Remove Occurrence Annotations (移除事件書籤): Alt+Shift+U <Editing Java Source>
Sort Members (排序成員)
Source Quick Menu (在編輯區顯示Source的快速功能表) : Alt+Shift+S <In Windows>
Surround with try/catch Block (以try/catch區塊包覆所選文字)
Toggle Comment (加上單行註解/取消單行註解) : Ctrl+/ 或 Ctrl+7 或 Ctrl+Shift+C
<Editing Java Source>
Uncomment (取消註解)

◎補充,Source 隱藏熱鍵:
1.Shift Right (向右移位,增加目前所選字行的內縮層次) : Tab <Editing Java Source>
2.Shift Left (向左移位,減少目前所選字行的內縮層次) : Shift+Tab <Editing Java Source>

Team - 團隊
Synchronize (同步化)

Text Editing - 文字編輯
Claer Mark (清除標記)
Copy Lines (將選取的文字,複製成新行) : Ctrl+Alt+方向鍵'↓' <Editing Text>
Cut Line (剪下單行文字)
Cut to Beginning of Line (剪下文字,範圍為選取處至單行開頭)
Cut to End of Line (剪下文字,範圍為選取處至單行尾端)
Delete Line (刪除單行文字) : Ctrl+D <Editing Text>
Delete Next (刪除下一行)
Delete Next Word (刪除下一個字組) : Ctrl+Delete <Editing Text>
Delete Previous (刪除前一行)
Delete Previous Word (刪除前一個字組) : Ctrl+Backspace <Editing Text>
Delete to Beginning of Line (刪除文字,範圍為選取處至單行開頭)
Delete to End of Line (刪除文字,範圍為選取處至單行尾端)
Duplicate Lines (將選取的文字,複製成新行) : Ctrl+Alt+方向鍵'↑' <Editing Text>
Insert Line Above Current Line (在游標處的該行上方插入新行) : Ctrl+Shift+Enter <Editing Text>
Insert Line Below Current Line (在游標處的該行下方插入新行) : Shift+Enter <Editing Text>
Line Down (游標往下移一行)
Line End (游標移至該行尾端)
Line Start (游標移至該行開頭)
Line Up (游標往上移一行)
Move Lines Down (將該行文字往下移動) : Alt+方向鍵'↓' <Editing Text>
Move Lines Up (將該行文字往上移動) : Alt+方向鍵'↑' <Editing Text>
Next Column (游標移至下一列)
Next Word (游標移至下一個字組) : Ctrl+方向鍵'→' <Editing Text>
Page Down (移至下一頁)
Page Up (移至上一頁)
Previous Column (游標移至上一列)
Previous Word (游標移至上一個字組) : Ctrl+方向鍵'←' <Editing Text>
Scroll Line Down (向下捲動) : Ctrl+方向鍵'↓' <Editing Text>
Scroll Line Up (向上捲動) : Ctrl+方向鍵'↑' <Editing Text>
Select Line Down (選取下一行)
Select Line End (選取至文字尾端)
Select Line Start (選取至文字開頭)
Select Line Up (選取上一行)
Select Next Column (選取下一列)
Select Next Word (選取下一個字組): Ctrl+Shift+方向鍵'→' <Editing Text>
Select Page Down (選取下一頁)
Select Page Up (選取上一頁)
Select Previous Column (選取上一列)
Select Previous Word (選取前一個字組) : Ctrl+Shift+方向鍵'←' <Editing Text>
Select Text End (選取至文字編輯器尾端)
Select Text Start (選取至文字編輯器開頭)
Select Window End (選取至視窗尾端)
Select Window Start (選取至視窗開頭)
Set Mark (設定標記)
Swap Mark (交換標記)
Text End (游標移至文字編輯器最底端)
Text Start (游標移至文字編輯器最頂端)
Toggle Overwrite (覆寫/插入 模式選擇) : Insert <Editing Text>
To Lower Case (將選取的英文字轉為小寫) : Ctrl+Shift+Y <Editing Text>
To Upper Case (將選取的英文字轉為大寫) : Ctrl+Shift+X <Editing Text>
Window End (將游標移至視窗尾端)
Window Start (將游標移至視窗開頭)

Views - 檢視
Ant (開啟Ant檢視視窗)
Bookmarks (開啟書籤檢視視窗)
Breakpoints (開啟中斷點檢視視窗)
Classic Search (開啟傳統的搜索檢視視窗)
Console (開啟主控台檢視視窗)
CVS Annotate (開啟CVS Annotate檢視視窗)
CVS Editors (開啟CVS Editor檢視視窗)
CVS Repositories (開啟CVS Repositories檢視視窗)
CVS Resource History (開啟CVS Resource History檢視視窗)
Debug (開啟除錯檢視視窗)
Display (org.eclipse.jdt.debug.ui.DisplayView) (開啟除錯-顯示檢視視窗)
Error Log (開啟錯誤記錄檢視視窗)
Expressions (開啟除錯-表示式檢視視窗)
Java Call Hierarchy (開啟Call Hierarchy檢視視窗)
Java Declaration (開啟宣告檢視視窗)
Javadoc (開啟Javadoc檢視視窗)
Java Members (開啟類別成員檢視視窗)
Java Package Explorer (開啟套件瀏覽器) : Alt+Shift+Q,P <In Windows>
Java Packages (開啟Java套件檢視視窗)
Java Projects (開啟Java專案檢視視窗)
Java Type Hierarchy (開啟類別階層檢視視窗) : Alt+Shift+Q,T <In Windows>
Java Types (開啟Java類別檢視視窗)
JUnit (開啟JUnit檢視視窗)
Memory (開啟除錯-記憶體檢視視窗)
Navigator (開啟導覽器)
Outline (開啟概要檢視視窗) : Alt+Shift+Q,O <In Windows>
Plug-in Dependencies (開啟Plug-in Dependencies檢視視窗)
Plug-in Registry (開啟Plug-in Registry檢視視窗)
Plug-ins (開啟Plug-ins檢視視窗)
Problems (開啟問題檢視視窗) : Alt+Shift+Q,X <In Windows>
Progress (開啟執行進度檢視視窗)
Properties (org.eclipse.ui.views.Property/Sheet) (開啟屬性檢視視窗)
Registers (開啟除錯-暫存器檢視視窗)
Search (開啟搜索檢視視窗) : Alt+Shift+Q,S <In Windows>
Synchronize (開啟同步化檢視視窗) : Alt+Shift+Q,Y <In Windows>
Tasks (開啟作業檢視視窗)
Threads and Monitors (開啟除錯-執行緒檢視視窗)
Variables (開啟除錯-變數檢視視窗)

◎補充:Views 檢視視窗的開啟處:
Window->Show View->Other。

Window - 視窗
Activate Editor (啟動編輯器) : F12 <In Windows>
Close All Perspectives (關閉全部視景)
Close Perspective (關閉視景)
Customize Perspective (自訂視景)
Hide Editors (隱藏編輯器)
Lock the Toolbars (鎖定工具列)
Maximize Active View or Editor (編輯區最大化) : Ctrl+M <In Windows>
Next Editor (切換至下一個編輯器) : Ctrl+F6 <In Windows>
Next Perspective (切換至下一個視景) : Ctrl+F8 <In Windows>
Next View (切換至下一個視圖) : Ctrl+F7 <In Windows>
Open Editor Drop Down (以浮動的方式在編輯區,顯示快速切換編輯器功能表) : Ctrl+E <In Windows>
Pin Editor (固定編輯器)
Preferences (開啟偏愛設定)
Previous Editor (切換至上一個編輯器) : Ctrl+Shift+F6 <In Windows>
Previous Perspective (切換至上一個視景) : Ctrl+Shift+F8 <In Windows>
Previous View (切換至上一個視圖) : Ctrl+Shift+F7 <In Windows>
Reset Perspective (重新設定視景)
Save Perspective As (儲存為新視景)
Show Ruler Context Menu (顯示尺規的內容功能表) : Ctrl+F10 <Editing Text>
Show Selected Element Only (僅顯示所選元素的程式碼)
Show System Menu (顯示系統功能表) : Alt+- <In Windows>
Show View Menu (顯示視圖功能表) : Ctrl+F10 <In Windows>
Switch to Editor (切換至編輯器) : Ctrl+Shift+E <In Windows>

posted @ 2006-09-13 20:20 ericli 阅读(1468) | 评论 (0)编辑 收藏

J2EE开发之常用开源项目介绍--转载(学习)

主要还是以Spring为核心,也总结了一些以前web开发常用的开源工具和开源类库
 
1持久层:       
1)Hibernate
这个不用介绍了,用的很频繁,用的比较多的是映射,包括继承映射和父子表映射
对于DAO在这里介绍个在它基础上开发的包bba96,目前最新版本是bba96 2.0它对Hibernate进行了封装, 查询功能包括执行hsql或者sql查询/更新的方法,如果你要多层次逻辑的条件查询可以自己组装QueryObject.可以参考它做HibernateDAO.也可以直接利用它
2) iBATIS
另一个ORM工具,没有Hibernate那么集成,自由度比较大,所以使用时普遍性能上比Hibernate要快一些.
2:SpringMVC
       原理说明和快速入门:
       配置文件为:
Spring的配置文件默认为WEB-INF/xxxx-servelet.xm其中xxx为web.xml中org.springframework.web.servlet.DispatcherServlet的servlet-name。
       Action分发:
Spring将按照配置文件定义的URL,Mapping到具体Controller类,再根据URL里的action= xxx或其他参数,利用反射调用Controller里对应的Action方法。
输入数据绑定:
Spring提供Binder 通过名字的一一对应反射绑定Pojo,也可以直接从request.getParameter()取数据。
输入数据验证
Sping 提供了Validator接口当然还可以使用开源的Commons-Validaor支持最好
Interceptor(拦截器)
Spring的拦截器提供接口需要自己编写,在这点不如WebWork做的好.全面
       (这里提一下WebWork和Struts的区别最主要的区别在于WebWork在建立一个Action时是新New一个对象而Struts是SingleMoule所有的都继承它的一个Action,所以根据项目需要合适的选择.)
3:View层
1) 标签库:JSP2.0/JSTL
由于Webwork或Spring的标签确实很有限,一般view层用JSTL标签,而且据说JSTL设计很好速度是所有标签中最快的使用起来也很简单
 
2) 富客户端:DOJO Widgets, YUI(YahooUI),FCKEditor, Coolest日历控件
Dojo主要提供Tree, Tab等富客户端控件,可以用其进行辅助客户端开发
YahooUI和DOJO一样它有自己的一套javascript调试控制台,主要支持ajax开发也有很多Tree,Table,Menu等富客户端控件
FCKEditor 最流行的文本编辑器
Coolest日历控件 目前很多日历控件可用,集成在项目中也比较简单,这个只是其中的一个,界面不错的说..
 
3) JavaScript:Prototype.js
Prototype.js作为javascript的成功的开源框架,封装了很多好用的功能,通过它很容易编写AJAX应用,现在AJAX技术逐渐成熟,框架资源比较丰富,比如YUI,DWR等等,也是因为JavaScript没有合适的调试工具,所以没有必要从零开始编写AJAX应用,个人认为多用一些成熟的Ajax框架实现无刷新更新页面是不错的选择.
 
4)表格控件:Display Tag ,Extreme Table
这两个的功能差不多,都是View层表格的生成,界面也比较相向,可以导出Excel,Pdf,对Spring支持很容易.
相比较而言比较推荐ExtremeTable,它的设计很好功能上比DisplayTag多一些,支持Ajax,封装了一些拦截器,而且最方面的是在主页wiki中有详细的中文使用文档.
 
5):OSCache
OSCache是OpenSymphony组织提供的一个J2EE架构中Web应用层的缓存技术实现组件,Cache是一种用于提高系统响应速度、改善系统运行性能的技术。尤其是在Web应用中,通过缓存页面的输出结果,可以很显著的改善系统的稳定性和运行性能。
它主要用在处理短时间或一定时间内一些数据或页面不会发生变化,或将一些不变的统计报表,缓冲在内存,可以充分的减轻服务器的压力,防治负载平衡,快速重启服务器(通过硬盘缓存).
 
6)SiteMesh
sitemesh应用Decorator模式主要用于提高页面的可维护性和复用性,其原理是用Filter截取request和response,把页面组件head,content,banner结合为一个完整的视图。通常我们都是用include标签在每个jsp页面中来不断的包含各种header, stylesheet, scripts and footer,现在,在sitemesh的帮助下,我们删掉他们轻松达到复合视图模式.
Sitemesh也是 OpenSymphony的一个项目现在最近的版本是2.2,目前OpenSymphony自从04年就没有更新的版本了..感觉它还是比较有创新的一种页面组装方式, OpenSymphony开源组织的代码一般写的比较漂亮,可以改其源代码对自己的项目进行适配.
测试发现Sitemesh还存在一些问题,比如中文问题,它的默认编码是iso-8859-1在使用时候需要做一些改动.
 
7)CSS,XHTML
这个不用说了,遵循W3C标准的web页面开发.
 
8)分页标签: pager-taglib组件
Pager-taglib 是一套分页标签库,可以灵活地实现多种不同风格的分页导航页面,并且可以很好的与服务器分页逻辑分离.使用起来也比较简单.
 
9)Form: Jodd Form taglib
Jodd Form taglib使用比较简单,只要把<form>的头尾以<jodd:form bean= "mybean">包住
就会自动绑定mybean, 自动绑定mybean的所有同名属性到普通html标记input, selectbox, checkbox,radiobox.....在这些input框里不用再写任何代码…
      
10)Ajax:DWR
       J2EE应用最常用的ajax框架
      
       11)报表 图表
Eclipse BIRT功能比较强大,也很庞大..好几十M,一般没有特别需求或别的图表设计软件可以解决的不用它
JasperReports+ iReport是一个基于Java的开源报表工具,它可以在Java环境下像其它IDE报表工具一样来制作报表。JasperReports支持PDF、HTML、XLS、CSV和XML文件输出格式。JasperReports是当前Java开发者最常用的报表工具。
JFreeChart主要是用来制作各种各样的图表,这些图表包括:饼图、柱状图(普通柱状图以及堆栈柱状图)、线图、区域图、分布图、混合图、甘特图以及一些仪表盘等等。
      
 
4:权限控制: Acegi
Acegi是Spring Framework 下最成熟的安全系统,它提供了强大灵活的企业级安全服务,如完善的认证和授权机制,Http资源访问控制,Method 调用访问控制等等,支持CAS
(耶鲁大学的单点登陆技术,这个单点登陆方案比较出名.我也进行过配置使用,可以根据项目需要,如果用户分布在不同的地方不同的系统通用一套登陆口令可以用它进行解决,一般注册机登陆机就是这样解决的)
       Acegi只是于Spring结合最好的安全框架,功能比较强大,当然还有一些其他的安全框架,这里列举一些比较流行的是我从网上找到的,使用方法看其官方文档把…
JAAS, Seraph, jSai - Servlet Security, Gabriel, JOSSO, Kasai, jPAM, OpenSAML都是些安全控制的框架..真够多的呵呵
 
5:全文检索
       1) Lucene
       Lucene是一套全文索引接口,可以通过它将数据进行倒排文件处理加入索引文件,它的索引速度和查询速度是相当快的,查询百万级数据毫秒级出结果,现在最火的Apache开源项目,版本更新速度很快现在已经到了2.0,每个版本更新的都比较大,目前用的最多的版本应该是1.4.3,但它有个不太方面的地方单个索引文件有2G文件限制,现在2.0版本没有这个限制,我研究的比较多,它的扩展性比较好,可以很方面的扩充其分词接口和查询接口.
       基于它的开发的系统很多,比如最常用的Eclipse的搜索功能,还有一些开源的软件比如Compass,Nutch,Lius,还有我最近做的InSearch(企业级FTP文件网页搜索)
6:公共Util类
       主要是Jakarta-Commons类库,其中最常用得是以下几个类库
1) Jakarta-Commons-Language
       最常用得类是StringUtils类,提供了使用的字符串处理的常用方法效率比较高
2) Jakarta-Commons-Beantuils
       主要用Beantuils能够获得反射函数封装及对嵌套属性,map,array型属性的读取。
3) Jakarta-Commons-Collections
       里面有很多Utils方法
 
7 日志管理
       Log4J
       任务是日志记录,分为Info,Warn,error几个层次可以更好的调试程序
 
8 开源的J2EE框架
       1) Appfuse
              Appfuse是Matt Raible 开发的一个指导性的入门级J2EE框架, 它对如何集成流行的Spring、Hibernate、iBatis、Struts、Xdcolet、JUnit等基础框架给出了示范. 在持久层,AppFuse采用了Hibernate O/R映射工具;在容器方面,它采用了Spring,用户可以自由选择Struts、Spring/MVC,Webwork,JSF这几个Web框架。
      
       2) SpringSide
       .SpringSide较完整的演示了企业应用的各个方面,是一个电子商务网站的应用 SpringSide也大量参考了Appfuse中的优秀经验。最重要的是它是国内的一个开源项目,可以了解到国内现在的一些实际技术动态和方向很有指导意义…
 
9:模版 Template
主要有Veloctiy和Freemarker
模板用Servlet提供的数据动态地生成 HTML。编译器速度快,输出接近静态HTML             页面的速度。
 
10:工作流
       我所知道比较出名的主要有JBpm Shark Osworkflow,由于对它没有过多的研究所以还不是很清楚之间有什么区别.
 
项目管理软件
dotProject:是一个基于LAMP的开源项目管理软件。最出名的项目管理软件
JIRA: 项目计划,任务安排,错误管理
Bugzilla:提交和管理bug,和eclipse集成,可以通过安装MyEclipse配置一下即可使用
BugFree借鉴微软公司软件研发理念、免费开放源代码、基于Web的精简版Bug管理
CVS:这个就不介绍了都在用.
SVN: SubVersion已逐渐超越CVS,更适应于JavaEE的项目。Apache用了它很久后,Sourceforge刚刚推出SVN的支持。
测试用例:主要JUnit单元测试,编写TestCase,Spring也对Junit做了很好的支持
 
后记:
       以Spring为主的应用开发可选用的组件中间件真是眼花缭乱,所以针对不同的项目需求可以利用不同的开源产品解决,比如用Spring+Hibernate/ iBATIS或Spring+WebWork+Hibernate/ iBATIS或Spring+Struts+Hibernate/ iBATIS,合理的框架设计和代码复用设计对项目开发效率和程序性能有很大的提高,也有利于后期的维护.

posted @ 2006-09-13 20:19 ericli 阅读(543) | 评论 (1)编辑 收藏

java 基本试题

1.通过什么参数分配Java内存使用?
java -Xms128m -Xmx512m

2.Treemap和Hashmap区别是什么?
TreeMap对Key进行排序,而HashMap不排序。HashMap通过hashcode对其内容进行快速查找, 而TreeMap中所有的元素都保持着某种固定的顺序,如果你需要得到一个有序的结果你就应该 使用TreeMap(HashMap中元素的排列顺序是不固定的)。

3.为何加入:private static final long serialVersionUID?
可以利用JDK的bin目录下的serialver.exe工具产生。
序列化时为了保持版本的兼容性,即在版本升级时反序列化仍保持 对象的唯一性。在java中serialVersionUID是唯一控制着能否反序列化成功的标志, 只要这个值不一样,就无法反序列化成功。

4.JSP编译和执行原理?
JSP文件的Scriptlets在编译后将被包含于该JSP servlet的service()方法。当JSP引擎处理客户端请求时,JSP Scriptlets在被请求的时候被执行。如果scriptlet产生输出,输出将在out (JSPWriter)对象中进行缓存然后最终发送到客户端。

5.怎么解决JSP/Servlet web中中文乱码的问题

6.JDBC中,statement,prepared statement ,Callable statement的区别是什么?
Statement 接口提供了执行语句和获取结果的基本方法。PreparedStatement 接口添加了处理 IN 参数的方法;而 CallableStatement 添加了处理 OUT 参数的方法。
PreparedStatement:对于同一条语句的多次执行,Statement每次都要把SQL语句发送给数据
库,这样做效率明显不高,而如果数据库支持预编译,PreparedStatement可以先把要执行的语句一次发给它,然后每次执行而不必发送相同的语句,效率当然提高,当然如果数据库不支持预编译,
PreparedStatement会象Statement一样工作,只是效率不高而不需要用户工手干预.
    另外PreparedStatement还支持接收参数.在预编译后只要传输不同的参数就可以执行,大大
提高了性能.
CallableStatement:是PreparedStatement的子类,它只是用来执行存储过程的.

7.什么情况下会抛出“打开游标超过最大数”的异常?

8.XML解析中:SAX和DOM的不同点有哪些?

9.抽象类和接口的区别?

10.什么是中间件

11.JSP的内置对象有那些?
Out, request, response, application, session, exception, config, page, pageContext.

12.有那些方法能防止、化解对web网站的攻击?
13.如何提高访问web网站的速度?
14.Java可采用的MVC框架技术有哪些?他们的特征和适用环境如何?
Struts, WebWork, Spring, JSF…

15.MVC的优缺点是什么,适用范围如何?
MVC开发模式分离数据访问层和数据表现层,并使开发人员开发一个可伸缩性的强、便于扩展的控制器,来维护整个流程。
16.TOMCAT配置中,有关安全的设置选项有哪些?
17.Apach与Tomcat配合使用,是为解决什么问题?怎么实现?运行过程如何?


18.Collection的四个接口
add();
addAll();
isEmpty();
iterator();
contains();

19.Cookie
20.多态
21.Web上参数的传输方式
22.HashTable和HashMap的区别
Hashtable和HashMap类有三个重要的不同之处。第一个不同主要是历史原因。Hashtable是基于陈旧的Dictionary类的,HashMap是Java 1.2引进的Map接口的一个实现。
也许最重要的不同是Hashtable的方法是同步的,而HashMap的方法不是。这就意味着,虽然你可以不用采取任何特殊的行为就可以在一个多线程的应用程序中用一个Hashtable,但你必须同样地为一个HashMap提供外同步。一个方便的方法就是利用Collections类的静态的synchronizedMap()方法,它创建一个线程安全的Map对象,并把它作为一个封装的对象来返回。这个对象的方法可以让你同步访问潜在的HashMap。这么做的结果就是当你不需要同步时,你不能切断Hashtable中的同步(比如在一个单线程的应用程序中),而且同步增加了很多处理费用。
第三点不同是,只有HashMap可以让你将空值作为一个表的条目的key或value。HashMap中只有一条记录可以是一个空的key,但任意数量的条目可以是空的value。这就是说,如果在表中没有发现搜索键,或者如果发现了搜索键,但它是一个空的值,那么get()将返回null。如果有必要,用containKey()方法来区别这两种情况。
23.运行环境中的GC

24.final,finally,finalzie的区别
final:
final可以让你控制你的成员、方法或者是一个类是否可被覆写或继承等功能,这些特点使final在Java中拥有了一个不可或缺的地位,也是学习Java时必须要知道和掌握的关键字之一。
final成员
当你在类中定义变量时,在其前面加上final关键字,那便是说,这个变量一旦被初始化便不可改变,这里不可改变的意思对基本类型来说是其值不可变,而对于对象变量来说其引用不可再变。其初始化可以在两个地方,一是其定义处,二是在构造函数中,两者只能选其一。
还有一种用法是定义方法中的参数为final,对于基本类型的变量,这样做并没有什么实际意义,因为基本类型的变量在调用方法时是传值的,也就是说你可以在方法中更改这个参数变量而不会影响到调用语句,然而对于对象变量,却显得很实用,因为对象变量在传递时是传递其引用,这样你在方法中对对象变量的修改也会影响到调用语句中的对象变量,当你在方法中不需要改变作为参数的对象变量时,明确使用final进行声明,会防止你无意的修改而影响到调用方法。
final方法
将方法声明为final那有两个原因,第一就是说明你已经知道这个方法提供的功能已经满足你要求,不需要进行扩展,并且也不允许任何从此类继承的类来覆写这个方法,但是继承仍然可以继承这个方法,也就是说可以直接使用。第二就是允许编译器将所有对此方法的调用转化为inline(行内)调用的机制,它会使你在调用final方法时,直接将方法主体插入到调用处,而不是进行例行的方法调用,例如保存断点,压栈等,这样可能会使你的程序效率有所提高,然而当你的方法主体非常庞大时,或你在多处调用此方法,那么你的调用主体代码便会迅速膨胀,可能反而会影响效率,所以你要慎用final进行方法定义。
final类
当你将final用于类身上时,你就需要仔细考虑,因为一个final类是无法被任何人继承的,那也就意味着此类在一个继承树中是一个叶子类,并且此类的设计已被认为很完美而不需要进行修改或扩展。对于final类中的成员,你可以定义其为final,也可以不是final。而对于方法,由于所属类为final的关系,自然也就成了final型的。你也可以明确的给final类中的方法加上一个final,但这显然没有意义。

finally:
finally 关键字是对 Java 异常处理模型的最佳补充。 finally 结构使代码总会执行,而不管有无异常发生。使用 finally 可以维护对象的内部状态,并可以清理非内存资源。如果没有 finally,您的代码就会很费解。例如,下面的代码说明,在不使用 finally 的情况下您如何编写代码来释放非内存资源:

finalize:
根据Java语言规范,JVM保证调用finalize函数之前,这个对象是不可达的,但是JVM不保证这个函数一定会被调用。另外,规范还保证finalize函数最多运行一次。
通常,finalize用于一些不容易控制、并且非常重要资源的释放,例如一些I/O的操作,数据的连接。这些资源的释放对整个应用程序是非常关键的。在这种情况下,程序员应该以通过程序本身管理(包括释放)这些资源为主,以finalize函数释放资源方式为辅,形成一种双保险的管理机制,而不应该仅仅依靠finalize来释放资源。

posted @ 2006-09-13 20:18 ericli 阅读(372) | 评论 (0)编辑 收藏

富客户端技术(之一)   FCKEditor   最流行的文本编辑器

     C/S体系结构采用了开放的模式在一定的程度上解决了人们对联机办公等的网络应用需求,导致了胖客户端应用(fat clients)流行。
    随着人们对网络应用需求的进一步深入,B/S结构的网络应用又随之诞生了,这种结构的网络应用又称为瘦客户端应用,只要通过Web浏览器,各种处理任务都可以调用系统资源来完成,这样大大简化了客户端,减轻了系统维护与升级的成本和工作量,降低了用户的总体拥有成本(TCO)。
    今天的网络应用需要从“什么都用Web浏览器”到“根据情况采用强化客户端技术”进行本质的转变。人们需要更为复杂而精美的应用交互界面,发布和表现多种复杂形式的多媒体和内容,对形式多样而丰富的信息内容进行更好的组织和表现,而这些正是目前广泛应用的B/S结构所不能达到的,于是富客户端技术应运而生。
   作为开源的j2ee技术中涉及到view层的富客户端是一种趋势,而今天要介绍就是其中的一个简单的开源技术FCKEditor,运用它,可以在你的的页面展示出类似word页面的效果,而且无须安装,只需要在你的项目中引入相关的jar文件,在需要控件的地方适当的调用就可以了。
   下面我做一个简单的演示给大家
   1.首先,我们先去了解一下FCKEditor   http://www.fckeditor.net/
   英文介绍简单明了:
   This HTML text editor brings to the web many of the powerful functionalities of desktop editors like MS Word.
It's lightweight and doesn't require any kind of installation on the client computer.  
   2.然后去下载相关的文件,这里我下载的是FCKeditor_2.3.1.zip文件
   3.解压文件,本人使用的jsp页面,还需要下载 FCKeditor.Java 的相关文件
   4.从sample01.jsp开始,使用中来体会学习
************************************jsp页面*********************************************
 
<%@ page language="java" import="com.fredck.FCKeditor.*" %>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
 <head>
  <title>FCKeditor - JSP Sample</title>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  <meta name="robots" content="noindex, nofollow">
  <link href="../sample.css" rel="stylesheet" type="text/css" />
  <script type="text/javascript">

function FCKeditor_OnComplete( editorInstance )
{
 window.status = editorInstance.Description ;
}

  </script>
 </head>
 <body>
  <h1>FCKeditor - JSP - Sample 1</h1>
  This sample displays a normal HTML form with an FCKeditor with full features
  enabled.
  <hr>
<!--  自己的 action 方式  -->
  <form action="sampleposteddata.jsp" method="get" target="_blank">
<%
//以下是修改的关键
FCKeditor oFCKeditor ;
oFCKeditor = new FCKeditor( request, "EditorDefault" ) ;//初始化的一些配置,第二个参数EditorDefault就是这个字段的名称
oFCKeditor.setBasePath( "/FCKeditor/" ) ;//为了寻找相应的frame页面:fckeditor.html;同时传递参数;这里设置为我们需要的路径!
//oFCKeditor.setBasePath( "/nhoa/FCKeditor/" ) ;//我的例子,就是指明你的文件放在哪个目录下
oFCKeditor.setValue( "ligang's example!" );//初始化页面显示的数据
out.println( oFCKeditor.create() ) ;//建立吧,看源码很简单!
%>
   <br>
   <input type="submit" value="Submit">
  </form>
 </body>
</html>
************************************jsp页面***********************************

使用步骤:
(1)项目中import 这个包包:fckEditor.jar(将src的java代码打包),需要看源码也可以!
(2)修改你的jsp页面,在需要添加该效果的地方模仿以上jsp页面进行修改
(3)想想,好象没有什么事了!
(4)试试吧!
5.分析实现原理
主要就是oFCKeditor.create() ,通过这个方法建立控件,在源代码中可以看见
public String create() {
  StringBuffer strEditor=new StringBuffer();
  
  strEditor.append("<div>");
  String encodedValue=HTMLEncode(value);

   if(isCompatible()) { //浏览器版本符合要求产生我们的控件
   strEditor.append("<input type=\"hidden\" id=\"" + instanceName + "\" name=\"" + instanceName + "\" value=\"" + encodedValue + "\">");
  
   strEditor.append(createConfigHTML());
   strEditor.append(createIFrameHTML());
  
  }
  else{//浏览器版本不符合要求,产生一个textarea,呵呵,也不失是一种弥补的方式
   strEditor.append("<TEXTAREA name=\"" + instanceName + "\" rows=\"4\" cols=\"40\" style=\"WIDTH: " + width + "; HEIGHT: " + height + "\" wrap=\"virtual\">"+encodedValue+"</TEXTAREA>");
  }
  strEditor.append("</div>");
  return strEditor.toString();
 }
   其实就是一个servelt向浏览器写一些html语句,其中的createConfigHTML()就是通过配置文件config.js向控件传递一些参数,在页面是通过hidden域来传递的,但是自己没有找到这个文件的位置,希望明白的朋友可以告诉我......
createIFrameHTML()就是建立控件的方法,其中最重要的一个参数就是sLink,
String sLink=basePath + "editor/fckeditor.html?InstanceName=" + instanceName;它指明了控件数据的名称就是instanceName,它有时在那里被用户初始化的呢,呵呵,是在构造方法里面,FCKeditor 类有一个构造方法
 public FCKeditor(HttpServletRequest req, String parInstanceName){
  request=req;
  basePath = request.getContextPath() + "/FCKeditor/";
  instanceName=parInstanceName;
  oConfig = new FCKeditorConfigurations() ;
 }
我们在jsp页面上,看到
oFCKeditor = new FCKeditor( request, "EditorDefault" ) ;//初始化的一些配置,第二个参数EditorDefault就是这个
变量
6.到这里,需要我们试用者了解的部分就这些了,剩下的工作就是控件自己实现了,呵呵,真的很简单啊
总结:
使用就要做两件事情:import fckEditor.jar,这个jar包是我自己通过开源的代码自己编译后打的
还有就是在需要的地方添加代码,注意两个地方,一个是选取合适的构造函数和构造函数参数,还有就是
设定好自己项目摆放FCKeditor文件的目录,基本没有什么问题,今天在工作的项目上使用了一下没有什么问题.

posted @ 2006-09-13 20:14 ericli 阅读(3751) | 评论 (4)编辑 收藏

tomcat 4.1.30启动过程的源码分析

前几天为了解决sinpool兄的《多线程的问题。》一帖,专门看了一下tomcat 4.1.30的源码,
其中重点研究了tomcat的启动这一部分,个人感觉tomcat的源码还是写的很清楚易懂,值得一看。
(以前看过struts的部分代码,感觉也比较经典)    
然后我看后的代码整理了一下,附在下面,希望对其他人有用,也希望感兴趣的兄弟可以多看看好的代码,
肯定对自己的程序设计和代码质量颇有益处。

一. 启动类(包含main()方法的类):
org.apache.catalina.startup.Bootstrap
这个类是tomcat的启动类,主要按照如下步骤,进行主要的启动工作:
1. 创建3个ClassLoader:common,catalina和share,它们对应tomcat的3个Classloader,我想对tomcat
的classloader有研究的兄弟对这个肯定不陌生,其中common classloader是紧跟在系统的classloader(也就是
系统环境变量中设置的CLASSPATH所对应的classloader),而catalina classloader是common的子classloader,是tomcat
运行所需要的类的classloader,而shared classloader也是common的子classloader,是和catalina平级的classloader,
之所以说是shared classloader,是因为它是所有tomcat下面发布的webapp classloader(每一个web app都有一个自己的classloader)
的父classloader。它们这3个classloader分别读取tomcat home下面的common, server和shared三个目录里面的classes和lib目录,
用于初始化自己所控制的类库和资源。

2. 创建启动一个的org.apache.catalina.startup.Catalina类的实例,并调用它的process方法,这里使用的是java的reflection技术。
然后调用这个实例的process方法,并把Bootstrap接受到的命令行参数传递进去了,这里Bootstrap类并没有解析传给它的命令行参数。
当然在调用process之前还使用setParentClassLoader方法设置了一下父classloader。这里简单介绍一下有关classloader的一个重要
特性,就是如果classloader要load一个类时,不是自己先找,而是先把这个任务委派给自己的父classloader,然后自己的父classloader
也不找,在把这个任务委派给自己的父classloader,直到找到最顶层的classloader,然后再自顶向下的找对应的这个要load的类的定义,
如果那个classloader先找到,就返回。所以接合上面第一点介绍的tomcat中3个classloader,大家就可以明白tomca的classloader找类
的顺序了,这个对程序开发人员来说特别重要。我想使用过tomcat或者其他app server的兄弟肯定碰到过一个类明明存在可就是找不到,
或者总是找到一个老的版本,我想主要是在多个地方放置的原因,或者哪里有重名的类:-)

二.org.apache.catalina.startup.Catalina类
现在程序转到org.apache.catalina.startup.Catalina类里面的process方法。
这个方法首先设置一下catalina的home和base目录,然后通过arguments方法解析命令行参数,
最后调用execute()方法启动server。而execute方法很简单,就是根据arguments解析的命令行参数,
决定是启动server,还是stop server,如果是start server,就调用start方法,而下面重点讲一下这个start()方法,
因为才算是一个真正开始的启动tomcat的地方:-)
1. start方法首先使用Digester(这个东东是jakarta commons里面的一个用于解析xml文件的工具包,一开始是专门用于解析struts配置文件的,
后来被抽象成现在的一个通用工具,主要还是用来解析xml配置文件,根据一些定义的rule自动生成对应的类的实例,具体信息可以参考
apache网站上的文档)来设置tomcat配置文件,也就是/conf/server.xml这个文件的解析规则
然后通过如下代码来将配置文件中的数据转化成内存中的实例:

代码:
        File file = configFile();

        try {

            InputSource is =

                new InputSource("file://" + file.getAbsolutePath());

            FileInputStream fis = new FileInputStream(file);

            is.setByteStream(fis);

            digester.push(this);

            digester.parse(is);

            fis.close();

        } catch (Exception e) {

            System.out.println("Catalina.start: " + e);

            e.printStackTrace(System.out);

            System.exit(1);

        }   
    
 


转换的规则如下(我只作一些简单的介绍),例如配置文件中的
a. Server对应可以产成一个org.apache.catalina.core.StandardServer类(这个类很重要,是tomcat server的实现)
b. Server/GlobalNamingResources对应生成org.apache.catalina.deploy.NamingResources类
而大家比较熟悉的监听8080端口的类配置如下:
c. Server/Service/Connector:org.apache.catalina.connector.http.HttpConnector
d. Server/Service/Engine/Host/Context/:org.apache.catalina.core.StandardContext
有兴趣的兄弟可以参考jakarta commons里面的Digester文档和org.apache.catalina.startup.Catalina
这个类里面的createStartDigester方法.
在这段代码之后,一个叫server的变量已经通过Digester工具生成了,它将会用于启动tomcat。
2. 然后程序进行了一些server启动前的设置工作,例如重定向log输出流等等。而server启动的代码如下:

代码:
        // Start the new server

        if (server instanceof Lifecycle) {

            try {

                server.initialize();

                ((Lifecycle) server).start();

                try {

                    // Register shutdown hook

                    Runtime.getRuntime().addShutdownHook(shutdownHook);

                } catch (Throwable t) {

                    // This will fail on JDK 1.2. Ignoring, as Tomcat can run

                    // fine without the shutdown hook.

                }

                // Wait for the server to be told to shut down

                server.await();

            } catch (LifecycleException e) {

                System.out.println("Catalina.start: " + e);

                e.printStackTrace(System.out);

                if (e.getThrowable() != null) {

                    System.out.println("----- Root Cause -----");

                    e.getThrowable().printStackTrace(System.out);

                }

            }

        }

    
 


其中server这个变量就是在刚才Digester解析时创建好的。
(当时这个地方我看了很长时间,后来才发现是这样的,因为以前不太了解Digester这个东东)。
然后大家可以看到server启动主要是分3步:
1. initialize方法进行server启动的初始化操作
2. start方法启动server,主要是server中的的service和service中的connector
3. await方法等待server shutdown
其中我重点给大家介绍一下initialize方法和start方法
initialize方法:
这里面只有一个主要任务,就是逐次调用server中所有定义的service的initialize方法,
而每个service的initialize方法中调用这个service中定义的所有connector的initialize方法,
而connector的initialize方法则是创建一个serversocket用于接受客户端的请求就结束了。
如果大家看一下tomcat下面conf/server.xml,就可以发现,tomcat默认只定义了一个service叫做Tomcat-Standalone,
而下面只有默认定义了3个connector:
1. 8080端口的http connector
2. 8443端口的http ssl connector
3. 8009端口的Coyote/JK2 AJP 1.3 Connector
我想大家对这3个端口一定不陌生吧。
start方法:
这个方法里面有一个tomcat很重要,也是我认为tomcat设计对一个亮点,就是Lifecycle这个东东,它很象一个bus(总线)。
我想大家进行过程序设计的一定知道,开始设计的时候总要根据一个原则分出几个模块来,是为了代码分块,或者将
一部分功能相似的代码组织成一个模块,这样比较清楚,例如一个进销存系统会有采购,销售,库存和财务等模块,但是
我想很多人也碰到过这样的情况就是虽然分了模块但是如果在开发完毕以后,另外一个客户说只想要其中的销售模块,我想
大部分的开发人员肯定傻眼,因为虽然当时设计的时候分了模块,但是这些模块编写的时候却是交织在一起,互相的接口定义
很模糊,基本上都是直接调用另一个模块的方法,这样肯定分不开。而tomcat的这个Lifecycle的设计理念就可以解决这个问题的
一部分,它的原理就象是一个bus(总线),例如一个模块做完一个动作以后,例如销售模块创建好一个订单后,本来要直接调用
库存模块的api锁住一部分库存(我只是随便举个例子,实际业务不一定是这样),这样销售模块就需要依赖库存模块了。但是使用了
bus方式。我们就可以在订单创建后,向bus上发送一个订单创建的消息,而总线有一个事件注册机制,有点象swing的event,listener,
例如库存模块有一个listener专门用于监听订单创建的消息,进行处理,这样2个模块就互不依赖了。有兴趣的兄喜可以看看jcp上面
的一个叫做infobus的专题。
当然这个方式只是解决有效降低模块偶合度的一个方面(因为有的时候必须要直接调用另外一个模块的接口,
例如库存模块一定要直接缺德一个销售订单的信息,那么就需要定义一个接口类来描述订单的详细信息啦,这里就不具体解释了,
有空可以专门发个帖子跟大家探讨这个问题:-) ),就是不要显式触发另一个模块的某个动作,而是通过bus机制来发送消息,
而每个模块都有一个自己的handler,会监听bus,对自感兴趣的事件进行处理。tomcat的Lifecycle就是这个东西。
下面再回到start方法:
1. 它首先向总线发送了2个事件:BEFORE_START_EVENT和START_EVENT
2. 然后调用每个service的start方法,最后发送AFTER_START_EVENT消息通知其他程序
而service的start方法主要进行的动作如下:
1. 发送BEFORE_START_EVENT消息
2. 调用container的start方法
3. 然后调用connector的start方法
4. 最后发送AFTER_START_EVENT消息.
而connector的start方法就是大家最熟悉的socket编程了,大家可以参看org.apache.catalina.connector.http.HttpConnector这个类,
主要是使用java里面的多线程操作,初始化一个HttpProcessor的线程池,然后通过wait方法阻塞住每个HttpProcessor线程,只有
当接受到一个http请求时,在通过notify方法激活HttpProcessor线程,让其处理用户的http请求。

到此为止主要简单介绍了一下tomcat 4.1.30的启动过程,下次有机会的话,可以再看看它的webapp的deploy的管理部分的代码,然后和大家分享。如果大家对我写的帖子有什么意见的话,也欢迎批评指正,希望感兴趣的兄弟可以一起探讨:-) 废话不说了,很晚了该睡觉了,祝大家周一工作愉快

posted @ 2006-09-13 20:12 ericli 阅读(224) | 评论 (0)编辑 收藏