2007年4月20日
这方面的文章网络上一搜一大堆。偶也不引用了。
偶的感觉是python的安装和组件安装乱七八糟。ruby的安装和插件安装感觉比较爽。其理念是学习linux的port和apt的包管理思路。
昨天准备离职了。
其实在这家公司里面,项目leader对我很不错,时间也是比较宽松的。给了我很多的机会学习。甚至曾经我有整整一个星期的时间去完整的学习ruby。对此我还是非常感激的。不过因为项目的原因以及各种管理上的不如意,我感觉自己始终不开心。
索性这次终于解放,于是我想先静下心来,思考一下人生未来的路。顺便学习一下我所喜爱的ruby和python。上次学习ruby已经是几个月以前的事情了,学完以后基本上没有得到什么使用的机会到现在基本上忘记了。这次一并将python也学了,并比较列出。
几乎所有的语言,都包含以下几个部分
1,数据类型 ————被处理的
一般包括数字,字符串,可能还包括布尔类型;复杂数据类型;对oo的语言还要包括对象等。
2,对数据的处理 ————语法部分,
a,操作符和表达式
b,条件判断语句
c,循环语句
d,跳转语句
f,异常处理
3,代码的组织
a,文件的组织
b,函数
c,对象
4,类库
a,标准输出入库
b,文件库
等
以上前三个部分,是一个语言基础的部分。但是对一个语言深入的了解,还必须结合这个语言的背景,哲学理念,才可以达到比较深刻的地步。是以我们对python和ruby的学习将从这个地方开始。
我曾是个技术粉丝
但是多年的开发经验,使得我对技术的本质认识的越来越清楚。至少对企业软件开发人员来说,纯粹的技术coding是没有多少价值的。如同建筑行业一样,真正有价值的东西在设计阶段已经完成了。
和传统建筑行业开发不同,软件开发行业不光是技术设计,还包括业务的设计。业务和技术掺杂在一起,构成了软件开发的复杂性。
在业务上,在技术上,尤其是在技术和业务的鸿沟之间,存在了太多太多因素。使得我们本来对相对简单的软件开发不敢抱有那么大的乐观。更何况真正一个成功的项目还需要市场,客户等等各个方面。
作为一个软件开发人员,真的应该放弃软件自大的心态,客观的去看待软件开发技术在整个软件开发工程中的位置和地位。以一种推动企业发展,推动项目发展和成功的心态和目的去看待整个项目。就明白了软件开发的真正意义和任务。也就能更好的完成自己的工作,甚至可以改变项目的成败。
所以成败不由技术,成败由你我的视野和努力。
最近公司项目经理派我研究工作流并考虑在项目中使用。很有一些心得。工作流应用我将之分为狭义工作流和广义工作流。对狭义工作流而言,你可以将之理解为在工作流设计器里画画节点以及方向箭头,设置好就节点数据,动作就差不多了。(具体可以参见jbpm的websale这个demo)。
广义的工作流是对服务之间的整合。核心问题是业务节点和工作流节点之间的映射,以及业务数据和工作流数据之间的映射,和普通工作流一样还有流程判断等等服务。实现了这些,各个业务模块之间的数据就可以通过服务,以定好的方式(进行方向控制和格式转化)在各个节点之间流通,达到了服务整合的目的。
IBM为ESB定义了四个必备的功能:“路由器”——根据信息内容,在不同应用和服务之间进行信息传输和路由;“转换器”——进行应用之间的通信协议转换;“翻译机”——进行应用之间的消息格式转换;“收发室”——处理来自不同渠道的业务事件(同步传输,异步传输,发布/订阅等方式)。
其中“路由器”和“收发室”都是针对服务的重用而设计的,而“转换器”和“翻译机”则专门用来解决异构的通信问题。
针对重用和异构这两个难题,倪晓兵认为ESB提供了两个核心的功能,服务的管理和数据的转换。
我们DEC项目的目标就是建立一个全能服务仓库(暂时我在DEC设计人员zy哪里得到的信息),而服务之间如何路由,如何转换,语义的协调都没有考虑,而后者却是成败的关键。
最关键的语义翻译这一点,就现在的技术上来说还不能做到(需要很高的机器智能才能达到使得不同的系统的业务词汇可以正确的映射,更何况是在所有的系统之间进行映射,同时应用在企业级的应用环境中)
也许真的有这样的幻想,但是真的能够做到这一步么?我深深的怀疑。就目前的技术手段,如果要达到数据映射的高度正确性,必须由人不同系统之间需要协调的数据进行语义确认方能进行有效的映射。
当考虑到还必须做到ESB系统对其接入的所有的服务数据的语义都这样做时。我怀疑真的需要做到协调所有的服务么?
也许ESB的应用范围就是在公司内部或者有限范围内的整合目标明确的业务节点之间业务的整合。
ruby很火,ror很火。但凡一个东西火,我们要知道他火的原因。
因为他开发快,你看
rails project_name
#config db
rake db:create:all
rake db:mirage scoffled table_name [field_name:field_type,.....]
#编辑model
rake db:mirage
#编辑action和route
ruby script/server
然后一个应用程序就生成啦,这个过程大概就2、3分钟;而且他热部署,所写即所得,语法超级强大,简单几句话就可以表达很复杂的逻辑,真正让人把精力集中在业务逻辑上和页面逻辑上(他的mirage真是太cool了,完美的体现了定义一次schame,到处使用的原则)
坦率的讲,这些别的东西——包括java都可以做到~,为什么到现在java还是这么杀手呢(不是应用程序杀手,是程序员杀手,开发起来罗嗦到死。
既然ror出现了,所以我想jor也很快了,不过ruby使人愉快的是,它从不限制你,包括写的更难懂——如果你真的觉得别人写的你看不懂的话——幸运的是,它也没有限制你写的更简单。
那就用ruby去快乐的编程吧
linux控制台分辨率调节
2007年12月07日 上午 11:16 | 640x480 800x600 1024x768 1280x1024
-----+-----------------------------------------------------
256 | 257 259 261 263
32k | 272 275 278 281
64k | 273 276 279 282
16M| 274 277 280 283
VESA:
Colors (depth) 640x480 800x600 1024x768 1280x1024 1600x1200
------------------+-----------+-----------+------------+-------------+-------------
256 ( 8 bit) | 769 771 773 775 796
32,768 (15 bit)| 784 787 790 793 797
65,536 (16 bit)| 785 788 791 794 798
16.8M (24 bit) | 786 789 792 795 799
查上面的表,编辑/boot/grub/menu.lst
kernel /boot/vmlinuz-2.6.15-23-386 root=/dev/hdb10 ro quiet splash vga=791
这行最后补上vga=792
说说韩寒
http://bbs.book.tom.com/i_87_22259.html
各项指数综合打分,活着的大陆人里面,韩寒应该可以排到我最欣赏(敬佩)的人的前三名——还有一位应该是晓波老师。还有一位?暂时空缺,欢迎推荐。王怡信教之后越来越看不懂了,连岳又太“大隐隐于市”了。
以前很傻,以为自己比韩寒年纪大就敢于瞧不起人,愚昧!后来才慢慢调整过来,敢于承认一个年轻人,一个谁都敢批几句的年轻人,其实比自己强太多了。
说几点我欣赏韩寒的地方:
语言才华:这个不用说了;
独立精神:主要是对作协的态度;
公民意识,有社会责任感:比如对厦门px的关注,比如帮助路人;
生活态度,有健康的心理:纯属个人感觉。聚光灯下,不变态很难,难得韩寒还有平常心。我挺认同韩寒自己说的“并不叛逆”;
职业精神:爱一行做一行,还都能做好,比如半月前夺得2007华赛CFR全国汽车场地锦标赛中国量产车1600cc组年度车手总冠军;
挣钱的能力也很强:现在越来越认同一个人赚钱的能力了,以前一直回避这一点。而且“君子爱财,取之有道”,这八个字说起来容易,真正做好很难;
还有,人也长得挺帅的,作为公众人物,这点还是很重要的,比如看了郭敬明的照片,吃饭就会很困难。
......
突然说起这些,是因为昨天看了南方周末对韩寒的采访。
南方周末:你怎么看待张悦然、小饭、郭敬明等大批“80后”作家加入作协这件事?
韩寒:我觉得也挺好的,总得有他们这样的人。要不然我又得和一帮人竞争。现在比较好,就我一个人,没有对手。我是一个冠军车手,我的内心比他们更强大,我觉得是这样。而且很多时候他们也只是可能有熟人介绍,面子上过不去罢了。以张和郭现在的能量,是否加入作协应该无所谓。
南方周末:如果他们邀请你,你会去参加作协吗?
韩寒:可以啊,我可以把这个当作代言活动。所有的商业代言我都是推掉的,这一次我可以代言你们作协,作协需要一个年轻化的代表,我代言你们给我200万,这算是我给作协的一个面子——第一次商业代言就给你,而且价格也不高。但你要我加入作协不可能。
我的立场一如既往,我绝不加入作协,打死我也不干。我认为,真正的艺术家应该永远独立,绝不能被组织左右。
韩寒:陆 天明骂作协门槛降低,把抄袭犯 都收进来了,说自己当年加入作协如何难,你进入作协身份就高人一等吗?我认为,国家就不应该有这些协会,想当初,加入作协对你的意识形态有所控制,文字更 加奴性,现在虽然好很多,但对创作没有任何帮助。一帮作家闲云野鹤的,自由创作多好,要什么协会。我在电视上一看到那些作家参加什么会的时候,和一些领导 弯腰点头握手,表示一定要把颂歌唱得更动听。作为一个作家,这种行为是不道德的,你仗着自己文字功底好,和喉舌机构的御用文人抢饭碗,怎么可以嘛。
南方周末:怎么看待那些和你一起出道的年轻人的改变?加入作协、买房、结婚、生子、赚钱。
韩寒:我不会因为岁数增长而改变。很多人婚姻不幸,其实是因为岁数的原因,到了一定的岁月就要结婚、生子、赚钱,所以才造成了很多不幸,我现在还住在老家乡下房子里。我内心很有安全感,房子、钱,我并不觉得这些很安定。一场地震一分钟就能摧毁一切,保险公司还都不赔。好多人没有安全感,所以要寄托在一些身外之物上。
南方周末:你也会老的,你能永远保持少年偶像这个形象吗?
韩寒:对 我来说,少年偶像是别人给的,不是我自己要保持,或者说不是我想保持下去我就能保持下去的。这是一种惯性,你自己一点办法都没有。生活里我是一个很不修边 幅的人,没有经纪人、助手。我也看过明星玩赛车,赛车前还在那里化妆,其实比赛的时候有很多汗,化妆干什么?他们才是真正想要做偶像,想要做明星。
几年前就有人请我拍戏、唱歌、做商演、做代言,都被我拒绝了。有钱是一个很好的事情,如果能够赚更多的钱更好,但问题是赚那个钱我心里会不舒服,我会权衡这个重要,还是钱重要。比如说你看到某个作家在那里推荐药、肾宝、洗液什么的,我觉得那个效果很怪。权衡下来,我觉得单纯的钱不能打动我。
我去年推掉的代言至少有500万到1000万,全部推掉了。去年靠写作和赛车,我一共赚了两百万,我认为自己归根结底还是一个写东西的人。
我发现,人生里30-50岁这一段是最尴尬的,不再是一个愤怒青年,也不是一个老顽童,有些不伦不类。我现在离尴尬的时间已经很近了,所以我要比他们更早地打好基础,以免像崔健一样,步入中年再交出DV短片那样的学生作业来。
南方周末:近两年,你一直在博客上发言,什么户口啊、交通啊都要参与一把。动机是什么?
韩寒:我在博客上写那么多免费文字,就一些社会问题发言,有人说我是在炒作知名度卖书。我写博客真的不是为了炒作自己,写那么多文字,甚至参与那么多讨论,还和人吵架,没有任何经济收益,甚至我觉得自己在影响书的销量。
作为一个公民,我认为我有权对一些社会问题发表自己的观点和意见。以前只对朋友说,现在对大家说。
南方周末:你愿意成为王小波那样的公共知识分子吗?
韩寒:想 起王小波是很难受的一件事情。王小波生前写了那么多文字,苦口婆心讲道理说常识。后来他死了,人们才假装发现了他作品的价值,觉得他写得不错,是个优秀的 作家。如果王小波没有死,到今天的话,他在人们口中应该算是那种一天到晚炒作的人吧。炒作和冒着一定的风险发表观点是有很大区别的,也是非常好分辨的。只 可惜,大家似乎都分辨不了。
在这个国家,做一个忧国忧民的人是最傻和最痛苦的,国家不乐意,国民不在意。我不要做那样的人,我只希望自己60岁时是个被年轻姑娘喜欢的深沉的老顽童。
工作两年了。这两年的时间一直感觉很充实。最近却突然感觉有点无聊的感觉——以前两年一直计划学的东西都学差不多了。
了解函数式编程的途径中,知道了lambda演算。简单看了看,发现居然是数学的一个分支——可叹我数学系理学学士,居然是第一次听说这个名词。google之下,不胜渺小了。于是后悔起大学的幼稚来。
上学的时候,总是抱怨整天学习、面对的都是数学,枯燥无聊而且没用,除了大一大二好好学习,大三大四都是逃课比上课多,而且居然因此自鸣得意.....幼稚...
最近有点小想法。突然觉得我这样的程序员没有技术含量,平心而论,现在我写的东西,一个高中生也可以写;而我大学学的那些东西,这些年一点没用上,这说明这份工作技术含量不是很大。那么做开发最大的技术含量在哪里?其实最大的技术含量(即..的解决方案)在写代码之前以前做完了,代码不过是把(..解决方案)具体化了而已,所谓的“..解决方案”,有业务的,也有技术上的,反正不是代码。无论精通struts还是hibernate,还是spring,会rpc、ext....都没有区别,之不过把已经有的组合起来,是谁都可以做。
考虑考虑自己的未来之路......
首先现了一个ruby的netbeans,地址....(这里没记清楚:),这个东西大小才30m,还不错。
然后感觉字体不爽,就想换字体(参见那个啥的方法——把vjre\lib\font*.properties里面东西改改),然后也ok了。
开始写代码的时候,觉得屏幕好像刷新率不够似的,于是又google了一下,把jdk换成6的(正搞个openjdk玩)——那个je上的啥说的。然后发现还是不快,换成jdk5的,快了好多。郁闷一个。。。。。
下面是ruby笔记第一天
#
# To change this template, choose Tools | Templates
# and open the template in the editor.
##一,了解ruby中类的构造
#定义一个类,并定义初始化方法,可以给new使用做构造函数
class Dog
def initialize(name)
@name = name
end
# def initialize(name,color)
# @name = name
# @color = color
# end
#在类中追加定义一个方法,@是ruby中的变量的开头.默认的ly,ruby认识什么是属性什么是变量
def eat(food)
@food = food
puts @name + "eat "+@food
end
end
class Dog
def la
puts @name +" la "+ @food
end
end
#你看,我们定义了一个啦的方法,把狗狗吃的全啦出来了:-)
@dd = Dog.new("Big Dog")
@dd.eat('food')
@dd.la
##他还支持重载么?看一下我们定义的构造函数(的时候没报错,现在调用的时候呢??)——他告诉我
#E:\mydoc\NetBeansProjects\RTest\lib\main.rb:29:in `new': Wrong # of arguments(1 for 2) (ArgumentError)
##from E:\mydoc\NetBeansProjects\RTest\lib\main.rb:29
#@gg = Dog.new("name", "color")
#@gg.eat(food)
#@gg.la
puts "Hello World"
——javablog居然不支持ruby的语法。就模拟vb的好了,反正都有个end
度假回来以后就来uns上班了。uns的框架是evan写的,看了两天:服务器端还满容易懂的,关键是ext.js不熟。
evan准备使用ext.js(jquery),以前我只使用过propetype.js,所以jquery.js也要看,还好,一看之下发现是更好用的js框架。
——马上台风了,接下来回家写。。。。。。
ext.js没什么感想。因为不熟,evan些的服务器端我有点感想。
1,首先可以肯定的是evan些的这个框架非常好,spring,hibernate,webwork等使用的非常恰当,层次结构也非常合理
2,evan和我说他们是TDD开发。我看了一下,其实不是的。
所谓的TDD是什么,是测试驱动,是需求驱动。现实的情况我们实践的是数据库驱动的开发,更严重的问题是这个结构一杆子到底。我不希望看到UserInfoDaoHiberante-UserInfoDao-UserInfoManagerImpl-UserInfoManager这样的结构。这是完全不对的,不论什么驱动的开发,都应该完全以业务为中心的结构UserInfoDao-UserDao-UserManagerImpl-UserManager
3,因此evan的所谓那个模板我看还是不用的好。
这里记下来,回头和evan讨论下。
去度假之前我塞了本spring到旅行包里,寻思路上看看。可是路上实在是净不下心来,于是暗暗告诫自己:回来的路上一定要看一看。。。。。回来的路上基本上光睡觉了。惭愧啊~~~
可是那本spring,的确是让人生厌,自从上周翻完以后,我就再也提不起再读一遍的兴趣来。我的想法就是理解其所以然,至于具体用法用时再查就是了。于是心安理得的看了一会恐怖片,上一会网——发现了一个讨论讨论ejb架构的帖子。于是想起来再读WITHOUT EJB的兴趣来。上次读的时候,大不以为然,大家把这本书都夸到天上去了,可是我什么都没看出来,感觉就是在谈概念,今天一读之下(其实还没读,就是看看目录,猜猜里面讲什么,猜测作者意图传达的想法)发现真不愧是人人传诵的好书。J2EE最近两三年的潮流,不外是此书罢了。
一年来,从当初刚刚会struts、hibernate的小菜鸟,经历了追逐新的技术潮流,理解新的技术理念,从仅仅负责项目的某个模块到自己负责整个项目,从编写代码的程序员到真个项目的需求规划管理等。我不是当初的我了。
所以我想:读书,何必强求自己呢!
倘若有读书的爱好,他喜欢读哈利波特,何必非要强求他读红楼梦呢。顺其自然而已。
刚刚离职,本来想好好休息一阵子。没想到前天以前一个同事对我说要我给他一份简历,他们公司缺人。我不好意思推托,就答应了。
面试那天,当然是我那个同事面试我的,可是令我惊奇的是另外一个面试官也是我的一个同事,不过是上上家公司的同事。。。。。好惊奇啊!!!!
于是大家攀谈了好大一会,顺便聊了点面试题目
1,xmlFactory和ApplicationContent的区别
我还真是不太了解,隐约知道后者好像是对前者的加强。。。。。哎~~~那个郁闷啊
第二个问题更郁闷呢~
2,悲观锁和乐观锁的区别
我隐约记得以前做同事的时候他就说过这个,那时候我还搜索了一下。现在却怎么也想不起来。。。。。哎~~~~更郁闷啊~~~~
还有第三个问题
3,写过JS表树么>.>..>......
刚开始我还以为是树,就说EXT不是有现成的么?他说是表格里的树。于是我明白类似RCP中的TableTreeViewer。老实说没有。然后他告诉我他们准备写一个这个。。。。。
我的那个汗~~~~~
面试还从来没这么窘迫过啊 ~~~~
不过,也挺有趣的。。。。
还没毕业就工作到现在,整整工作了两年半的时间,一直是忙忙碌碌。有时候我很奇怪为什么我所在的这两家公司为什么就这么忙,另外那几个程序员同学就这么悠闲,但是很幸运的是,爱思考和学习的习惯一直保持着,这两年一直感觉很充实。
记得刚毕业的那会,非常好玩:除了jsp啥也不会,啥是struts,啥是hibernate,啥是spring都不知道,也不敢问——因为进公司的时候CTO考我struts会不会。其实我根本就是因为上一家面试听说过这个词语马上回去google了些概念。就开始忽悠,还非常理直气壮的。。。。。后来我就进去了,工作了两个星期以后有一天发现CTO缠着一个高程问struts到底是啥玩意。。。。。。汗~~~~~~
那时候好辛苦,因为谎撒下来了。所以即使不懂也要硬着头皮干,而且还要在工期内。。。。。。基本上每天晚上回去我都要翻书学习,或者跟别人讨论概念。很辛苦可是很愉快。
两年后的今天,虽然我感觉自己还是很菜,可是居然发现很多工作了4、5年的甚至还不如我。我很郁闷。。。。。。所谓的程序员,不好好学习写程序,整天在说:做这个好累。。。赚钱好少。。。加班好辛苦。。。转型做管理。。。。云云。
浮躁啊。。。。。
回忆起来,选择现在这家公司真的很轻率。当时对RIA技术真的很看好,就希望能到使用RIA的公司。尽管另外一个非常年轻的有活力的team极力邀请我还是选择了她。可是在这里真的很孤单。找不到志同道合的朋友,大家都是写写代码能RUN就行,没有规划,没有测试,没有重构,没有对代码的美的追求。。。。。。曾经我希望给大家做个培训,可是反问自己有资格么?我努力做了很多改进,可是经过了项目经理的手就被改的面目全非。。。。。。
解脱了。
很多人都直接编辑html,保存成xls就当成excel报表了。挺方便的,于是我写了这个简化工作的工具类——将一个html的表格模板解析成一个xls报表
模板如下
<?xml version="1.0" encoding="GB2312" ?>
<div style="width:100%;height:450;overflow-x:auto;overflow-y:auto">
<table width="100%" border="1" cellspacing="2" cellpadding="0">
<tr id="title" bgcolor="#fefcce">
<td nowrap="true" >客户</td>
<td nowrap="true" >产品</td>
<td nowrap="true" >中文名称</td>
<td nowrap="true" >英文名称</td>
<td nowrap="true" >产品分类</td>
<td nowrap="true" >包装</td>
<td nowrap="true" >单位</td>
<td nowrap="true" >数量</td>
<td nowrap="true" >冻结数量</td>
<td nowrap="true" >可用数量</td>
<td nowrap="true" id="CUBIC"></td>
<td nowrap="true" id="WEIGHT"></td>
</tr>
<tr id="record">
<td nowrap="true" id="CUSTOMERID"></td>
<td nowrap="true" id="SKU_ID"></td>
<td nowrap="true" id="SKU_DESCR_C"></td>
<td nowrap="true" id="SKU_DESCR_E"></td>
<td nowrap="true" id="SKU_CLASS"></td>
<td nowrap="true" id="PACKAGE_ID"></td>
<td nowrap="true" id="UOM"></td>
<td nowrap="true" id="QUANTITY"></td>
<td nowrap="true" id="FREEZE_QUANTITY"></td>
<td nowrap="true" id="AVAILABLE_QUANTITY"></td>
<td nowrap="true" id="CUBIC"></td>
<td nowrap="true" id="WEIGHT"></td>
</tr>
</table>
</div>
工具类如下
public class ExcelTemplateUtil {
private static String CHARSET = "";
private static final String ROOT = "ROOT";
private static final String TITLE = "TITLE";
private static final String RECORD = "RECORD";
private static Map temp = new HashMap();
public static String generateListToTemplate(Object titleObj, List recordList, File templateFile)
{
readTemplateFile(templateFile);
ByteArrayOutputStream os = (ByteArrayOutputStream) builderExcelOutput(titleObj, recordList);
return removeXMLHeader(os);
}
public static void readTemplateFile(File file)
{
try {
Document templateDocument = new SAXReader().read(file);
Element root = templateDocument.getRootElement();
List trList = root.selectNodes("//div/table/tr");
Element titleTemp = (Element) trList.get(0);
Element recordTemp = (Element) trList.get(1);
root.element("table").remove(titleTemp);
root.element("table").remove(recordTemp);
temp.put(TITLE, trList.get(0));
temp.put(RECORD, trList.get(1));
temp.put(ROOT, root);
} catch (DocumentException e) {
e.printStackTrace();
throw new RuntimeException("Parse xml file error, Cause:", e);
}
}
public static OutputStream builderExcelOutput(Object titleObj, List list)
{
ByteArrayOutputStream os = new ByteArrayOutputStream();
Element root = (Element) ((Element) temp.get(ROOT)).clone();
Document document = DocumentHelper.createDocument();
document.setRootElement(root);
Element tableEle = root.element("table");
tableEle.add(parseTitleElement(titleObj));
for (int i = 0; i < list.size(); i++) {
tableEle.add(parseRecordElement(list.get(i)));
}
try {
OutputFormat format = new OutputFormat("", true, "GB2312");
XMLWriter writer = new XMLWriter(os, format);
writer.write(document);
writer.flush();
writer.close();
os.close();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
throw new RuntimeException("Parse outstream error, Cause:", e);
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException("Parse outstream error, Cause:", e);
}
return os;
}
public static Element parseTitleElement(Object titleObj)
{
Element titleEle = (Element) ((Element) temp.get(TITLE)).clone();
if (null == titleObj) return titleEle;
List tdList = titleEle.selectNodes("td");
Element td;
for (int i = 0; i < tdList.size(); i++) {
td = (Element) tdList.get(i);
fullField(td, titleObj);
}
return titleEle;
}
public static Element parseRecordElement(Object recordObj)
{
Element recordEle = (Element) ((Element) temp.get(RECORD)).clone();
List tdList = recordEle.selectNodes("td");
Element td;
for (int i = 0; i < tdList.size(); i++) {
td = (Element) tdList.get(i);
fullField(td, recordObj);
}
return recordEle;
}
public static void fullField(Element tdEle, Object obj)
{
Attribute att = tdEle.attribute("id");
if (null == att || null == att.getText() || 0 == att.getText().trim().length()) {
return;
}
String fieldName = att.getText();
if (null == fieldName || fieldName.trim().length() == 0) return;
Method[] objMethod = obj.getClass().getDeclaredMethods();
Object value;
for (int i = 0; i < objMethod.length; i++) {
if (("get" + (fieldName.trim())).equals(objMethod[i].getName())) {
try {
value = objMethod[i].invoke(obj, new Object[]{});
value = (null == value ? "" : value);
tdEle.setText(value.toString());
} catch (IllegalAccessException e) {
e.printStackTrace();
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
}
}
public static String removeXMLHeader(OutputStream os)
{
String xml = os.toString();
int position = xml.indexOf(">");
// xml = xml.substring(position+1,xml.length());
// position = xml.indexOf(">");
return xml.substring(position + 1, xml.length());
}
}
调用
OutputStream os = response.getOutputStream();
response.setHeader("Content-disposition", "attachment; filename=" + excelFileName);
response.setContentType("application/msexcel");
File excelTemplateFile = new File(report_path + templateFileName);
String out = ExcelTemplateUtil.generateListToTemplate(titleObj, resultList, excelTemplateFile);
response.getOutputStream().write(out.getBytes());
os.close();
异常争论
异常有两个模型:中止模型和继续模型
中止模型认为异常不应该再回来,他做的是善后工作。而继续模型保持异常时环境,希望再一次能运行成功。
Java采用的是前者(一般语言都是前者),而OS一般采用后者。
Java异常有三类:错误,运行时异常,检查型异常。
官方的观点是
第 39 条:最好为异常条件使用异常。也就是说,最好不为控制流使用异常。
第 40 条:为可恢复的条件使用检查型异常,为编程错误使用运行时异常。
第 41 条:避免不必要的使用检查型异常。
第 43 条:抛出与抽象相适应的异常。(使处理异常更直观)
在异常的使用上,专家的观点是很不一样的
C#作者Anders根本就忽略检查型异常。
Bruce Eckel,声称在使用 Java 语言多年后,他已经得出这样的结论,认为检查型异常是一个错误 —— 一个应该被声明为失败的试验。
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
缺点1,代码中包含了过多的catch,使得代码不清晰
缺点2,有时候捕捉的异常没有什么实际意义
缺点3,不够清晰的错误指示。
缺点4,过深的异常层次。
缺点4,性能。
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Eckel 提倡将所有的异常都作为非检查型的,并且提供将检查型异常转变为非检查型异常的一个方法,同时保留当异常从栈向上扩散时捕获特定类型的异常的能力
Rod Johnson ,他采取一个不太激进的方法。他列举了异常的多个类别,并且为每个类别确定一个策略。一些异常本质上是次要的返回代码(它通常指示违反业务规则),而一些异常则是“发生某种可怕错误”(例如数据库连接失败)的变种。Johnson 提倡对于第一种类别的异常(可选的返回代码)使用检查型异常,而对于后者使用运行时异常。在“发生某种可怕错误”的类别中,其动机是简单地认识到没有调用者能够有效地处理该异常,因此它也可能以各种方式沿着栈向上扩散而对于中间代码的影响保持最小(并且最小化异常淹没的可能性)。
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
解决1:谨慎的抛出检查型异常。或者你认为,你可以处理它。否则,包装为运行时异常。
解决2:如果遵守1,2不是问题
解决3:异常不跨层,否则必须捕捉或者包装。
比如持久层丢出的SalException,你或者丢弃/处理/包装(为运行时异常),或者重新包装为业务层异常。保持JEE层的独立和异常的清晰性。
包装底层异常,保持异常链。
解决4:如果符合1,4也不是问题。再次强调,能捕捉就捕捉。
解决5:减少异常使用,减少层次。
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
在je里面,robin认为异常是流程控制的一部分——当然,考虑到性能问题,这个流程不应该是大概率流程——也就是异常流程
例如用户登录
Try{
用户登录(用户名,密码);
登录成功;
}catch(没有这个用户异常 e){
错误提示界面;
}
Potian则认为,没有用户是正常业务逻辑的一部分
If(!用户业务层.没有这个用户(用户名))错误提示界面;
If(用户业务层.检验密码(用户名,密码))登录成功;
else 登录失败;
Potian认为不应该在一个业务中包含了过多的责任。
Ps:在servlet中,我喜欢仅仅简单的在action中调用最好一个业务层方法就可以完成此action的任务。这意味着我的servlet非常瘦,可以比较容易的被替换。如果采用了potian的办法,则意味着我要把业务层中的代码前移到servlet中来,这模糊了业务层的责任。解决的办法是回到老路子上来。
Ps:我还认为,没有异常的业务方法表达能力太弱,异常给了他们更丰富的表达能力。这使得业务层可以更丰富的表达业务意义。避免将业务责任分散掉。
我认为在业务层中,恰恰要包含足够的责任。不多也不要少(流程分支-2最好)。在别的层次中,要细致一点。
在爵士主场被连扳2场,比分2:2平。
比分不重要,关键气势上被完全压倒了。
当初头又大中心win那两场,也比较悬;输掉的这2场却比较爽快,这说明爵士是非常有韧性的球队,恰巧mm都是比较软的0
难道又是05?
那jeff可以考虑走人,火箭考虑重建。
可以看的出来,火箭当初请jeff很大一个原因就是在最好的教练(jeff,布朗,禅师)里面,他比较擅长执教中锋。
可是输掉这两场,却看的出来他有几个比较致命的缺点:临场战术指挥能力差,战术死板缺少变化,使用板凳或保守或激进——毫无章法
火箭输掉的这两场可以看的出来mm几乎被对方研究透了,经常看到yao一转身,人家手一伸把球给给拍掉,tm高位挡拆,人家阻夹tm(tm和yao的挡拆只挡不拆)。。。。。战术被人家研究透了,节奏完全混乱,常常看到替补们空位不进。这个时候教练应该做点什么,可是jeff做了什么??!
jeff做的非常好的地方,还是防守。我常常看到火箭进攻的时候就tm底线卷切出来接球,这个时候yao提到45度附近一个挡拆,然后tm就得到空位突破或者跳投,如果突破得到协防的话,常常的看到tm分到空位三分。还有yao底线卡位拿球攻击内线,迫使对方收缩防线,或投或传。可是当这两招被对方破解以后,我看到火箭队员在进攻的时候开始茫然无措了,不知道如何跑位,不知道如何配合了。——于此相反,一回到自己半场,就好像死鱼放回水里一样,防守起来却是井井有条,活跃起来。
你永远也别向光用防守来解决问题——因为篮球经常出现无法防守的局面(比如kb大婶发飙,tm手感到来,这个时候你派谁,用什么战术,使他陷入多么不合理的出手都不行),这个时候你要用进攻来回应他。
jeff没有办法。
你回家吧jeff,如果你证明自己只是这个样子的话。不是我黑你,你太让我们失望了,你在浪费yao和tm的生命。
版权声明:可以任意转载,转载时请务必以超链接形式标明文章原始出处和作者信息及本声明
英文原文地址:
http://www.javaworld.com/javaworld/jw-06-2005/jw-0620-tikeswing.html中文地址:
http://www.matrix.org.cn/resource/article/43/43731_Swing_MVC_POJOs.html关键词: Swing MVC POJOs
摘要:TikeSwing 是一个开放源码的Swing框架,它提供了一个高度MVC(模型-视图-控制器)模式的体系结构并且使SWING组件的使用非常简单。它通过将视图组件和JavaBeans直接连接来支持POJO编程模式。在这篇文章中将阐述TikeSwing的特点,并且将示范怎样使用这个框架创建一个清晰的MVC的系结构。(2,400个英文单词;2005年6月20日)
最近,在Java社区里面,丰富的互联网应用程序(RIAs)的兴起成为一个热点话题。另外一些新的技术,像AJAX(异步的JavaScript和XML),MacroMedia Flex, 和Laszlo,以及与Java Web Start一起使用的虽旧而好的Swing,它们都被提议作为RIA技术。
然而,Java社区里面的很多人对Java基础类库(JFC)和Swing提出了批评。Swing在建立高度MVC模式的客户端体系方面不能提供太多的帮助。任何合理的服务器应用程序返回传递的对象,或者称为简单初始Java对象(POJOs),把它传递到客户端的技术证明了J2EE世界的窘境。从POJO范围映射到Swing组件需要太多的手动的代码,反之亦然。
同样的,实现Swing其他的功能,就像线程句柄和验证域,也是很费力的事情。而且有时候Swing组件很难使用:创建一个合适的表格或者树模型通常需要很多的编码,而且需要深入的研究Swing编程文档中的API。
TikeSwing 是一个开放源码的Swing框架,它提供了一个高度MVC(模型-视图-控制器)模式的体系结构并且实现了模型,组件和控制器通信的自动化。它简化了Swing组件的使用,并通过将视图组件和JavaBeans直接连接来支持POJO编程模式。
这篇文章将示范怎样使用TikeSwing创建一个清晰的MVC的体系结构。也将阐述建立TikeSwing组件的原则,并简单描述在这个框架中包含的最佳体验和机制。
MVC体系结构众所周知,MVC范例是推荐的图形用户界面发展的基本体系。它还有很多的可用的变种,就像MVC++, HMVC (Hierarchical MVC), MVC Model 2, MVC Push, and MVC Pull,它们每一个都有些不同之处。TikeSwing基于下面的MVC原则:
●Model 模型:
o来自一些真实世界或者系统的抽象
o包装其数据和函数
o在数据改变时通知观察者 (编者注:observer, 设计模式术语)
●View 视图:
o系统的用户界面
o依附于模型并通过显示界面将它的内容显示出来
o在模型改变时自动刷新受到影响的部分
●Controller 控制器:
o控制应用程序的流程
o接受用户的输入,并根据用户输入指导模型和视图完成任务
下面的图表表示了TikeSwing中MVC的类结构。
图 1. 一个使用TikeSwing的应用的MVC类图
类MyModel, MyView, 和MyController由一个使用框架的应用来实现。MyModel和MyController扩展了TikeSwing的YModel 和YController类。一个视图的类可以是任何实现了YIComponent接口的java.awt.Component。
TikeSwing在装配类结构的时候不使用任何的配置文件。当YController,YModel和视图组件提供了要求的功能特性的时候,扩展适当的类已经足够了。下面讲述如何使用TikeSwing来实现模型、视图和控制器类。
模型TikeSwing的模型是一个为实现视图而包含数据的JavaBeans组件。一个模型类可能包含嵌套的JavaBeans,数组,映射和集合。和标准JavaBeans中要求的一样,所有模型的类变量必须有适当的GET和SET方法。从这种意义上说,TikeSwing就像很多的网络应用程序框架那样工作,所以在不同的技术之间重用模型类是很容易的。
YModel是模型的基类。它提供了报告数据改变的方法。当触发了一个事件的时候,框架会更新与之相连的视图。在分布式环境中,一个模型类有从服务器应用程序中得到POJOs的方法(通常是从隐藏了业务服务的实现细节的业务代理中)。模型自身存储了POJOs,且它有责任通知观察者。在有些MVC的体系结构中,一个控制器类和服务器通信,POJOs存储在控制器中。然而,TikeSwing分离出YModel类的方法有下面的优势:控制器专著于流程,另外的方法(操作模型数据的)可以被加在客户端。YModel遵循了传统的MVC模式,所以MVC中类的责任就清晰地分开了。
下面的代码演示了模型类如何通过给定的参数找到customers。模型的类变量name和id是搜索标准,customers是包含搜索结果的Customer POJOs的集合。findCustomers()方法通过customerServiceDelegate从服务器应用程序中得到customers。当方法notifyObservers()激活时,框架会自动更新相连的视图。
public class FindCustomerModel extends YModel {
private String name;
private String id;
private Collection customers;
private CustomerServiceDelegate delegate = new CustomerServiceDelegate();
public void findCustomers() {
setCustomers(delegate.findCustomers(id, name));
notifyObservers("customers");
}
public void setCustomers(Collection customers) {
this.customers = customers;
}
public Collection getCustomers() {
return customers;
}
public void setId(String id) {
this.id = id;
}
public String getId() {
return id;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
视图TikeSwing视图是包含其他Swing组件的Swing组件。通常,一个视图类是一个面板,一个对话框,或者一个帧,它们建立了子组件并将之添加到自身(就像在通常的Swing开发环境中一样)。然而,TikeSwing应用程序中使用的所有组件都必须实现适当的接口以连接框架的MVC体系结构。幸运的是,框架包含一个很大的为了这种目的已经实现的组件的集合。
一个特殊的名字必须赋予一个视图组件,这样框架就能在组件和被命名的模型类变量之间复制数据。命名的惯例和其他的用于网络应用程序框架的和Apache BeanUtils库(它通常用于框架的执行)类似。下面是支持的命名格式:
●简单的: 直接连接到模型域的组件;例如,field1
●嵌套的:连接到模型内部的JavaBeans域的组件;例如,field1.field2
●索引的:连接到模型内的数组域的组件;例如myArray[1]
●映射的:连接到模型内的映射域组件;例如,myHashMap(“foo”)
●组合的:通过结合符号连接到模型的内部域的组件;例如,field.myArray[1].myHashMap["foo"]
除了模型类的GET和SET方法外,视图类必须为每一个视图组件建立一个GET方法。
下面的例子是为FindCustomerModel建立的视图类。它使用了扩展了基础Swing类的TikeSwing组件(从JLabel到YLabel,JTextField到YTextField,等)。例子的代码和标准的Swing视图很像,只有setMVCNames()方法包含了TikeSwing特有的代码。依照上面讲述的原则,它设定了模型组件的连接。resultTable列通过YColumn对象与customers集合中的POJO域相连。findButton不显示任何从模型得到的数据,但是MVC的名字是为TikeSwing的事件句柄设定的(以后再讲)。
public class FindCustomerView extends YPanel {
private YLabel idLabel = new YLabel("Id");
private YLabel nameLabel = new YLabel ("Name");
private YTextField idField = new YTextField();
private YTextField nameField = new YTextField();
private YPanel criteriaPanel = new YPanel();
private YTable resultTable = new YTable();
private YButton findButton = new YButton("Find");
public FindCustomerView () {
addComponents();
setMVCNames();
}
private void setMVCNames() {
idField.getYProperty().put(YIComponent.MVC_NAME,"id");
nameField.getYProperty().put(YIComponent.MVC_NAME,"name");
resultTable.getYProperty().put(YIComponent.MVC_NAME,"customers");
findButton.getYProperty().put(YIComponent.MVC_NAME,"findButton");
YColumn[] columns = {
new YColumn("id"),
new YColumn("name")};
resultTable.setColumns(columns);
}
private void addComponents() {
this.setLayout(new BorderLayout());
this.add(criteriaPanel, BorderLayout.NORTH);
idField.setPreferredSize(new Dimension(100, 19));
nameField.setPreferredSize(new Dimension(100, 19));
criteriaPanel.add(idLabel);
criteriaPanel.add(idField);
criteriaPanel.add(nameLabel);
criteriaPanel.add(nameField);
criteriaPanel.add(findButton);
this.add(resultTable, BorderLayout.CENTER);
}
public YTextField getIdField() {
return idField;
}
public YLabel getIdLabel() {
return idLabel;
}
public YTextField getNameField() {
return nameField;
}
public YLabel getNameLabel() {
return nameLabel;
}
public YTable getResultTable() {
return resultTable;
}
public YButton getFindButton() {
return findButton;
}
}
现在,无论任何时候用户修改idField 或者nameField,改变的地方都会自动更新到模型。而且,当notifyObservers()在 FindCustomerModel中调用的时候,框架会更新变化到resultTable。然而,为了匹配结构,一个控制器必须是特定的。
控制器TikeSwing的控制器通过调用视图和模型的方法来处理应用程序的流程。一个控制器的类必须扩展YController,它提供了控制关系中的必要的方法。通常,控制器也创建视图和模型对象,但是要注意的是,几个视图和控制器可能共享相同的模型对象。
一个控制器类可能有好几种方法来获取用户事件。TikeSwing组件包括基于反射的事件句柄:一个事件可以通过实现带有合适签名的方法而在控制器类中得到处理。例如,当用户点击按钮的时候,一个MVC名字为myButton的按钮在控制器中会调用myButtonPressed()方法(如果实现了的话)。这与标准的Swing事件监听接口和适配器相比是很方便的。
另一方面,事件方法签名中的字符在编译器中是不显示的,但是Swing适配器类的情况是:编译器不说明public void actionperformed是一个新的或者重载的方法。因为监听接口经常需要许多空的方法的执行,基于反射的简单的事件处理一定会加快代码的进程。作为选择,你可以在视图类中使用标准的监听者,而手动调用控制器的方法。
下面的代码是FindCustomerModel和FindCustomerView的控制器的一个例子。控制器通知MVC的结构是通过调用setUpMVC()方法和使用findButton 来处理基于反射的事件。
public class FindCustomerController extends YController {
private FindCustomerView view = new FindCustomerView();
private FindCustomerModel model = new FindCustomerModel();
public FindCustomerController() {
super();
setUpMVC(model, view);
}
public void findButtonPressed() {
model.findCustomers();
}
}
YController是TikeSwing中功能的核心。除了上面讲述的特点之外,它还提供了很多有用的方法能用于:
●捕获特定域的改变
●在控制器中发送和接收信息
●跟踪用户的修改
●取消用户的改变
●捕获模型抛出的异常
●验证域值的有效性
TikeSwing组件TikeSwing基于这样一种思想,组件负责处理在模型中相关联的对象。这种思想以前在Sun的《Swing指南》中的WholeNumberField演示中有体现。组件必须知道怎样在屏幕上面显示模型的值和怎样转换用户给定的值到模型中。
框架现在提供了一个足以使大多数应用程序使用的组件的集合。框架组件的行为就像基础的Swing组件,当然了,你必须阅读Java文档以理解组件和MVC类的交互(组件可以处理什么类型的模型域和它提供了什么事件的方法)。TikeSwing组件也提供了其他的特点和简洁的开发。例如,一个POJOs的集合可以在不创建任何特殊的组件模型的情况下直接使用于YTable和YTree。
TikeSwing组件基本上可以是任何的java.awt.Component。然而,一个组件必须实现适合的TikeSwing接口,那样它就能被集成到框架的MVC的体系结构中。它通常包含扩展了带有四个简单方法的标准Swing组件,因此这将是一个比较琐碎的任务。下面的代码是一个例子。和模型的集成是通过getModelValue() 和setModelValue()方法实现的。组件值的改变的通知是addViewListener()方法实现的。为了能在框架内部使用,必须实现getYProperty()方法。
下面的代码演示了一个支持Integer对象的简单文本域:
public class YIntegerField extends JTextField implements YIModelComponent {
/** Gets value of this field for the model. */
public Object getModelValue() {
try {
return new Integer(getText());
} catch (Exception ex) {
return null;
}
}
/** Sets the model value into this field. */
public void setModelValue(Object obj) {
if (obj == null) {
setText("");
} else {
setText(obj.toString());
}
}
/** Notifies the framework when the component value might have changed. */
public void addViewListener(final YController controller) {
this.addFocusListener(new FocusAdapter() {
public void focusLost(FocusEvent ev) {
controller.updateModelAndController(YIntegerField.this);
}
});
}
// The rest is for the framework internal use,
// the implementation must be copied to each new component:
private YProperty myProperty = new YProperty();
public YProperty getYProperty() {
return myProperty;
}
}
其它的特点除了MVC的体系结构,TikeSwing还有很多协助进行Swing开发的其它的特点。这些特点不是什么革命性的东西,它们可以在很多已经实现的Swing应用程序上面看到。但是,没有必要重新发明轮子,一些最好的Swing开发的体验包含在了这个框架中。
TikeSwing支持控制器多层结构的创建,就像在HMVC和MVC++中描述的那样。框架提供了使控制器之间实现父子关系的方法,这使类结构更协调和清晰。这种关系又助于和客户应用程序通信,而且可以用来和众所周知的设计模式集成。TikeSwing支持任务链模式,这种模式中,一个请求直到控制器对象才处理事件时才被传递。TikeSwing也支持Observer/Observable模式:一个控制器类可能传递一个能被所有已经注册了的控制器处理的事件。
TikeSwing也包含一种为tabbed panes检索慵懒数据(lazy data)的机制。在一个分布式的系统中,一下子从服务器得到所有tabs的数据可能需要很长的时间。为了优化性能,有必要只在每个tab被选择后才为其检索一次数据。框架提供了简化这种功能的机制,所以代码的复杂性,特别是在嵌套的tabbed panes里面,已经减少了许多。
当用户触发一个事件,可能导致刚修改的数据丢失的时候,一些应用程序会检查未被保存的改变。这些事件可能是下面的例子,关闭窗口,改变tabbed pane的tab的焦点,或者选择一个表格的列。TikeSwing 提供了进行检查特殊事件的工具。TikeSwing也会自动弹出“是否保存更新?”的对话框,并委托一个控制器方法来保存。另外,框架记得视图在特定时刻的状态,可以在稍晚的时候返回那种状态。这就意味着框架可以在不取得原始数据的情况下取消改变。
当两个或更多的组件执行相同的函数的时候,Swing的行为被证明是有用的。一个Action对象提供了集中的事件处理,但是如果行为用于单独的类的话,代码会因为增加的耦合而更加复杂。TikeSwing包含了一个集中处理产生事件的场所,因此一个动作可以用于不同的视图类而且不会直接耦合。
Swing组件只能由事件分派的线程进行创造,修改和查询,这使Swing应用程序中的线程处理更加复杂。《Swing指南》中说SwingWorker类对这个问题提供了帮助。TikeSwing封装了SwingWorker,并且使线程处理更加简单。例如,一些应用程序在进行远程调用或I/O操作的时候不会死锁。使用TikeSwing,在进行这样的操作时可以弹出一个可管理的,可重画的对话框,而且实现只需要几行代码。
Summary 总结由于有了高级的MVC和POJO的支持,TikeSwing简化了Swing的开发。使用TikeSwing是合理的,特别是在分布式环境中,由服务器应用程序返回的POJOs可以直接用于模型类,这个类直接连接到视图类。这个框架也包含了一些解决复杂开发问题的最佳实践。因此,TikeSwing减少了为Swing客户所写的代码,加快了开发。
TikeSwing自身提供了丰富的平台无关的用户界面库。Swing开发已经成为这几年一些重要的IDE的一部分,所以可见即所得的设计,单元测试和调试已经被广泛地支持。早先的工作站上性能的问题现在已经不是问题了,Java的网络应用也简化了分布式的Java应用程序。与网络应用程序的框架相比,Swing提供了更加友好的用户界面,没有JavaScript支持的问题,通过工作站上面的客户逻辑简化了网路上的通信量。
对Swing复杂性的批判依旧是正当的。但是,使用像TikeSwing的高级MVC框架,复杂性就减少了,Swing就转换成了一个生产力很高的客户端技术。我希望Java社区为Swing开发和采用一个开源的MVC框架,这将使其成为RIA技术中的一员。可能像Spring似的肥客户端技术更加接近目标。与其等待,不如请出TikeSwing,体验一下它是如何适应你的RIA工程的。
关于作者Tomi Tuomainen是Entra e-Solutions的顾问和架构师,他从1999年开始使用J2EE应用系统和Java框架。他是计算机科学的理学硕士和SUN的认证企业架构师。他的兴趣(Java之外的)在于音乐,吉他和体操训练。你可以说他是芬兰最强的IT顾问之一。
资源 ●最新版本的TikeSwing(包括类路径,源代码,用户指南和Javadoc API的必需的JAR文件)可以在这里下载:
http://sourceforge.net/projects/tikeswing
●关于TikeSwing遵循的MVC范例的基本信息:
http://ootips.org/mvc-pattern.html
●就像JavaBeans规范中说的那样,TikeSwing的模型对象必须包含GET和SET方法:
http://java.sun.com/products/javabeans/docs/spec.html
●Swing指南:
http://java.sun.com/docs/books/tutorial/uiswing/index.html
●HMVC范例分解了客户端为父子MVC层,这也能用于TikeSwing。阅读 “HMVC:用于开发强壮客户端层的层次模式,” Jason Cai, Ranjit Kapila, and Gaurav Pal (JavaWorld, 2000年7月),可获取更多信息:
http://www.javaworld.com/javaworld/jw-07-2000/jw-0721-hmvc.html
●MVC++范例共享了HMVC的关于控制器层次的想法:
http://www.cs.uta.fi/~jyrki/ohto02/mvc.ppt
●Apache BeanUtils库,包含了能用于JavaBeans域(在TikeSwing中使用了)引用的格式的描述:
http://jakarta.apache.org/commons/beanutils/api/index.html
●和TikeSwing有共通之处的Spring肥客户端工程:
http://www.springframework.org/spring-rcp
●关于Swing开发的更多文章,浏览JavaWorld的AWT/Swing部分的论题索引:
http://www.javaworld.com/channel_content/jw-awt-index.shtml
●关于UI设计的更多文章,浏览JavaWorld的User Interface Design部分的论题索引:
http://www.javaworld.com/channel_content/jw-ui-index.shtml
●最后,浏览JavaWorld论题索引的Development Tools部分:
http://www.javaworld.com/channel_content/jw-tools-index.shtml
当初乔布斯在车库里弄出了个人电脑,IBM很不屑;两年以后不屑变成了震惊和无力。于是IBM制定了另外一种政策来抢夺pc市场:“标准”。
IBM定义了一系列标准接口,各自领域的厂家只要生产符合标准的部件,这些部件就可以组装在一起,成为一个可以运行的电脑——毫无疑问,苹果无论如何精致诱人是搞不过一群如狼似虎的家伙的。
于是苹果很快被“湮没”了。
然后事情很是“出乎”IBM的预料,这场战争他击败了对手,可是胜利者并不是IBM自己。而是所谓的WINTEL联盟。
WINTEL就是WINDOWS和INTEL的合称。
其实WINDOWS并不从来是WINDOWS,刚开始的时候他也是黑乎乎的,而且也不是MS自己做的。因为盖茨非常喜欢苹果风格的os,所以MS公司就开始了视窗的研究——MS似乎对自己的视窗并没有什么信心,同时和IBM开发了OS WARP视窗系统。然后MS内部一个GATES不太喜欢的小组成功的开发出了WINDOS。WINDOWS最初的几个版本并不成功,直到版本三。
当然,最初出风头的是95,可是95没有浏览器。稳定性也不是很好,98是真正为人所广泛称道和接受的windows操作系统,他内置了浏览器,内置了虚拟机以及网络协议。
如同IBM刚开始不屑苹果一样,GATES眼睛里只有IBM,根本就不屑新秀netscapes;然而后者同样使他吓了一跳。但是MS不同于恐龙IBM的是,他迅速而坚决的采取了对策——绑定,免费。于是netscape不可抗拒的衰落了(被AOL收购)
如同今天google对ms的挑战一样,多年以前aol也是一个强劲的挑战者;风头甚劲,他资助ns继续开发,产生了现在的netscape6系列和firefox系列。
新锐就是新锐,历史的积淀是无法轻与的,对这些公司来说,没有一个公司能像SUN或者IBM一样,让MS感觉到羡慕和压力。
MS的眼光也不可轻忽,无论是模仿苹果制作windows还是模仿java制作J++/.NET,他都展示了后来居上青出去蓝的技术和商业眼光。
或许傲慢是强者的权利,MS似乎从来不屑按标准出牌,他总是在告诉别人:我就是标准,如果你想在我的标准里打败我,我就改变标准。无论是w3c标准还是css标准,无论是js标准还是java规范......
尤其是WEB开发,造就了现在这样的窘况。
我比较看好ms的XAML技术,可以说这个技术集B/S的快速部署能力、html的简洁UI开发、C/S技术的强大展现能力与一体。作为一个java程序员,我自然最关心的是java里面的对应实现物XUL。
要实现类似于XAML这样的技术,java必须在客户端装JRE。可能SUN觉得说服用户装一个jre很困难,于是提出webstart、jsf等一大堆更复杂的技术——但是我质疑的是jsf比XUL强大简洁么?webstart下载付出的代价难道小于一个jre么?既然flash可以强制没有装播放器的用户去装,为什么jre就不可以?
作为一个JAVA程序员,自然希望JAVA更好。可是不得不承认.net在开发效率,开发体验上,已经远远超越了JAVA。对.net的疑虑之存在于系统平台、第二提供方、健壮性等企业级顾虑中。
自由是痛苦的,无力的。
计算机软件从最开始的ui+db到后面的n层机构,无疑是很大的一个进步。就以j2ee标准来说,持久层负责以对象的形式为业务层提供数据,并维护数据;业务层负责组装业务逻辑,并以适当的方式将数据分发出去。这方面的技术很多,方案也很好。但是困扰web开发的问题在于client端。
我觉得client端最大的问题在于无状态,导致模型层和展现层的分离。我认为最佳的解决方案是客户端有一个强大的容器,能够请求、展现、维护客户端模型,并展现、修改客户端展现层。不妨定义为客户端容器语言,此语言是否可以考虑成领域语言,专注于客户展现层展现和客户端模型层。
js在客户端展现没有问题,但是js多了brower是不是太慢了,而且js本身写起来比较难,比较容易出错。在模型层方面,似乎不那么让人放心。关键问题是不是js的语言不够规范标准,容器不够强??
flash,感觉有点偏锋。他完全绕开了html。ui完全是自己的,我觉得还是用html正统。
xaml,他也是完全绕开html。除此以外,完全和我想的一样。
java技术是否可以考虑在client端有一个容器,操作html,负责与服务器交互并维护模型层,指导展现层??
读书是一件很有趣的事情。
碰到喜欢读的书,我总是拿一只笔。看到喜欢的地方或者感慨的地方,就做一个标记或者拉一条曲线,写上自己的评价。等很久以后再读到这里,努力想想,还是很容易想其以前自己的思路和想法。感觉自己好想回到了以前一样,还是很年轻。。。。而那书里所言,自然有印象;不亦乐乎。
还有一个比较坏的习惯,就是wc的时候一定要带上一点读的东西。记得以前听到一个散文来抒发此习惯。至今不能忘记。这也是比较爽的事情。
我上大学的时候,很是叛逆。课很少上,自然老师不喜欢我,及格也很困难。但自己常常独自跑到图书馆,徜徉在书海,胡乱翻翻,最后挑来挑去,把借书证数额满满用光,抱着一陀书回去。很有满足感——当然也未必看的完,说不定简单看了一番就杀将回来继续如此。
书非借不能读也。如今工作以后,虽然还是经常买书,每次搬家的时候,最重的那部分就是书,可是仔细想想,很多还真没看的很仔细。
但读书的乐趣,又怎么可以以此来苛求呢?
POI读写Excel文件[转]
我转自虫语【http://blog.matrix.org.cn/ljhj210423/entry/poi%E8%AF%BB%E5%86%99excel%E6%96%87%E4%BB%B6_%E8%BD%AC】,他也写了一个转,转自哪里就不知道了
一、Excel基础
二、HSSF概况
三、通过usermodel读取文件
四、通过usermodel写入文件
五、通过eventusermodel读取文件
六、HSSF电子表格结构
七、通过HPSF读取文档属性
八、文档摘要信息
九、附录
━━━━━━
正文:
━━━━━━
在上一篇文章中,我们介绍了POI项目的基本概念,了解了如何用POI来读写OLE 2复合文档结构,并给出了两个简单的例子:用POI来读写Excel文件的Workbook流。本文继续前文的话题,阐述如何用POI来读取/写入完整的Excel文件。
约定:POI项目2.0版现在已经接近正式发行阶段,开发进度迅速,不断有新的功能集成到原有的系统,同时也有对原有系统的修改。为了保证本文的及时性,本文将按照最近的1.9开发版说明。虽然编译最近的发行版源代码也能正常运行,但现在的代码和2.0的发行版会有一些出入。
一、Excel基础
Microsoft Excel 97文件格式也被称为BIFF8,最近版本的Excel只对该格式作了少量的改动。增加对新格式的支持除了增加项目的复杂性之外,唯一的效果也许只是不得不使每个用户升级代码,没有什么实际的好处。因此,在下文说明中,凡是提到Excel 97格式的地方其实都是指Excel从97到XP的格式。
二、HSSF概况
POI项目实现的Excel 97文件格式称为HSSF——也许你已经猜到,HSSF是Horrible SpreadSheet Format的缩写,也即“讨厌的电子表格格式”(微软使某些原本简单的事情过分复杂,同时又过分简单地处理了某些原本需要灵活性的事情,让人不胜佩服!)也许HSSF的名字有点滑稽,就本质而言它是一个非常严肃、正规的API。通过HSSF,你可以用纯Java代码来读取、写入、修改Excel文件。
前面一篇文章提到了POIFS,那么HSSF和POIFS又有什么关系呢?就象其他POI的API一样,HSSF建立在 POIFS的基础上,因此在HSSF内的有些代码和前文的某些代码很相似。不过,当我们编写基于HSSF API的代码时,一般不需要了解POIFS API的细节。
HSSF为读取操作提供了两类API:usermodel和eventusermodel,即“用户模型”和“事件-用户模型”。前者很好理解,后者比较抽象,但操作效率要高得多。usermodel主要有 org.apache.poi.hssf.usermodel和org.apache.poi.hssf.eventusermodel包实现(在 HSSF的早期版本中,org.apache.poi.hssf.eventusermodel属于eventmodel包)。
usermodel包把Excel文件映射成我们熟悉的结构,诸如Workbook、Sheet、Row、Cell等,它把整个结构以一组对象的形式保存在内存之中。eventusermodel要求用户熟悉文件格式的底层结构,它的操作风格类似于XML的SAX API和AWT的事件模型(这就是eventusermodel名称的起源),要掌握窍门才能用好。另外,eventusermodel的API只提供读取文件的功能,也就是说不能用这个API来修改文件。
三、通过usermodel读取文件
用HSSF的usermodel读取文件很简单。首先创建一个InputStream,然后创建一个HSSFWorkbook:
InputStream myxls = new FileInputStream("workbook.xls"));
HSSFWorkbook wb = new HSSFWorkbook(myxls);
有了HSSFWorkbook实例,接下来就可以提取工作表、工作表的行和列,例如:
HSSFSheet sheet = wb.getSheetAt(0); // 第一个工作表
HSSFRow row = sheet.getRow(2); // 第三行
HSSFCell cell = row.getCell((short)3); // 第四个单元格
上面这段代码提取出第一个工作表第三行第四单元格。利用单元格对象可以获得它的值,提取单元格的值时请注意它的类型:
if (cell.getCellType() == HSSFCell.CELL_TYPE_STRING) {
("单元格是字符串,值是: " + cell.getStringCellValue());
} else if (cell.getCellType() == HSSFCell.CELL_TYPE_NUMERIC) {
("单元格是数字,值是: " + cell.getCellValue());
} else () {
("单元格的值不是字符串或数值。");
}
如果搞错了数据类型,程序将遇到异常。特别地,用HSSF处理日期数据要小心。Excel内部以数值的形式保存日期数据,区别日期数据的唯一办法是通过单元格的格式(如果你曾经在Excel中设置过日期格式,应该明白这是什么意思)。
因此,对于包含日期数据的单元格,cell.getCellType()将返回HSSFCell.CELL_TYPE_NUMERIC,不过利用工具函数HSSFDateUtil.isCellDateFormatted(cell)可以判断出单元格的值是否为日期。 isCellDateFormatted函数通过比较单元格的日期和Excel的内置日期格式得出结论——可以想象,按照这种判断方法,很多时候 isCellDateFormatted函数会返回否定的结论,存在一定的误判可能。
本文附录包含了一个在Servlet环境中利用HSSF创建和返回Excel工作簿的实例。
四、通过usermodel写入文件
写入XLS文件比读取XLS文件还要简单。创建一个HSSFWorkbook实例,然后在适当的时候创建一个把文件写入磁盘的OutputStream,但延迟到处理结束时创建OutputStream也可以:
HSSFWorkbook wb = new HSSFWorkbook();
FileOutputStream fileOut
= new FileOutputStream("workbook.xls");
wb.write(fileOut);
fileOut.close();
创建工作表及其内容必须从相应的父对象出发,例如:
HSSFSheet sheet = wb.createSheet();
HSSFRow row = sheet.createRow((short)0);
HSSFCell cell = row.createCell((short)0);
cell.setCellValue(1);
row.createCell((short)1).setCellValue(1.2);
row.createCell((short)2).setCellValue("一个字符串");
row.createCell((short)3).setCellValue(true);
如果要设置单元格的样式,首先要创建一个样式对象,然后把它指定给一个单元格——或者把它指定给多个具有相同样式的单元格,例如,如果Excel表格中有一个摘要行,摘要行的数据必须是粗体、斜体,你可以创建一个summaryRowStyle样式对象,然后把这个样式指定给所有摘要行上的单元格。
注意,CellFormat和CellStyle对象是工作簿对象的成员,单元格对象只是引用它们。
...
HSSFCellStyle style = workbook.createCellStyle();
style.setDataFormat
(HSSFDataFormat.getBuiltinFormat("($#,##0_);[Red]($#,##0)"));
style.setFillBackgroundColor(HSSFColor.AQUA.index);
style.setFillPattern(HSSFCellStyle.BIG_SPOTS);
...
someCell.setCellStyle(style);
someOtherCell.setCellStyle(style);
版本较新的HSSF允许使用数量有限的Excel公式。这一功能目前还是“Beta级质量”,正式使用之前务必仔细测试。指定公式的方式类如:someCell.setCellFormula(SUM(A1:A2:);。
当前,公式中已经可以调用所有内建的函数或操作符,但逻辑操作符和函数(例如IF函数)除外,这部分功能目前还在开发之中。
五、通过eventusermodel读取文件
通过eventusermodel读取文件要比使用usermodel复杂得多,但效率也要高不少,因为它要求应用程序一边读取数据,一边处理数据。 eventusermodel实际上模拟了DOM环境下SAX处理XML文档的办法,应用程序首先要注册期望处理的数据,eventusermodel将在遇到匹配的数据结构时回调应用程序注册的方法。使用eventusermodel最大的困难在于你必须熟悉Excel工作簿的内部结构。
在HSSF中,低层次的二进制结构称为记录(Record)。记录有不同的类型,每一种类型由org.apache.poi.hssf.record包中的一个Java类描述。例如,BOFRecord记录表示Workbook或Sheet区域的开始,RowRecord表示有一个行存在并保存其样式信息。所有具有CellValueRecordInterface接口的记录表示Excel的单元格,包括NumericRecord、 LabelSSTRecord和FormulaRecord(还有其他一些,其中部分已被弃置不用,部分用于优化处理,但一般而言,HSSF可以转换它们)。
下面是一个注册事件处理句柄的例子:
private EventRecordFactory factory = new EventRecordFactory();
factory.registerListener(new ERFListener() {
public boolean processRecord(Record rec) {
(got BOF Record);
return true;
}
}, new short[] {BOFRecord.sid});
factory.processRecords(someInputStream);
六、HSSF电子表格结构
如前所述,HSSF建立在POIFS的基础上。具体地说,Excel 97+文件是OLE 2复合文档( OLE 2 Compound Document),底层的OLE 2复合文档保存了一个总是命名为Workbook(Excel 95除外,HSSF不支持Excel 95)的流。然而,宏和图片并不保存在Workbook流,它们有自己独立的流,有时甚至会放到OLE 2 CDF文件之内的另一个目录。理想情况下,宏也应该被保留,不过目前POI项目中还没有合适的API来处理宏。
每一个流之内是一组记录,一个记录其实就是一个字节数组,可分为记录头、记录体两部分。记录头指明了记录的类型(也即ID)以及后继数据的长度,记录体被分割成多个字段(Field),字段包含数值数据(包括对其他记录的引用)、字符数据或标记。
下图概要说明了Excel工作簿的顶级结构:
Bla.xls {
OLE2CDF headers
"Workbook" stream {
Workbook {
Static String Table Record..
Sheet names… and pointers
}
Sheet {
ROW
ROW
…
NUMBER RECORD (cell)
LABELSST Record (cell)
…
}
Sheet
}
}
… images, macros, etc.
Document Summary
Summary
七、通过HPSF读取文档属性
在Microsoft Word、Excel、PowerPoint等软件中,用户可以通过“文件”→“属性”菜单给文档添加附加信息,包括文档的标题、主题、摘要、类别、关键词等,同时应用软件本身还会加入最后访问的用户、最后访问和修改/打印的日期时间等信息。
文档的属性和正文是分开保存的。如前所述,OLE 2 CDF文件内部就象是一个容器,里面包含许多类似目录和文件的结构,而POIFS就是用来访问其中的文件的工具。这些文件也称为流,文档的属性就保存在 POIFS文件系统中专用的流里面。以一个Word文档为例:虽然在资源管理器中你只看到一个叫做MyFile.doc的文档,其实在这个文档的内部,又包含了一个WordDocument、一个SummaryInformation和一个DocumentSummaryInformation文档;通常还会有其他的文档,这里暂且不管。
你能够猜出这些文档(流)分别包含什么内容吗?不错,WordDocument包含了你在 Word里面编辑的文本,文档的属性保存在SummaryInformation和DocumentSummaryInformation流里面。也许将所有属性保存在单个文档里面看起来太简单了,所以Microsoft决心要使用两个流,为了使事情更复杂一点,这两个流的名字前面还加上了八进制的 \005字符——这是一个不可打印的字符,因此前面就把它省略了。
Microsoft定义的标准属性有一个好处,它们并不在乎主文档到底是什么类型——不管是Word文档、Excel工作簿还是PowerPoint幻灯。只要你知道如何读取Excel文档的属性,就知道了如何读取其他文档的属性。
读取文档属性其实并不复杂,因为Java程序可以利用POI项目的HPSF包。HPSF是 Horrible Property Set Format的缩写,译成中文就是“讨厌的属性集格式”。HPSF包是POI项目实现的读取属性工具,目前还不支持属性写入。
对于读取Microsoft定义的标准属性,通过HPSF提供的API可以很方便地办到;但如果要读取任意属性集就要用到更一般化的API,可以想象它要比读取标准属性的API复杂不少。本文只介绍读取标准属性的简单API,因为对大多数应用程序来说这已经完全足够了。
下面就是一个读取OLE 2 CDF文档的标题(title)属性的Java程序:
import java.io.*;
import org.apache.poi.hpsf.*;
import org.apache.poi.poifs.eventfilesystem.*;
/**
* 读取OLE 2文档标题的示例程序,
* 在命令行参数中指定文档的文件名字。
*/
public class ReadTitle
{
public static void main(String[] args) throws IOException
{
final String filename = args[0];
POIFSReader r = new POIFSReader();
r.registerListener(new MyPOIFSReaderListener(),
"\005SummaryInformation");
r.read(new FileInputStream(filename));
}
static class MyPOIFSReaderListener
implements POIFSReaderListener
{
public void processPOIFSReaderEvent(POIFSReaderEvent event)
{
SummaryInformation si = null;
try
{
si = (SummaryInformation)
PropertySetFactory.create(event.getStream());
}
catch (Exception ex)
{
throw new RuntimeException
("属性集流\"" + event.getPath() +
event.getName() + "\": " + ex);
}
final String title = si.getTitle();
if (title != null)
System.out.println("标题: \"" + title + "\"");
else
System.out.println("该文档没有标题.");
}
}
}
main()方法利用POIFS的事件系统从命令行指定的OLE 2文档读取名为\005SummaryInformation的流,当POIFSReader 遇到这个流时,它把控制传递给MyPOIFSReaderListener的processPOIFSReaderEvent()方法。
processPOIFSReaderEvent() 到底有什么用呢?它通过参数获得一个输入流,该输入流包含了文档标题等属性。为了访问文档的属性,我们从输入流创建一个PropertySet实例,如下所示:
si = (SummaryInformation) PropertySetFactory.create(event.getStream());
这个语句其实包含三个步骤的操作:
◆ event.getStream()从POIFSReader传入的POIFSReaderEvent获得输入流。
◆ 以刚才获得的输入流为参数,调用PropertySetFactory的静态方法create()。正如其名字所暗示的, PropertySetFactory是一个工厂类,它有一台“机器”能够把一个输入流转换成一个PropertySet实例,这台机器就是create ()方法。
◆ 把create()方法返回的PropertySet定型(cast)成为SummaryInformation。PropertySet提供了按照一般办法读取属性集的各种机制,SummaryInformation是PropertySet的子类,即SummaryInformation类在 PropertySet类的基础上增加了操作Microsoft标准属性的便捷方法。
在这个处理过程中,可能引起错误的因素很多,因此我们把这部分内容放入了一个try块——不过这个示例程序只按照最简单的方式处理了异常,在实际应用中,最好能够对可能出现的不同异常类型分别处理。除了一般的I/O异常之外,还有可能遇到HPSF特有的异常,例如,如果输入流不包含属性集或属性集非法,就会抛出 NoPropertySetStreamException异常。
有一种错误不太常见,但也不是绝无可能——\005SummaryInformation包含一个合法的属性集,但不是摘要信息属性集。如果出现这种情况,则定型成SummaryInformation操作会失败,引发ClassCastException异常。
获得SummaryInformation实例之后,剩下的事情就很简单了,只要调用getTitle()方法,然后输出结果。
除了getTitle()之外,SummaryInformation还包含其他一些便捷方法,例如getApplicationName()、 getAuthor()、getCharCount()、和getCreateDateTime()等。HPSF的JavaDoc文档详细说明了所有这些方法。
八、文档摘要信息
遗憾的是,并非所有的属性都保存在摘要信息属性集之中。许多(但不是全部)OLE 2文件还有另一个属性集,称为“文档摘要信息”,对应的流是\005DocumentSummaryInformation。这个属性集保存的属性包括文档的类别、PowerPoint幻灯的多媒体剪辑数量,等等。
要访问文档摘要信息属性集,程序的处理过程也和上例相似,只是注册的目标应该改成\005DocumentSummaryInformation流——有时,你可能想要同时注册到摘要信息和文档摘要信息这两个流。其余的处理方式和前面的例子差不多,你应该把包含文档摘要信息的流传递给PropertySetFactory.create(),但这次工厂方法将返回一个 DocumentSummaryInformation对象(而不是前面例子中的SummaryInformation对象)。如果同时注册到了两个流,注意检查返回值的具体类型,或者使用Java的instanceof操作符,或者使用专用的isSummaryInformation()和 isDocumentSummaryInformation()方法。记住,create()方法返回的总是一个PropertySet对象,因此你总是可以对create()返回对象调用isSummaryInformation()和isDocumentSummaryInformation()方法,PropertySet类之所以要提供这两个方法,是因为属性集可能是自定义的。
如果你想要处理自定义的属性集,或者要从标准的属性集读取用户定义的属性,必须使用一个更一般化的API,前面已经提到,这个API要复杂得多,本文不再讨论,请参见HPSF的HOW-TO文档和POI的文档。
结束语:本文探讨了HSSF的应用以及如何输出到Excel文件,另外还涉及了HPSF以及如何读取属性集文档摘要信息。POI是一个功能非常强大的项目,许多主题本文尚未涉及,例如如何用HSSF Serializer将XML文档转换成Excel格式等,这一切就有待你自己去研究了。
参考:
Jakarta POI项目主页
Jakarta POI 源代码
九、附录
实例:利用Servlet创建和返回一个工作簿。
package org.apache.poi.hssf.usermodel.examples;
import java.io.*;
import java.net.*;
import javax.servlet.*;
import javax.servlet.http.*;
import org.apache.poi.hssf.usermodel.*;
public class HSSFCreate extends HttpServlet {
public void init(ServletConfig config)
throws ServletException {
super.init(config);
}
public void destroy() {
}
/** 处理HTTP GET 和POST请求
* @param request:请求
* @param response:应答
*/
protected void processRequest(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("application/vnd.ms-excel");
HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet sheet = wb.createSheet("new sheet");
// 创建一个新的行,添加几个单元格。
// 行号从0开始计算
HSSFRow row = sheet.createRow((short)0);
// 创建一个单元格,设置单元格的值
HSSFCell cell = row.createCell((short)0);
cell.setCellValue(1);
row.createCell((short)1).setCellValue(1.2);
row.createCell((short)2).setCellValue("一个字符串值");
row.createCell((short)3).setCellValue(true);
// 写入输出结果
OutputStream out = response.getOutputStream();
wb.write(out);
out.close();
}
/** 处理HTTP GET请求
* @param request:请求
* @param response:应答
*/
protected void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
/** 处理HTTP POST请求
* @param request:请求
* @param response:应答
*/
protected void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
/** 返回关于Servlet的简单说明
*/
public String getServletInfo() {
return "示例:在Servlet中用HSSF创建Excel工作簿";
}
}
POI HSSF 操作MS Excel简述 (OMIS二期设计阶段寻找Excel导入导出实现方法)
POI HSSF 操作MS Excel简述
POI HSSF是一个专门操作EXCEL的java包,可通过纯java操作xls文件。
POI HSSF的类文件都放在在org.apache.poi.hssf包下,通过此包中的类就可实现用java操作Excel文件了。
下面是用POI HSSF操作Excel文件的方法简述:
一, 建立Excel工作薄
HSSFWorkbook wb = new HSSFWorkbook();
二, 建立Excel工作表,每个工作表对应的是Excel界面左下角的一个标签sheet1,sheet2 …
HSSFSheet sheet1 = wb.createSheet("new sheet");
三, 在工作表中建立单元格
//首先,建立行对像,行号作为参数传给createRow方法,第一行由0开始计算。
HSSFRow row = sheet.createRow((short)0);
//建单元格
HSSFCell cell = row.createCell((short)0);
//给单元格赋值
cell.setCellValue(1);
//也可同一行内完成建立单元格和赋值
row.createCell((short)1).setCellValue(1.2);
row.createCell((short)2).setCellValue("This is a string");
row.createCell((short)3).setCellValue(true);
//数据格式可通过创建单元格值时默认如上面所视
//也可以创建单元格后调用setCellType指定
cell.setCellType(CELL_TYPE_NUMERIC);
四, 向单元格插入日期值
HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet sheet = wb.createSheet("new sheet");
// 可通过Sheet.setSheetName(sheetindex,"SheetName",encoding)设定工作表名
// 创建新行并向其加入单元格,行号由0开始。
HSSFRow row = sheet.createRow((short)0);
// 创建一个单元格并向其输入一日期值,但这第一个单元格并非是日期格式。
HSSFCell cell = row.createCell((short)0);
cell.setCellValue(new Date());
// 我们将这第二个单元格改成日期格式,这需要从工作薄创建一个新的单元格格式,这可// 以只影响当前建立的一个单元格。
HSSFCellStyle cellStyle = wb.createCellStyle();
cellStyle.setDataFormat(HSSFDataFormat.getBuiltinFormat("m/d/yy h:mm"));
cell = row.createCell((short)1);
cell.setCellValue(new Date());
cell.setCellStyle(cellStyle);
五, 各种单元格样式
HSSFCellStyle cellStyle = wb.createCellStyle();
//对齐
cellStyle.setAlignment(HSSFCellStyle.ALIGN_CENTER);
//带边框
cellStyle.setBorderBottom(HSSFCellStyle.BORDER_THIN);
//颜色与填充样式
cellStyle.setFillBackgroundColor(HSSFColor.AQUA.index);
cellStyle.setFillPattern(HSSFCellStyle.BIG_SPOTS);
cellStyle.setFillForegroundColor(HSSFColor.ORANGE.index);
cellStyle.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND);
六, 行高,列宽。
HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet sheet = wb.createSheet("new sheet");
HSSFRow row = sheet.createRow((short)0);
//2是行高值
row.setRowHeight(2);
//3是列号,4是列宽值
sheet.setColumnWidth(3, 4);
七,例程
首先调用一个方法将Oracle数据库中的数据取出,放到List实例中,这里我调用了srrd项目中ProductData类的 listProductQuery()取得一个List实例。List中的对象是一系列名为ProductQuery实体类的实例。然后读List,将 ProductQuery实例中的数据取出放到HSSFCell单元格中。最后将HSSFWorkbook中的数据输出到输出流,完成数据导出。
//建工作薄
HSSFWorkbook wb = new HSSFWorkbook();
//建名为example的工作表
HSSFSheet sheet = wb.createSheet("example");
//给工作表前8列定义列宽
sheet.setColumnWidth((short)0,(short)2500);
sheet.setColumnWidth((short)1,(short)6000);
sheet.setColumnWidth((short)2,(short)3500);
sheet.setColumnWidth((short)3,(short)9000);
sheet.setColumnWidth((short)4,(short)8000);
sheet.setColumnWidth((short)5,(short)8000);
sheet.setColumnWidth((short)6,(short)20000);
sheet.setColumnWidth((short)7,(short)8000);
//在表中建行
HSSFRow row = sheet.createRow(0);
//建立单元格
HSSFCell cell[] = new HSSFCell[8];
for (short i = 0; i < 8; i++) {
cell = row.createCell(i);
//将单元格定义成UTF_16编码,这样才能使输出数据不会乱码
cell.setEncoding(HSSFCell.ENCODING_UTF_16);
}
//写单元格标题
cell[0].setCellValue("登记ID");
cell[1].setCellValue("登记号");
cell[2].setCellValue("所在地市ID");
cell[3].setCellValue("产品中文名");
cell[4].setCellValue("产品英文名");
cell[5].setCellValue("产品服务对象");
cell[6].setCellValue("产品功能描述");
cell[7].setCellValue("产品类别");
//查询数据库,取得数据列表的List实例
List list = new ArrayList();
ProductDataManager mgr = new ProductDataManager();
try {
list = mgr.listProductQuery("","", "", "", "", "1999-2-1", "2004-2-1");
} catch (SrrdException e) {
e.printStackTrace();
}
//从List中取出数据放入工作表中
if (list != null && list.size() > 0) {
for (int i = 0; i < list.size() - 1; i++) {
ProductQuery query = (ProductQuery) list.get(i);
HSSFRow datarow = sheet.createRow(i + 1);
HSSFCell data[] = new HSSFCell[8];
for (short j = 0; j < 8; j++) {
data[j] = datarow.createCell(j);
//将单元格定义成UTF_16编码,这样才能使输出数据不会乱码
data[j].setEncoding(HSSFCell.ENCODING_UTF_16);
}
data[0].setCellValue(query.getCertId());
data[1].setCellValue(query.getCertNum());
data[2].setCellValue(query.getCityCode());
data[3].setCellValue(query.getSoftWareCname());
data[4].setCellValue(query.getSoftWareEname());
data[5].setCellValue(query.getSoftwareFor());
data[6].setCellValue(query.getSoftwareFuncDesc());
data[7].setCellValue(query.getSoftwareType());
}
}
//将工作薄输出到输出流
ServletOutputStream sos=response.getOutputStream();
wb.write(sos);
sos.close();
//也可输出成xls文件
File file = new File("workbook.xls");
try {
FileOutputStream fileOut = new FileOutputStream(file);
wb.write(fileOut);
fileOut.close();
} catch (IOException e) {
e.printStackTrace();
}