iamzzb

 
 

常用链接

  • 我的随笔
  • 我的评论
  • 我的参与
  • 最新评论

留言簿

  • 给我留言
  • 查看公开留言
  • 查看私人留言

随笔分类

  • 开始学习springside(1) (rss)

随笔档案

  • 2008年4月 (1)
  • 2008年3月 (1)
  • 2008年2月 (1)
  • 2007年7月 (1)
  • 2007年6月 (1)
  • 2007年5月 (4)

文章档案

  • 2008年3月 (1)
  • 2007年7月 (1)

收藏夹

  • j2ee(2) (rss)

搜索

  •  

积分与排名

  • 积分 - 2222
  • 排名 - 1457

最新评论

  • 1. re: MVC三层模型(struts+spring+hibernate)总结
  • 我就是搞不懂
    逻辑层的作用
    感觉没有也行
    不知道
  • --小数

阅读排行榜

  • 1. MVC三层模型(struts+spring+hibernate)总结(957)
  • 2. 学习springside过程跟踪之一“熟悉例子helloworld代码”(407)
  • 3. 周鸿祎:网络业的柔道搏击术(转)(175)
  • 4. 用Java解析XML文档(173)
  • 5. 什么是敏捷开发?(138)

评论排行榜

  • 1. MVC三层模型(struts+spring+hibernate)总结(1)
  • 2. RoR是什么?(0)
  • 3. 学习springside过程跟踪之一“熟悉例子helloworld代码”(0)
  • 4. 什么是敏捷开发?(0)
  • 5. 用Java解析XML文档(0)

Powered by: 博客园
模板提供:沪江博客
BlogJava | 首页 | 发新随笔 | 发新文章 | 联系 | 聚合 | 管理

2008年4月3日

周鸿祎:网络业的柔道搏击术(转)
文:易晓波

  过去的两年,中国互联网江湖格局变化不大。腾讯、百度、网易、新浪、阿里巴巴这些数年前掌权的“老人”,今天依然牢牢维持住他们在各自领域里的垄断地位,还利用既有优势在新战线上对创业公司无情阻击。新创公司该如何以小博大、以弱胜强?

  “中国互联网巨头本身创新能力不够,就会反过来模仿小公司压制创新,创业公司的空间会越来越小。”盘腿坐在摆放着两座巨大立柜音响的工作间里,听着舒缓的古典音乐,周鸿祎说:“所以今天的创业者亟需柔道思维!”

  这个创立过两家公司,也做过职业经理人和VC(风险投资)的天使投资人相信:“对付腾讯和百度,要么别跟他们竞争,离得远远的,要么就用柔道。不要期望追随在他们后面,就可以分到一杯残羹。”

  借力打力

  中国互联网诞生过许多白手起家的英雄,但过去两年,江湖格局变化不大。而在太平洋对岸的美国,新陈代谢却不曾停滞。8年前雅虎在美国崛起,中国有三大门户;5年前eBay脱颖而出,中国有阿里巴巴;3年前谷歌一飞冲天,中国有百度。但在美国崛起YouTube、MySpace和Facebook的今天,中国几乎交了白卷。两年前最红火的三家2.0概念的网站是“博客中国”、千橡、和讯。后来,方兴东“阵亡”,陈一舟在苟延残喘,而当年的和讯掌门谢文赋闲在家恰好两年。

  新浪、腾讯、百度、网易、阿里巴巴这些数年前掌权的“老人”,今天依然牢牢维持住他们在新闻、即时通信、搜索、邮箱、电子商务上的垄断,还利用既有优势在新战线上对创业公司无情阻击。腾讯成为最强大的2.0公司,这只恐怖的怪兽胃口之大,见什么吃什么。百度帖吧成为最大的论坛,网易垄断了在线相册,阿里孵出的淘宝和支付宝是最大的C2C和电子支付平台,而新浪把两年前风光一时的专业博客网站打得落花流水。人人都在好奇地等待:在新的机会出现时,比如视频领域,腾讯和新浪的播客(视频分享)是否会同样把土豆网们逐一歼灭呢?

  如何以小博大?这是所有创业者都必须思考的问题。而周鸿祎的经历无疑颇有研究价值。

  3721时代,周鸿祎在渠道上做文章,超过了先行者CNNIC;主政中国雅虎时期,他用“一搜”在MP3搜索上超过百度,并甩开几家门户,把雅虎邮箱做到第二。而在近期战役中,他用短短一年就使“360安全卫士”进入安全软件前三位,而奇虎网也迅速成为社区内容聚合的领导者。作为天使投资人的他,更是频频出手,比如投资迅雷和Discuz!,两者分别是下载和社区软件的领先者。两次投资同时吸引到谷歌的注资。

  “如果我挑战任何一个巨头,对方都不会等闲视之。我都有能力冲击它,撼动它。”周鸿祎以并不夸张的语调坚定地说。近半年,他总在公司内部以及业内朋友间推荐“柔道战略”以及相应书籍,甚至要求把目前仅存的只能在金书网买到的几十本《柔道战略》“通通买回,让奇虎员工都看看”。周也愿意向业界传播这种思维方式。

  2000年9月,在日本东京的Kodokan会所,身为前特工的俄罗斯总统普京竟被一个10岁女学生摔倒在地,甘拜下风。这个体形娇小的女学生打败黑带高手普京所用的技巧,即是以小博大、以弱胜强的柔道。关于柔道,《哥伦比亚百科全书》的定义是:将对手的体能和力量为己所用,借力打力,击败对手而获胜的一种武术,它使弱者或体重处于劣势的人能够战胜身体占优势的对手。

  美国学者大卫·尤费在其著作《柔道战略》中把柔道看作一种商业策略,核心是借力打力。作为弱势的一方,不能与强大的一方硬碰,而要设法把对手的体重、力量等优势变成对其不利的因素。比如寻找到一个支点,利用杠杆作用把对手撬起来、甩出去!对方的劲越大、体重越大,就摔得越惨。

  一种有效的策略,一般是知道的人越少越好。不过,周鸿祎却不同凡俗。三年前当他还带领着雅虎跟百度、网易作战时,就讲述过自己的策略:“我期望天下大乱,期望所有的小公司都来挑战大公司;大公司是产业规则的制定者,当然不期望乱,乱对小公司有好处,因为乱世会打破既有的规则和平衡,乱世出英雄。大乱之后会大治,我期望那时争取到应得的位置。”

  从这个角度讲,同为广大创业者中一员的枭雄周鸿祎,与这个群体的利益是一致的。且看他眼中的历次互联网战役。
劲越大,越可能刹不住车

  借力打力是柔道的核心。大块头对手向你猛冲过来,但你不能硬挡,而是顺势一退一拉,大块头就失去重心冲了出去。他的劲越大,就越可能刹不住车而摔倒。大企业所拥有的庞大资产,是其打击小企业的强大武器。所以柔道中思考的关键是:如何让大企业的资产成为它的累赘?在什么情况下,大企业的资产反而令其痛苦?

  2001年,做网络实名的3721开始跟卖域名的CNNIC竞争。当时CNNIC已经有万网等九大代理商,他们各有一套自上而下的代理体系。这个庞大的代理渠道、经销商和客户群,就是CNNIC的重要资产,3721无法与其相争。3721不能去用这九大代理商,CNNIC随时可利用自己的影响力封杀3721,况且3721也没有足够人力绕开经销商去直销。CNNIC的代理体系隔离了3721和它潜在的客户群。

  但这个代理体系有其天生弱点:CNNIC高居代理体系最上层,并未深入到基层的代理和客户中去,并且这些代理对中国底层的小企业也渗透得不够。

  周鸿祎的对策是自建一个不同于总代理、地方代理这样的分层级,即一个扁平的代理体系。在每个城市都圈定一家代理,在全国圈定上千家,由3721直接管理。这弥补了CNNIC代理体系的不足。

  更重要的是,CNNIC无法模仿3721的做法。如果CNNIC自建扁平渠道,就难以处理其与既有代理体系间的关系。比如原有的层级代理自上而下有不同的价格差,以维持每一级都有利可图,但新的扁平渠道都直接执行来自3721总部的一个价格,二者必然相互干扰。而CNNIC原有的代理体系越庞大,产生的摩擦和震荡就越大,反而开始成为一种负资产,阻碍CNNIC变革。

  随后一年CNNIC都按兵不动,眼睁睁看着3721快速发展。但一年之后CNNIC决定不再忍受,开始从3721挖角,建立新扁平体系。而这正是周鸿祎“所期望看到的事情”,新的直属代理商的进入,打破了CNNIC原有的九大代理商的势力均衡。壮士断腕,血流不止。

  就在CNNIC逐步建立起跟3721竞争的通用网址代理体系时,其原来。cn域名的代理体系也逐步瓦解,新网、万网等大代理商都看淡。cn域名交易。今天,cn域名每年使用费已经跌至1元钱。3721自始至终都没有直接打击CNNIC的代理体系,但CNNIC被自己内部的力量打翻在地。

  在周鸿祎那里,柔道思维其实就是些简单的问题。只要碰到大公司,就应该下意识地去想:“它最珍贵的东西是什么?它最大的优势是什么?这个优势里有什么问题?它的敌人是谁?它最害怕什么?你可以做什么事情,但你强大的对手却不能跟着去做!”

  当然,CNNIC还算不上太强大的对手,但百度绝对是。2004年开始,周鸿把雅虎搜索的发力点定在MP3上,为此推出了独立的搜索网站“一搜”,并把它吹嘘成“中国最大的娱乐音乐搜索”。但即便百度的MP3搜索流量远大于“一搜”,百度却不敢站出来戳穿“谎言”,不敢声明自己才是“中国最大的娱乐音乐搜索”,而任由“一搜”独享这一定位带来的市场认知,高速增长。

  因为百度作为“中国最大的网页搜索”有其软肋。百度所有的收入都来自中小企业投放的精准广告,而当时所有中小企业都认为网页搜索上的流量才有价值,不认为娱乐性质的MP3流量会有价值。如果百度这么做,就与其网页搜索的定位不符,会损害在广告主中的形象。周鸿说,“百度的网页搜索越成功,就越导致它不敢在MP3上向我发力,所以我就专打MP3.”一年后,“一搜”超过百度,成为名副其实的“中国最大娱乐音乐搜索”。

  做对手不敢做的事

  2004年,谷歌跟雅虎和微软也玩了一场柔道比赛。当时微软Hotmail和雅虎邮箱分别名列第一、第二,而谷歌没有邮箱服务,其用户和资产都为零。众所周知,谷歌高调推出了容量高达1G的免费邮箱Gmail,容量是Hotmail和雅虎邮箱的几十倍。一旦Hotmail们跟随Gmail扩容,其庞大的用户群将导致其成本呈数量级往上翻,技术难度也会成倍增加,从零开始的Gmail所面临的成本和难度要小很多。Hotmail们难以立即跟进扩容而留给Gmail一段飞速圈地的时间,从而一举奠定了三分天下的地位。

  2004年周鸿祎也用了一样的策略。当时雅虎邮箱在中国基本无用户,网易邮箱遥遥领先居第一,其次是搜狐等几个门户。雅虎邮箱首先扩容到1G,给对手非常大的压力。第一,扩容会导致成本大幅提高,搜狐等纳斯达克上市公司会面临业绩压力;第二,技术难度升级,扩容后容易出现邮件丢失的情况。

  这一仗之后,雅虎邮箱进入前三,搜狐等基本被挤出了市场。但网易的老大地位依然牢固,这一仗对它基本没能产生动摇。第一,网易邮箱的技术很好;第二,虽是上市公司,但一股独大的丁磊并不把股市放在眼里,舍得成本投入。所以周鸿 开始使出第二招,直指网易。

  “网易是老大,其最有价值的就是品牌和用户,正因为如此,网易不会愿意跟别人分享。所以我就往反方向使力,愿意跟别人分享。而且网易肯定有竞争对手,像盛大、联众、浩方,甚至265、当当也都是潜在对手。我不用跟网易直接竞争,转而去找它的对手合作。这是网易决不能做的事情。”

  雅虎一口气跟联众、携程、当当、265等12家专业网站签订协议,为他们提供以各家品牌为地址的1G邮箱。雅虎放弃了品牌,得到了几百万用户,而这些合作网站则可以对自己的用户提供属于自己品牌的邮箱服务,此举间接地削弱了对手网易。

  这一仗后,雅虎邮箱成为第二,虽然没有超越网易,但也造成了不小压力。当时丁磊暗示这是种欺骗的方式,说“老虎吃人之前都会微笑”。但周鸿祎反问,我怎么可能“骗”到这么多网站?“实际情况就是我们跟丁磊打一场人民战争。丁磊这么高傲的人,要不是打到他的痛处,他是不会出来骂我的。”

  后来周鸿祎飞到广州拜访丁磊。周安慰丁磊:雅虎就是要获取应得的市场份额,但网易应该自信,自己很强大。而另一方面,周鸿祎又准备好了针对网易的第三仗。

  2004年,网易还在全力推即时通信产品泡泡,当时丁磊还抱有希望。雅虎也有自己的即时通信工具“雅虎通”,而周鸿祎计划再做一件“网易不会做的事”。就是把雅虎通牺牲掉,不推广雅虎通,而是把它改装成支持雅虎邮箱的附属工具。这个被称为雅虎邮箱通的小应用会自动提醒新邮件,能自动打开网页,然后回信的人可以直接在邮件里跟对方即时聊天。

  其实这就是后来Gmail所采取的方式,即Gtalk。这个小应用的确帮助Gmail进一步扩展了份额,在美国成为继MSN和AOL之后的第三大聊天工具。不过因为周鸿祎随后离任,这个计划未在雅虎中国公司实施。

  小心被自己绊倒

  借力打力虽是放倒对手的必杀技,但还需要两个技巧来保驾护航:移动和平衡。不断移动,不要挑起强大对手对你发起正面攻击,像只小狗那样低调而不要像大狗那样叫嚣。在移动中需要保持平衡,千万不要自己摔倒!

  两年前风风火火而今天已经无人过问的“博客中国”,就是因为不遵守移动和平衡原则而劫数难逃衰落的命运。周鸿祎认为这是“典型的反面案例”。

  2003年,博客概念在中国风生水起,IT评论人士方兴东的“博客中国”是领先者。由于两年都专注于为IT博客提供服务而营造出一个IT精英社区,“博客中国”迅速进入中文网站前50强。

  但在2005年初赛富基金投入1000万美元后,方兴东便一改往日低调。他在北京世纪坛广邀嘉宾和媒体,隆重宣布“博客时代的来临”,还叫嚷着“一年超新浪,两年上市”的目标,这不仅把资讯业最强的新浪直接当成对手,更让所有人警惕:博客就是下一个金矿!当“博客中国”导航条上的子频道不断增多,也把自己置于与门户面对面竞争的境地,而其他几家专业博客网站也不甘示弱,竞相宣布庞大的融资和强劲的增长。

  这场闹剧之后不到半年,新浪出手了。接二连三地,腾讯、搜狐、百度也大兴资源正式进场厮杀。其实在“博客中国”创立时,新浪总编辑陈彤作为朋友还给过方兴东支持,这给“博客中国”早期的发展带来了不少帮助。但接下来发生的同样是事实,方兴东激昂高调对博客的宣讲“提醒”了陈彤,而也是方兴东持续不断的公开挑衅,最终“激怒”了陈彤。之后新浪和搜狐都把博客作为一项战略业务来发展,要求每个频道都设置博客专员,倾全公司资源发展博客,并且方兴东和“博客中国”的一切言论和新闻,也都被封杀。

  以新浪为首的门户很快大获全胜。博客前五名里,再没有专业网站的身影。而方兴东无疑成了“行业罪人”,他的一些做法引发了一群网站的萧条。时任和讯总编辑的刘骏曾如此感叹:“你可以把那些话写出来、贴在墙头,每天睡觉前和醒来后在心里默念三遍,但为什么要叫出来呢?”

  方兴东的错误还不止于此。与移动相配合的另一原则 —— 平衡,也没能做到。就在融资1000万美元后,“博客中国”大肆招兵买马,员工从40人快速扩张到300人。方兴东作为一个从未管理过40人以上规模企业的“评论人士”,聘用了超过10个副总裁以上的高管。“博客中国”也很快从专注于IT扩展到几乎所有领域,子频道之多与门户无异。

  快速膨胀导致失控,方兴东无力维持。之后,“博客中国”经历了大幅裁员和方本人被投资方架空的悲剧。当强大的敌人进攻时,“博客中国”想抓紧时间快速发展,但没能保持住平衡,重重地摔倒了。

  周鸿祎从另一个角度解释失败原因:选择了错误的竞争方式。方兴东本应选择柔道的方式,但却选择了相扑的方式。虽然同样发源于日本,但相扑的方式完全与柔道相反:靠体重和体力来正面抗衡,并且一开始就双眼盯住对方,激起彼此的斗志,一旦开战就速战速决。
无力的模仿者

  网易与三个挑战者在网络游戏上的竞争正可验证相扑和柔道策略的差别。

  凭借精巧而强势的两款产品,网易在2005年后期坐上了中国网游公司头把交椅。柔道选手盛大并未跟网易比拼产品,而是改变规则:免费玩游戏,靠卖道具赚钱。而网易既有的模式与之完全相反:收费玩游戏,靠时间和技巧赢得道具。这令网易处于困境:原来的游戏越成功,转型新模式所造成的损失和震荡就越大。一年半过去了,网易还未推出一款免费游戏,眼睁睁看着盛大在2007年一季度超过自己成为第一。

  相比盛大,新兵征途更没资格跟网易硬碰,同样是柔道选手的史玉柱在颠覆传统上比陈天桥走得更远。征途不再遵守游戏与现实间的界限,彻底引入现实世界的“不均衡”。比如有钱人主宰一切,一些所谓的“道德”也被抛弃,赌博开始大行其道。游戏规则的设计和营销渠道更向二三线地区倾斜,这些做法直接否定了通行的行业模式。“异端”征途因此扶摇直上,与盛大、网易分庭抗礼。

  而相扑选手金山却不同,要与网易正面交锋。自恃研发力量雄厚,金山跟随网易大搞自主研发,期望借助一两款大作的暴发奠定地位。金山在全国四个城市设独立研发机构,斥巨资从网易挖走骨干,推出与网易相似的游戏正面抢夺玩家,还在公关宣传上与其针锋相对,渲染丁磊与雷军的私人恩怨。但从2003年入局至今,金山一直未挤入一流阵营,也没能研发出一款堪与网易比肩的大作。

  周鸿祎说:“作为追随和模仿者,唯一的价值就是让领先者更加强大。你帮它挑错,它不断改正;你跟它学,等你学会了,它又有了新的东西。”

  从战役到战局

  柔道战略的核心借力打力,隐含有这样一个几乎是真理的假设:任何资产,在特定环境下都可能变成累赘。强势里必然藏有弱势。

  对于柔道选手本身,其实这种辩证关系也存在。柔道是处于下风的弱势一方所借助的技巧,适用于企业成长初期;而一旦处于上风,实力占优的一方就应该过渡到相扑法则,与对方正面交锋、比拼实力。不过,柔道思维越娴熟,过渡到相扑思维就越困难。

  这正是周鸿祎心头之病:“我适合初创企业。但如果做到很大规模需要整合资源,这些功夫就不是我擅长的。我的缺陷是缺乏宏观的眼光,对趋势的判断和对自己的认知不足。”

  2003年卖掉3721正是一次错误。自认为技术落后、实力弱小而“妄自菲薄”,周鸿祎把当时在营收和渠道上都胜过百度的3721卖给雅虎,而结果尽人皆知:3721的基业最终坍塌,而对手百度则在上市后一飞冲天。周鸿祎假设:“如果当时我选择融几千万美元,弥补技术上跟百度的差距,借助渠道和收入的优势跟它全面抗衡,那今天我在中国搜索市场不是老大也是老二。”

  离开雅虎后,周鸿祎去IDG做了半年VC(风险投资),其实就是有意识地弥补,换个角度看问题。“打仗很兴奋,但老处在战场之中,就光看到一次战役的布局,而看不到战争的格局。有时争夺某个山头,确实有冲击力。但一个山头的得失不等于整个战争的胜败。”以灵巧的方式赢得局部,还要有统帅全局的稳健战略,周鸿 认识到这两个要结合起来。

  柔道高手、善于率领小企业冲击大企业的周鸿祎,如何能成就一番大事业、让自己成长为一个巨无霸?

  奇虎、360安全卫士、迅雷、Discuz!是周鸿祎或亲自执掌或天使投资的四块资产,姑且称为奇虎系。奇虎以搜索为切入口整合论坛资讯,360占据了安全这最后一块尚未被巨头垄断的互联网基础服务,迅雷已经成为仅次于QQ的第二大应用软件和客户端软件,Discuz!正在成为论坛软件的垄断者和论坛资讯的整合者。四块资产相互独立,正给了柔道高手施展以小博大策略的有利环境。

  “我觉得不要把所有业务装在一个公司里,因为业务多了必然多元化,多元化很难形成聚焦。”周鸿祎说,“所以用资本的方式,找到不同的创业团队,然后去扶持他们。最后大家在市场中各自占据有利的地形,可以互相支援,互相合作。”

  虽然还没有人清晰地给出四块资产的合力会成就怎样一番图景,但互联网观察家胡延平曾经谨慎地下过判断:未来有期望在底层改变互联网而成就霸业的三家企业是腾讯、百度和奇虎系。

  陈一舟的千橡,正好成为周鸿祎奇虎系的对比参照。从2003年开始,陈一舟进行了大小10余次并购,集合了各种论坛和各种服务的千橡集团意欲快速成长为巨无霸。2006年中期,有人曾抛给陈一舟一个问题:你是在做投资还是在做企业?陈思考片刻说道:“我是在用投资的方式做企业。”不过后来证明,这是次失败的尝试。

  周鸿祎认为,天使和魔鬼都隐藏在细节之中,不深入企业、不激励创业团队,单靠并购的资本方式难以做好企业,而且并购后整合的难度非常大。所以另外一套做法是通过深入企业帮助它们产生影响力和支配力,而不是通过并购来控制。另外要保持企业的独立,在独立的基础上相互协作,而不是并购之后强行整合。这就是与陈一舟完全不同的做法,周鸿祎称为:“用做企业的方式做投资。

  另一个互联网观察家张云帆提出了“隐形舰队”的概念。陈一舟利用并购组建了一个巨型舰队,这立刻引起了对手的关注和攻击,同时管理这个舰队的难度也大。而“周鸿祎组建的是一个隐形舰队,它沉在水下各自为阵,避免了强大对手的注视,并能以周鸿所擅长的柔道方式攻击别人。而一旦时机成熟,就会快速集结,发挥统一的能量”。

  这些独立舰船能否通过资本安排最终合为一体,周鸿祎并无把握,“要看缘分”。即便不成,“我最后的成就感,也就是通过摸索出一些模式,影响更多的创业公司和这个行业的发展”。

  美国学者大卫 . 尤费在其著作《柔道战略》中把柔道看作一种商业策略,核心是借力打力。作为弱势的一方,不能与强大的一方硬碰,而要设法把对手的体重、力量等优势变成对其不利的因素。比如寻找到一个支点,利用杠杆作用把对手撬起来、甩出去!对方的劲越大、体重越大,就摔得越惨。

  周鸿祎眼中的经典战役:

  3721构建扁平渠道打击CNNIC的层级代理体系

  以对手的同盟为杠杆,同盟越多,转型代价越大。

  雅虎邮箱扩容以打击拥有巨大用户量的门户

  建立新规则,对手资产越多,累赘就越多。

  雅虎邮箱同盟

  以对手的对手为杠杆,对手越强大,对手的对手的反击情绪就越大。

  “一搜”高调出击MP3搜索攻击百度

  把对手的资产变成我的筹码,对手资产越大,越不敢向我发力。

  百度接受谷歌注资

  成功避免了强大对手的猛烈攻击,赢得了独立发展的宝贵时间。

  盛大和征途改变游戏规则攻击网易

  把对手的资产变成我的筹码,对手原有资产越成功,越不敢模仿。

  “博客中国”高调攻击新浪

  违反小狗策略,激起对手正面攻击;未能界定一个狭小领域独立发展。

  新浪成功反击“博客中国”

  杰出的相扑策略,搞名人博客,通过把博客媒体化而把博客竞争引入自己熟悉的媒体战场。

  金山与网易正面抗衡

  错误地选择了相扑战略,跟随者会让领先者做得更好。

  谷歌在中国大肆入股注资外围公司获取流量

  错误地选择了相扑战略,想借助资本优势获取流量与百度正面竞争。

posted @ 2008-04-03 13:49 iamzzb 阅读(175) | 评论 (0) | 编辑 收藏
 

2008年3月21日

简化JavaMail:小巧 Jakarta Commons-Email 简单教程 (转)
Jakarta发布了Commons Emails 1.0 released 版本,目的是为了简化JavaMail。

知道有它几个class吗?你一定想不到,只有8个!

好了,开始我们的jakarta commons emails 之旅:)

一:Quick Start
通过SimpleEmail发送邮件
1java.lang.Object
2  org.apache.commons.mail.Email
3      org.apache.commons.mail.SimpleEmail

1SimpleEmail email = new SimpleEmail();
2email.setHostName("mail.4ya.cn");
3email.setAuthentication("<username>","<password>")
4email.addTo("martin.xus@gmail.com", "martin");
5email.setFrom("martin@4ya.cn", "martin");
6email.setSubject("测试主题");
7email.setMsg("这里是邮件内容");
8email.send();

就如代码里字面上的意思一样简单:
1:创建以SimpleEmail对象
2:设定发送信件的smtp服务器,如果没有设定,会寻找系统变量中mail.host值。
3:设定smtp的用户和密码
4:收件人
5:发件人
6:主题
7:内容
8:发送

二:发送带附件的邮件
我们可以发送本机的附件,当然我们也可以发送非本机的附件,如果发送的是一个存在网络上的附件的url,则邮件发送的时候会自动下载,添加到附件中。

   1:)发送本地附件:
1EmailAttachment attachment = new EmailAttachment();
2attachment.setPath("test/test.rar");
3attachment.setDisposition(EmailAttachment.ATTACHMENT);
4attachment.setDescription("python resource");
5attachment.setName("resource");

   2:)发送不存在本地的附件
1EmailAttachment attachment = new EmailAttachment();
2attachment.setURL(new URL("http://www.smilinglibrary.org/sldoc/pics/index03.jpg"));
3attachment.setDisposition(EmailAttachment.ATTACHMENT);
4attachment.setDescription("微笑图书馆");
5attachment.setName("微笑图书馆");


next,添加附件到我们的邮件中
 1MultiPartEmail email = new MultiPartEmail();
 2email.setHostName("mail.4ya.cn");
 3    email.setAuthentication("<username>","<password>")
 4email.addTo("martin.xus@gmail.com", "martin");
 5email.setFrom("martin@4ya.cn", "martin");
 6email.setSubject("邮件主题");
 7email.setMsg("邮件内容");

 8//添加附件
 9email.attach(attachment);
10
11//发送邮件
12email.send();

如果需要发送多个附件,只需创建多个EmailAttachement,即可
1email.attach(attachment1)
2email.attach(attachment2)

三:发送html格式的邮件
通过HtmlEmail我们可以发送Html格式的邮件:

1java.lang.Object
2  org.apache.commons.mail.Email
3      org.apache.commons.mail.MultiPartEmail
4          org.apache.commons.mail.HtmlEmail
5

如下:
 1//HtmlEmail!
 2HtmlEmail email = new HtmlEmail();
 3email.setHostName("mail.4ya.cn");
 3   email.setAuthentication("<username>","<password>")
 5email.addTo("martin@4ya.cn"martin");
 6email.setFrom("martin.xus@gmail.com"martin");
 7email.setSubject("主题:该邮件包括html格式内容");
 
 8// embed the image and get the content id
 9// 注意这里:embed 将帮助我们创建标签如:cid:xxx url
10URL url = new URL("http://www.apache.org/images/asf_logo_wide.gif");
11String cid = email.embed(url, "Apache logo");
12
13/** *//**
14set the html message
15我们看到HtmlEmail extends Email的,它依然有setMsg(),但是这里发送的邮件包括了插入在邮件内容中的图片,所以不能在使用了setMsg(),而要以setHtmlMsg 或setTextMsg代码
16**/

17email.setHtmlMsg("<html>The apache logo - <img src=\"cid:"+cid+"\"></html>");
18
19// set the alternative message
20email.setTextMsg("Your email client does not support HTML messages");
21
22//set mail
23email.send();
24

四:最后一步
如果需要实现更复杂authenticator 你可以extends javax.mail.Authenticator ,实现你自己的东西,然后调用Email.setAuthenticator(javax.mail.Authenticator newAuthenticator)即可

这一点jakarta也做了,给我们提供了一个defaultAuthenticator
1java.lang.Object
2  javax.mail.Authenticator
3      org.apache.commons.mail.DefaultAuthenticator

覆盖掉该方法,实现你自己的东东 o_o
1protected javax.mail.PasswordAuthentication getPasswordAuthentication()


五:any more?
当然有了 o_o 以后再写.
posted @ 2008-03-21 09:25 iamzzb 阅读(23) | 评论 (0) | 编辑 收藏
 

2008年2月22日

字符串进行DES加密和解密

// 字符串进行 DES 加密和解密

import javax.crypto.Cipher;

import javax.crypto.SecretKey;

import javax.crypto.spec.SecretKeySpec;

 

public class DesedeStrDemo {   

    static void getDecrypt() throws Exception {

       /****************** 加密 ******************/

       String APSecret = "dddddd" ; // 密钥字符串

       String info = "iamzzb" ; // 加密字符串

      

       byte [] key = new byte [24]; // 密钥预存放字节数组

       byte [] APSecretB = APSecret.getBytes();

       int keylen = APSecretB. length ;

       if (keylen > 24)

           keylen = 24;

       System.arraycopy(APSecretB, 0, key, 0, keylen);  // 密钥格式化为字节数组 , 并复制到 key  

       SecretKey deskey = new SecretKeySpec(key, "DESede" ); // 生成秘密(对称)密钥

       Cipher c = Cipher.getInstance( "DESede/ECB/PKCS5Padding" ); // 提供了针对加密和解密的密码 cipher 功能

       c.init(Cipher. ENCRYPT_MODE , deskey); // 用密钥初始化此 cipher

       byte info_new[] = c.doFinal(info.getBytes()); // 结束多部分加密操作

      

       /****************** 解密 ******************/    

       Cipher cc = Cipher.getInstance( "DESede/ECB/PKCS5Padding" ); // 提供了针对加密和解密的密码 cipher 功能

       cc.init(Cipher. DECRYPT_MODE , deskey); // 用密钥初始化此 cipher

       byte info_old[] = cc.doFinal(info_new); // 结束多部分解密操作

       String info_olds = new String(info_old);

       System. out .println(info_olds); 

      

    }  

    public static void main(String[] args) throws Exception {

       getDecrypt();

    }

}

 

posted @ 2008-02-22 17:33 iamzzb 阅读(61) | 评论 (0) | 编辑 收藏
 

2007年7月5日

post带空格的values时只能传递空格前部份

解决办法:
HTML部件value=""要加双引号
value="成败 呒呒.txt"

代码1:MyHtml.html
-----------------------------------------------------------------------------------------------
<html>
<script language = javascript type="text/javascript">
function submitBatch(form,func){   
    var action = func + "";
 var countFid = getCheckedCount(form.elements["fid"]);
 alert ("countFid = "+countFid);
 form.action = action;
   
    form.submit();
    return true;                    
   
}
//调用的函数1 getCheckedCount(control)
function getCheckedCount(control) {
    var count = 0;
    if (control) {
        if (control.length) {
            for (var i=0; i<control.length; i++) {
                var c = control[i];
                if (c.checked) count ++;
            }
        } else {
            var c = control;
            if (c.checked) count ++;
        }
    }
    return count;
}
//调用的函数2 resConfirmDelete(fileCount, folderCount)
function resConfirmDelete(fileCount, folderCount) {
    return confirm("你确定要删除选定的 "
            + folderCount + " 项吗?")
}
</script>
<body>
<form action="" method="post" >
<input name="test_name" type="text" value="zhaozb"  />zhaozb<br>
<input name="fid" type="checkbox" value= zhao zb  />zhao zb<br>
<input name="fid" type="checkbox" value=小 兵 />小 兵<br>
<input name="fid" type="checkbox" value="zhaozb3" />zhaozb3<br>
<input name="submitTest" type="text" onClick="submitBatch(form,'MyJsp.jsp');" value="提交">

</form>
</body>
</html>
-----------------------------------------------------------------------------------------------
代码2:MyJsp.jsp
-----------------------------------------------------------------------------------------------
<%@ page language="java" import="java.util.*" pageEncoding="gb2312"%>
<html>
  <head>  
    <title>test</title>  
  </head> 
  <body>
    看看能不能接受到. <br>
    <%
        request.setCharacterEncoding("gb2312");
        String str="ok";
        str = request.getParameter("test_name");
        String items[]=request.getParameterValues("fid");
       
       for(int i=0;i<items.length;i++){       
     str = items[i];
      out.println(i+" = "+str+"<br>");      
  }
  //out.println("items="+items.length);
  //out.print(str+"ss"); 
    
    %>
  </body>
</html>
-----------------------------------------------------------------------------------------------

posted @ 2007-07-05 16:56 iamzzb 阅读(46) | 评论 (0) | 编辑 收藏
 

2007年6月7日

用Java解析XML文档

一、前言
  
  用Java解析XML文档,最常用的有两种方法:使用基于事件的XML简单API(Simple API for XML)称为SAX和基于树和节点的文档对象模型(Document Object Module)称为DOM。Sun公司提供了Java API for XML Parsing(JAXP)接口来使用SAX和DOM,通过JAXP,我们可以使用任何与JAXP兼容的XML解析器。
  
  JAXP接口包含了三个包:
  
  (1)org.w3c.dom W3C推荐的用于XML标准规划文档对象模型的接口。
  
  (2)org.xml.sax  用于对XML进行语法分析的事件驱动的XML简单API(SAX)
  
  (3)javax.xml.parsers解析器工厂工具,程序员获得并配置特殊的特殊语法分析器。
  
  二、前提
  
  DOM编程不要其它的依赖包,因为JDK里自带的JDK里含有的上面提到的org.w3c.dom、org.xml.sax 和javax.xml.parsers包就可以满意条件了。
  
  三、使用DOM解析XML文档
  
  我们现在来看看DOM是如何解析XML的吧!同样的,我将从一个简单的不能再简单的例子来说明DOM是如何解析XML文档的,先让我们看看XML是什么内容吧:
  
 <?xml version="1.0" encoding="gb2312"?>
 <books>
 <book email="iamzzb@tom.com">
 <name>hibernate</name>
 <price>100</price>
 <param>不错误</param>
 </book>
 <book email="iamzzb@126.com">
 <name>spring</name>
 <price>120</price>
 <param>太喜欢了</param>
 </book>
 </books>
  
简单的不能再简单了。但是该有的都有了,根元素、属性、子节点。好了,能反应问题就行了,下面来看看解析这个XML文件的Java代码吧!

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

public class DomParse {

 public DomParse() {

  DocumentBuilderFactory domfac = DocumentBuilderFactory.newInstance();

  try {
   DocumentBuilder dombuilder = domfac.newDocumentBuilder();
   InputStream is = new FileInputStream("src/com/app.xml");
   Document doc = dombuilder.parse(is);
   Element root = doc.getDocumentElement();
   NodeList books = root.getChildNodes();

   if (books != null) {
    for (int i = 0; i < books.getLength(); i++) {
     Node book = books.item(i);
     if (book.getNodeType() == Node.ELEMENT_NODE) {
      String email = book.getAttributes().getNamedItem("email").getNodeValue();
      System.out.println(email);
      //String name2 = book.getAttributes().getNamedItem("name").getNodeValue();
      //System.out.println("name2="+name2);
      
      for (Node node = book.getFirstChild(); node != null; node = node.getNextSibling()) {
       if (node.getNodeType() == Node.ELEMENT_NODE) {

        if (node.getNodeName().equals("name")) {         
         String name = node.getFirstChild().getNodeValue();         
         System.out.println("name="+name);
        }
        if (node.getNodeName().equals("price")) {
         String price = node.getFirstChild().getNodeValue();
         System.out.println("price="+price);
        }
        if (node.getNodeName().equals("param")) {
         String param = node.getFirstChild().getNodeValue();
         System.out.println("param="+param);
        }
       }
      }
     }
    }

   }

  } catch (Exception e) {
  }
 }

 public static void main(String[] args) {

  new DomParse();

 }
}  


  四、代码解释
  
  先看看这个程序引用类:
  
  import java.io.FileInputStream;
  
  import java.io.FileNotFoundException;
  
  import java.io.IOException;
  
  import java.io.InputStream;
  
  import javax.xml.parsers.DocumentBuilder;
  
  import javax.xml.parsers.DocumentBuilderFactory;
  
  import javax.xml.parsers.ParserConfigurationException;
  
  //下面主要是org.xml.sax包的类
  
  import org.w3c.dom.Document;
  
  import org.w3c.dom.Element;
  
  import org.w3c.dom.Node;
  
  import org.w3c.dom.NodeList;
  
  import org.xml.sax.SAXException;
  
  上面那么简单的代码一看就明白了,但是为了介绍个DOM编程的大概还是来看看这个程序吧:
  
  (1)得到DOM解析器的工厂实例
  
  DocumentBuilderFactory domfac=DocumentBuilderFactory.newInstance();
  
  得到javax.xml.parsers.DocumentBuilderFactory;类的实例就是我们要的解析器工厂
  
  (2)从DOM工厂获得DOM解析器
  
  DocumentBuilder dombuilder=domfac.newDocumentBuilder();
  
  通过javax.xml.parsers.DocumentBuilderFactory实例的静态方法newDocumentBuilder()得到DOM解析器
  
  (3)把要解析的XML文档转化为输入流,以便DOM解析器解析它
  
  InputStream is=new FileInputStream("bin/library.xml");
  
  InputStream是一个接口。
  
  (4)解析XML文档的输入流,得到一个Document
  
  Document doc=dombuilder.parse(is);
  
  由XML文档的输入流得到一个org.w3c.dom.Document对象,以后的处理都是对Document对象进行的
  
  (5)得到XML文档的根节点
  
  Element root=doc.getDocumentElement();
  
  在DOM中只有根节点是一个org.w3c.dom.Element对象。
  
  (6)得到节点的子节点
  
  NodeList books=root.getChildNodes();
  
  for(int i=0;i<books.getLength();i++){
  
  Node book=books.item(i);
  
  }
  
  这是用一个org.w3c.dom.NodeList接口来存放它所有子节点的,还有一种轮循子节点的方法,后面有介绍
  
  (7)取得节点的属性值
  
  String email=book.getAttributes().getNamedItem("email").getNodeValue();
  
  System.out.println(email);
  
  注意,节点的属性也是它的子节点。它的节点类型也是Node.ELEMENT_NODE
  
  (8)轮循子节点
  
for (Node node = book.getFirstChild(); node != null; node = node.getNextSibling()) {
if (node.getNodeType() == Node.ELEMENT_NODE) {

if (node.getNodeName().equals("name")) {
String name = node.getFirstChild().getNodeValue();
System.out.println("name="+name);
}
if (node.getNodeName().equals("price")) {
String price = node.getFirstChild().getNodeValue();
System.out.println("price="+price);
}
if (node.getNodeName().equals("param")) {
String param = node.getFirstChild().getNodeValue();
System.out.println("param="+param);
}
}
}
  
这段代码的打印输出为:
iamzzb@tom.com
name=hibernate
price=100
param=不错误
iamzzb@126.com
name=spring
price=120
param=太喜欢了
  
注意:  
String name=node.getNodeValue();  是一个空值。而  
String name1=node.getFirstChild().getNodeValue(); 才是真正的值,这是因为DOM把<name>hibernate</name>也当作是两层结构的节点.

posted @ 2007-06-07 11:39 iamzzb 阅读(173) | 评论 (0) | 编辑 收藏
 

2007年5月23日

什么是敏捷开发?
杂、需求多变、时间要求紧迫等特点。敏捷方法以人为核心,注重交流与协作,强调软件可持续开发,实现频繁交付对客户最重要的价值,它包括敏捷项目管理,敏捷需求管理以及敏捷软件开发三部分。敏捷方法出现以来,在越来越多的软件开发项目中都获得成功,被证明是一种行之有效的管理思想和实践方法。
posted @ 2007-05-23 11:54 iamzzb 阅读(138) | 评论 (0) | 编辑 收藏
 
学习springside过程跟踪之一“熟悉例子helloworld代码”

    学习最快的方式看代码是一个很好的方法,到springside网站下栽springside-2.0-RC1-allinone.zip,看最简单的例子helloword,
包结构很清晰,典型的mvc三层架构,model、service、web.

    看一眼mode.User.java,数据库的实体bean,没什么可说的。

    第二眼service.UserManager.java,心里想肯定是逻辑层中的Dao对数据库的操作,增加、读取、更新和删除,editpus完后出乎我的意料
之外,里面没有方法的实现
/**
 * 用户管理业务类.
 * <p/>
 * 继承于HibernateEntityDao,不需任何代码即拥有默认的对User对象的CRUD函数. 如果想了解不继承于EntityDao,自行编写CRUD的写法, 参考{@link UserManagerNativeVersion}.
 *
 * @author calvin
 * @see HibernateEntityDao
 * @see org.springside.core.dao.HibernateGenericDao
 * @see UserManagerNativeVersion
 */
public class UserManager extends HibernateEntityDao<User> {
 // ....CRUD以外的其它商业方法
} 
这里面出现了CRUD看完了才知道就是增加、读取、更新和删除几个单词的首字母简写 即Create,Read,Update,Delete四个单词的缩写。是数据库操作的基本功,往往是程序员的入门级课程之一,也是最无聊的工作,在ROR中,这种工作被简化了好多,很多很牛的Java程序员也自己写了机器人去完成这些工作,这里的HibernateEntityDao<User>
肯定就是机器人了.
    下面看机器人HibernateEntityDAO,查springside参考手册知道它已存在默认的CRUD函数,子类只要用泛型语法声明自己管理的Entity类型即可,如果你的Manager是简单的CRUD类,没有其他的商业方法,那就恭喜了,上面的代码就是全部。
    SpringSide是如何对进行Hibernate封装的呢?看相关文档如下:
SpringSide对Hibernate做了三层封装:

第一层:HibernateGenericDao,基于spring的HibernateDaoSupport,但加入了分页函数与各种Finder函数,并使用泛型避免了返回值强制类型转换。

第二层:HibernateEntityDao,基于HibernateGenericDao,用泛型声明Dao所管理的Entity类,默认拥有该entity的CRUD方法。

第三层:HibernateExtendDao,基于HibernateEntityDao,主要扩展各种选择性的功能。

关于三个类的详细注解请看JavaDoc,大致描述如下:

1 HibernateGenericDao
   在Spring HibernateDaoSupport基础上封装的DAO,功能如下:

   1.应用泛型:使得find(), get() 这些函数不再返回Object,而是返回T,不再需要强制类型转换。

   2.提供各种finder的简便函数
      应用了JDK5可变参数的hsql查询函数:List find(String hql, Object... values),支持find(hql),find(hql, param1); find(hql,param1,param2);find(hql,new Object[] {param1,param2}) 四种接口。

      简单查询的简化函数:findBy(Class entityClass,String name,Object value) ,findUniqueBy(Class entityClass,String name, Object value),findByLike(Class entityClass,String name,Object value)

   3.获得设置好的Query和Criteria:createQuery(String hql,Object... values)  和 createCriteria(Class<T> entityClass,Criterion... criterions)

      Spring并没有很好的接口封装支持firstResult, maxResult, fetchsize,cache,cacheRegion 等多个查询参数,所以springside宁愿返回已设置好查询条件的Query和Criteria,让大家继续剩下的参数设置,最后再执行list(),注意那几个参数可以连续设置的,如:

createQuery(hql,param1).setFirstResult(10).setMaxResult(20).llist();
   4.分页函数:Page pagedQuery(Criteria criteria, int pageNo, int pageSize) 和Page pagedQuery(String hql, int pageNo, int pageSize, Object... args)

      Page是SpringSide自行封装的一个典型Page类,pagedQuery与hibernate自身分页查询的差别是先运行一次count,获得符合条件的总记录数。

      如果查询不需要总记录数,用普通的hibernate API,加上setFirstResult(),setMaxResult()就解决,不需要pagedQuery()。

   5.判别对象属性在数据库中唯一的函数:isUnique(Class<T> entityClass,Object entity,String names)。

2. HibernateEntityDao
    所有UserManager, ProductManager之类只管理一类对象的Manager类的基类,只需要在类定义处声明Entity类型即可

public class BookManager extends HibernateEntityDao<Book> {
}
  通过<Book>的定义,避免了HibernateGenericDao类各方法中必有的Class entityClass参数。

  如果需要操作其他的Entity,比如BookManager可能需要处理Category(图书目录),可以注入CategoryManager。无需担心事务的问题,JavaEE的默认事务模型已能很好处理。

  如果没有对应的CategoryManager,或者各种原因不想注入的话,可以使用BookManager继承自HibernateGenericDao的带entityClass参数的函数来操作Category的增删改,如Category category= this.get(Category.class, 1);

3. HibernateExtendDao
      此类演示SpringSide 所作的一些扩展,大家可以按照自己的需要进行修改和扩展。

     1. 支持对象不能被直接删除,只能设置状态列为无效。
        接口UndeleteableEntityOperation,定义了要支持此功能必须实现的函数。

        可以有接口(UndeletableEntity)和annotation(@Undeletable)两种形式来定义无效列,annotation列形式还可以定义标识对象已删除的状态属性的名称,用接口则必须实现setStatus()接口,在里面操作实际的状态属性。

        两种方式的异同见侵入,非侵入?Interface vs Annotation。

      2. 重载save(),在保存前先调用onValid() 函数
      3. 增加find(Map map) 接口
       默认查找与map中全部条件相同的entity。

       条件的比较运算符默认为相同,用户也可以为属性名加上like_,largerthen_ 这样的前缀,则使用相应的运算符作比较(未完成) 
第三眼 看web.UserAction.java,心想应该和struts里的acition一样,editpuls完后得到
/**
 * 用户管理Controller.
 * <p/>
 * 继承于StrutsEntityAction,不需编码就拥有默认的对User对象的CRUD响应函数. 如果想了解不继承于EntityAction,自行编写CRUD的写法, 参考{@link UserActionNativeVersion}.
 *
 * @author calvin
 * @see org.springside.core.web.StrutsEntityAction
 * @see org.springside.core.web.StrutsAction
 * @see UserActionNativeVersion
 */
public class UserAction extends StrutsEntityAction<User, UserManager> {

 @SuppressWarnings("unused")
 private UserManager userManager;    //看到serviceContext.xml中面有userManager应该是spring的注入

 public void setUserManager(UserManager userManager) {
  this.userManager = userManager;
 }
} 

第四眼 看了web.xml、struts-config.xml、applicationContext.xml、dataAccessContext.xml、serviceContext.xml 五个配置文件。
接下来调试程序,用运成功。

posted @ 2007-05-23 11:49 iamzzb 阅读(407) | 评论 (0) | 编辑 收藏
 
RoR是什么?

RoR是Ruby on Rails的缩写。
Ruby on Rails是一个用于编写网络应用程序的框架,它基于计算机软件语言Ruby,给程序开发人员提供强大的框架支持。
Ruby on Rails包括两部分内容:Ruby语言和Rails框架。

posted @ 2007-05-23 11:41 iamzzb 阅读(90) | 评论 (0) | 编辑 收藏
 

2007年5月18日

MVC三层模型(struts+spring+hibernate)总结

1 SSH在开发中的位置

现在J2EE的开源框架多的数不清楚,目前(已经、正在)比较流行的常用框架大概有struts,spring,hibernate,jsf,webwork,而 struts+spring+hibernate(SSH)这种轻量级架构被誉为“黄金组合”。spring和hibernate更是被许多人认为是未来五年内不会被淘汰的技术,犹如当年的struts,今天的开发中依然被广泛采用。

2 为什么使用SSH  

其实,就算用Java建造一个不是很烦琐的web应用,也不是件轻松的事情。 在构架的一开始就有很多事情要考虑。从高处看,摆在开发者面前有很多问题:要考虑是怎样建立用户接口?在哪里处理业务逻辑? 怎样持久化的数据。 而这三层构架中,每一层都有他们要仔细考虑的。 各个层该使用什么技术?怎样的设计能松散耦合还能灵活改变? 怎样替换某个层而不影响整体构架?应用程序如何做各种级别的业务处理(比如事务处理)?

    构架一个Web应用需要弄明白好多问题。 幸运的是,已经有不少开发者已经遇到过这类问题,并且建立了处理这类问题的框架。 一个好框架具备以下几点:减轻开发者处理复杂的问题的负担("不重复发明轮子");内部有良好的扩展; 并且有一个支持它的强大的用户团体。 好的构架一般有针对性的处理某一类问题,并且能将它做好(Do One Thing well)。 然而,你的程序中有几个层可能需要使用特定的框架,已经完成的UI(用户接口) 并不代表你也可以把你的业务逻辑和持久逻辑偶合到你的UI部分。 举个例子,你不该在一个Controller(控制器)里面写JDBC代码作为你的业务逻辑, 这不是控制器应该提供的。 一个UI 控制器应该委派给其它给在UI范围之外的轻量级组件。 好的框架应该能指导代码如何分布。 更重要的是,框架能把开发者从编码中解放出来,使他们能专心于应用程序的逻辑(这对客户来说很重要)。 

他们里面有很我优秀的设计理念及模式应用。比如, struts属于MVC框架,关键是要了解MVC的概念及大致原理,掌握就很容易了;而hibernate属于orm系统,属于持久层的解决方案,同样需要对ORM的概念及原理有一个总体的了解,必要时可以去查查EJB1及EJB2里面用于持久层的Entity Bean的使用。而spring属于应用程序框架,其核心是IOC容器以及AOP,把这两个核心概念(也可称为大模式)了解以后,再加上一定的内力修为,其它就都不难了。Spring中还集成了很多适用东西(不过这些东西80%的在某一个项目中可能一直用不上),比如对JDBC的封装、自己的MVC、对动态语言的简洁访问等,这些你根据自己的项目情况来选择学习,用到的时候再看看他的文档,一个项目下来应该就能把握。

3 对于SSH的理解

在SSH框架中,struts用来解决MVC中显示、请求控制部分,spring主要负责访问数据库DAO类的事务控制以及它被人称誉的IOC思想在业务类中的恰当运用,hibernate主要是充当数据访问层组件。由于spring对hibernate的良好支持,在DAO类主要由spring来完成,hibernate更多关注的应是O/R影射文件上的配置,如级联关系,延迟加载等如何设置才能使效率更高。见图1 (框架组合示意图)

4 收获和问题

4.1 actionform,PO,VO三对象的运用

讨论最多的是actionform,PO,VO三对象的运用,本人倾向的观点是:在SSH框架中,PO和VO可以不必区分,即业务层和持久层都可以使用hibernate产生的PO对象,我暂时把对象分成actionform和po两种来分析,action 应该是actionform和po的分界点,po不能穿透业务层,突破action到达页面显示层,同样actionform也不能突破action传到后台业务、持久层。(原因:po是持久对象,到达页面后就脱离了session成为无状态(暂理解为脱管态)的对象,而hibernate的持久对象是有状态(包含数据库主键)的,无状态的对象传到后台在调用hibernate的保存方法时会出错,一定要把无状态的对象先转化成持久态对象才能保存)在action中应该对两对象进行转化,转化的方法目前我还没发现有什么非常好的方法(欢迎高手不惜赐教),最普通的就是用get(),set()方法,也可以使用struts提供的属性复制方法BeanUtils类,但这个好象只支持单个类的转化,对于集合对象不行,需要我们自己扩展。

4.2 spring事务管理

在配置spring的事务管理中,最好把事务控制配置在业务类上,而不要配置在DAO类(需要保证多个原子事务操作同时失败回滚时这是一种解决办法);

4.3 action如何获取业务类

action中如何获取业务类:写一