<?xml version="1.0" encoding="utf-8" ?>
<!-- generator="FEEDCREATOR_VERSION" -->
<rss version="2.0" xmlns:sns="http://blog.sina.com.cn/sns">
    <channel>
        <title>小杨的BLOG</title>
        <description></description>
        <link>http://blog.sina.com.cn/yangsq</link>
        <lastBuildDate>Thu, 31 Dec 2009 04:55:28 GMT+8</lastBuildDate>
        <generator>FEEDCREATOR_VERSION</generator>
        <language>zh-cn</language>
        <copyright>Copyright 1996 - 2009 SINA Inc. All Rights Reserved.</copyright>
        <pubDate>Wed, 30 Dec 2009 20:55:28 GMT+8</pubDate>
        <item>
            <title>XPath学习笔记——基本概念
</title>
            <link>http://blog.sina.com.cn/s/blog_4b86f2ee010009sb.html</link>
            <description><![CDATA[<DIV>
&nbsp;XPath是W3C定义的一个规范语言，用于定位XML文档中的某些信息。XPath通常情况下使用在XSLT里，通过XPath表达式(XPath
expression)的形式来定位XML元素。</DIV>
<DIV>&nbsp;&nbsp;&nbsp;
XPath表达式是一种规范，必须遵循W3C的定义。此外，为了是XPath表达式更容易使用，它还内置了许多函数。</DIV>
<DIV>&nbsp;&nbsp;&nbsp;
既然XPath是规范的，那么他处理的XML也应该是规范的。它把XML看作是一些节点的树形结构。在学习XPath表达式之前，先说明一些概念。为了方便说明，这里举一个XML文档为例：</DIV>
<DIV>
<TABLE STYLE="BORDER-RIGHT: #999 1px solid; BORDER-TOP: #999 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #999 1px solid; WIDTH: 80%; BORDER-BOTTOM: #999 1px solid" ALIGN="center">
<TBODY>
<TR>
<TD>
<P>&lt;?xml version="1.0" encoding="UTF-8"?&gt;</P>
<P>&lt;bookstore&gt;</P>
<P>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;book&gt;<br/>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&lt;title lang="en"&gt;Harry Potter&lt;/title&gt;<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&lt;author&gt;J K. Rowling&lt;/author&gt;<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&lt;year&gt;2005&lt;/year&gt;<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&lt;price&gt;29.99&lt;/price&gt;<br/>
&nbsp;&nbsp;&nbsp;&nbsp;
&lt;/book&gt;</P>
<P>&lt;/bookstore&gt;</P>
</TD>
</TR>
</TBODY>
</TABLE>
</DIV>
<DIV>&nbsp;</DIV>
<DIV>1.&nbsp;document node (文档节点/根节点)</DIV>
<DIV>&nbsp;&nbsp;&nbsp;
就是像&lt;bookstore&gt;这样的节点。</DIV>
<DIV>&nbsp;</DIV>
<DIV>2. element node (元素节点）</DIV>
<DIV>
&nbsp;&nbsp;&nbsp;&nbsp;像&lt;author&gt;J
K. Rowling&lt;/author&gt;这样。</DIV>
<DIV>&nbsp;</DIV>
<DIV>3. attribute node (属性节点)</DIV>
<DIV>
&nbsp;&nbsp;&nbsp;&nbsp;像lang="en"这样的。</DIV>
<DIV>&nbsp;</DIV>
<DIV>4. atomic value (原子值）</DIV>
<DIV>&nbsp;&nbsp;&nbsp; 如J K.
Rowling、"en"，显然这些节点没有“孩子”也没有“父母”。</DIV>
<DIV>&nbsp;</DIV>
<DIV>节点间的关系：</DIV>
<DIV>1. parent （父母）</DIV>
<DIV>&nbsp;&nbsp;&nbsp;
在上面的XML文档中，&lt;book&gt;就是&lt;title&gt;,&lt;author&gt;等的父母。</DIV>
<DIV>&nbsp;</DIV>
<DIV>2. children (孩子)</DIV>
<DIV>&nbsp;&nbsp;&nbsp;
与“父母”正好相反，&lt;title&gt;,&lt;author&gt;等节点就是&lt;book&gt;的孩子。</DIV>
<DIV>&nbsp;</DIV>
<DIV>3. sibling (兄弟)</DIV>
<DIV>&nbsp;&nbsp;&nbsp;
显然，具有相同父母的节点就是兄弟节点，如&lt;title&gt;和&lt;author&gt;。</DIV>
<DIV>&nbsp;</DIV>
<DIV>4. ancestor (祖先)</DIV>
<DIV>&nbsp;&nbsp;&nbsp;
节点的父母，父母的父母，父母的父母的父母....就叫做该节点的祖先。</DIV>
<DIV>&nbsp;</DIV>
<DIV>5. <FONT FACE="宋体">descendant （后代）</FONT></DIV>
<DIV>&nbsp;&nbsp;&nbsp;
节点的孩子，孩子的孩子，孩子的孩子的孩子....就叫做该节点的后代。</DIV>
]]></description>
            <author>小杨</author>
            <category>XML相关</category>
            <comments>http://blog.sina.com.cn/s/blog_4b86f2ee010009sb.html#comment</comments>
            <pubDate>Mon, 03 Sep 2007 07:25:54 GMT+8</pubDate>
            <guid>http://blog.sina.com.cn/s/blog_4b86f2ee010009sb.html</guid>
        </item>
        <item>
            <title>XSLT学习笔记（1）——简介</title>
            <link>http://blog.sina.com.cn/s/blog_4b86f2ee010008t6.html</link>
            <description><![CDATA[<DIV>&nbsp;&nbsp;&nbsp;
XSLT——XML Stylesheet Language
Transformations，字面的意思就是做XML文档转换的语言。它不仅可以把XML文档转换为另外的XML文档（不仅可以一到一，还可以是*个到*个），还可以把XML文档转化为其他形式的文档，如HTML、XHTML、PDF等。</DIV>
<DIV>&nbsp;&nbsp;&nbsp;
在学习XSLT时，我们应该知道，进行XML转化的不是XSLT本身，而是需要另外的XSLT转换引擎。那么XSLT是干什么的？它是转换引擎在进行转化时参考的依据，要不引擎怎么能够知道怎样进行转化呢。</DIV>
<DIV>&nbsp;&nbsp;&nbsp;
在学习XSLT之前，先说比较好的XSLT转换引擎吧，其中有Xalan(IBM
transfer to Apache project)、Oracle
XSLT、Saxon（个人感觉很不错，目前出道8以上的版本了，支持XSLT2.0）、MSXML（这么重要的东西，当然是微软的，只不过可惜，其他开源项目都支持了XSLT最新技术时，它却实现了一个XSLT的过时版本，可能这就是微软的与众不同吧）、XML::XSLT（SourceForge的，Perl写的）。最后要说一个工具，提到XML，就不能提到XMLSpy，目前的版本是2007，个人感觉是超好使，呵呵，不是在做广告呦。</DIV>
<DIV>&nbsp;&nbsp;&nbsp;
XSLT是一种类XML语言，编码风格就是XML式的。它有自己的标签，当转换引擎进行转换时，就是根据这些标签进行怎样转化的。个人感觉，学习XSLT就是学习它的那些标签是怎么个意思。当然，在学习具体标签之前，学习一下XSLT是怎样转化的是完全有必要的。</DIV>
<DIV>&nbsp;&nbsp;&nbsp;
转换引擎首先把XML和XSLT分别构造为树型结构（XML的叫做“输入树”，XSLT的叫做“模板树”，这是很容易做到的），接下来转换引擎的的所有操作都是针对这颗树的。一定要记得，主角是输入树，而不是。</DIV>
<DIV>&nbsp;&nbsp;&nbsp;
实际上，XSLT是由一个个的转换模板构成的，当进行转换是，遍历输入树的每个节点，并查找模板树相应的模板，执行相应的转化。</DIV>
<DIV>&nbsp;&nbsp;&nbsp;
需要说明的是，在学习XSLT之前要有一些其他知识的储备，其中包括XML、namespace、XPath。XSLT是W3C推荐的标准，许多厂商的浏览器中也提供了对XSLT的支持。目前XSLT的最新版本是2.0，其中增加了许多有用的新功能，也是非常实用的。以后会逐步的学习这些知识。</DIV>
]]></description>
            <author>小杨</author>
            <category>XML相关</category>
            <comments>http://blog.sina.com.cn/s/blog_4b86f2ee010008t6.html#comment</comments>
            <pubDate>Thu, 21 Jun 2007 12:28:42 GMT+8</pubDate>
            <guid>http://blog.sina.com.cn/s/blog_4b86f2ee010008t6.html</guid>
        </item>
        <item>
            <title>SaaS风暴：中国软件企业如何应对挑战？(转)</title>
            <link>http://blog.sina.com.cn/s/blog_4b86f2ee010008s7.html</link>
            <description><![CDATA[<DIV>&nbsp;（转自 <A HREF="http://www.donews.com/"><FONT FACE="宋体">http://www.donews.com</FONT></A>）</DIV>
<DIV>&nbsp;</DIV>
<DIV>
<TABLE STYLE="BORDER-RIGHT: #999 1px solid; BORDER-TOP: #999 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #999 1px solid; WIDTH: 95.02%; BORDER-BOTTOM: #999 1px solid; HEIGHT: 20px" ALIGN="center">
<TBODY>
<TR>
<TD>&nbsp;&nbsp;&nbsp;
业界炒作了几年的SaaS，是否会重蹈上世纪90年代末ASP（Application
Service
Provider，应用服务提供商）无疾而终的覆辙？如今的SaaS热，是虚热还是实热？</TD>
</TR>
</TBODY>
</TABLE>
</DIV>
<P>&nbsp;</P>
<P>&nbsp;&nbsp;&nbsp;
SaaS(Software-as-a-Service,软件即服务)作为一种新型软件服务形式，正在全球兴起。作为软件产业的一次革命，中国软件供应商如何看待这场革命？它在中国市场的命运是否会一帆风顺？</P>
<P>&nbsp;</P>
<P><STRONG><STRONG>起跳</STRONG></STRONG></P>
<P>&nbsp;</P>
<P>&nbsp;&nbsp;&nbsp;
主营业务为Call
center的北京讯鸟软件有限公司，半年前成为Xtools在线CRM软件的使用者。他们的使用动机很简单，“公司拥有4个办公区，属于分散办公，而且，我们不知道哪个CRM软件比较好用”；他们的要求也很简单，“只要能用就行，连接速度要快，不要发生数据丢失的情况”。</P>
<P>&nbsp;</P>
<P>&nbsp;&nbsp;&nbsp;
经过在网上试用后，总裁吴益民感觉还不错。很快，经过注册、申请、开通账户后，讯鸟的销售人员开始了网上办公。使用中，吴益民发现软件还提供了工作总结、任务进展等模块，不久，公司为一些技术人员在Xtools.cn上开通了账户。</P>
<P>
　　如今，讯鸟软件不仅是在线软件的使用者，还即将成为SaaS大潮中的一员。公司正在筹划构建基于SaaS模式的Call
center中心，用户只需注册账户就可以开通Call
center，预计年底前上线。</P>
<P>&nbsp;</P>
<P>
　　讯鸟软件只是中国SaaS生态图中的一个分子。Salesforce.com的创始人，曾经在甲骨文公司供职的Benioff多次表示，这种新型的软件提供方式将颠覆传统软件。“颠覆”有点儿夸张，但是我们却无法忽视它，这就是SaaS。</P>
<P>&nbsp;</P>
<P><STRONG>贴近电子商务</STRONG></P>
<P>&nbsp;</P>
<P>&nbsp;&nbsp;&nbsp;
今年9月，前eBay首席运营官迈纳德·韦伯被Salesforce董事会任命为公司董事，任期至2009年。这会是Salesforce融合进电子商务元素的信号吗？</P>
<P>&nbsp;</P>
<P>
　　SaaS与电子商务结合，听起来还不错。在国内，电子商务巨擘阿里巴巴推出了阿里软件，其中整合了在线CRM的功能。传统ERP厂商金算盘又打起了电子商务和在线应用软件的“算盘”，于今年7月推出了整合性的全程电子商务平台。</P>
<P>&nbsp;</P>
<P>
　　金算盘全程电子商务平台由金算盘全程供应链管理系统——金算盘9i(全程供应链管理系统)、亿禧网（<A HREF="http://www.72ec.com/">www.72ec.com</A>）、eTools（金算盘电子商务工具）三部分构成。平台中嵌入了基于互联网的eERP、eCRM、eSRM和eOA等新型管理软件系统，除了为用户提供、发布信息和资讯、搜索商机、即时通讯和信用保障等传统电子商务服务外，还可提供商务协同、在线工作、物流管理、供应链整合与管理等服务。</P>
<P>&nbsp;</P>
<P>
　　“这个平台将企业的管理范畴从企业内部延伸到了企业外部的上游供应商和下游客户，形成了一个完整的电子商务供应链管理系统。”金算盘副总裁张志鹏表示。</P>
<P>&nbsp;</P>
<P>
　　与Xtools和800CRM不同，在金算盘的服务模式下，用户希望将产品信息等公开地放在网络服务器上，而客户自己的信息、数据则在本地处理，用户可以把金算盘的服务器作为中转站，传送公司的远程数据，使企业随时随地使用在线数据。</P>
<P>&nbsp;</P>
<P>
　　从2004年末开始调研，2005年进入产品研发阶段，金算盘累计为这个业务投入了2000多万元。7月份平台发布之前，就已经有1000多家客户，如今客户数量更是近万家。这些客户几乎都同时使用了电子商务和ERP管理软件两方面的服务，其中又有7成以上的客户带来了实际收益，截至目前，这项收入达到200多万元。</P>
<P>&nbsp;</P>
<P>
　　张志鹏坦言，这比他们预期的数字要好。他乐观预计，这一业务在明年年底至少能实现盈亏平衡。而记者通过综合采访得到的信息判断，Xtools和800CRM用两年时间达到的业绩，金算盘用了3个多月就达到了。</P>
<P>&nbsp;</P>
<P>
　　金算盘的精明，在于它从企业认为有价值的电子商务平台服务切入，并附加上ERP、CRM、OA等管理软件功能。凭借自己在软件领域的品牌影响力，金算盘开始攻城略地。阿里巴巴则巧妙地把SaaS服务融入电子商务，通过增加越来越多的软件功能，提升电子商务平台对客户的价值。</P>
<P>&nbsp;</P>
<P>
　　从现阶段看，两者似乎有异曲同工之妙，长远来看，金算盘毕竟是一个软件企业，对它产生最大价值的不会是电子商务，而是在线软件服务市场。</P>
<P>&nbsp;</P>
<P><STRONG>虚热，实热？</STRONG></P>
<P>&nbsp;</P>
<P>&nbsp;&nbsp;&nbsp;
Xtools.cn是国内较早试水SaaS的厂商之一，谢亿民是这家公司的营销总监。7月份以来，他先后接受了多家媒体采访，主题无一例外都是SaaS。这让他不禁感叹，SaaS真的越来越热了。</P>
<P>&nbsp;</P>
<P>
　　据IDC预测，2009年全球SaaS开支将达到107亿美元。“在过去3到5年里，内部安装CRM的总体拥有成本（TCO）一直在上升，相比而言，托管型CRM肯定经济。”Yangkee咨询机构的一位分析师说。</P>
<P>&nbsp;</P>
<P>
　　业界炒作了几年的SaaS，是否会重蹈上世纪90年代末ASP（Application
Service
Provider，应用服务提供商）无疾而终的覆辙？如今的SaaS热，是虚热还是实热？</P>
<P>&nbsp;</P>
<P>
　　有人认为，SaaS是ASP的变种。事实上，两者还是有区别的。以往的ASP更多强调“软件托管”这一形式，软件架构本身没有改变，还是以客户端-服务器的形式提供。而SaaS的软件架构已经悄然改变，广泛应用了Ajax技术，支持界面局部刷新，使得在线服务更像在用软件而不是网页浏览。用户通过互联网的形式在线使用软件，实现了随时试用、随时申请、随时开通，交纳月租费就行。</P>
<P>&nbsp;</P>
<P>
　　业界一种普遍的看法是，由于价格优势，SaaS非常适合中小企业采用。谢亿民却指出，“价格并不是SaaS吸引用户的最大优势”。在他看来，以下三个原因更重要。首先，SaaS使交易过程全部透明，包括产品和价格，“中小企业最担心重金买回来的东西不符合使用需求，而且传统CRM的报价很虚，企业购买产品时没有可参考的价格标准，厂商与用户要经过长时间的讨价还价才能达成一致”。</P>
<P>&nbsp;</P>
<P>
　　由此带来了另外两个优势：交易过程简单、交易成本较低，以及使用门槛与风险降低。使用传统CRM软件，用户需要花很多时间和精力对比产品、选型，即便使用一段时间后发现产品不符合要求，已经投入的成本也只能打水漂了。而且，企业都希望重金实施管理软件后，能够马上见到回报，虽然信息化的回报没这么快，SaaS这种“即用即买”的投入，有助于消除企业的顾虑。</P>
<P>&nbsp;</P>
<P>
　　除了这些优势外，国际软件厂商对SaaS的态度推动了中国企业的跟进。今年2月，SAP推出了CRM
on
demand，并在全球范围内销售，目前已完成在中国市场的本土化工作，开始在中国市场销售；甲骨文收购Siebel后，延续了其SaaS模式的CRM产品线；今年7月，微软宣布将在2007年推出它的CRM在线版。</P>
<P>&nbsp;</P>
<P>
　　关键的问题是，时机是否已经成熟？SAP大中华地区首席技术官张侠博士表示，SaaS可以看做是软件服务的外包，这与IT业务流程外包的观念相一致。此外，如今的互联网发展远比2000年前后更成熟，企业办公对互联网的依赖程度也越来越高，今天的SaaS比当年的ASP更具备成功的要素。</P>
<P>&nbsp;</P>
<P><STRONG>平台化</STRONG></P>
<P>&nbsp;</P>
<P>&nbsp;&nbsp;&nbsp;
互联网正在成为社会的基础设施。SaaS显示出，软件产业正呈现出平台网络化的趋势：灵活的软件平台架构，用户可以方便地增加所需要的功能模块，一体化地在网络上运行等。另一方面，SaaS呈现出平台化的发展趋势。</P>
<P>&nbsp;</P>
<P>
　　微软首席软件设计师奥齐曾经说，网络软件注重团体的协作性，而传统软件则负责一些复杂的功能并且提供良好的安全性，网络软件是传统软件的有益补充，软件工业的未来是软件加服务。</P>
<P>&nbsp;</P>
<P>
　　今年6月，微软和苏州软件园宣布启动中国第一个SaaS软件孵化器。孵化器为企业提供各种支持服务，包括网络环境、合格的SaaS
IDC机房设施、SaaS架构深度培训、SaaS顾问服务等，从而降低入孵企业的风险和成本，提高SaaS软件的成活率和成功率。</P>
<P>&nbsp;</P>
<P>　　负责微软在线托管软件和服务的高级总监John
Zanni对媒体表示，今年微软提供的在线托管服务除了传统的网站托管、电子邮件和即时通讯托管外，还包括应用软件的托管。去年，微软发布了Visual
Studio和SQL Server
2005，这两个产品都有一个特殊的版本针对在线托管服务商。</P>
<P>&nbsp;</P>
<P>
　　“有很多应用软件开发商开发了应用软件，同时也希望以在线托管的方式来发布软件，我们将帮助软件开发商把它的应用软件变成托管，并帮他们实现相关的需求。”John
Zanni说。</P>
<P>&nbsp;</P>
<P>
　　微软显然感受到了来自Salesforce的挑战，后者正意欲成为新的大型平台提供商。此前，Salesforce开发出了AppExchange管理软件在线开发平台。基于此平台，用户可以根据自己的管理需求开发管理软件，开发出的软件将在Salesforce的托管平台上使用。用户在AppExchange上购买和出售应用程序时，Salesforce将从中获利。有分析人士认为，Saleforce的举动正在使其成为下一个大型平台的提供商。</P>
<P>&nbsp;</P>
<P>
　　国内的800CRM也独立推出了类似的平台，800CRM的合作伙伴可以利用它的中文应用软件在线开发平台（800APP
Native）或协同开发平台（800APP
COMPOSITE）快速开发或集成增值软件产品。类似开发平台的出现，无疑将推动SaaS概念的普及，也会推动一批SaaS软件的诞生。</P>
<P>&nbsp;</P>
<P>
　　SAP的张侠则提出，对软件业真正影响深远的革命性话题是SOA(面向服务的体系结构Service-oriented
architecture)。在SOA架构中，企业应用都是由分布在网络上的服务组合而成的。企业业务流程是快速变化着的，企业创新时，SOA这种灵活架构可以支持企业IT灵活改变，为创新服务。张侠推断，将来回头看，SOA有着广阔的应用前景，将促使软件行业工业化，而SaaS可能只是SOA大潮里的几个小浪花。</P>
<P>&nbsp;</P>
<P><STRONG>决胜</STRONG></P>
<P>&nbsp;</P>
<P>
　　美国Salesforce.com的成功故事激励着很多后来者。截止到7月31日，Salesforce二季度的收益从7190万美元增加到1.181亿美元左右，分析师预计第三季度收入达到1.261亿美元。我们却不能忘了，Salesforce已经成立了7年多，并在2004年成功上市。</P>
<P>&nbsp;</P>
<P>
　　从理念到实践，中国的SaaS还有很长的路要走。在这个过程中，渠道应该如何建设？产品应该如何提供？谁又会主导这个产业的发展？</P>
<P>&nbsp;</P>
<P><STRONG>构建生态链</STRONG></P>
<P>&nbsp;</P>
<P>
　　800CRM的刘向军认为，SaaS模式中包含着三个角色：产品提供商、托管运营商和服务提供商。产品提供商具有软件产品的研发能力；服务提供商对终端客户提供实施、操作的培训和服务；托管运营商则成为连接产品供应商和用户的中间桥梁。</P>
<P>&nbsp;</P>
<P>
　　这应该是理想中的状态。虽然Salesforce.com由产品提供商成功地发展为平台运营商，但中国的Xtools、800CRM还属于大包大揽型，从产品研发、渠道拓展到服务提供基本上都由自己做。由于企业数量还不够多，发展时间较短，中国并没有形成明确的角色定位和专业化分工。</P>
<P>&nbsp;</P>
<P>
　　“未来的趋势可能是少数运营商存活，平台上有许多产品提供商提供的多种产品，产品排他性并不强，给客户自由选择的空间较大。”刘向军认为。</P>
<P>&nbsp;</P>
<P>
　　谁会成为这个掌握着通路的运营商呢？目前来看，电信运营商的潜力最大。中国电信有“商务领航”；类似的，中国网通有“宽带新动力”。在商务领航的网站中，我们能看到类似月租型CRM、邮件系统、OA系统等服务。XTools与中国电信和网通都建立了合作关系，企业客户能够通过运营商的平台了解并使用XTools的月租型CRM服务。</P>
<P>&nbsp;</P>
<P>
　　用友、金蝶等传统企业管理软件厂商，既有品牌又有客户资源，既有深厚的产品研发优势也有一定服务能力。因此，它们有实力去做运营和服务，转型SaaS的基础较好。记者获悉，金蝶在线产品事业部正在开发租用型的软件产品，预计年底之前发布。用友在线服务事业部的负责人也表示，SaaS是一个很重要的业务领域，相关产品也在研发中。</P>
<P>&nbsp;</P>
<P>
　　这一类软件厂商面临的最大问题，就是传统软件许可的售卖方式与软件租用模式之间的冲突。从短期看，前者获得的收益更大，那么软件厂商是否有足够的决心开拓SaaS市场？金算盘特意聘请原酷鹏网的副总夏平负责公司新推出的互联网业务，并独立推出了亿禧网，就是希望避免与传统业务冲突。</P>
<P>&nbsp;</P>
<P>
　　计世资讯资深分析师曹开彬相信，互联网可能会成为中小软件企业的后发优势。“对小型软件企业来说，越是低端的软件，许可证方式越收不到钱，完全转向服务的可能性就越大。高端的软件，反而可能在一段时间内继续维持原有的许可证方式。”曹开彬分析说。这批中小软件企业可能从产品提供商切入，如果起步较早，甚至可能同吃产业链。</P>
<P>&nbsp;</P>
<P><STRONG>渠道建设是个槛</STRONG></P>
<P>&nbsp;</P>
<P>
　　金算盘全程电子商务平台推出的时候，制定了一个颇为可观的代理商发展目标：20万家注册服务商和1000家应用服务商。</P>
<P>&nbsp;</P>
<P>
　　所谓注册服务商，就是无须压货、只需注册就能够加入金算盘的全程电子商务发展计划，依靠发展“注册用户”来获益。应用服务商作为注册服务商的补充，主要为用户提供后续增值服务，如帮助客户培训、管理、认证或者提供无线增值业务等。这两类服务商从客户那里所获得的收益都将与金算盘对半分成。</P>
<P>&nbsp;</P>
<P>
　　金算盘集团董事长兼总裁杨春将这种全新的渠道模式戏称为“像传销一样卖软件”。3个月过去了，如今，注册服务商发展了3000多家、应用服务商新增200多家，总数达到700多家。</P>
<P>&nbsp;</P>
<P>
　　虽然张志鹏表示，前期金算盘采取了比较谨慎的拓展措施，以后发展速度会更快，但是这个数据已经相当可观了。</P>
<P>&nbsp;</P>
<P>
　　Xtools采取了互联网线上推广、线下渠道代理商等推广方式，在全国共有30多家推广中心。如今，这家两岁多的企业拥有客户近千家，这个数字包括中国电信、中国网通、新浪企业服务等合作伙伴推广的客户。</P>
<P>&nbsp;</P>
<P>
　　800CRM宣称注册账户达到4万个，不过它有一个供用户免费使用的标准版，这就占去了近一半的账户数。另外，考虑到一家企业可能会开通几十个甚至上百个账户，800CRM拓展的企业数应该不太多。</P>
<P>&nbsp;</P>
<P>
　　从中不难发现，传统软件厂商的渠道优势更明显一些。然而，原有的渠道一方面推广许可型软件，一方面推广在线租用软件，肯定会发生左右手“互搏”的情况。到目前为止，金算盘渠道策略还没有出现大问题，恐怕与其现阶段电子商务平台色彩更浓一些不无关系。打算做SaaS的企业，必须想清楚，渠道要怎么做？</P>
<P>&nbsp;</P>
<P><STRONG>软件通用是方向</STRONG></P>
<P>&nbsp;</P>
<P>
　　很多投资商都看好SaaS的盈利前景，因为他们相信，一个SaaS企业前期最主要的投入就是软件研发和硬件购置成本，而这部分成本都是可以复用的。一旦企业投入运营，新用户的增加能够带来收益的增加，却并不带来太大的成本增加。</P>
<P>&nbsp;</P>
<P>
　　刘向军却有一个相反的观点，即随着用户数量的增加，亏损会增大。“托管模式下的软件营销方式还无法颠覆传统软件的营销模式，两者的营销成本相差不大，但收入却差很多。传统软件实施前能收60%的预付款甚至全款，租用则一般一次性收6个月的租金，基本相当于传统软件1/5-1/10的金额。”</P>
<P>&nbsp;</P>
<P>
　　提供通用性产品还是个性化定制？记者发现，不同的选择会导致不同的结果。张志鹏反复强调一个观点，客户想喝水，却并不需要自己建大水库，只需打开水龙头即可，金算盘要做的就是这个大水库。在产品上，金算盘不会针对某一个用户，而是一个通用平台，但是会把应用细化、模块化，供客户选择，“否则就成为项目了”，他指出。</P>
<P>&nbsp;</P>
<P>
　　800CRM坚持了一定程度上的定制，它认为，客户很多需求还是与提供的产品有差别，需要企业提供咨询、培训。他们发现，1/3以上的客户存在着这种需求。</P>
<P>
　　显然，既然以互联网的形式提供软件应用，就必须要适应互联网时代的特点。互联网对所有用户都是平等的，标准化的服务是其特征。即使存在着个性化定制，这种操作也必须是易学易用的。</P>
<P>&nbsp;</P>
<P>
　　如果说如何找到用户是在线软件服务提供商面临的一道槛，那么接下来，他们必须面对的是，粘住用户，让用户愿意继续掏钱购买服务。未来一段时间内，进入SaaS市场的企业会更多，用户的选择余地也就更大。同样都是基于互联网的服务，如果别人做得更好，用户就可以随时更换，就像给手机换壳一样。如何维系用户的使用忠诚度，将成为企业新的课题。</P>
<P>&nbsp;</P>
<P><STRONG>链接一:SaaS要点</STRONG></P>
<P>&nbsp;</P>
<P>
　　SaaS（Software-as-a-service，软件即服务），是一种颠覆传统的软件服务方式，它将使软件供应商与客户的关系发生彻底转变，从售卖关系转变为服务关系；</P>
<P>　　这一服务方式的特点是：</P>
<P>　　1． 软件基于互联网运用，采用标准浏览器作为交互界面；</P>
<P>
　　2．实现了移动办公，用户可以在任何可以上互联网的地方使用该软件，不必增加任何特别的软件和硬件；</P>
<P>　　3．具备快速、简洁的交付、设置和培训过程；</P>
<P>
　　4．采用“一对多”模式，是一种多订户系统构架，可以同时支持数千名用户同时使用；</P>
<P>
　　5．付费方式灵活，一般按照服务模式进行付费，用多少付多少，也可按使用时间支付；</P>
<P>
　　6．数据交换接口友好，包括数据的导入和数据的导出等，便于SaaS的数据与客户内部的系统进行数据的输入和输出。</P>
<P>&nbsp;</P>
<P><STRONG>链接二:Salesforce传奇</STRONG></P>
<P>&nbsp;</P>
<P>　　Marc Benioff是Salesforce公司的董事长兼CEO，Evan
Goldberg则是NetSuite公司的董事长。Salesforce提供在线CRM软件，NetSuite则提供面向中小型企业的
CRM/ERP/Ecommerce
等综合性网络应用软件。两人有一个共同点：他们都曾经在甲骨文就职，并且，他们的公司都得到了甲骨文CEO
Larry Ellison的资金支持。</P>
<P>&nbsp;</P>
<P>　　Evan Goldberg对早在1998年，他与Larry
Ellison的一次谈话记忆犹新。他对Ellison说，自己在考虑办一家公司，提供帮助小公司管理销售人员的软件。Ellison听后提了三点想法，“开办网络公司；改做会计和记账软件；让我来投资。”</P>
<P>&nbsp;</P>
<P>　　Goldberg记得跟Ellison谈话后不久，好友Marc
Benioff就告诉他：他也在考虑创办一家公司，提供Siebel
Systems为大公司开发的那种客户管理软件，不过其服务对象是小公司。大约过了3个月，Salesforce.com和NetSuite问世了。</P>
<P>&nbsp;</P>
<P>
　　2004年7月，Salesforce刚上市时，股票交易价为11美元。最近一个月，它的股价则在40美元附近徘徊。成立7年多的Salesforce.com在全世界的2.48万个公司拥有超过50.1万个用户，并以每年80%的速度增长，客户满意度高达97%。</P>
<P>&nbsp;</P>
<P><STRONG>评论一:并非软件“终结者”</STRONG></P>
<P>&nbsp;</P>
<P>
　　传统软件厂商对SaaS抱有一种既爱又恨的矛盾心理，正如网络媒体崛起时，纸质媒体、电视媒体对它的恐惧一样。</P>
<P>&nbsp;</P>
<P>
　　这些企业的观望可以理解。做SaaS吧，会不会搬起石头砸了自己的脚？不做吧，又确实有一部分用户存在这样的需求，如果自己不提供在线的解决方案，无异于把客户向竞争对手怀抱里推。</P>
<P>&nbsp;</P>
<P>
　　事实上，互联网是强大的，也是脆弱的。9月底的一天，由于遭受大量黑客的攻击，国内最大的域名和网站托管服务提供商——万网无法正常浏览。在这之前，国内另一域名服务商新网的域名解析服务器发生故障，导致30％多在其注册的网站无法访问。黑客攻击、网络故障、病毒肆虐，这决定了企业的软件应用不可能完全建立在互联网上。</P>
<P>&nbsp;</P>
<P>
　　更重要的是，用户的需求决定着软件供应商提供软件的方式。无论是2000年用友推出ASP模式的伟库，还是2004年Xtools和800CRM推出SaaS模式的在线CRM产品，本质上都反映了“on
demand”理念。</P>
<P>&nbsp;</P>
<P>
　　中小企业由于业务复杂度较低、标准化程度较高，具有使用租用型软件的需求，而传统的许可证售卖方式仍然能在大型企业中找到生存空间；由于涉及商业核心数据、应用稳定、投资较简单，财务管理软件和一些基础软件更多地还是本地部署，而SaaS在CRM、HR、EMAIL、ERP等相对不那么核心的领域有更大的应用空间。</P>
<P>&nbsp;</P>
<P>　　正如SAP虽然也推出了CRM on
demand产品，并根据客户的需要继续推出SaaS产品，但这些产品只是SAP为企业提供的全面解决方案的一小部分。无论是虾米还是大象，都能找到自己的位置，SaaS无法颠覆传统软件，两者必然以互补的形式共同满足用户的不同需求。</P>
<P>&nbsp;</P>
<P><STRONG>评论二:管理软件厂商将向服务提供商转变</STRONG></P>
<P>&nbsp;</P>
<P>
　　虽然目前中国的服务市场主要由以HP、IBM、蓝色快车和神州数码为代表的传统服务商占据着，但国内的管理软件厂商正面临向服务提供商的转变。未来5年，将很可能出现由管理软件厂商演变的服务提供商。</P>
<P>&nbsp;</P>
<P>
　　与国外管理软件厂商相比，本土管理软件厂商在向服务提供商转变方面具有独特的优势，如政策优势，政府对国产软件厂商提供了政策支持，如国务院颁发的18号文和47号文，使得本土管理软件厂商在开拓国内市场，尤其是政府市场方面掌握了先机；其次，还有客户关系优势，通过遍布全国的代理网络，管理软件厂商与客户保持了良好的合作关系，以至于当用户遇到IT服务方面的问题时，首先想到的不是专门的服务商，而是熟识的管理软件商，并且，管理软件商也非常乐意为用户提供帮助和支持。</P>
<P>&nbsp;</P>
<P>
　　随着用户信息化建设经验的增长，用户购买软件产品越来越重视厂商的服务能力，促使软件厂商提供付费服务。从目前的发展情况看，以用友、金蝶为首的国内领先管理软件厂商的收入结构已经出现了向服务倾斜的趋势。</P>
<P>&nbsp;</P>
<P>
　　服务市场的快速发展也推动了管理软件厂商的转变，数据显示，2005年中国IT服务市场规模已经达到451亿元，预计2006年将达到558亿元；而管理软件的市场规模仅有178亿元和215.4亿元。由管理软件厂商向服务提供商转变，不仅有助于提高软件的质量，还意味着更广阔的市场空间。</P>
]]></description>
            <author>小杨</author>
            <category>杂谈</category>
            <comments>http://blog.sina.com.cn/s/blog_4b86f2ee010008s7.html#comment</comments>
            <pubDate>Tue, 19 Jun 2007 01:25:59 GMT+8</pubDate>
            <guid>http://blog.sina.com.cn/s/blog_4b86f2ee010008s7.html</guid>
        </item>
        <item>
            <title>Acegi Authorization 授权机制</title>
            <link>http://blog.sina.com.cn/s/blog_4b86f2ee010008ol.html</link>
            <description><![CDATA[<DIV><A HREF="http://album.sina.com.cn/pic/4b86f2ee02000st2" TARGET="_blank"></A>&nbsp;&nbsp;&nbsp;
<FONT FACE="宋体">身份验证主要是根据用户的信息，在数据库（或其他方式）获得其权限信息，并把它放到SecurityContext里面(权限信息被封装成GrantedAuthority数组，由AuthenticationManager放到代表用户的Authentication里)，而并不决定对资源的真正访问权。用户对资源的访问权是由授权机制来控制的。</FONT></DIV>
<DIV>&nbsp;&nbsp;&nbsp;
Acegi中，管理授权机制的是<FONT FACE="宋体">AccessDecisionManager。<FONT FACE="宋体">AccessDecisionManager采用民主决策机制判断用户是否有权访问目标程序资源，它包含了多个AccessDecisionVoter。在访问决策时每个AccessDecisionVoter都拥有投票权，AccessDecisionManager统计投票结果，并按照某种决策方式根据这些投票结果决定最终是否向用户开放受限资源的访问。</FONT></FONT></DIV>
<DIV>&nbsp;&nbsp;&nbsp;
Acegi的投票方式有多种，每个Voter对访问权可以投赞成，弃权或反对，当然，我们除了使用Acegi自带的Voter（<FONT FACE="宋体">org.acegisecurity.vote.RoleVoter</FONT>）外，我们还可以有自定义的Voter。甚至我们可以编写自己的<FONT FACE="宋体">AccessDecisionManager来管理投票（如给某项投票分配一定的权重）。</FONT></DIV>
<DIV>&nbsp;&nbsp;&nbsp;
GrantedAuthority接口仅有如下方法，<FONT FACE="宋体">AccessDecisionManager通过这个方法获</FONT>
<TABLE STYLE="BORDER-RIGHT: #999 1px solid; BORDER-TOP: #999 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #999 1px solid; WIDTH: 80%; BORDER-BOTTOM: #999 1px solid" ALIGN="center">
<TBODY>
<TR>
<TD>public String getAuthority();</TD>
</TR>
</TBODY>
</TABLE>
</DIV>
<DIV>得权限信息。</DIV>
<UL>
<LI><FONT FACE="宋体"><STRONG>Pre-Invocation
Handling</STRONG></FONT></LI>
</UL>
<P>&nbsp;&nbsp;&nbsp;
<FONT FACE="宋体">AbstractSecurityInterceptor调用<FONT FACE="宋体">AccessDecisionManager来做最终的资源访问控制的决定。</FONT></FONT>下面我们来仔细看一下<FONT FACE="宋体">AccessDecisionManager这个接口，它包含如下三个方法：</FONT></P>
<DIV>
<TABLE STYLE="BORDER-RIGHT: #999 1px solid; BORDER-TOP: #999 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #999 1px solid; WIDTH: 88.52%; BORDER-BOTTOM: #999 1px solid; HEIGHT: 32px" ALIGN="center">
<TBODY>
<TR>
<TD>
<P><FONT FACE="宋体">public void <FONT COLOR="#FF0000">decide</FONT>(Authentication authentication, Object
object, ConfigAttributeDefinition config) throws <FONT COLOR="#009900">AccessDeniedException</FONT>;</FONT></P>
<P><FONT FACE="宋体">public boolean <FONT COLOR="#FF0000">supports</FONT>(ConfigAttribute attribute);</FONT></P>
<P><FONT FACE="宋体">public boolean <FONT COLOR="#FF0000">supports</FONT>(Class clazz);</FONT></P>
</TD>
</TR>
</TBODY>
</TABLE>
</DIV>
<DIV>
这个接口的主要方法是decide，从参数我们可以看出它包含了所有“有价值”的信息。如果访问被拒绝的话，会抛出一个AccessDeniedException。Acegi的灵活之处是用户可以根据自己情况来实现<FONT FACE="宋体">AccessDecisionManager及投票制度。当然，系统也提供了几个很好用的实现，使得我们大部分情况下使用Acegi提供的类就够了，下面这张类图完整的表达了<FONT FACE="宋体">AccessDecisionManager的层次关系（如不清晰，可点击图片后在查看）。</FONT></FONT></DIV>
<DIV>&nbsp;</DIV>
<DIV><A HREF="http://album.sina.com.cn/pic/4b86f2ee02000ssq" TARGET="_blank"><IMG SRC="http://album.sina.com.cn/pic_3/4b86f2ee02000ssq" WIDTH="500" BORDER="0"></A></DIV>
<DIV>&nbsp;&nbsp;&nbsp;
AccessDecisionVoter除了上面的三个方法外，还有三个静态常量，这些常量就是vote返回的int值。
<TABLE STYLE="BORDER-RIGHT: #999 1px solid; BORDER-TOP: #999 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #999 1px solid; WIDTH: 80%; BORDER-BOTTOM: #999 1px solid" ALIGN="center">
<TBODY>
<TR>
<TD><FONT FACE="宋体">int ACCESS_GRANTED = 1;<br/>
int ACCESS_ABSTAIN = 0;<br/>
int ACCESS_DENIED = -1;</FONT></TD>
</TR>
</TBODY>
</TABLE>
</DIV>
<P>&nbsp;&nbsp;&nbsp;
Acegi提供了三个具体的类去管理投票结果，他们三个的不同存在于对投票结果的统计上。<CODE CLASS="literal">ConsensusBased类根据非弃权票中同意和反对的数量来决定是否通过。在同意和反对票相同时或都投弃权票时可以根据设置来决定授权与否。<CODE CLASS="literal">AffirmativeBased类只要有一个或多个投票赞成就会给予通过，同样我们可以设置在都投弃权票的时候是否通过。<CODE CLASS="literal">UnanimousBased类会在所有的非弃权票都投赞成票时才通过，同样的设置出现在都投弃权票的时候。</CODE></CODE></CODE></P>
<P>&nbsp;&nbsp;&nbsp;
Acegi提供了<FONT FACE="宋体">AccessDecisionVoter的两个实现类。对于<CODE CLASS="literal">RoleVoter，我们需要配置它的rolePrefix属性（如ROLE_），当ConfigAttribute以rolePrefix开头时，<CODE CLASS="literal">RoleVoter才投票，否则投弃权票（ACCESS_ABSTAIN）。开始投票时，如果存在一个<CODE CLASS="literal">GrantedAuthority和被访问资源的一个或多个<CODE CLASS="literal">ConfigAttributes完全匹配时，投赞成票，否则投反对票。</CODE></CODE></CODE></CODE></FONT></P>
<UL>
<LI><FONT FACE="宋体"><CODE CLASS="literal"><CODE CLASS="literal"><CODE CLASS="literal"><CODE CLASS="literal"><STRONG>After
Invocation
Handling</STRONG></CODE></CODE></CODE></CODE></FONT></LI>
</UL>
<P><FONT FACE="宋体"><CODE CLASS="literal"><CODE CLASS="literal"><CODE CLASS="literal"><CODE CLASS="literal"><FONT FACE="宋体">&nbsp;&nbsp;&nbsp;
AbstractSecurityInterceptor调用<FONT FACE="宋体">AccessDecisionManager来决定资源（业务或domain对象）的访问。有时候，我们还需要在访问资源后进行一些操作，例如对返回数据的再加工。实际上，在Spring里使用AOP是很容易实现的，但Acegi提供了更加方便的类使我们对访问后资源进行操作。</FONT></FONT></CODE></CODE></CODE></CODE></FONT></P>
<P><CODE CLASS="literal"><CODE CLASS="literal"><CODE CLASS="literal"><CODE CLASS="literal"><FONT FACE="宋体">&nbsp;&nbsp;&nbsp;
同访问前控制类似，访问后控制由<FONT FACE="宋体">AfterInvocationManager管理，下面是它的实现类图：</FONT></FONT></CODE></CODE></CODE></CODE></P>
<P><CODE CLASS="literal"><CODE CLASS="literal"><CODE CLASS="literal"><CODE CLASS="literal"><A HREF="http://album.sina.com.cn/pic/4b86f2ee02000st2" TARGET="_blank"><IMG SRC="http://album.sina.com.cn/pic_3/4b86f2ee02000st2" WIDTH="500" BORDER="0"></A></CODE></CODE></CODE></CODE></P>
<P><CODE CLASS="literal"><CODE CLASS="literal"><CODE CLASS="literal"><CODE CLASS="literal">&nbsp;&nbsp;&nbsp;
可以看到，<FONT FACE="宋体">AfterInvocationManager接口和<FONT FACE="宋体">AccessDecisionManager接口有着相似的方法。需要注意的是，<FONT FACE="宋体">AfterInvocationManager的decide方法的参数包含了一个returnedObject，这个就是业务方法执行后返回的结果，我们可以对其进行再次的加工。</FONT></FONT></FONT></CODE></CODE></CODE></CODE></P>
<P><CODE CLASS="literal"><CODE CLASS="literal"><CODE CLASS="literal"><CODE CLASS="literal"><FONT FACE="宋体">&nbsp;&nbsp;&nbsp;
AfterInvocationProviderManager是用于管理Provider的，我们可以配置多个Provider，每个Provider都个对返回结果进行操作，操作顺序由配置顺序决定。上一个Provider的结果会传到下一个Provider。</FONT></CODE></CODE></CODE></CODE></P>
<P><CODE CLASS="literal"><CODE CLASS="literal"><CODE CLASS="literal"><CODE CLASS="literal"><FONT FACE="宋体">&nbsp;&nbsp; 配置完<FONT FACE="宋体">AfterInvocationProviderManager，一定要把它注入到<FONT FACE="宋体">MethodSecurityInterceptor里。</FONT></FONT></FONT></CODE></CODE></CODE></CODE></P>
]]></description>
            <author>小杨</author>
            <category>开源应用</category>
            <comments>http://blog.sina.com.cn/s/blog_4b86f2ee010008ol.html#comment</comments>
            <pubDate>Mon, 11 Jun 2007 10:22:38 GMT+8</pubDate>
            <guid>http://blog.sina.com.cn/s/blog_4b86f2ee010008ol.html</guid>
        </item>
        <item>
            <title>Acegi Authentication 验证机制</title>
            <link>http://blog.sina.com.cn/s/blog_4b86f2ee010008oh.html</link>
            <description><![CDATA[<DIV>&nbsp;&nbsp;&nbsp;
用户的认证权限管理是通常系统的必要组成部分，但由于其与业务一般是不相关的，所以时常单独设计。简单的做法是先开发业务，最后把用户管理授权等编码加到业务执行代码之前。这样做虽然直观、简单，但由于和业务混在了一起，所以降低了逻辑性，增大了耦合性。</DIV>
<DIV>&nbsp;&nbsp;&nbsp;
Acegi安全框架是一款非常优秀的开源框架，它致力于为企业应用提供一个良好的认证授权机制。它建立在Spring框架上的，目的是为J2EE应用提供一个非侵入的安全解决方案，通过不断的改进和应用测试，Acegi已经成为目前解决Java安全性的首选框架。为了更符合企业安全性的应用，Acegi社区还特意请了许多的安全专家为其出谋划策，以使Acegi不断的完善。</DIV>
<DIV>&nbsp;&nbsp;&nbsp;
Acegi的安全管理分为两个大的部分，Authentication（验证）和Authority（授权）。它能够对Http请求、业务逻辑（method）和domain对象进行有效的保护。在实现机制上，由于应用在Spring框架上，它可以借助Spring的IoC进行管理。同时Acegi也是简单的，因为你可以只通过配置文件就可以实现强大的权限管理功能，它的配置文件的格式与Spring完全相同；Acegi是可扩展性的，在实际应用中，我们完全可以设计自己的权限验证机制；此外，Acegi依靠Spring框架，达到了非侵入性的目的，我们可以专注业务上的设计，最后再来考虑系统权限，Acegi的这一特点也使我们对现有系统的改造变得简单容易。</DIV>
<P>&nbsp;&nbsp;&nbsp;
在进行用户身份验证时，最一般的方法是把合法用户的信息放到Session（或其他持久介质如cookie中），以后每次涉及到身份验证时，就把用户的信息取出，在和相应的权限做比较，完成身份验证。</P>
<P>&nbsp;&nbsp;&nbsp;
Acegi的验证机制和上面提到的是完全一样的，不同的是Acegi通过xml文件配置的方式来实现这种验证，而不是把验证过程嵌入到业务逻辑里。</P>
<P>&nbsp;&nbsp;&nbsp;
在Acegi框架里，充当session角色的是SecurityContextHolder类，它保存着应用的安全信息（SecurityContext），其中就包括当前正在使用该系统的用户信息及权限。<FONT FACE="宋体">SecurityContext的认证主体安全信息在一个HTTP请求线程的多个调用之间是共享的（通过<FONT COLOR="#FF0000">ThreadLocal</FONT>），但它不能在多个请求之间保持共享。为了解决这个问题，Acegi将认证主</FONT><FONT FACE="宋体">体安全信息缓存于HttpSession中，当用户请求一个受限的资源时，Acegi通过HttpSessionContextIntegrationFilter将认证主体信息从HttpSession中加载到SecurityContext实例</FONT><FONT FACE="宋体">中，认证主体关联的SecurityContext实例保存在Acegi容器级的SecurityContextHolder里。当请求结束之后，HttpSessionContextIntegrationFilter执行相反的操作，将</FONT><FONT FACE="宋体">SecurityContext中的认证主体安全信息重新转存到HttpSession中，然后从SecurityContextHolder中清除对应的SecurityContext实例。通过HttpSession转存机制，用户的安全信</FONT><FONT FACE="宋体">息就可以在多个HTTP请求间共享，同时保证SecurityContextHolder中仅保存当前有用的用户安全信息。如下图（摘自<A HREF="http://tech.it168.com/j/2007-05-22/200705221448921_1.shtml"><FONT FACE="宋体">http://tech.it168.com/j/2007-05-22/200705221448921_1.shtml</FONT></A>）：</FONT></P>
<P ALIGN="center"><A HREF="http://album.sina.com.cn/pic/4b86f2ee02000so9" TARGET="_blank"><IMG SRC="http://album.sina.com.cn/pic_3/4b86f2ee02000so9" BORDER="0"></A></P>
<P ALIGN="left">&nbsp;</P>
<P>&nbsp;&nbsp;&nbsp;
上图说明了如何在用户发出请求(可能是一个Http的请求，也可能是也是method或domain
object的请求)后，Acegi是如何获得用户信息及其权限的。上面提到，实际上SecurityContext包含的是Authentication对象，这是一个封装对象，通过它，我们即可以获得UserDetail（用户信息），有可以获得GrantedAuthority（用户权限）。</P>
<P>&nbsp;&nbsp;&nbsp;
获得用户身份后，Acegi通过AuthenticationManager来实现身份的认证。<FONT FACE="宋体">它象一个安检入口，对用户身份进行核查，用户必须提供身份认证的凭证（一般是用户名/密码）。在进行身份认证时，AuthenticationManager将身份认证的工作委托给多个AuthenticationProvider。因为在具体的系统中，用户身份可能存储在不同的用户信息安全系统中（如数据库、CA中心、LDAP服务器），不同用户信息安全系统需要不同的AuthenticationProvider执行诸如用户信息查询、用户身份判断、用户授权信息获取等工作。只要有一个AuthenticationProvider可以识别用户的身份，AuthenticationManager就通过用户身份认证，并将用户的授权信息放入到SecurityContext中。</FONT></P>
<P>&nbsp;&nbsp;
上述过程就是一个身份验证的过程。下面描述的就是这个流程：</P>
<P>&nbsp; （1）用户访问首页，并点击了一个链接；</P>
<P>&nbsp;
（2）url请求到达服务器后，服务器判断这个资源是不是受保护的；</P>
<P>&nbsp;
（3）当访问的是受保护资源时，如果用户还没有相应的认证信息
（SecurityContext里没有该用户的Authentication对象），服务器将返回一个http相应，更一般的情形是返回一个页面，通常是登录页面；否则进行第7步；</P>
<P>&nbsp;
（4）在登录页面，用户输入信息（一般是用户名、密码），并提交到服务器；</P>
<P>
&nbsp;&nbsp;（5）服务器获得用户输入的信息；</P>
<P>&nbsp;
（6）服务器验证用户信息是否是合法的，如果不是，通常将再次回到登录页面；</P>
<P>&nbsp;
（7）服务器通过用户身份获得其权限信息（一般放在数据库里），并验证这些权限信息是否能获得该资源的访问。如果能，则返回相应视图；如果不能，则抛出异常（AccessDeniedException）。（能与不能就不是验证机制了，而是授权机制，下面就讨论授权）</P>
]]></description>
            <author>小杨</author>
            <category>开源应用</category>
            <comments>http://blog.sina.com.cn/s/blog_4b86f2ee010008oh.html#comment</comments>
            <pubDate>Mon, 11 Jun 2007 07:52:58 GMT+8</pubDate>
            <guid>http://blog.sina.com.cn/s/blog_4b86f2ee010008oh.html</guid>
        </item>
        <item>
            <title>maven学习笔记（一）</title>
            <link>http://blog.sina.com.cn/s/blog_4b86f2ee0100089u.html</link>
            <description><![CDATA[<DIV>&nbsp;&nbsp;&nbsp;
以前只使用过ant去构建一个应用，但在IBM使用的是maven，它也是一个<FONT FACE="宋体">java项目管理工具，深化了ant，但又有自己一整套的项目集成策略。</FONT></DIV>
]]></description>
            <author>小杨</author>
            <category>开源应用</category>
            <comments>http://blog.sina.com.cn/s/blog_4b86f2ee0100089u.html#comment</comments>
            <pubDate>Wed, 09 May 2007 05:00:06 GMT+8</pubDate>
            <guid>http://blog.sina.com.cn/s/blog_4b86f2ee0100089u.html</guid>
        </item>
        <item>
            <title>StringBuilder学习笔记</title>
            <link>http://blog.sina.com.cn/s/blog_4b86f2ee0100083r.html</link>
            <description><![CDATA[<DIV>&nbsp;&nbsp;&nbsp;
在学习jdk5.0的字符串类型时，我们看到了StringBuilder的出现，StringBuilder同样继承自CharSequence（其它三个CharSequence接口的类是CharBuffer，String，StringBuffer，除CharBuffer不推荐使用外，String和StringBuffer是我们经常使用的）。通过查看StringBuilder的API
reference，我们可以看到，它和StringBuffer的方法基本上没有什么区别。但在具体实现上，StringBuilder是线程安全的，这点和StringBuffer有着本质的不同。</DIV>
<DIV>&nbsp;&nbsp;&nbsp;
我们知道String类已经提供了强大的功能（众多的API），那么我们为什么还要StirngBuilder呢？这是因为String对象的是固定的，一旦赋值就不能改变了，并且对其操作也是重量级的；而StringBuilder恰可以提供可变长字符串的灵活应用。下面介绍一下StirngBuilder的主要功能：</DIV>
<DIV>&nbsp;</DIV>
<DIV>1.&nbsp; <STRONG><FONT COLOR="#FF0000">append方法</FONT></STRONG></DIV>
<DIV>&nbsp;&nbsp;&nbsp;
StringBuilder的append方法提供了许多的重载方式，如</DIV>
<DIV>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<FONT COLOR="#009900">append(boolean/char/int/..所有基本类型)</FONT></DIV>
<DIV><FONT COLOR="#009900">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
append(char[])&nbsp; append(char[], int offset, int
len)</FONT></DIV>
<DIV><FONT COLOR="#009900">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
append(CharSequence)&nbsp; append(CharSequence, int
start, int end)</FONT></DIV>
<DIV><FONT COLOR="#009900">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
append(String)&nbsp;&nbsp;</FONT></DIV>
<DIV><FONT COLOR="#009900">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
append(StringBuffer)</FONT></DIV>
<DIV><FONT COLOR="#009900">&nbsp;&nbsp;&nbsp;</FONT>
<FONT COLOR="#000000">我们经常使用“+”来进行两个String的合并，在这个过程中，实际上又创建了第三个String类，所以说“+”对String是重量级的；相反，对于StirngBuilder的append方法，是在原有StringBuilder对象上操作的，显然要轻量的多。</FONT></DIV>
<DIV>2.&nbsp; <FONT COLOR="#FF0000"><STRONG>delete操作</STRONG></FONT></DIV>
<DIV>
&nbsp;&nbsp;&nbsp;&nbsp;包括：</DIV>
<DIV>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
delete(int start, int
end)&nbsp;&nbsp;&nbsp;&nbsp;</DIV>
<DIV>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
说明：start是删除的启示字符，删除时包括该字符，end是删除的结束</DIV>
<DIV>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
字符，删除时不包括该字符。</DIV>
<DIV>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;deleteCharAt(int
index)</DIV>
<DIV>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
说明：删除的index个字符，我们知道，java中字符串实际上就是字符</DIV>
<DIV>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
数组，下标从0开始。</DIV>
<DIV>3.&nbsp;&nbsp;<STRONG><FONT COLOR="#FF0000">insert方法</FONT></STRONG></DIV>
<DIV>
&nbsp;&nbsp;&nbsp;&nbsp;StringBuilder的insert方法也提供了许多的重载方式，和append方法相似，只是在insert方法中增加了一个参数——插入位置，典型方式如下：</DIV>
<DIV>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
insert(int index, T)&nbsp;</DIV>
<DIV>
&nbsp;&nbsp;&nbsp;&nbsp;
在index指示的字符之前插入。</DIV>
<DIV>4.&nbsp; <STRONG><FONT COLOR="#FF0000">replace方法</FONT></STRONG></DIV>
<DIV>
&nbsp;&nbsp;&nbsp;&nbsp;替换某一子串，不再赘述。</DIV>
<DIV>5.&nbsp; <FONT STYLE="BACKGROUND-COLOR: #ffffff" FACE="宋体" COLOR="#FF0000"><STRONG>capacity方法</STRONG></FONT></DIV>
<DIV>&nbsp;&nbsp;&nbsp;
指示该StringBuilder的容量，初始时是16。&nbsp;</DIV>
<DIV>
&nbsp;&nbsp;&nbsp;&nbsp;</DIV>
]]></description>
            <author>小杨</author>
            <category>java基础技术</category>
            <comments>http://blog.sina.com.cn/s/blog_4b86f2ee0100083r.html#comment</comments>
            <pubDate>Tue, 24 Apr 2007 12:55:37 GMT+8</pubDate>
            <guid>http://blog.sina.com.cn/s/blog_4b86f2ee0100083r.html</guid>
        </item>
        <item>
            <title>DOM的基本方法(转)</title>
            <link>http://blog.sina.com.cn/s/blog_4b86f2ee0100082x.html</link>
            <description><![CDATA[<DIV>
&nbsp;最近在网上学习了一篇文章，叫《DOM的基本方法》，感觉很有用，拿过来贴一下，原文地址：<FONT STYLE="BACKGROUND-COLOR: #ffffff" FACE="宋体"><A HREF="http://www.cnblogs.com/QiuYun/archive/2007/03/12/672274.aspx">http://www.cnblogs.com/QiuYun/archive/2007/03/12/672274.aspx</A>。</FONT></DIV>
<DIV>&nbsp;</DIV>
<DIV>
<P><FONT STYLE="BACKGROUND-COLOR: #ffffff" FACE="宋体">一.直接引用结点<br/>
1.document.getElementById(id);<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
--在文档里面通过id来找结点<br/>
2.document.getElementByTagName(tagName);<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
--返回一个数组，包含对这些结点的引用<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
--如：document.getElementByTagName("span");将返回所有类型为span的结点</FONT></P>
<P><FONT STYLE="BACKGROUND-COLOR: #ffffff" FACE="宋体">二.间接引用结点<br/>
3.element.childNodes<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
--返回element的所有子结点,可以用element.childNodes[i]的方式来调用<br/>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
--element.firstChild=element.childNodes[0];<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
--element.lastChild=element.childNodes[element.childNonts.length-1];<br/>

4.element.parentNode<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
--引用父结点<br/>
5.element.nextSibling;&nbsp;&nbsp;
//引用下一个兄弟结点<br/>
&nbsp;&nbsp;
element.previousSibling;&nbsp;
//引用上一个兄弟结点</FONT></P>
<P><FONT STYLE="BACKGROUND-COLOR: #ffffff" FACE="宋体">三.获得结点信息<br/>
6.nodeName属性获得结点名称<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
--对于元素结点返回的是标记名称,如：&lt;a
herf&gt;&lt;a&gt;返回的是"a"<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
--对于属性结点返回的是属性名称,如：class="test" 返回的是test<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
--对于文本结点返回的是文本的内容<br/>
7.nodeType返回结点的类型<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
--元素结点返回1<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
--属性结点返回2<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
--文本结点返回3<br/>
8.nodeValue返回结点的值<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
--元素结点返回null<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
--属性结点返回undefined<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
--文本结点返回文本内容<br/>
9.hasChildNodes()判断是否有子结点<br/>
10.tagName属性返回元素的标记名称<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
--这个属性只有元素结点才有，等同于元素结点的nodeName属性</FONT></P>
<P><FONT STYLE="BACKGROUND-COLOR: #ffffff" FACE="宋体">四.处理属性结点<br/>
11.每个属性结点都是元素结点的一个属性，可以通过(元素结点.属性名称)访问<br/>

12.利用setAttribute()方法给元素结点添加属性<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
--elementNode.setAttribute(attributeName,attributeValue);<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
--attributeName为属性的名称，attributeValue为属性的值<br/>
13.使用getAttribute()方法获得属性值<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
--elementNode.getAttribute(attributeName);</FONT></P>
<P><FONT STYLE="BACKGROUND-COLOR: #ffffff" FACE="宋体">五.处理文本结点<br/>
14.innerHTML和innerText属性，这两个方法相信大家都很熟悉,不介绍了,值得注意的是无论是ie还是firefox都容易把空格、换行、制表符等当成文本结点。所有一般通过element.childNodes[i]引用文本结点的时候，一般要处理：<br/>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&lt;script language"javaScript" type="text/javascript"&gt;<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
function cleanWhitespace(element)<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
{<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
for(var i=0; i&lt;element.childNotes.length; i++)<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
{<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
var node = element.childNodes[i];<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
if(node.nodeType == 3 &amp;&amp; !/\S/.test(node.nodeValue))<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
{<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
node.parentNode.removeChild(node);<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
}<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
}<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
}<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&lt;/script&gt;</FONT></P>
<P><FONT STYLE="BACKGROUND-COLOR: #ffffff" FACE="宋体">六.改变文档的层次结构<br/>
15.document.createElement()方法创建元素结点<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
--如：document.createElement("Span");<br/>
16.document.createTextNode()方法创建文本结点<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
--如：document.createTextNode("&amp;nbsp;");
//注:他不会通过html编码，也就是说这里创建的不是空格，而是字符串&amp;nbsp;<br/>

17.使用appendChild()方法添加结点<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
--parentElement.appendChild(childElement);<br/>
18.使用insertBefore()方法插入子节点<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
--parentNode.insertBefore(newNode,referenceNode);<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
--newNode为插入的节点，referenceNode为将插入的节点插入到这之前<br/>

19.使用replaceChild方法取代子结点<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
--parentNode.replaceChild(newNode,oldNode);<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
--注：oldNode必须是parentNode的子结点,<br/>
20.使用cloneNode方法复制结点<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
--node.cloneNode(includeChildren);<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
--includeChildren为bool，表示是否复制其子结点<br/>
21.使用removeChild方法删除子结点<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
--parentNode.removeChild(childNode);</FONT></P>
<P><FONT STYLE="BACKGROUND-COLOR: #ffffff" FACE="宋体">七.表格的操作<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
--注：ie中无法直接将一个完整的表格结点插入到文档中<br/>
22.添加行和单元格<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
var
_table=document.createElement("table");&nbsp;&nbsp;
//创建表<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
table.insertRow(i);//在table的第i行插入行<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
row.insertCell(i);//在row的第i个位置插入单元格<br/>
23.引用单元格对象<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
--table.rows[i].cells[i];<br/>
24.删除行和单元格<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
--table.deleteRow(index);<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
--row.deleteCell(index);<br/>
25.交换两行获得两个单元格的位置<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
node1.swapNode(node2);<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
--这个方法在firefox中将出错<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
通用方法：<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
function swapNode(node1,node2)<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
{<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
var _parent=node1.parentNode;<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
var _t1=node1.nextSubling;<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
var _t2=node2.nextSubling;<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
if(_t1)parent.insertBefore(node2,_t1);<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
else
_parent.appendChild(node2);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br/>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
if(_t2)parent.insertBefore(node1,_t2);<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
else _parent.appendChild(node1);<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
}<br/></FONT></P>
</DIV>
]]></description>
            <author>小杨</author>
            <comments>http://blog.sina.com.cn/s/blog_4b86f2ee0100082x.html#comment</comments>
            <pubDate>Sun, 22 Apr 2007 13:59:36 GMT+8</pubDate>
            <guid>http://blog.sina.com.cn/s/blog_4b86f2ee0100082x.html</guid>
        </item>
        <item>
            <title>java按拼音排序</title>
            <link>http://blog.sina.com.cn/s/blog_4b86f2ee0100082j.html</link>
            <description><![CDATA[<DIV>
&nbsp;&nbsp;&nbsp;&nbsp;最近遇到一个把汉字按拼音排序的，搞了半天，从网上找到了字符转换关系的代码，可是最后结果总不对。以为算法有问题，又搞了半天算法，结果发现实际上，java的汉字比较直接就是按拼音的，不用什么转换，直接comparaTo就OK了。</DIV>
]]></description>
            <author>小杨</author>
            <category>java基础技术</category>
            <comments>http://blog.sina.com.cn/s/blog_4b86f2ee0100082j.html#comment</comments>
            <pubDate>Sun, 22 Apr 2007 01:38:15 GMT+8</pubDate>
            <guid>http://blog.sina.com.cn/s/blog_4b86f2ee0100082j.html</guid>
        </item>
        <item>
            <title>java范型学习(2)</title>
            <link>http://blog.sina.com.cn/s/blog_4b86f2ee01000821.html</link>
            <description><![CDATA[<DIV>&nbsp;&nbsp;&nbsp;
实际上，java的范型机制并不是很完善，因为java的创建之初并没有想到以后会实现范型机制，等到现在真要实现范型了，要考虑的问题就复杂了。下面学习一下java范型的限制。</DIV>
<OL>
<LI><STRONG><FONT COLOR="#FF0000">对于基本类型</FONT></STRONG></LI>
</OL>
<P><FONT COLOR="#FF0000"><STRONG>&nbsp;</STRONG></FONT><FONT COLOR="#000000">&nbsp;&nbsp;&nbsp;java中存在8个基本类型（整型4个：int,short,long,byte；浮点型2个：float,double；unicode编码的字符类型：char；真值类型：boolean），他们都不能做范型的邦定类型，如Demo&lt;int&gt;。如果想这样做，java提供了包装类型。</FONT></P>
<P>&nbsp;</P>
<P>&nbsp;&nbsp; 2.<STRONG><FONT COLOR="#FF0000">&nbsp;运行时类型检查</FONT></STRONG></P>
<P>&nbsp;</P>
<P>
&nbsp;&nbsp;&nbsp;&nbsp;
java提供了instanceof关键字来提供运行时刻类型检查，如：</P>
<TABLE STYLE="BORDER-RIGHT: #999 1px solid; BORDER-TOP: #999 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #999 1px solid; WIDTH: 80%; BORDER-BOTTOM: #999 1px solid" ALIGN="center">
<TBODY>
<TR>
<TD>
<P>if(a instanceof A) {</P>
<P>// do something</P>
<P>}</P>
</TD>
</TR>
</TBODY>
</TABLE>
<P>
&nbsp;&nbsp;&nbsp;&nbsp;
需要说明的是这种机制对范型是不起作用的</P>
<TABLE STYLE="BORDER-RIGHT: #999 1px solid; BORDER-TOP: #999 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #999 1px solid; WIDTH: 80%; BORDER-BOTTOM: #999 1px solid" ALIGN="center">
<TBODY>
<TR>
<TD>
<P>A&lt;String&gt; a = new A&lt;String&gt;();</P>
<P>if(a instanceof A&lt;String&gt;) {</P>
<P>// do something</P>
<P>}</P>
</TD>
</TR>
</TBODY>
</TABLE>
<P>
&nbsp;&nbsp;&nbsp;&nbsp;
上面这样做编译就通不过。</P>
<P>&nbsp;</P>
<P>&nbsp;&nbsp; 3. <STRONG><FONT COLOR="#FF0000">异常处理</FONT></STRONG></P>
<P>&nbsp;</P>
<P>
&nbsp;&nbsp;&nbsp;&nbsp;当你要使用范型和异常处理相结合时（抛出或捕获范型类），可要注意了，因为这样做很可能连编译都通不过。</P>
<P>&nbsp;</P>
<P>&nbsp;&nbsp; 4. <STRONG><FONT COLOR="#FF0000">范型数组</FONT></STRONG></P>
<P>&nbsp;</P>
<P><FONT COLOR="#FF0000"><STRONG>&nbsp;&nbsp;&nbsp;</STRONG></FONT>
<FONT COLOR="#000000">在实践中，我们是不能使用范型数组的，例如像下面那样：</FONT></P>
<TABLE STYLE="BORDER-RIGHT: #999 1px solid; BORDER-TOP: #999 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #999 1px solid; WIDTH: 80%; BORDER-BOTTOM: #999 1px solid" ALIGN="center">
<TBODY>
<TR>
<TD><FONT STYLE="BACKGROUND-COLOR: #ffffff" FACE="宋体">DemoClass&lt;String&gt;[] demos = new
DemoClass&lt;String&gt;[10];</FONT></TD>
</TR>
</TBODY>
</TABLE>
<P>
&nbsp;&nbsp;&nbsp;&nbsp;
这样写是不能编译通过的。</P>
<P>&nbsp;</P>
<P>&nbsp;&nbsp;&nbsp;5.
<STRONG><FONT COLOR="#FF0000">静态范型类型</FONT></STRONG></P>
<P>
&nbsp;&nbsp;&nbsp;&nbsp;</P>
<P>
&nbsp;&nbsp;&nbsp;&nbsp;
声明一个静态范型方法或属性是被禁止的，如下：</P>
<TABLE STYLE="BORDER-RIGHT: #999 1px solid; BORDER-TOP: #999 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #999 1px solid; WIDTH: 80%; BORDER-BOTTOM: #999 1px solid" ALIGN="center">
<TBODY>
<TR>
<TD>
<P>......</P>
<P>private static T value;</P>
<P>private static T getValue() {.....}</P>
<P>......</P>
</TD>
</TR>
</TBODY>
</TABLE>
<P>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
这样写会发生编译异常。</P>
<P>&nbsp;</P>
<P>&nbsp;&nbsp;&nbsp;
以上是引入范型机制后，java语言使用的一些限制，总体来讲，这些限制都是由范型的实现机制引起的。在使用时，当出现范型的地方报错时，就可能是以上原因造成的。</P>
]]></description>
            <author>小杨</author>
            <category>java基础技术</category>
            <comments>http://blog.sina.com.cn/s/blog_4b86f2ee01000821.html#comment</comments>
            <pubDate>Fri, 20 Apr 2007 09:00:56 GMT+8</pubDate>
            <guid>http://blog.sina.com.cn/s/blog_4b86f2ee01000821.html</guid>
        </item>
        <item>
            <title>快速排序（温习算法+范型学习）</title>
            <link>http://blog.sina.com.cn/s/blog_4b86f2ee01000820.html</link>
            <description><![CDATA[<DIV>&nbsp;
快速排序可以说是经典算法中的经典了，想当初，我只会pascal，刚学完数据结构的快速排序，用其实现了这个算法。现在，刚学完java范型，决定把它重新实现一下。闲话少说，贴代码：
<TABLE STYLE="BORDER-RIGHT: #999 1px solid; BORDER-TOP: #999 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #999 1px solid; WIDTH: 96.52%; BORDER-BOTTOM: #999 1px solid; HEIGHT: 20px" ALIGN="center">
<TBODY>
<TR>
<TD>
<P><FONT STYLE="BACKGROUND-COLOR: #ffffff" FACE="宋体"><FONT COLOR="#80FFFF">/**<br/>
&nbsp;* @author yangsq<br/>
&nbsp;* @Date 2007-4-20<br/>
&nbsp;* 快速排序实践<br/>
&nbsp;*/</FONT><br/>
public class QuickSortDemo {<br/>
&nbsp;private final int CUTOFF = 5;<br/>
&nbsp;<br/>
&nbsp;<FONT COLOR="#80FFFF">/**<br/>
&nbsp; * @author yangsq<br/>
&nbsp; * @Date 2007-4-20<br/>
&nbsp; * 交换数组中的两个元素<br/>
&nbsp; * @param a 需要排序的数组<br/>
&nbsp; * @param i,j 要交换的两个元素的位置<br/>
&nbsp; */<br/></FONT>&nbsp;private &lt;T
extends Comparable&gt; void swap(T[] a, int i, int j) {<br/>
&nbsp;&nbsp;T temp = a[i];<br/>
&nbsp;&nbsp;a[i] = a[j];<br/>
&nbsp;&nbsp;a[j] = temp;<br/>
&nbsp;}</FONT></P>
<P><FONT STYLE="BACKGROUND-COLOR: #ffffff" FACE="宋体"><FONT COLOR="#80FFFF">/**<br/>
&nbsp; * @author yangsq<br/>
&nbsp; * @Date 2007-4-20<br/>
&nbsp; * 选出中枢元<br/>
&nbsp; * @param a 需要排序的数组<br/>
&nbsp; */</FONT><br/>
&nbsp;private &lt;T extends Comparable&gt; T
hingeSel(T[] a) {<br/>
&nbsp;&nbsp;int left = 0;<br/>
&nbsp;&nbsp;int right = a.length - 1;<br/>
&nbsp;&nbsp;int mid = a.length/2;<br/>
&nbsp;&nbsp;<br/>
&nbsp;&nbsp;if(a[left].compareTo(a[mid])
&gt; 0) {<br/>
&nbsp;&nbsp;&nbsp;swap(a, left,
mid);<br/>
&nbsp;&nbsp;}<br/>
&nbsp;&nbsp;<br/>
&nbsp;&nbsp;if(a[left].compareTo(a[right])
&gt; 0) {<br/>
&nbsp;&nbsp;&nbsp;swap(a, left,
right);<br/>
&nbsp;&nbsp;}<br/>
&nbsp;<br/>
&nbsp;&nbsp;if(a[mid].compareTo(a[right])
&gt; 0) {<br/>
&nbsp;&nbsp;&nbsp;swap(a, mid,
right);<br/>
&nbsp;&nbsp;}<br/>
&nbsp;&nbsp;<br/>
&nbsp;&nbsp;swap(a, mid, right-1);<br/>
&nbsp;&nbsp;return a[right-1];<br/>
&nbsp;}<br/>
&nbsp;<br/>
<FONT COLOR="#80FFFF">&nbsp;/**<br/>
&nbsp; * @author yangsq<br/>
&nbsp; * @Date 2007-4-20<br/>
&nbsp; * 对数组进行快速排序<br/>
&nbsp; * @param a 需要排序的数组<br/>
&nbsp; * @param left 数组的左边界<br/>
&nbsp; * @param right 数组的右边界<br/>
&nbsp; */<br/></FONT>&nbsp;public &lt;T
extends Comparable&gt; void quickSort(T[] a, int left, int right)
{<br/>
&nbsp;&nbsp;if(left + CUTOFF &lt;= right)
{<br/>
&nbsp;&nbsp;&nbsp;T pivot =
hingeSel(a);<br/>
&nbsp;&nbsp;&nbsp;<br/>
&nbsp;&nbsp;&nbsp;int i = left,
j = right-1;<br/>
&nbsp;&nbsp;&nbsp;for(;;){<br/>

&nbsp;&nbsp;&nbsp;&nbsp;while(a[i++].compareTo(pivot)&lt;0);<br/>

&nbsp;&nbsp;&nbsp;&nbsp;while(a[j--].compareTo(pivot)&gt;0);<br/>

&nbsp;&nbsp;&nbsp;&nbsp;<br/>

&nbsp;&nbsp;&nbsp;&nbsp;if(i&lt;j)
{<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;swap(a,
i, j);<br/>
&nbsp;&nbsp;&nbsp;&nbsp;}
else<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;break;<br/>

&nbsp;&nbsp;&nbsp;}<br/>
&nbsp;&nbsp;&nbsp;<br/>
&nbsp;&nbsp;&nbsp;swap(a, i,
right-1);<br/>
&nbsp;&nbsp;&nbsp;quickSort(a,
left, i-1);<br/>
&nbsp;&nbsp;&nbsp;quickSort(a,
i+1, right);<br/>
&nbsp;&nbsp;} else {<br/>
&nbsp;&nbsp;&nbsp;insertSort(a,
left, right);<br/>
&nbsp;&nbsp;}<br/>
&nbsp;}<br/>
&nbsp;<br/>
&nbsp;<FONT COLOR="#80FFFF">/**<br/>
&nbsp; * @author yangsq<br/>
&nbsp; * @Date 2007-4-20<br/>
&nbsp; * 插入排序<br/>
&nbsp; * @param a 需要排序的数组<br/>
&nbsp; * @param left 数组的左边界<br/>
&nbsp; * @param right 数组的右边界<br/>
&nbsp; */<br/></FONT>&nbsp;private &lt;T
extends Comparable&gt; void insertSort(T[] a, int left, int right)
{<br/>
&nbsp;&nbsp;int j;<br/>
&nbsp;&nbsp;for(int i=left+1; i&lt;=right;
i++) {<br/>
&nbsp;&nbsp;&nbsp;T temp =
a[i];<br/>
&nbsp;&nbsp;&nbsp;for(j=i;
j&gt;left &amp;&amp; temp.compareTo(a[j-1])&lt;0; j--) {<br/>
&nbsp;&nbsp;&nbsp;&nbsp;a[j]
= a[j-1];<br/>
&nbsp;&nbsp;&nbsp;}<br/>
&nbsp;&nbsp;&nbsp;a[j] =
temp;<br/>
&nbsp;&nbsp;}<br/>
&nbsp;}<br/>
}</FONT></P>
</TD>
</TR>
</TBODY>
</TABLE>
</DIV>
上面是完整的快速排序代码，下面是测试代码：
<TABLE STYLE="BORDER-RIGHT: #999 1px solid; BORDER-TOP: #999 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #999 1px solid; WIDTH: 97.04%; BORDER-BOTTOM: #999 1px solid; HEIGHT: 22px" ALIGN="center">
<TBODY>
<TR>
<TD>
<P><FONT STYLE="BACKGROUND-COLOR: #ffffff" FACE="宋体">import
junit.framework.TestCase;<br/>
<FONT COLOR="#80FFFF">/**<br/>
&nbsp;* @author yangsq<br/>
&nbsp;* @Date 2007-4-20<br/>
&nbsp;* 测试快速排序<br/>
&nbsp;*/</FONT><br/>
public class QuickSortDemoTest extends TestCase {<br/>
&nbsp;private QuickSortDemo demo;<br/>
&nbsp;<br/>
&nbsp;protected void setUp() throws Exception {<br/>
&nbsp;&nbsp;demo = new
QuickSortDemo();<br/>
&nbsp;}</FONT></P>
<P><FONT STYLE="BACKGROUND-COLOR: #ffffff" FACE="宋体">&nbsp;public void testQuickSort() {<br/>
&nbsp;&nbsp;String[] strings = {"yangsq",
"hudeyong", "caomudan", "renshui",<br/>
&nbsp;&nbsp;&nbsp;&nbsp;"shimin",
"wangyi", "xiaoyang", "mingming", "xiaohong",<br/>
&nbsp;&nbsp;&nbsp;&nbsp;"tabaccosis",
"yacht", "ubehebe", "oakmoss", "pacemaking",<br/>
&nbsp;&nbsp;&nbsp;&nbsp;"wadeite"};<br/>

&nbsp;&nbsp;demo.quickSort(strings, 0,
strings.length-1);<br/>
&nbsp;&nbsp;for(int i=0;
i&lt;strings.length; i++) {<br/>
&nbsp;&nbsp;&nbsp;System.out.println(strings[i]);<br/>

&nbsp;&nbsp;}<br/>
&nbsp;}<br/>
}</FONT></P>
</TD>
</TR>
</TBODY>
</TABLE>
输出结果就不贴了:)
]]></description>
            <author>小杨</author>
            <comments>http://blog.sina.com.cn/s/blog_4b86f2ee01000820.html#comment</comments>
            <pubDate>Fri, 20 Apr 2007 07:59:34 GMT+8</pubDate>
            <guid>http://blog.sina.com.cn/s/blog_4b86f2ee01000820.html</guid>
        </item>
        <item>
            <title>java范型学习(1)</title>
            <link>http://blog.sina.com.cn/s/blog_4b86f2ee0100081z.html</link>
            <description><![CDATA[<DIV>&nbsp;&nbsp;&nbsp;
java的范型机制看起来有点像C++的模版，但相比较C++的模版类，java中的范型没有关键字template，并且有着不同的实现机制（本质区别）。</DIV>
<OL>
<LI><STRONG><FONT COLOR="#009900">范型类</FONT></STRONG></LI>
</OL>
<DIV>&nbsp;&nbsp;&nbsp;
先看一个范型类的例子：</DIV>
<DIV>
<TABLE STYLE="BORDER-RIGHT: #999 1px solid; BORDER-TOP: #999 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #999 1px solid; WIDTH: 80%; BORDER-BOTTOM: #999 1px solid" ALIGN="center">
<TBODY>
<TR>
<TD>
<P>public class Demo1&lt;T&gt; {</P>
<P>&nbsp;&nbsp; private T value;</P>
<P>&nbsp;&nbsp; public&nbsp;
Demo1(T value) &nbsp;{</P>
<P>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
this.value= value;</P>
<P>&nbsp;&nbsp; }</P>
<P>&nbsp;&nbsp; public void setFirst(T
value){this.value= value;}</P>
<P>&nbsp;&nbsp; public&nbsp;T
getFirst(){return value;}</P>
<P>}</P>
</TD>
</TR>
</TBODY>
</TABLE>
</DIV>
&nbsp;&nbsp;&nbsp;
类型变量T，用&lt;&gt;括起，放在类名的后面。当我们需要这个类是，只需要如下定义：
<TABLE STYLE="BORDER-RIGHT: #999 1px solid; BORDER-TOP: #999 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #999 1px solid; WIDTH: 80%; BORDER-BOTTOM: #999 1px solid" ALIGN="center">
<TBODY>
<TR>
<TD>
<P>String string = "yangsq";</P>
<P>Demo1&lt;String&gt; demo1 = new Demo1&lt;String&gt;(string);</P>
</TD>
</TR>
</TBODY>
</TABLE>
<P>&nbsp;&nbsp;&nbsp;
这样声明的一个Demo1类相当于把上面定义的Demo1类中<FONT COLOR="#FF0000">T</FONT>都换作<FONT COLOR="#FF0000">String</FONT>了。</P>
<P>&nbsp;&nbsp;&nbsp;
范型类可以有多个类型，例如像上面的Demo1类，我们可以改作如下：</P>
<TABLE STYLE="BORDER-RIGHT: #999 1px solid; BORDER-TOP: #999 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #999 1px solid; WIDTH: 80%; BORDER-BOTTOM: #999 1px solid" ALIGN="center">
<TBODY>
<TR>
<TD>
<P>public class Demo2&lt;T,U&gt; {</P>
<P>&nbsp;&nbsp; private T tValue;</P>
<P>&nbsp;&nbsp; private U uValue;</P>
<P>&nbsp;&nbsp; public&nbsp;
Demo2(T tValue, U uValue) &nbsp;{</P>
<P>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
this.tValue= tValue;</P>
<P>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
this.uValue = uValue;</P>
<P>&nbsp;&nbsp; }</P>
<P>&nbsp;&nbsp; ... ...</P>
<P>}</P>
</TD>
</TR>
</TBODY>
</TABLE>
<P>&nbsp;</P>
<P>&nbsp;
2.&nbsp;&nbsp;<STRONG><FONT COLOR="#009900">范型方法</FONT></STRONG>&nbsp;&nbsp;</P>
<P>&nbsp;</P>
<P>
&nbsp;&nbsp;&nbsp;&nbsp;如果我们不需要范型类，也可以只定义我们需要的范型方法。范型方法可以在普通类里定义。</P>
<TABLE STYLE="BORDER-RIGHT: #999 1px solid; BORDER-TOP: #999 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #999 1px solid; WIDTH: 80%; BORDER-BOTTOM: #999 1px solid" ALIGN="center">
<TBODY>
<TR>
<TD>
<P>public class Demo3 {</P>
<P>&nbsp;&nbsp;&nbsp;
public&nbsp;<FONT COLOR="#FF0000">&lt;T&gt;</FONT> T
getMiddle(T[] array) {</P>
<P>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
return array[array.length/2];</P>
<P>&nbsp;&nbsp;&nbsp;
}&nbsp;</P>
<P>}</P>
</TD>
</TR>
</TBODY>
</TABLE>
&nbsp;&nbsp;&nbsp;&nbsp;
这里需要注意的是类型修饰符号（红体标出的&lt;T&gt;），它要放在public（static/final）后和返回类型前。在我们需要使用这个方法时，可以如下调用：
<TABLE STYLE="BORDER-RIGHT: #999 1px solid; BORDER-TOP: #999 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #999 1px solid; WIDTH: 80%; BORDER-BOTTOM: #999 1px solid" ALIGN="center">
<TBODY>
<TR>
<TD>
<P>Demo3 demo3 = new Demo3();</P>
<P>String[] strings = {"abc", "def", "wxy"};</P>
<P>String middle = demo3<FONT COLOR="#FF0000">.&lt;String&gt;</FONT>getMiddle(strings);</P>
</TD>
</TR>
</TBODY>
</TABLE>
<P>
&nbsp;&nbsp;&nbsp;&nbsp;
实际上，红体标出的&lt;String&gt;完全可以省略。因为可以根据strings的String[]类型推出。</P>
<P>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;我们还可以对范型加以约束，例如我们可能希望T的类型能应用comparaTo方法，即T的类型实现了Comparable接口，那么我们可以像下面那样定义一个实现插入排序算法的方法：</P>
<TABLE STYLE="BORDER-RIGHT: #999 1px solid; BORDER-TOP: #999 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #999 1px solid; WIDTH: 92.38%; BORDER-BOTTOM: #999 1px solid; HEIGHT: 32px" ALIGN="center">
<TBODY>
<TR>
<TD>
<P><FONT STYLE="BACKGROUND-COLOR: #ffffff" FACE="宋体">private
<FONT COLOR="#FF0000">&lt;T extends Comparable&gt;</FONT> void
insertSort(T[] a, int left, int right) {</FONT></P>
<P>&nbsp;&nbsp;&nbsp;
<FONT STYLE="BACKGROUND-COLOR: #ffffff" FACE="宋体">int
j;</FONT></P>
<P>&nbsp;&nbsp;&nbsp;
<FONT STYLE="BACKGROUND-COLOR: #ffffff" FACE="宋体">for(int
i=left+1; i&lt;=right; i++) {</FONT></P>
<P>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<FONT STYLE="BACKGROUND-COLOR: #ffffff" FACE="宋体">T temp =
a[i];</FONT></P>
<P>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<FONT STYLE="BACKGROUND-COLOR: #ffffff" FACE="宋体">for(j=i;
j&gt;left &amp;&amp; temp.compareTo(a[j-1])&lt;0; j--) {</FONT></P>
<P>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<FONT STYLE="BACKGROUND-COLOR: #ffffff" FACE="宋体">a[j] =
a[j-1];</FONT></P>
<P>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<FONT STYLE="BACKGROUND-COLOR: #ffffff" FACE="宋体">}<br/>
&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;a[j] =
temp;</FONT></P>
<P>
&nbsp;&nbsp;&nbsp;&nbsp;
<FONT STYLE="BACKGROUND-COLOR: #ffffff" FACE="宋体">}<br/>
&nbsp;}</FONT></P>
</TD>
</TR>
</TBODY>
</TABLE>
<P>&nbsp;&nbsp;&nbsp;
可以看到，T类型需要实现接口Comparable（像String，Date）。</P>
<P>&nbsp;&nbsp;&nbsp;
Comparable是一个接口，那么为什么用extends而不是implements呢？实际上T也可以是一个接口。所以为了省事，就统一使用extends了。此外，邦定类型可以不唯一，如下：</P>
<TABLE STYLE="BORDER-RIGHT: #999 1px solid; BORDER-TOP: #999 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #999 1px solid; WIDTH: 89.29%; BORDER-BOTTOM: #999 1px solid; HEIGHT: 20px" ALIGN="center">
<TBODY>
<TR>
<TD><FONT STYLE="BACKGROUND-COLOR: #ffffff" FACE="宋体">private
<FONT COLOR="#FF0000">&lt;T extends Comparable &amp;
Serializable&gt;</FONT> void insertSort(T[] a, int left, int right)
{</FONT></TD>
</TR>
</TBODY>
</TABLE>
<P>&nbsp;</P>
<P>&nbsp;&nbsp;3.&nbsp;
<FONT COLOR="#009900"><STRONG>范型实现原理</STRONG></FONT></P>
<P>&nbsp;</P>
<P><FONT COLOR="#009900"><STRONG>&nbsp;&nbsp;&nbsp;</STRONG></FONT><FONT COLOR="#000000">我们都知道在java以前的版本是不支持范型的，现在引入范型，是不是要对虚拟机进行大刀阔斧的改变呢？答案当然是否定的，因为我们需要向后兼容性。</FONT></P>
<P>&nbsp;&nbsp;&nbsp;
实际上，范型类型的定义虚拟机都会提供一个原始类型，像上面的Demo1类的原始类型定义如下：</P>
<TABLE STYLE="BORDER-RIGHT: #999 1px solid; BORDER-TOP: #999 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #999 1px solid; WIDTH: 80%; BORDER-BOTTOM: #999 1px solid" ALIGN="center">
<TBODY>
<TR>
<TD>
<P>public class Demo1 {</P>
<P>&nbsp;&nbsp;
private&nbsp;<FONT COLOR="#FF0000">Object</FONT>
value;</P>
<P>&nbsp;&nbsp; public&nbsp;
Demo1(<FONT COLOR="#FF0000">Object</FONT> value)
&nbsp;{</P>
<P>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
this.value= value;</P>
<P>&nbsp;&nbsp; }</P>
<P>&nbsp;&nbsp; public void
setFirst(<FONT COLOR="#FF0000">Object</FONT> value){this.value=
value;}</P>
<P>&nbsp;&nbsp;
public&nbsp;<FONT COLOR="#FF0000">Object</FONT>
getFirst(){return value;}</P>
<P>}</P>
</TD>
</TR>
</TBODY>
</TABLE>
&nbsp;&nbsp;&nbsp;
原始类型是根据第一个边界的类型变量来替换的，例如有如下范型类定义：
<TABLE STYLE="BORDER-RIGHT: #999 1px solid; BORDER-TOP: #999 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #999 1px solid; WIDTH: 80%; BORDER-BOTTOM: #999 1px solid" ALIGN="center">
<TBODY>
<TR>
<TD>
<P>public class Demo4&lt;T extends Comparable &amp;
Serializable&gt; {</P>
<P>&nbsp;&nbsp; private T value;</P>
<P>&nbsp;&nbsp; ... ...</P>
<P>}</P>
</TD>
</TR>
</TBODY>
</TABLE>
&nbsp;&nbsp;&nbsp;&nbsp;那么其原始类型如下：
<TABLE STYLE="BORDER-RIGHT: #999 1px solid; BORDER-TOP: #999 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #999 1px solid; WIDTH: 80%; BORDER-BOTTOM: #999 1px solid" ALIGN="center">
<TBODY>
<TR>
<TD>
<P>public class Demo4 {</P>
<P>&nbsp;&nbsp; private Comparable
value;</P>
<P>&nbsp;&nbsp; ... ...</P>
<P>}</P>
</TD>
</TR>
</TBODY>
</TABLE>
&nbsp;&nbsp;&nbsp;&nbsp;介绍一个编程技巧，即当我们的T有多个邦定类是（像上面那样），最好把标签接口放在后面。
]]></description>
            <author>小杨</author>
            <category>java基础技术</category>
            <comments>http://blog.sina.com.cn/s/blog_4b86f2ee0100081z.html#comment</comments>
            <pubDate>Fri, 20 Apr 2007 06:42:11 GMT+8</pubDate>
            <guid>http://blog.sina.com.cn/s/blog_4b86f2ee0100081z.html</guid>
        </item>
        <item>
            <title>一些有用的Oracle sql语句</title>
            <link>http://blog.sina.com.cn/s/blog_4b86f2ee010007sl.html</link>
            <description><![CDATA[<DIV>&nbsp;
现在的数据库管理员大都使用可视化的数据库工具了，对sql语句也就越来越生熟了，可使对数据库应用开发人员来说，sql语句可是大有用途的。下面是我平时开发时用到的一些有用的sql语句，希望日积月累，对将来的开发有所帮助，这里只针对oracle数据库。</DIV>
<OL>
<LI>序列</LI>
</OL>
<P>&nbsp;&nbsp;
在oracle中，并没有什么主键自动增长的概念，要实现这样的功能，需要使用序列，一个序列是一个全局的变量。例如定义一个序列叫MYSEQUENCE，则MYSEQUENCE<FONT FACE="Courier New">.CURRVAL
返回当前序列的值（如：10），则MYSEQUENCE.NEXTVAL
返回下一值（为：11）。语句如下</FONT></P>
<TABLE STYLE="BORDER-RIGHT: #999 1px solid; BORDER-TOP: #999 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #999 1px solid; WIDTH: 80%; BORDER-BOTTOM: #999 1px solid" ALIGN="center">
<TBODY>
<TR>
<TD>
<P><FONT FACE="Courier New">select MYSEQUENCE<FONT FACE="Courier New">.CURRVAL</FONT> from DUAL;</FONT></P>
<P><FONT FACE="Courier New">select
MYSEQUENCE.NEXTVAL&nbsp;from DUAL;</FONT></P>
</TD>
</TR>
</TBODY>
</TABLE>
<P>&nbsp;</P>
<P>&nbsp;&nbsp;
2.&nbsp;获得某张表的主键名称</P>
<TABLE STYLE="BORDER-RIGHT: #999 1px solid; BORDER-TOP: #999 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #999 1px solid; WIDTH: 80%; BORDER-BOTTOM: #999 1px solid" ALIGN="center">
<TBODY>
<TR>
<TD><FONT FACE="Courier New">select a.column_name from <FONT COLOR="#FF0000">user_cons_columns</FONT> a,<FONT COLOR="#FF0000">user_constraints</FONT> b where
a.constraint_name=b.constraint_name and a.table_name='表名称' and
b.constraint_type='P' and b.table_name='表名称';</FONT></TD>
</TR>
</TBODY>
</TABLE>
<P>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
注意：两个表命称是一样的，且要大写。</P>
<P>&nbsp;</P>
<P>&nbsp;&nbsp;&nbsp;3.
选取字段不为空的值</P>
<TABLE STYLE="BORDER-RIGHT: #999 1px solid; BORDER-TOP: #999 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #999 1px solid; WIDTH: 80%; BORDER-BOTTOM: #999 1px solid" ALIGN="center">
<TBODY>
<TR>
<TD><FONT STYLE="BACKGROUND-COLOR: #ffffff" FACE="宋体">select name
from&nbsp;mytable where name <FONT COLOR="#FF0000">is
not NULL</FONT>;</FONT></TD>
</TR>
</TBODY>
</TABLE>
<P>&nbsp;</P>
<P>&nbsp;&nbsp;
4.&nbsp;选则用户所拥有的表</P>
<TABLE STYLE="BORDER-RIGHT: #999 1px solid; BORDER-TOP: #999 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #999 1px solid; WIDTH: 80%; BORDER-BOTTOM: #999 1px solid" ALIGN="center">
<TBODY>
<TR>
<TD><FONT STYLE="BACKGROUND-COLOR: #ffffff" FACE="宋体">select
table_name from user_tables</FONT></TD>
</TR>
</TBODY>
</TABLE>
<P>&nbsp;&nbsp;</P>
<P>&nbsp;&nbsp;&nbsp;5.
获得某个表的某个字段的类型</P>
<TABLE STYLE="BORDER-RIGHT: #999 1px solid; BORDER-TOP: #999 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #999 1px solid; WIDTH: 80%; BORDER-BOTTOM: #999 1px solid" ALIGN="center">
<TBODY>
<TR>
<TD><FONT FACE="宋体">select data_type from user_tab_columns where
table_name='表名称' and column_name='字段名称';</FONT></TD>
</TR>
</TBODY>
</TABLE>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
注意大写
]]></description>
            <author>小杨</author>
            <category>数据库应用</category>
            <comments>http://blog.sina.com.cn/s/blog_4b86f2ee010007sl.html#comment</comments>
            <pubDate>Wed, 28 Mar 2007 06:27:31 GMT+8</pubDate>
            <guid>http://blog.sina.com.cn/s/blog_4b86f2ee010007sl.html</guid>
        </item>
        <item>
            <title>将Java对象存储到Oracle数据库中</title>
            <link>http://blog.sina.com.cn/s/blog_4b86f2ee010007s2.html</link>
            <description><![CDATA[<DIV>&nbsp;&nbsp;
最近一直在忙毕设，公司那边也有一个新的项目，所以好久没有更新blog了，恰巧这两天毕设和公司的项目都遇到了一个问题，即如何将对象持久化到数据库中，所以研究了一下，在这里写写心得。</DIV>
<DIV>&nbsp;&nbsp;
对象持久化，也就是可以把这个对象永远的保存起来，这里的保存不仅是对象本身，还包括他的属性和所依赖的其他类。通常，对象可以持久化到文件或者是数据库中。我这里只介绍如何将对象存储到数据库中。恰巧Oracle数据库为我们提供了这样的方便。</DIV>
<DIV>&nbsp;&nbsp;
在Oracle中，有一种blog的字段类型，它是用来存储大量的二进制数据的。我们就利用这个字段去存储对象信息。</DIV>
<DIV>&nbsp;&nbsp;
首先建立一个测试表：</DIV>
<DIV>
<TABLE STYLE="BORDER-RIGHT: #999 1px solid; BORDER-TOP: #999 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #999 1px solid; WIDTH: 80%; BORDER-BOTTOM: #999 1px solid" ALIGN="center">
<TBODY>
<TR>
<TD><FONT FACE="Courier New">create table TESTBLOB<br/>
(<br/>
&nbsp;
NAME&nbsp;&nbsp;&nbsp;
VARCHAR2(50) not null,<br/>
&nbsp; <FONT COLOR="#FF0000">CONTENT BLOB not
null,<br/></FONT>&nbsp;
ID&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
NUMBER(8) not null<br/>
)<FONT FACE="Courier New">alter table TESTBLOB<br/>
&nbsp; add constraint IDFORTEST primary key
(ID);</FONT></FONT></TD>
</TR>
</TBODY>
</TABLE>
</DIV>
<P>&nbsp;&nbsp;
只用三个字段，其中id是属性，content是我们要存储对象的字段。</P>
<P>&nbsp;&nbsp;
先来看看我们要存入的对象：</P>
<TABLE STYLE="BORDER-RIGHT: #999 1px solid; BORDER-TOP: #999 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #999 1px solid; WIDTH: 80%; BORDER-BOTTOM: #999 1px solid" ALIGN="center">
<TBODY>
<TR>
<TD>
<P><FONT FACE="Courier New">import java.io.Serializable;<br/>
import java.util.Date;<br/>
import java.util.List;</FONT></P>
<P><FONT FACE="Courier New">public class TestObject <FONT COLOR="#FF0000">implements Serializable</FONT> {<br/>
&nbsp;<br/>
&nbsp;private static final long serialVersionUID =
4558876142427402513L;<br/>
&nbsp;/**<br/>
&nbsp; * @param args<br/>
&nbsp; */<br/>
&nbsp;private String name;<br/>
&nbsp;private String password;<br/>
&nbsp;private Date date;<br/>
&nbsp;private List&lt;City&gt; cityList;<br/>
&nbsp;<br/>
&nbsp;public List&lt;City&gt; getCityList() {<br/>
&nbsp;&nbsp;return cityList;<br/>
&nbsp;}<br/>
&nbsp;public void setCityList(List&lt;City&gt;
cityList) {<br/>
&nbsp;&nbsp;this.cityList = cityList;<br/>
&nbsp;}<br/>
&nbsp;public Date getDate() {<br/>
&nbsp;&nbsp;return date;<br/>
&nbsp;}<br/>
&nbsp;public void setDate(Date date) {<br/>
&nbsp;&nbsp;this.date = date;<br/>
&nbsp;}<br/>
&nbsp;public String getName() {<br/>
&nbsp;&nbsp;return name;<br/>
&nbsp;}<br/>
&nbsp;public void setName(String name) {<br/>
&nbsp;&nbsp;this.name = name;<br/>
&nbsp;}<br/>
&nbsp;public String getPassword() {<br/>
&nbsp;&nbsp;return password;<br/>
&nbsp;}<br/>
&nbsp;public void setPassword(String password) {<br/>
&nbsp;&nbsp;this.password = password;<br/>
&nbsp;}<br/>
}</FONT></P>
</TD>
</TR>
</TBODY>
</TABLE>
&nbsp;&nbsp;&nbsp;
记得要实现<FONT FACE="Courier New">Serializable接口，可以看到这是一个包含了string，date，和list类型的对象，为了给测试增加复杂度，我们的list是另外一个对象（city）的list，如下：</FONT>
<TABLE STYLE="BORDER-RIGHT: #999 1px solid; BORDER-TOP: #999 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #999 1px solid; WIDTH: 80%; BORDER-BOTTOM: #999 1px solid" ALIGN="center">
<TBODY>
<TR>
<TD>
<P><FONT FACE="Courier New">import java.io.Serializable;</FONT></P>
<P><FONT FACE="Courier New">public class City implements
Serializable{<br/>
&nbsp;private static final long serialVersionUID =
4558876127402513L;<br/>
&nbsp;private String name;<br/>
&nbsp;private String code;<br/>
&nbsp;public String getCode() {<br/>
&nbsp;&nbsp;return code;<br/>
&nbsp;}<br/>
&nbsp;public void setCode(String code) {<br/>
&nbsp;&nbsp;this.code = code;<br/>
&nbsp;}<br/>
&nbsp;public String getName() {<br/>
&nbsp;&nbsp;return name;<br/>
&nbsp;}<br/>
&nbsp;public void setName(String name) {<br/>
&nbsp;&nbsp;this.name = name;<br/>
&nbsp;}<br/>
}</FONT></P>
</TD>
</TR>
</TBODY>
</TABLE>
<P>
&nbsp;&nbsp;&nbsp;&nbsp;
City对象包括了城市名称和区号。下面是主要的应用了。</P>
<TABLE STYLE="BORDER-RIGHT: #999 1px solid; BORDER-TOP: #999 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #999 1px solid; WIDTH: 80%; BORDER-BOTTOM: #999 1px solid" ALIGN="center">
<TBODY>
<TR>
<TD>
<P><FONT FACE="Courier New">import
java.io.BufferedInputStream;<br/>
import java.io.ByteArrayInputStream;<br/>
import java.io.ByteArrayOutputStream;<br/>
import java.io.InputStream;<br/>
import java.io.ObjectInputStream;<br/>
import java.io.ObjectOutputStream;<br/>
import java.io.OutputStream;<br/>
import java.sql.Connection;<br/>
import java.sql.DriverManager;<br/>
import java.sql.ResultSet;<br/>
import java.sql.Statement;<br/>
import java.util.ArrayList;<br/>
import java.util.Date;<br/>
import java.util.List;</FONT></P>
<P><FONT FACE="Courier New">import oracle.sql.BLOB;</FONT></P>
<P><FONT FACE="Courier New">public class Test {</FONT></P>
<P><FONT FACE="Courier New">&nbsp;public static void
main(String[] args) {<br/>
&nbsp;&nbsp;//创建测试用对象<br/>
&nbsp;&nbsp;<FONT COLOR="#0080FF">City
beijing = new City();<br/>
&nbsp;&nbsp;beijing.setName("北京");<br/>
&nbsp;&nbsp;beijing.setCode("010");<br/>
&nbsp;&nbsp;<br/>
&nbsp;&nbsp;City shanghai = new
City();<br/>
&nbsp;&nbsp;shanghai.setName("上海");<br/>
&nbsp;&nbsp;shanghai.setCode("020");<br/>
&nbsp;&nbsp;<br/>
&nbsp;&nbsp;City tianjin = new
City();<br/>
&nbsp;&nbsp;tianjin.setName("天津");<br/>
&nbsp;&nbsp;tianjin.setCode("021");<br/>
&nbsp;&nbsp;<br/>
&nbsp;&nbsp;List&lt;City&gt; cityList = new
ArrayList&lt;City&gt;();<br/>
&nbsp;&nbsp;cityList.add(beijing);<br/>
&nbsp;&nbsp;cityList.add(shanghai);<br/>
&nbsp;&nbsp;cityList.add(tianjin);<br/>
&nbsp;&nbsp;<br/>
&nbsp;&nbsp;TestObject obj = new
TestObject();<br/>
&nbsp;&nbsp;obj.setName("yangsq");<br/>
&nbsp;&nbsp;obj.setPassword("111");<br/>
&nbsp;&nbsp;obj.setDate(new Date());<br/>
&nbsp;&nbsp;obj.setCityList(cityList);</FONT><br/>

&nbsp;&nbsp;<br/>
&nbsp;&nbsp;try{<br/>
&nbsp;&nbsp;&nbsp;//将对象存入blob字段<br/>

&nbsp;&nbsp;&nbsp;<FONT COLOR="#FF0000">ByteArrayOutputStream byteOut=new
ByteArrayOutputStream();<br/>
&nbsp;&nbsp;&nbsp;ObjectOutputStream
outObj=new ObjectOutputStream(byteOut);<br/>
&nbsp;&nbsp;&nbsp;outObj.writeObject(obj)
;<br/>
&nbsp;&nbsp;&nbsp;final byte[]
objbytes=byteOut.toByteArray();<br/>
&nbsp;&nbsp;&nbsp;<br/>
&nbsp;&nbsp;&nbsp;Class.forName("oracle.jdbc.driver.OracleDriver");<br/>

&nbsp;&nbsp;&nbsp;Connection
con = DriverManager.getConnection(<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"jdbc:oracle:thin:@***.***.***.***:1521:****",
"yangsq", "yangsq");<br/>
&nbsp;&nbsp;&nbsp;con.setAutoCommit(false);<br/>

&nbsp;&nbsp;&nbsp;Statement st
= con.createStatement();<br/>
&nbsp;&nbsp;&nbsp;<br/>
&nbsp;&nbsp;&nbsp;st.executeUpdate("insert
into TESTBLOB (ID, NAME, CONTENT) values (1, 'test1',
empty_blob())");<br/>
&nbsp;&nbsp;&nbsp;ResultSet rs
= st.executeQuery("select CONTENT from TESTBLOB where ID=1 for
update");<br/>
&nbsp;&nbsp;&nbsp;<br/>
&nbsp;&nbsp;&nbsp;if
(rs.next()) {<br/>
&nbsp;&nbsp;&nbsp;&nbsp;BLOB
blob = (BLOB) rs.getBlob("CONTENT");<br/>
&nbsp;&nbsp;&nbsp;&nbsp;OutputStream
outStream = blob.getBinaryOutputStream();<br/>
&nbsp;&nbsp;&nbsp;&nbsp;outStream.write(objbytes,
0, objbytes.length);<br/>
&nbsp;&nbsp;&nbsp;&nbsp;outStream.flush();<br/>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
outStream.close();<br/>
&nbsp;&nbsp;&nbsp;}<br/>
&nbsp;&nbsp;&nbsp;<br/>
&nbsp;&nbsp;&nbsp;byteOut.close();<br/>

&nbsp;&nbsp;&nbsp;outObj.close();<br/>

&nbsp;&nbsp;&nbsp;con.commit();<br/>
</FONT>&nbsp;&nbsp;&nbsp;<br/>
&nbsp;&nbsp;&nbsp;//取出blob字段中的对象，并恢复<br/>

&nbsp;&nbsp;&nbsp;<FONT COLOR="#009900">rs = st.executeQuery("select CONTENT from TESTBLOB where
ID=1");<br/>
&nbsp;&nbsp;&nbsp;BLOB inblob =
null;<br/>
&nbsp;&nbsp;&nbsp;if
(rs.next()) {<br/>
&nbsp;&nbsp;&nbsp;&nbsp;inblob
= (BLOB) rs.getBlob("CONTENT");<br/>
&nbsp;&nbsp;&nbsp;}<br/>
&nbsp;&nbsp;&nbsp;InputStream
is = inblob.getBinaryStream();<br/>
&nbsp;&nbsp;&nbsp;BufferedInputStream
input = new BufferedInputStream(is);<br/>
&nbsp;&nbsp;&nbsp;<br/>
&nbsp;&nbsp;&nbsp;byte[] buff =
new byte[inblob.getBufferSize()];<br/>
&nbsp;&nbsp;&nbsp;while(-1 !=
(input.read(buff, 0, buff.length)));<br/>
&nbsp;&nbsp;&nbsp;<br/>
&nbsp;&nbsp;&nbsp;ObjectInputStream
in =<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
new ObjectInputStream(<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
new ByteArrayInputStream(<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;
buff));<br/>
&nbsp;&nbsp;&nbsp;TestObject w3
=
(TestObject)in.readObject();<br/></FONT>&nbsp;&nbsp;&nbsp;System.out.println(w3.getName());<br/>

&nbsp;&nbsp;&nbsp;System.out.println(w3.getPassword());<br/>

&nbsp;&nbsp;&nbsp;System.out.println(w3.getDate());<br/>

&nbsp;&nbsp;&nbsp;<br/>
&nbsp;&nbsp;&nbsp;List&lt;City&gt;
list = w3.getCityList();<br/>
&nbsp;&nbsp;&nbsp;for(City city
: list){<br/>
&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(city.getName()
+ "&nbsp;&nbsp;&nbsp; " +
city.getCode());<br/>
&nbsp;&nbsp;&nbsp;}<br/>
&nbsp;&nbsp;&nbsp;<br/>
&nbsp;&nbsp;&nbsp;st.close();<br/>

&nbsp;&nbsp;&nbsp;con.close();<br/>

&nbsp;&nbsp;} catch (Exception ex) {<br/>
&nbsp;&nbsp;&nbsp;ex.printStackTrace();<br/>

&nbsp;&nbsp;&nbsp;System.exit(1);<br/>

&nbsp;&nbsp;}<br/>
&nbsp;}<br/>
}<br/></FONT></P>
</TD>
</TR>
</TBODY>
</TABLE>
&nbsp;
代码的蓝色部分创建了要存储的对象。再看红色的对象写入部分，它首先把对象转化成二进制流的形式。对于blob字段，我们不能简单的在insert时插入，实际上，insert时，对于blob字段，只能先插入一个空的blob对象<FONT FACE="Courier New"><FONT COLOR="#FF0000">empty_blob()，</FONT>然后再进行<FONT FACE="Courier New"><FONT COLOR="#FF0000">"select
CONTENT from TESTBLOB where ID=1 for
update"</FONT>对blob字段进行更新。返回后，我们只要把对象的二进制流写入即可。</FONT></FONT>
<TABLE STYLE="BORDER-RIGHT: #999 1px solid; BORDER-TOP: #999 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #999 1px solid; WIDTH: 80%; BORDER-BOTTOM: #999 1px solid" ALIGN="center">
<TBODY>
<TR>
<TD><FONT FACE="Courier New">OutputStream outStream =
blob.getBinaryOutputStream();<br/>
outStream.write(objbytes, 0, objbytes.length);</FONT></TD>
</TR>
</TBODY>
</TABLE>
<P>&nbsp; 需要注意的是，上述步骤必须设置<FONT FACE="Courier New"><FONT COLOR="#FF0000">con.setAutoCommit(false)，</FONT>否则oracle会抛出异常。</FONT></P>
<P>&nbsp;
接下来，绿色的代码是读取数据库中blob字段的对象，并恢复。我们要知道的是，所有对blob字段的操作都是二进制的，即插入时是二进制流，读出时也是二进制流。然后用io修饰器（<FONT FACE="Courier New">ObjectInputStream</FONT>）去修饰。以前学习java时，感觉它的io好繁琐啊，现在感觉还真是各有其用。下面是测试的输出结果。</P>
<TABLE STYLE="BORDER-RIGHT: #999 1px solid; BORDER-TOP: #999 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #999 1px solid; WIDTH: 80%; BORDER-BOTTOM: #999 1px solid" ALIGN="center">
<TBODY>
<TR>
<TD><FONT FACE="Courier New">yangsq<br/>
111<br/>
Tue Mar 27 12:11:28 CST 2007<br/>
北京&nbsp;&nbsp;&nbsp;
010<br/>
上海&nbsp;&nbsp;&nbsp;
020<br/>
天津&nbsp;&nbsp;&nbsp;
021</FONT></TD>
</TR>
</TBODY>
</TABLE>
<P>&nbsp;&nbsp;&nbsp;
需要说明一下，buff的size一定要足够大，否则将抛出异常。在这里，我使用的是inblob.getBufferSize()来设置buff的size，这并不是一种好的方法，因为一般inblob.getBufferSize()都是32768，很可能出现异常，所以这个size最好自己设置，或系统运行时刻设置（幸好java提供数组长度的运行时刻设置）。</P>
<P>
上面是我本人研究的结果，当然少不了网友们的知识奉献。下面就转自一位网友的blog。</P>
<P><FONT COLOR="#FF9900">转：</FONT></P>
<P><FONT FACE="Courier New">1.新建记录，插入BLOB数据<br/>
&nbsp;1.1首先新建记录的时候，使用oracle的函数插入一个空的BLOB，假设字段A是BLOB类型的:<br/>

&nbsp; insert xxxtable(A,B,C)
values(empty_blob(),'xxx','yyyy')<br/>
&nbsp;1.2后面再查询刚才插入的记录，然后更新BLOB，在查询前，注意设置Connection的一个属性:<br/>

&nbsp;
conn.setAutoCommit(false);如果缺少这一步，可能导致fetch out of
sequence等异常.<br/>
&nbsp;1.3 查询刚才插入的记录,后面要加“ for update
”,如下：<br/>
&nbsp; select A from xxxtable where xxx=999 for update
,如果缺少for update，可能出现row containing the LOB value is not
locked</FONT></P>
<P><FONT FACE="Courier New">的异常<br/>
&nbsp;1.4 从查询到的
BLOB字段中，获取blob并进行更新，代码如下：<br/>
&nbsp; BLOB blob = (BLOB) rs.getBlob("A");<br/>
&nbsp; OutputStream os =
blob.getBinaryOutputStream();<br/>
&nbsp; BufferedOutputStream output = new
BufferedOutputStream(os);</FONT></P>
<P><FONT FACE="Courier New">&nbsp;
后面再使用output.write方法将需要写入的内容写到output中就可以了。例如我们将一个文件写入这个字段中：<br/>

&nbsp; BufferedInputStream input = new
BufferedInputStream(new
File("c:\\hpWave.log").toURL().openStream());<br/>
&nbsp; byte[] buff = new byte[2048];&nbsp;
//用做文件写入的缓冲<br/>
&nbsp; int bytesRead;<br/>
&nbsp; while(-1 != (bytesRead = input.read(buff, 0,
buff.length))) {<br/>
&nbsp;&nbsp; output.write(buff, 0,
bytesRead);<br/>
&nbsp;&nbsp;
System.out.println(bytesRead);<br/>
&nbsp; }<br/>
&nbsp;
上面的代码就是从input里2k地读取，然后写入到output中。<br/>
&nbsp;1.5上面执行完毕后，记得关闭output,input,以及关闭查询到的ResultSet<br/>

&nbsp;1.6最后执行conn.commit();将更新的内容提交，以及执行conn.setAutoCommit(true);
改回Connction的属性<br/>
2.修改记录，方法与上面的方法类似，<br/>
&nbsp;2.1首先更新BLOB以外的其他字段<br/>
&nbsp;2.2 使用1.3中类似的方法获取记录<br/>
&nbsp;2.3 修改的过程中，注意以下:a
需要更新的记录中，BLOB有可能为NULL,这样在执行blob.getBinaryOutputStream()获取的值可能为</FONT></P>
<P><FONT FACE="Courier New">null,那么就关闭刚才select的记录，再执行一次update
xxxtable set A = empty_blob() where xxx,
这样就先写入了一个空的BLOB(不是null),然后再</FONT></P>
<P><FONT FACE="Courier New">使用1.3,1.4中的方法执行更新记录.b
注意别忘了先执行setAutoCommit(false),以及"for
update",以及后面的conn.commit();等。<br/>
3.读取BLOB字段中的数据.<br/>
&nbsp;3.1 读取记录不需要setAutoCommit(),以及 select
....for update.<br/>
&nbsp;3.2 使用普通的select 方法查询出记录<br/>
&nbsp;3.3 从ResultSet中获取BLOB并读取，如下：<br/>
&nbsp; BLOB b_to = (BLOB) rs.getBlob("A");<br/>
&nbsp; InputStream is = b_from.getBinaryStream();<br/>
&nbsp; BufferedInputStream input = new
BufferedInputStream(is);<br/>
&nbsp; byte[] buff = new byte[2048];<br/>
&nbsp; while(-1 != (bytesRead = input.read(buff, 0,
buff.length))) {<br/>
&nbsp;&nbsp;
//在这里执行写入，如写入到文件的BufferedOutputStream里<br/>
&nbsp;&nbsp;
System.out.println(bytesRead);<br/>
&nbsp; }<br/>
&nbsp;
通过循环取出blob中的数据，写到buff里,再将buff的内容写入到需要的地方<br/>

4.两个数据库间blob字段的传输<br/>
类似上面1和3的方法，一边获取BufferedOutputStream,另外一边获取BufferedInputStream，然后读出写入，需要注意的是写入所用的</FONT></P>
<P><FONT FACE="Courier New">Connection要执行conn.setAutoCommit(false);以及获取记录时添加“
for update ”以及最后的commit();</FONT></P>
<P><FONT FACE="Courier New">总结以上方法，其根本就是先创建空的BLOB，再获取其BufferedOutputStream进行写入，或获取BufferedInputStream进行读取</FONT></P>
]]></description>
            <author>小杨</author>
            <category>java基础技术</category>
            <comments>http://blog.sina.com.cn/s/blog_4b86f2ee010007s2.html#comment</comments>
            <pubDate>Tue, 27 Mar 2007 03:19:41 GMT+8</pubDate>
            <guid>http://blog.sina.com.cn/s/blog_4b86f2ee010007s2.html</guid>
        </item>
        <item>
            <title>Spring学习笔记（二十）----Spring AOP</title>
            <link>http://blog.sina.com.cn/s/blog_4b86f2ee010007ah.html</link>
            <description><![CDATA[&nbsp;&nbsp; Aspect Oriented
Program，如果硬要使中文译过来，就只能叫做“面向切面的编程”。可不要把它和OOP并列看待，他们描述的完全不是相同方向的东西。那么什么叫做AOP呢，首先要了解什么是Aspect。<br/>

&nbsp;&nbsp;
在我们的业务流程中，时常要进行一些与业务无关的操作，如日志输出，事务管理，身份验证，我们把这些事情叫做Cross-cutting
concerns。实际上，我们不愿意把这些代码和业务流程混在一起。这时我们就把这些Cross-cutting
concerns提取出来，单独设计成通用的，不介入业务流程的类，就成了Aspect。<br/>

&nbsp;&nbsp;
Spring的一个主要的特点即是AOP，Spring提供了强大的AOP框架。我们知道，Spring的另一个主要概念是IoC，那么AOP和IoC是什么关系呢，下面是spring-reference的解释<br/>

<TABLE STYLE="border: 1px solid rgb(153, 153, 153); width: 500px; font-size: 12px; height: 23px;" ALIGN="center">
<TBODY>
<TR>
<TD>One of the key components of Spring is the AOP framework. While
the Spring IoC containers (BeanFactory and ApplicationContext) do
not depend on AOP, meaning you don't need to use AOP if you don't
want to, AOP complements Spring IoC to provide a very capable
middleware solution.</TD>
</TR>
</TBODY>
</TABLE>
<br/>
要想了解AOP，还是先从它的几个概念入手：<br/>
<UL>
<LI><SPAN STYLE="font-weight: bold;">Aspect:</SPAN>
就像先前解释的一样，把嵌入业务的日志输出，事务管理，身份验证等Cross-cutting
concerns提取出来形成的类，就叫做Aspect。</LI>
<LI><SPAN STYLE="font-weight: bold;">Joinpoint:</SPAN>Aspect在业务流程中执行的时刻就是Joinpoint。例如，我们在一个插入数据库的业务前要有个日志输出，那么把这个日志输出抽出来形成一个Aspect，这个Aspect执行的时刻就是Joinpoint。一般的，Joinpoint实在某个方法执行之前或之后或前后都有或异常发生时。Spring
AOP为这些提供了支持。<br/></LI>
<LI><SPAN STYLE="font-weight: bold;">Advice:</SPAN>说白了，Aspect的具体实现（实例）就是一个Advice。也是是说，在Joinpoint点执行的是一个Advice（Aspect实例）。</LI>
<LI><SPAN STYLE="font-weight: bold;">Pointcut:</SPAN>我们定义了Aspect和Joinpoint，那么我们让Aspect的实例在那个（些）Joinpoint(s)中执行呢，这就是Pointcut干的事了。</LI>
<LI><SPAN STYLE="font-weight: bold;">Introduction:</SPAN>为一个现有的类增加行为或功能，这时在运行时加入的，所以只需要原有类的class文件即可，而不需修改原有类代码。</LI>
<LI><SPAN STYLE="font-weight: bold;">Target
object:</SPAN>顾名思义，即原有业务类。</LI>
<LI><SPAN STYLE="font-weight: bold;">AOP proxy:</SPAN>AOP
的实现方式有好多种，在spring中，主要是通过动态代理方式来实现。</LI>
<LI><SPAN STYLE="font-weight: bold;">Weaving:</SPAN>“编织”，决定Aspect何时生成Advice，主要在编译时期或运行时期。Spring
AOP是在运行时期进行的，即在运行时将Advice倒入Target。<br/></LI>
</UL>
Spring只支持方法的Joinpoint，也就是说Advice将在方法的前后被应用。Spring目前还不支持对field的Joinpoint，但个人感觉这一点无伤大雅。<br/>

<br/>
Advice是Spring AOP
framework的一个重要部分。Spring提供了以下几种不同的Advice：<br/>
<UL>
<LI><SPAN STYLE="font-weight: bold;">Before advice:</SPAN>
在目标方法之前被唤醒（执行），但它并不能阻止execution
flow流向目标方法，除非 Throwable or Exception。</LI>
<LI><SPAN STYLE="font-weight: bold;">After returning
advice:</SPAN>在目标方法执行之后被唤醒（执行）。</LI>
<LI><SPAN STYLE="font-weight: bold;">Around
advice:</SPAN>这是功能最强的Advice，它可以在目标方法前后都被唤醒（执行），可以决定是否执行目标方法，还可以对目标方法返回的结果进行修改。</LI>
<LI><SPAN STYLE="font-weight: bold;">Throws
advice:</SPAN>当advice抛出异常时被执行。</LI>
</UL>
虽然Spring提供了这么多Advice，但是还是提倡使用最合适的Advice，如果只需要在方法之前有个日志输出，最好使用Before
advice，而不是Around advice。<br/>
<br/>
&nbsp;&nbsp;
]]></description>
            <author>小杨</author>
            <category>开源应用</category>
            <comments>http://blog.sina.com.cn/s/blog_4b86f2ee010007ah.html#comment</comments>
            <pubDate>Tue, 20 Feb 2007 07:33:50 GMT+8</pubDate>
            <guid>http://blog.sina.com.cn/s/blog_4b86f2ee010007ah.html</guid>
        </item>
        <item>
            <title>java 代理模式</title>
            <link>http://blog.sina.com.cn/s/blog_4b86f2ee0100078s.html</link>
            <description><![CDATA[&nbsp; &nbsp;&nbsp;
我们去买东西，
经常听到某个经销商说“我们是xxx的一级代理”（这种现象在中关村菜市场很常见）。在现实生活中，我们买到的东西实际上都是代理商卖给我们的。在程序设计中，也常常需要这些“代理商”们，以便得到更好的服务，这就是“代理模式”。<br/>

&nbsp;&nbsp;&nbsp;
在了解代理模式之前，先看看几个有用的概念。<br/>
<UL>
<LI><SPAN STYLE="font-weight: bold;">抽象角色</SPAN>：声明真实对象和代理对象的共同接口；</LI>
<LI><SPAN STYLE="font-weight: bold;">代理角色</SPAN>：代理对象角色内部含有对真实对象的引用，从而可以操作真实对象，同时代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象。同时，代理对象可以在执行真实对象操作时，附加其他的操作，相当于对真实对象进行封装。</LI>
<LI><SPAN STYLE="font-weight: bold;">真实角色</SPAN>：代理角色所代表的真实对象，是我们最终要引用的对象。<br/>
</LI>
</UL>
&nbsp;&nbsp;&nbsp;
所谓代理模式，就是<SPAN STYLE="color: rgb(0, 153, 2); font-weight: bold;">为其他对象提供一种代理以控制对这个对象的访问</SPAN>。那么是什么原因让我们不去直接与“生产部门”打交道，而要通过中间的“代理商”呢？这主要是因为以下几种情况：<br/>

<UL>
<LI><SPAN STYLE="color: rgb(255, 1, 2);">远程（Remote）代理：</SPAN>为一个位于不同的地址空间的对象提供一个局域代表对象。比如：你可以将一个在世界某个角落一台机器通过代理假象成你局域网中的一部分。</LI>
<LI><SPAN STYLE="color: rgb(255, 1, 2);">虚拟（Virtual）代理：</SPAN>根据需要将一个资源消耗很大或者比较复杂的对象延迟的真正需要时才创建。比如：如果一个很大的图片，需要花费很长时间才能显示出来，那么当这个图片包含在文档中时，使用编辑器或浏览器打开这个文档，这个大图片可能就影响了文档的阅读，这时需要做个图片Proxy来代替真正的图片。</LI>
<LI><SPAN STYLE="color: rgb(255, 1, 2);">保护（Protect or
Access）代理：</SPAN>控制对一个对象的访问权限。比如：在论坛中，不同的身份登陆，拥有的权限是不同的，使用代理模式可以控制权限（当然，使用别的方式也可以实现）。</LI>
<LI><SPAN STYLE="color: rgb(255, 1, 2);">智能引用（Smart
Reference）代理：</SPAN>提供比对目标对象额外的服务。比如：纪录访问的流量（这是个再简单不过的例子），提供一些友情提示等等。</LI>
</UL>
从实现的角度，代理又可分为“静态代理”和“动态代理”。静态代理比较简单，但是通用性不好，要为每个需要的类都建立代理类。下面的这个例子将介绍这动态代理模式。<br/>

<TABLE STYLE="border: 1px solid rgb(153, 153, 153); width: 484px; font-size: 12px; height: 23px;" ALIGN="center">
<TBODY>
<TR>
<TD>public interface HelloWorld {<br/>
&nbsp;&nbsp;&nbsp; public void
speakHello (String word);<br/>
&nbsp;&nbsp;&nbsp; public void
speakWelcome (String word);<br/>
}<br/></TD>
</TR>
</TBODY>
</TABLE>
这是一个简单的接口，下面是这个接口的实现类：<br/>
<TABLE STYLE="border: 1px solid rgb(153, 153, 153); width: 484px; font-size: 12px; height: 23px;" ALIGN="center">
<TBODY>
<TR>
<TD>public class HelloWorldImpl implements HelloWorld {<br/>
&nbsp;&nbsp;&nbsp; public void
speakHello(String word) {<br/>
&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;
System.out.println("hello " + word);<br/>
&nbsp;&nbsp;&nbsp; }<br/>
&nbsp;&nbsp;&nbsp; public void
speakWelcome(String word) {<br/>
&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;
System.out.println("welcome " + word);<br/>
&nbsp;&nbsp;&nbsp; }<br/>
}<br/></TD>
</TR>
</TBODY>
</TABLE>
它实现了上面接口中定义的两个方法。现在我们想在每个方法执行前后加入一些说明语句。如果不使用代理的话，可能要在HelloWorldImpl里加代码了。但我们可以定义一个代理，不用修改HelloWorldImpl也能实现这个功能。<br/>

<TABLE STYLE="border: 1px solid rgb(153, 153, 153); width: 484px; font-size: 12px; height: 23px;" ALIGN="center">
<TBODY>
<TR>
<TD>import java.lang.reflect.InvocationHandler;<br/>
import java.lang.reflect.Method;<br/>
import java.lang.reflect.Proxy;<br/>
<br/>
public class HelloProxy implements InvocationHandler {<br/>
&nbsp;&nbsp;&nbsp; private
Object delegate;<br/>
&nbsp;&nbsp;&nbsp;<br/>
&nbsp;&nbsp;&nbsp; public
Object getProxy(Object delegate){<br/>
&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;
this.delegate = delegate;<br/>
&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp; return
Proxy.newProxyInstance(<br/>
&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;
delegate.getClass().getClassLoader(),<br/>
&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;
delegate.getClass().getInterfaces(),<br/>
&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp; this);<br/>
&nbsp;&nbsp;&nbsp; }<br/>
&nbsp;&nbsp;&nbsp;<br/>
&nbsp;&nbsp;&nbsp; public
Object invoke(Object proxy, Method method,<br/>
&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp; Object[]
args) throws Throwable {<br/>
&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp; Object
result = null;<br/>
&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp; try{<br/>
&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;
System.out.println("before " + method + " is invoked");<br/>
&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp; result =
method.invoke(delegate, args);<br/>
&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;
System.out.println("after " + method + " is invoked");<br/>
&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;
}catch(Exception ex){<br/>
&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;
ex.printStackTrace();<br/>
&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp; }<br/>
&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp; return
result;<br/>
&nbsp;&nbsp;&nbsp; }<br/>
}<br/></TD>
</TR>
</TBODY>
</TABLE>
实际上，上面的这个代理并不仅仅可以代理HelloWorldImpl，也适用于其他“厂商”。可以看出，HelloProxy实现了InvocationHandler接口。InvocationHandler接口仅有一个方法需要实现：<br/>

<TABLE STYLE="border: 1px solid rgb(153, 153, 153); width: 484px; font-size: 12px; height: 23px;" ALIGN="center">
<TBODY>
<TR>
<TD>public object invoke(object proxy, method method, object[]
args)<br/></TD>
</TR>
</TBODY>
</TABLE>
第一个参数是代理对象，第二个参数是代理角色的某个方法，第三个参数是这个方法的参数数组。<br/>

Proxy的静态方法newProxyInstance用于产生一个代理类，第一个参数是被代理类的加载类，第二个参数是被代理类的接口列表。<br/>

下面我们创建一个测试类：<br/>
<TABLE STYLE="border: 1px solid rgb(153, 153, 153); width: 484px; font-size: 12px; height: 23px;" ALIGN="center">
<TBODY>
<TR>
<TD>public class HelloDemo {<br/>
<br/>
&nbsp;&nbsp;&nbsp; public
static void main(String[] args) {<br/>
&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp; HelloProxy
helloProxy = new HelloProxy();<br/>
&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp; <SPAN STYLE="color: rgb(255, 1, 2);">HelloWorld helloWorld =
(HelloWorld)helloProxy.getProxy(</SPAN><BR STYLE="color: rgb(255, 1, 2);"></BR>
<SPAN STYLE="color: rgb(255, 1, 2);">&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp; new
HelloWorldImpl());</SPAN><br/>
&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;
helloWorld.speakHello("yangsq");<br/>
&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;
helloWorld.speakWelcome("yangsq");<br/>
&nbsp;&nbsp;&nbsp; }<br/>
}<br/></TD>
</TR>
</TBODY>
</TABLE>
上面用红笔标出的语句创建了一个代理类helloWorld，<SPAN STYLE="color: rgb(255, 1, 2); font-weight: bold;">注意所有的代理都是面向接口的</SPAN>，所以类型是HelloWorld。<br/>

<br/>
<br/>
<br/>
]]></description>
            <author>小杨</author>
            <category>java基础技术</category>
            <comments>http://blog.sina.com.cn/s/blog_4b86f2ee0100078s.html#comment</comments>
            <pubDate>Thu, 15 Feb 2007 08:14:22 GMT+8</pubDate>
            <guid>http://blog.sina.com.cn/s/blog_4b86f2ee0100078s.html</guid>
        </item>
        <item>
            <title>Spring学习笔记（十九）----Spring声明式事务</title>
            <link>http://blog.sina.com.cn/s/blog_4b86f2ee0100078a.html</link>
            <description><![CDATA[<DIV>&nbsp;&nbsp;&nbsp;
Spring的一个最主要的特性是AOP（以前还没涉及过，以后会学习），它的目的是使与业务无关的代码与业务代码相分离。Spring的声明式事务采用就是是AOP框架。在学习编程式事务时，我们看到事务管理的代码侵入到了业务代码里（这里所谓的业务的save，del等函数），如果将来需要变更，将不得不去更改代码。</DIV>
<DIV>&nbsp;&nbsp;&nbsp; Spring
AOP特性的声明式事务管理，就可以避免编程式事务带来的难维护性。声明式事务只需要在配置文件中声明即可。这样的好处是业务逻辑（Dao）就不会意识到事务管理的存在，而且维护起来极其方便。</DIV>
<DIV>&nbsp;&nbsp;&nbsp;
使用声明式事务管理时，通常要把我们的Dao交给一个代理，由其进行管理。这个代理一般是<STRONG>org.springframework.transaction.interceptor.TransactionProxyFactoryBean</STRONG>，下面是其简要定义：
<TABLE STYLE="BORDER-RIGHT: #999 1px solid; BORDER-TOP: #999 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #999 1px solid; WIDTH: 101.79%; BORDER-BOTTOM: #999 1px solid; HEIGHT: 208px" ALIGN="center">
<TBODY>
<TR>
<TD>
<P>public class TransactionProxyFactoryBean{</P>
<P>&nbsp;&nbsp;&nbsp;
......</P>
<P>
&nbsp;&nbsp;&nbsp;&nbsp;public
void setTransactionManager(PlatformTransactionManager
transactionManager);</P>
<P>&nbsp;&nbsp;&nbsp; public
void setTarget(Object target);</P>
<P>&nbsp;&nbsp;&nbsp; public
void setProxyInterfaces(String[] interfaceNames);</P>
<P>&nbsp;&nbsp;&nbsp; public
void setTransactionAttributes(Properties
transactionAttributes);</P>
<P>&nbsp;&nbsp;&nbsp; public
void setTransactionAttributeSource(TransactionAttributeSource
transactionAttributeSource);</P>
<P>&nbsp;&nbsp;&nbsp;
......</P>
<P>}</P>
</TD>
</TR>
</TBODY>
</TABLE>
</DIV>
<P>
看到上面的TransactionProxyFactoryBean简要定义，我们就知道在进行配置时需要注入那些东西了。这里需要对一些属性进行说明。</P>
<UL>
<LI>transactionManager</LI>
</UL>
<P>&nbsp;&nbsp;&nbsp;
这个就不用解释了吧，不管声明式还是编程式事务，就是由transactionManager进行管理的。</P>
<UL>
<LI>target</LI>
</UL>
<P>&nbsp;&nbsp;&nbsp;
这个就是TransactionProxyFactoryBean所代理的目标类。</P>
<UL>
<LI>proxyInterfaces</LI>
</UL>
<P>&nbsp;&nbsp;&nbsp;
定义了target指定的类所实现的接口，<STRONG>Spring可是大力提倡面向接口编程的</STRONG>。这是一个list，所以可以注入多个接口类，但要包括完整路径，如com.yangsq.dao.UserDao这样。</P>
<UL>
<LI>transactionAttributes</LI>
</UL>
<P>&nbsp;&nbsp;
通过察看TransactionAttribute的定义，知道他implements
TransactionDefinition（在编程式事物里有介绍）。这样我们就知道了它定义了事物的执行策略，如传播属性，隔离等级等。</P>
<UL>
<LI>transactionAttributeSource</LI>
</UL>
<P>&nbsp;&nbsp;&nbsp;
下面是他的定义：</P>
<TABLE STYLE="BORDER-RIGHT: #999 1px solid; BORDER-TOP: #999 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #999 1px solid; WIDTH: 97.36%; BORDER-BOTTOM: #999 1px solid; HEIGHT: 23px" ALIGN="center">
<TBODY>
<TR>
<TD>
<P>public abstract interface TransactionAttributeSource{</P>
<P>&nbsp;&nbsp;&nbsp; public
abstract TransactionAttribute&nbsp;
getTransactionAttribute(Method arg, Class arg);</P>
<P>}</P>
</TD>
</TR>
</TBODY>
</TABLE>
<P>&nbsp;&nbsp;
原来它是对TransactionAttribute进行管理的。</P>
<P>下面是对编程式事务里那个例子的更改。</P>
<P>首先把com.yangsq.dao.impl.UserDaoImpl里的事务管理语句去掉：</P>
<TABLE STYLE="BORDER-RIGHT: #999 1px solid; BORDER-TOP: #999 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #999 1px solid; WIDTH: 97.36%; BORDER-BOTTOM: #999 1px solid; HEIGHT: 23px" ALIGN="center">
<TBODY>
<TR>
<TD>
<P>import java.util.List;</P>
<P>import
org.springframework.orm.hibernate3.support.HibernateDaoSupport;</P>
<P>import com.yangsq.dao.UserDao;<br/>
import com.yangsq.domain.User;</P>
<P>public class UserDaoImpl extends HibernateDaoSupport implements
UserDao {<br/>
&nbsp;<br/>
&nbsp;public void saveOrUpdate(User user){<br/>
&nbsp;&nbsp;this.getHibernateTemplate().saveOrUpdate(user);<br/>

&nbsp;}<br/>
&nbsp;......<br/>
}</P>
</TD>
</TR>
</TBODY>
</TABLE>
<P>com.yangsq.dao.UserDao:</P>
<TABLE STYLE="BORDER-RIGHT: #999 1px solid; BORDER-TOP: #999 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #999 1px solid; WIDTH: 98.19%; BORDER-BOTTOM: #999 1px solid; HEIGHT: 23px" ALIGN="center">
<TBODY>
<TR>
<TD>
<P>import com.yangsq.domain.User;</P>
<P>public interface UserDao {<br/>
&nbsp;public void saveOrUpdate(User user);<br/>
&nbsp;public void delete(User user);<br/>
&nbsp;public User find(Integer id);<br/>
&nbsp;public User findByUserName(String
userName);<br/>
}</P>
</TD>
</TR>
</TBODY>
</TABLE>
<P>/WEB-INF/data-access.xml:</P>
<TABLE STYLE="BORDER-RIGHT: #999 1px solid; BORDER-TOP: #999 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #999 1px solid; WIDTH: 98.19%; BORDER-BOTTOM: #999 1px solid; HEIGHT: 23px" ALIGN="center">
<TBODY>
<TR>
<TD>
<P>&lt;?xml version="1.0" encoding="UTF-8"?&gt;<br/>
&lt;!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "<A HREF="http://www.springframework.org/dtd/spring-beans.dtd">http://www.springframework.org/dtd/spring-beans.dtd</A>"&gt;</P>
<P>&lt;beans&gt;<br/>
&nbsp;&lt;!--PropertyPlaceholderConfigurer--&gt;<br/>
&nbsp;&lt;bean id="propertyConfigurer"<br/>
&nbsp;&nbsp;class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"&gt;<br/>

&nbsp;&nbsp;&lt;property
name="locations"&gt;<br/>
&nbsp;&nbsp;&nbsp;&lt;list&gt;<br/>

&nbsp;&nbsp;&nbsp;&nbsp;&lt;value&gt;WEB-INF/props/jdbc.properties&lt;/value&gt;<br/>

&nbsp;&nbsp;&nbsp;&lt;/list&gt;<br/>

&nbsp;&nbsp;&lt;/property&gt;<br/>
&nbsp;&lt;/bean&gt;<br/>
&nbsp;<br/>
&nbsp;&lt;!--org.apache.commons.dbcp.BasicDataSource
has the connect pool--&gt;<br/>
&nbsp;&lt;bean id="dataSource"<br/>
&nbsp;&nbsp;class="org.apache.commons.dbcp.BasicDataSource"<br/>

&nbsp;&nbsp;destroy-method="close"&gt;<br/>

&nbsp;&nbsp;&lt;property
name="driverClassName"&gt;<br/>
&nbsp;&nbsp;&nbsp;&lt;value&gt;${jdbc.driverClassName}&lt;/value&gt;<br/>

&nbsp;&nbsp;&lt;/property&gt;<br/>
&nbsp;&nbsp;&lt;property
name="url"&gt;<br/>
&nbsp;&nbsp;&nbsp;&lt;value&gt;${jdbc.url}&lt;/value&gt;<br/>

&nbsp;&nbsp;&lt;/property&gt;<br/>
&nbsp;&nbsp;&lt;property
name="username"&gt;<br/>
&nbsp;&nbsp;&nbsp;&lt;value&gt;${jdbc.username}&lt;/value&gt;<br/>

&nbsp;&nbsp;&lt;/property&gt;<br/>
&nbsp;&nbsp;&lt;property
name="password"&gt;<br/>
&nbsp;&nbsp;&nbsp;&lt;value&gt;${jdbc.password}&lt;/value&gt;<br/>

&nbsp;&nbsp;&lt;/property&gt;<br/>
&nbsp;&lt;/bean&gt;</P>
<P>&nbsp;&lt;!--sessionFactory--&gt;<br/>
&nbsp;&lt;bean id="sessionFactory"<br/>
&nbsp;&nbsp;class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"<br/>

&nbsp;&nbsp;destroy-method="close"&gt;<br/>

&nbsp;&nbsp;&lt;property
name="dataSource"&gt;<br/>
&nbsp;&nbsp;&nbsp;&lt;ref
bean="dataSource" /&gt;<br/>
&nbsp;&nbsp;&lt;/property&gt;<br/>
&nbsp;&nbsp;&lt;property
name="mappingResources"&gt;<br/>
&nbsp;&nbsp;&nbsp;&lt;list&gt;<br/>

&nbsp;&nbsp;&nbsp;&nbsp;&lt;value&gt;com/yangsq/orm/user.hbm.xml&lt;/value&gt;<br/>

&nbsp;&nbsp;&nbsp;&lt;/list&gt;<br/>

&nbsp;&nbsp;&lt;/property&gt;<br/>
&nbsp;&nbsp;&lt;property
name="hibernateProperties"&gt;<br/>
&nbsp;&nbsp;&nbsp;&lt;props&gt;<br/>

&nbsp;&nbsp;&nbsp;&nbsp;&lt;prop
key="hibernate.dialect"&gt;<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;${hibernate.dialect}<br/>

&nbsp;&nbsp;&nbsp;&nbsp;&lt;/prop&gt;<br/>

&nbsp;&nbsp;&nbsp;&lt;/props&gt;<br/>

&nbsp;&nbsp;&lt;/property&gt;<br/>
&nbsp;&lt;/bean&gt;</P>
<P>&nbsp;&lt;!--HibernateTransactionManager--&gt;<br/>
&nbsp;&lt;bean id="transactionManager"<br/>
&nbsp;&nbsp;class="org.springframework.orm.hibernate3.HibernateTransactionManager"&gt;<br/>

&nbsp;&nbsp;&lt;property
name="sessionFactory"&gt;<br/>
&nbsp;&nbsp;&nbsp;&lt;ref
bean="sessionFactory"/&gt;<br/>
&nbsp;&nbsp;&lt;/property&gt;<br/>
&nbsp;&lt;/bean&gt;<br/>
&nbsp;<br/>
&nbsp;<FONT COLOR="#660099">&lt;!--userDaoImpl--&gt;<br/>
&nbsp;&lt;bean id="userDaoImpl"<br/>
&nbsp;&nbsp;class="com.yangsq.dao.impl.UserDaoImpl"&gt;<br/>

&nbsp;&nbsp;&lt;property
name="sessionFactory"&gt;<br/>
&nbsp;&nbsp;&nbsp;&lt;ref
local="sessionFactory" /&gt;<br/>
&nbsp;&nbsp;&lt;/property&gt;<br/>
&nbsp;&lt;/bean&gt;</FONT><br/>
&nbsp;<br/>
&nbsp;<FONT COLOR="#009900">&lt;bean
id="userDaoProxy"<br/>
&nbsp;&nbsp;class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"&gt;<br/>

&nbsp;&nbsp;&lt;property
name="proxyInterfaces"&gt;<br/>
&nbsp;&nbsp;&nbsp;&lt;list&gt;<br/>

&nbsp;&nbsp;&nbsp;&nbsp;&lt;value&gt;com.yangsq.dao.UserDao&lt;/value&gt;<br/>

&nbsp;&nbsp;&nbsp;&lt;/list&gt;<br/>

&nbsp;&nbsp;&lt;/property&gt;<br/>
&nbsp;&nbsp;&lt;property
name="target"&gt;<br/>
&nbsp;&nbsp;&nbsp;&lt;ref
bean="userDaoImpl"/&gt;<br/>
&nbsp;&nbsp;&lt;/property&gt;<br/>
&nbsp;&nbsp;&lt;property
name="transactionManager"&gt;<br/>
&nbsp;&nbsp;&nbsp;&lt;ref
bean="transactionManager"/&gt;<br/>
&nbsp;&nbsp;&lt;/property&gt;<br/>
&nbsp;&nbsp;&lt;property
name="transactionAttributes"&gt;<br/>
&nbsp;&nbsp;&nbsp;&lt;props&gt;<br/>

&nbsp;&nbsp;&nbsp;&nbsp;&lt;prop
key="saveOrUpdate"&gt;PROPAGATION_REQUIRED&lt;/prop&gt;<br/>
&nbsp;&nbsp;&nbsp;&lt;/props&gt;<br/>

&nbsp;&nbsp;&lt;/property&gt;<br/>
&nbsp;&lt;/bean&gt;<br/></FONT>&lt;/beans&gt;</P>
</TD>
</TR>
</TBODY>
</TABLE>
<P>
需要说明的是在TransactionProxyFactoryBean里的transactionAttributes，他的prop元素中的key属性定义了target类的某个方法，可以使用通配符，如<FONT COLOR="#FF0000">save*，</FONT>prop元素的值定义了对key中定义方法应用的事务执行属性，指定格式如下：</P>
<P>传播行为，隔离等级，只读，+异常，-异常</P>
<P>其中传播行为必须定义，其他可省，例如：</P>
<TABLE STYLE="BORDER-RIGHT: #999 1px solid; BORDER-TOP: #999 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #999 1px solid; WIDTH: 99.84%; BORDER-BOTTOM: #999 1px solid; HEIGHT: 23px" ALIGN="center">
<TBODY>
<TR>
<TD>......<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&lt;prop
key="xxxx"&gt;PROPAGATION_REQUIRED,readOnly,-DataAccessException&lt;/prop&gt;<br/>

......</TD>
</TR>
</TBODY>
</TABLE>
<P>
"-"表示发生指定异常后立即回滚，"+"表示发生指定异常后立即提交。</P>
<P>
经过上面xml定义，就相当于在UserDaoImpl的saveOrUpdate方法前后加入了事务处理的代码。</P>
<P>
注意：定义了userDaoProxy后，我们在想使用userDaoImpl，就需要注入userDaoProxy的bean了，而不是userDaoImpl的bean，下面是/WEB-INF/mvc-controller.xml的更改：</P>
<TABLE STYLE="BORDER-RIGHT: #999 1px solid; BORDER-TOP: #999 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #999 1px solid; WIDTH: 100.25%; BORDER-BOTTOM: #999 1px solid; HEIGHT: 23px" ALIGN="center">
<TBODY>
<TR>
<TD>......<br/>
&nbsp;&lt;!--SimpleFormController--&gt;<br/>
&nbsp;&lt;bean id="userController"<br/>
&nbsp;&nbsp;class="com.yangsq.controller.UserController"&gt;<br/>

&nbsp;&nbsp;&lt;property
name="formView"&gt;<br/>
&nbsp;&nbsp;&nbsp;&lt;value&gt;user&lt;/value&gt;<br/>

&nbsp;&nbsp;&lt;/property&gt;<br/>
&nbsp;&nbsp;&lt;property
name="successView"&gt;<br/>
&nbsp;&nbsp;&nbsp;&lt;value&gt;success&lt;/value&gt;<br/>

&nbsp;&nbsp;&lt;/property&gt;<br/>
&nbsp;&nbsp;&lt;property
name="commandClass"&gt;<br/>
&nbsp;&nbsp;&nbsp;&lt;value&gt;com.yangsq.domain.User&lt;/value&gt;<br/>

&nbsp;&nbsp;&lt;/property&gt;<br/>
&nbsp;&nbsp;&lt;property
name="sessionForm"&gt;<br/>
&nbsp;&nbsp;&nbsp;&lt;value&gt;true&lt;/value&gt;<br/>

&nbsp;&nbsp;&lt;/property&gt;<br/>
&nbsp;&nbsp;&lt;property
name="commandName"&gt;<br/>
&nbsp;&nbsp;&nbsp;&lt;value&gt;userForm&lt;/value&gt;<br/>

&nbsp;&nbsp;&lt;/property&gt;<br/>
&nbsp;&nbsp;<FONT COLOR="#FF0000">&lt;property name="userDao"&gt;<br/>
&nbsp;&nbsp;&nbsp;&lt;ref
bean="userDaoProxy"/&gt;<br/>
&nbsp;&nbsp;&lt;/property&gt;<br/></FONT>&nbsp;&lt;/bean&gt;<br/>

......</TD>
</TR>
</TBODY>
</TABLE>
<P>
工程的其他地方并某有修改，方便吧，当不想使用事务管理时，把userController注入的userDaoProxy改为注入userDaoImpl就行了。执行效果和编程式事物一样，不再多嘴了。</P>
<P>
还要说说TransactionAttributeSource接口，Spring为我们提供了它的一个实现org.springframework.transaction.interceptor.MatchAlwaysTransactionAttributeSource。如果我们对TransactionProxyFactoryBean注入它，则对每个方法都执行事务管理，默认传播行为为PROPAGATION_REQUIRED，隔离等级为ISOLATION_DEFAULT。如下：</P>
<TABLE STYLE="BORDER-RIGHT: #999 1px solid; BORDER-TOP: #999 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #999 1px solid; WIDTH: 101.49%; BORDER-BOTTOM: #999 1px solid; HEIGHT: 23px" ALIGN="center">
<TBODY>
<TR>
<TD>
<P>......<br/>
&nbsp;<FONT COLOR="#000066">&lt;bean
id="transactionAttributeSource"<br/>
&nbsp;&nbsp;class="org.springframework.transaction.interceptor.MatchAlwaysTransactionAttributeSource"/&gt;<br/>
</FONT>&nbsp;<br/>
&nbsp;&lt;bean id="userDaoProxy"<br/>
&nbsp;&nbsp;class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"&gt;<br/>

&nbsp;&nbsp;&lt;property
name="proxyInterfaces"&gt;<br/>
&nbsp;&nbsp;&nbsp;&lt;list&gt;<br/>

&nbsp;&nbsp;&nbsp;&nbsp;&lt;value&gt;com.yangsq.dao.UserDao&lt;/value&gt;<br/>

&nbsp;&nbsp;&nbsp;&lt;/list&gt;<br/>

&nbsp;&nbsp;&lt;/property&gt;<br/>
&nbsp;&nbsp;&lt;property
name="target"&gt;<br/>
&nbsp;&nbsp;&nbsp;&lt;ref
bean="userDaoImpl"/&gt;<br/>
&nbsp;&nbsp;&lt;/property&gt;<br/>
&nbsp;&nbsp;&lt;property
name="transactionManager"&gt;<br/>
&nbsp;&nbsp;&nbsp;&lt;ref
bean="transactionManager"/&gt;<br/>
&nbsp;&nbsp;&lt;/property&gt;<br/>
&nbsp;&nbsp;<FONT COLOR="#009900">&lt;property name="transactionAttributeSource"&gt;<br/>
&nbsp;&nbsp;&nbsp;&lt;ref
bean="transactionAttributeSource"&gt;<br/>
&nbsp;&nbsp;&lt;/property&gt;<br/></FONT>&nbsp;&lt;/bean&gt;<br/>

......</P>
</TD>
</TR>
</TBODY>
</TABLE>
]]></description>
            <author>小杨</author>
            <category>开源应用</category>
            <comments>http://blog.sina.com.cn/s/blog_4b86f2ee0100078a.html#comment</comments>
            <pubDate>Wed, 14 Feb 2007 01:40:19 GMT+8</pubDate>
            <guid>http://blog.sina.com.cn/s/blog_4b86f2ee0100078a.html</guid>
        </item>
        <item>
            <title>Spring学习笔记（十八）----Spring编程式事务</title>
            <link>http://blog.sina.com.cn/s/blog_4b86f2ee01000784.html</link>
            <description><![CDATA[<DIV>&nbsp;&nbsp;&nbsp;
Spring的编程式事物管理直接继承自PlatformTransactionManager接口。根据不同的Dao技术，Spring提供了不同的PlatformTransactionManager实现类，这里只介绍针对Hibernate的实现方式，如下：</DIV>
<DIV><A HREF="http://album.sina.com.cn/pic/4b86f2ee02000i89" TARGET="_blank"><IMG SRC="http://album.sina.com.cn/pic/4b86f2ee02000i89" BORDER="0"></A></DIV>
<DIV>
在应用中我们可以直接使用HibernateTransactionManager的实例进行事物管理<STRONG>。/WEB-INF/mvc-config.xml</STRONG>和上个例子没有什么不同，下面是<STRONG>/WEB-INF/data-access.xml</STRONG>:</DIV>
<DIV>
<TABLE STYLE="BORDER-RIGHT: #999 1px solid; BORDER-TOP: #999 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #999 1px solid; WIDTH: 96.95%; BORDER-BOTTOM: #999 1px solid; HEIGHT: 23px" ALIGN="center">
<TBODY>
<TR>
<TD>
<P>&lt;?xml version="1.0" encoding="UTF-8"?&gt;<br/>
&lt;!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "<A HREF="http://www.springframework.org/dtd/spring-beans.dtd">http://www.springframework.org/dtd/spring-beans.dtd</A>"&gt;</P>
<P>&lt;beans&gt;<br/>
&nbsp;&lt;!--PropertyPlaceholderConfigurer--&gt;<br/>
&nbsp;&lt;bean id="propertyConfigurer"<br/>
&nbsp;&nbsp;class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"&gt;<br/>

&nbsp;&nbsp;&lt;property
name="locations"&gt;<br/>
&nbsp;&nbsp;&nbsp;&lt;list&gt;<br/>

&nbsp;&nbsp;&nbsp;&nbsp;&lt;value&gt;WEB-INF/props/jdbc.properties&lt;/value&gt;<br/>

&nbsp;&nbsp;&nbsp;&lt;/list&gt;<br/>

&nbsp;&nbsp;&lt;/property&gt;<br/>
&nbsp;&lt;/bean&gt;<br/>
&nbsp;<br/>
&nbsp;&lt;!--org.apache.commons.dbcp.BasicDataSource
has the connect pool--&gt;<br/>
&nbsp;&lt;bean id="dataSource"<br/>
&nbsp;&nbsp;class="org.apache.commons.dbcp.BasicDataSource"<br/>

&nbsp;&nbsp;destroy-method="close"&gt;<br/>

&nbsp;&nbsp;&lt;property
name="driverClassName"&gt;<br/>
&nbsp;&nbsp;&nbsp;&lt;value&gt;${jdbc.driverClassName}&lt;/value&gt;<br/>

&nbsp;&nbsp;&lt;/property&gt;<br/>
&nbsp;&nbsp;&lt;property
name="url"&gt;<br/>
&nbsp;&nbsp;&nbsp;&lt;value&gt;${jdbc.url}&lt;/value&gt;<br/>

&nbsp;&nbsp;&lt;/property&gt;<br/>
&nbsp;&nbsp;&lt;property
name="username"&gt;<br/>
&nbsp;&nbsp;&nbsp;&lt;value&gt;${jdbc.username}&lt;/value&gt;<br/>

&nbsp;&nbsp;&lt;/property&gt;<br/>
&nbsp;&nbsp;&lt;property
name="password"&gt;<br/>
&nbsp;&nbsp;&nbsp;&lt;value&gt;${jdbc.password}&lt;/value&gt;<br/>

&nbsp;&nbsp;&lt;/property&gt;<br/>
&nbsp;&lt;/bean&gt;</P>
<P>&nbsp;&lt;!--sessionFactory--&gt;<br/>
&nbsp;&lt;bean id="sessionFactory"<br/>
&nbsp;&nbsp;class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"<br/>

&nbsp;&nbsp;destroy-method="close"&gt;<br/>

&nbsp;&nbsp;&lt;property
name="dataSource"&gt;<br/>
&nbsp;&nbsp;&nbsp;&lt;ref
bean="dataSource" /&gt;<br/>
&nbsp;&nbsp;&lt;/property&gt;<br/>
&nbsp;&nbsp;&lt;property
name="mappingResources"&gt;<br/>
&nbsp;&nbsp;&nbsp;&lt;list&gt;<br/>

&nbsp;&nbsp;&nbsp;&nbsp;&lt;value&gt;com/yangsq/orm/user.hbm.xml&lt;/value&gt;<br/>

&nbsp;&nbsp;&nbsp;&lt;/list&gt;<br/>

&nbsp;&nbsp;&lt;/property&gt;<br/>
&nbsp;&nbsp;&lt;property
name="hibernateProperties"&gt;<br/>
&nbsp;&nbsp;&nbsp;&lt;props&gt;<br/>

&nbsp;&nbsp;&nbsp;&nbsp;&lt;prop
key="hibernate.dialect"&gt;<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;${hibernate.dialect}<br/>

&nbsp;&nbsp;&nbsp;&nbsp;&lt;/prop&gt;<br/>

&nbsp;&nbsp;&nbsp;&lt;/props&gt;<br/>

&nbsp;&nbsp;&lt;/property&gt;<br/>
&nbsp;&lt;/bean&gt;</P>
<P>&nbsp;<FONT COLOR="#009900">&lt;!--HibernateTransactionManager--&gt;<br/>
&nbsp;&lt;bean id="transactionManager"<br/>
&nbsp;&nbsp;class="org.springframework.orm.hibernate3.HibernateTransactionManager"&gt;<br/>

&nbsp;&nbsp;&lt;property
name="sessionFactory"&gt;<br/>
&nbsp;&nbsp;&nbsp;&lt;ref
bean="sessionFactory"/&gt;<br/>
&nbsp;&nbsp;&lt;/property&gt;<br/>
&nbsp;&lt;/bean&gt;</FONT><br/>
&nbsp;<br/>
&nbsp;&lt;!--userDao--&gt;<br/>
&nbsp;&lt;bean id="userDao"<br/>
&nbsp;&nbsp;class="com.yangsq.dao.impl.UserDaoImpl"<br/>

&nbsp;&nbsp;init-method="createTransactionDefinition"&gt;<br/>

&nbsp;&nbsp;&lt;property
name="sessionFactory"&gt;<br/>
&nbsp;&nbsp;&nbsp;&lt;ref
local="sessionFactory" /&gt;<br/>
&nbsp;&nbsp;&lt;/property&gt;<br/>
&nbsp;&nbsp;<FONT COLOR="#009900">&lt;property name="transactionManager"&gt;<br/>
&nbsp;&nbsp;&nbsp;&lt;ref
local="transactionManager"/&gt;<br/>
&nbsp;&nbsp;&lt;/property&gt;<br/></FONT>&nbsp;&lt;/bean&gt;<br/>

&nbsp;<br/>
&lt;/beans&gt;</P>
</TD>
</TR>
</TBODY>
</TABLE>
</DIV>
<DIV>
定义了一个HibernateTransactionManager的实例，并把它注入到我们要进行事物管理的UserDaoImpl类。在bean的管理周期中，bean的初始化顺序是生成bean实例（构造方法），执行setter注入，执行init-method属性定义的函数（简化版，呵呵：）。我们针对以前的UserDaoImpl类进行修改，增加事物管理代码部分，如下：</DIV>
<DIV>
<TABLE STYLE="BORDER-RIGHT: #999 1px solid; BORDER-TOP: #999 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #999 1px solid; WIDTH: 97.36%; BORDER-BOTTOM: #999 1px solid; HEIGHT: 22px" ALIGN="center">
<TBODY>
<TR>
<TD>
<P>import java.util.List;</P>
<P>import org.springframework.dao.DataAccessException;<br/>
import
org.springframework.orm.hibernate3.HibernateTransactionManager;<br/>

import
org.springframework.orm.hibernate3.support.HibernateDaoSupport;<br/>

import
org.springframework.transaction.support.DefaultTransactionDefinition;<br/>

import org.springframework.transaction.TransactionDefinition;<br/>
import org.springframework.transaction.TransactionStatus;</P>
<P>import com.yangsq.dao.UserDao;<br/>
import com.yangsq.domain.User;</P>
<P>public class UserDaoImpl extends HibernateDaoSupport implements
UserDao {<br/>
&nbsp;private HibernateTransactionManager
transactionManager;<br/>
&nbsp;private DefaultTransactionDefinition def;<br/>
&nbsp;<br/>
&nbsp;public HibernateTransactionManager
getTransactionManager() {<br/>
&nbsp;&nbsp;return
transactionManager;<br/>
&nbsp;}<br/>
&nbsp;<br/>
&nbsp;public void
setTransactionManager(HibernateTransactionManager
transactionManager) {<br/>
&nbsp;&nbsp;this.transactionManager =
transactionManager;<br/>
&nbsp;}<br/>
&nbsp;<br/>
&nbsp;<FONT COLOR="#FF0000">public void
createTransactionDefinition(){<br/>
&nbsp;&nbsp;def = new
DefaultTransactionDefinition();<br/>
&nbsp;&nbsp;def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);<br/>

&nbsp;&nbsp;def.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);<br/>

&nbsp;}</FONT><br/>
&nbsp;<br/>
&nbsp;public void saveOrUpdate(User user){<br/>
&nbsp;&nbsp;<FONT COLOR="#009900">TransactionStatus status =
transactionManager.getTransaction(def);<br/></FONT>&nbsp;&nbsp;try{<br/>

&nbsp;&nbsp;&nbsp;<STRONG>this.getHibernateTemplate().saveOrUpdate(user);<br/>
</STRONG>&nbsp;&nbsp;}catch(DataAccessException
ex){<br/>
&nbsp;&nbsp;&nbsp;<FONT COLOR="#009900">transactionManager.rollback(status);</FONT><br/>
&nbsp;&nbsp;&nbsp;throw
ex;<br/>
&nbsp;&nbsp;}<br/>
&nbsp;&nbsp;<FONT COLOR="#009900">transactionManager.commit(status);<br/></FONT>&nbsp;}<br/>

&nbsp;......<br/>
}</P>
</TD>
</TR>
</TBODY>
</TABLE>
</DIV>
<P>&nbsp;&nbsp;&nbsp;
生成UserDaoImpl实例后，注入transactionManager方法，然后执行createTransactionDefinition，在这个函数里，定义了事物的一些参数，如传播行为，隔离级别等。下面是TransactionDefinition的继承图谱及主要参数：</P>
<P><A HREF="http://album.sina.com.cn/pic/4b86f2ee02000i8a" TARGET="_blank"><IMG SRC="http://album.sina.com.cn/pic/4b86f2ee02000i8a" BORDER="0"></A></P>
<P>&nbsp;&nbsp;&nbsp;
在调用transactionManager的getTransaction方法后，返回一个TansactionStatus实例，TansactionStatus接口定义了事物执行时的状态。执行saveOrUpdate溢出后，将回滚（rollback），否则提交（commit）。</P>
<P>&nbsp;&nbsp;
在实际断点执行时（断点在saveOrUpdate处），执行完saveOrUpdate，察看数据库后，数据并没有插入，只有当执行完transactionManager.commit后，数据才真正插入到数据库中。</P>
<P>&nbsp;&nbsp;
工程的其他部分与原来的例子一模一样，执行效果也不变。</P>
]]></description>
            <author>小杨</author>
            <category>开源应用</category>
            <comments>http://blog.sina.com.cn/s/blog_4b86f2ee01000784.html#comment</comments>
            <pubDate>Tue, 13 Feb 2007 09:33:48 GMT+8</pubDate>
            <guid>http://blog.sina.com.cn/s/blog_4b86f2ee01000784.html</guid>
        </item>
        <item>
            <title>Spring学习笔记（十七）----Spring中的事务</title>
            <link>http://blog.sina.com.cn/s/blog_4b86f2ee0100077z.html</link>
            <description><![CDATA[<DIV>&nbsp;&nbsp;&nbsp;
事务就是一组数据库操作，但这组操作是具有原子性的（atomic）。所谓原子操作，即这组数据库操作要么就都执行成功，要么就一个也没执行。当有一些操作成功了（“成功”指数据库里的数据已更新或提交），但中间出现异常，后边的也就无法执行时，事务要回滚，即恢复到什么也没执行以前的状态。</DIV>
<DIV>&nbsp;&nbsp;&nbsp;
举个比较常见的例子，一位顾客要进行银行转帐，把100元从A帐户转到B帐户，一般的过程是把A帐户的总额减去100，B帐户的总额加上100（只考虑最简单情况）。当在A帐户总额减去100后出现异常情况，使得无法再对B帐户进行操作。如果没使用事务，那么这位顾客就损失了100元，但如果使用了事务，那么出现异常后，事务回滚，使得A帐户总额恢复到减去100以前的金额。虽然使用事务并没有达到顾客转帐的目的，但避免了出现异常情况时顾客的损失。</DIV>
<DIV>
<TABLE STYLE="BORDER-RIGHT: #999 1px solid; BORDER-TOP: #999 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #999 1px solid; WIDTH: 92%; BORDER-BOTTOM: #999 1px solid; HEIGHT: 23px" ALIGN="center">
<TBODY>
<TR>
<TD><FONT COLOR="#660099"><STRONG>事务就相当于我们做一件事情（这件事由许多小的步骤组成），要么我们就把这件事情完完整整地做成功，要么我们就一点也不要做。不能做到半截撂摊子不干了。</STRONG></FONT></TD>
</TR>
</TBODY>
</TABLE>
</DIV>
<P>&nbsp;
回到Spring来，Spring的Dao框架对事务提供了强大的支持。它包括有两种事物管理，即：</P>
<UL>
<LI>编程式事物管理（programmatic tansaction management）</LI>
<LI>声明式事物管理（Declarative tansaction management）</LI>
</UL>
<P>&nbsp;&nbsp;
所谓编程式事物管理，就是把事物管理以代码的形式编写到你的应用中要使用事物管理的地方，灵活性较强。而声明式事物管理是以配置文件的形式在xml文件中定义，好处是不具有代码入侵性，当不需要事物管理时，可以直接修改配置文件，而不用修改代码。以后会介绍这两种事物管理。</P>
<P>&nbsp;&nbsp;
在Spring中，主要涉及以下几种事物属性：</P>
<P><FONT COLOR="#FF0000">1.&nbsp; 传播行为（propagation
behavior）</FONT></P>
<P>&nbsp;&nbsp;
它是对事物的起始，暂停，终止时刻的定义，主要有以下几种</P>
<TABLE STYLE="WIDTH: 455px; HEIGHT: 187px" CELLSPACING="1" CELLPADDING="1" WIDTH="455" BORDER="1">
<TBODY>
<TR>
<TD>&nbsp;参数</TD>
<TD>&nbsp;含义</TD>
</TR>
<TR>
<TD>&nbsp;PROPAGATION_REQUIRED</TD>
<TD>
&nbsp;如果存在事物的话，就继续这个事物，如果不存在，新建一个事物。</TD>
</TR>
<TR>
<TD>&nbsp;PROPAGATION_SUPPORTS</TD>
<TD>
&nbsp;如果存在事物的话，就继续这个事物，如果不存在，就以非事务的方式进行。</TD>
</TR>
<TR>
<TD>&nbsp;PROPAGATION_MANDATORY</TD>
<TD>&nbsp;必须在现存事物中执行，否则抛出异常。</TD>
</TR>
<TR>
<TD>&nbsp;PROPAGATION_REQUIRES_NEW</TD>
<TD>
&nbsp;建立一个新事物，如果现存一个事物，则暂停它。</TD>
</TR>
<TR>
<TD>&nbsp;PROPAGATION_NOT_SUPPORTED</TD>
<TD>&nbsp;不再事务中执行，如果现存事物，则暂停它。</TD>
</TR>
<TR>
<TD>&nbsp;PROPAGATION_NEVER</TD>
<TD>
&nbsp;不再事务中执行，如果现存事物，则抛出异常。</TD>
</TR>
<TR>
<TD>&nbsp;PROPAGATION_NESTED</TD>
<TD>
&nbsp;在一个嵌入的事物中执行，否则同PROPAGATION_REQUIRED</TD>
</TR>
</TBODY>
</TABLE>
<P>
上述参数是在org.springframework.transaction.TransactionDefinition接口中定义的（类型是public
static final，值从0到6）。上述参数中最常用的是<FONT COLOR="#0000FF"><STRONG>PROPAGATION_REQUIRED</STRONG></FONT>。</P>
<P><FONT COLOR="#FF0000">2. 隔离等级（isolation level）</FONT></P>
<P>&nbsp;&nbsp;
在一个应用应用程序中，可能有多个事务在运行，这时就会产生一些问题。</P>
<UL>
<LI><FONT COLOR="#660099">dirty read</FONT></LI>
</UL>
<P>
&nbsp;&nbsp;&nbsp;&nbsp;一个事物更新了数据库中的某些数据，另一个事物读取了这些数据，这时前一个事物由于某些原因回滚了，那么第二个事物读取的数据就是“脏数据”。</P>
<UL>
<LI><FONT COLOR="#660099">non-repeatable read</FONT></LI>
</UL>
<P>&nbsp;&nbsp;&nbsp;
一个事物需要两次查询同一数据，但两次查询中间可能有另外一个事物更改了这个数据，导致前一个事物两次读出的数据不一致。</P>
<UL>
<LI><FONT COLOR="#660099">phantom read</FONT></LI>
</UL>
<P>&nbsp;&nbsp;&nbsp;
一个事物两次查询同一个表，但两次查询中间可能有另外一个事物又向这个表中插入了一些新数据，导致前一个事物的两次查询不一致。</P>
<P>&nbsp;&nbsp;&nbsp;
为了解决以上问题，Spring的事物管理定义了一些隔离级别，所谓“隔离”，即对数据的锁定。</P>
<TABLE STYLE="WIDTH: 475px; HEIGHT: 141px" CELLSPACING="1" CELLPADDING="1" WIDTH="475" BORDER="1">
<TBODY>
<TR>
<TD>&nbsp;参数</TD>
<TD>&nbsp;含义</TD>
</TR>
<TR>
<TD>&nbsp;ISOLATION_DEFAULT</TD>
<TD>&nbsp;使用数据库默认的隔离级别</TD>
</TR>
<TR>
<TD>&nbsp;ISOLATION_READ_UNCOMMITTED</TD>
<TD>
&nbsp;容许事物读取其他并行事物还未提交的数据。这种级别会出现上面三种情况。</TD>
</TR>
<TR>
<TD>&nbsp;ISOLATION_READ_COMMITTED</TD>
<TD>
&nbsp;容许事物读取其他并行事物已经提交（commit）的数据，可防止dirty
read</TD>
</TR>
<TR>
<TD>&nbsp;ISOLATION_REPEATABLE_READ</TD>
<TD>
<P>&nbsp;这种级别会可以防止上面三种情况发生。</P>
</TD>
</TR>
<TR>
<TD>&nbsp;ISOLATION_SERIALIZABLE</TD>
<TD>
&nbsp;使用事物锁，锁定相应数据，可以防止上面三种情况发生，但效率比较低。</TD>
</TR>
</TBODY>
</TABLE>
<P>
上述参数也是在org.springframework.transaction.TransactionDefinition接口中定义的（类型是public
static
final，值为-1，1，2，4，8）。上述参数中最常用的是<STRONG><FONT COLOR="#0000FF">ISOLATION_DEFAULT</FONT></STRONG>。</P>
<P><FONT COLOR="#FF0000">3.&nbsp; read only</FONT></P>
<P>&nbsp;&nbsp;&nbsp;
应用这项属性时，底层的数据库可以对读取进行最优化，但要配合PROPAGATION_REQUIRED，PROPAGATION_REQUIRES_NEW或PROPAGATION_NESTED使用，即只能在事物中使用。</P>
<P><FONT COLOR="#FF0000">4.&nbsp; timeout</FONT></P>
<P>&nbsp;&nbsp;&nbsp;
在多事物并行情况下，为了保证正确性，有些事物的操作会有延迟，甚至死锁。设置事物超时时间，可以避免事物的长时间等待。设置事物超时时间也要配合PROPAGATION_REQUIRED，PROPAGATION_REQUIRES_NEW或PROPAGATION_NESTED使用。</P>
<P>
以上的四种属性及其相应方法都定义在org.springframework.transaction.TransactionDefinition接口及其实现类（如org.springframework.transaction.support.DefaultTransactionDefinition）里。</P>
]]></description>
            <author>小杨</author>
            <category>开源应用</category>
            <comments>http://blog.sina.com.cn/s/blog_4b86f2ee0100077z.html#comment</comments>
            <pubDate>Tue, 13 Feb 2007 07:38:59 GMT+8</pubDate>
            <guid>http://blog.sina.com.cn/s/blog_4b86f2ee0100077z.html</guid>
        </item>
        <item>
            <title>Eclipse的自动编译问题</title>
            <link>http://blog.sina.com.cn/s/blog_4b86f2ee0100077k.html</link>
            <description><![CDATA[&nbsp;
eclipse具有自动编译的功能，即当创建一个类时，保存后（ctrl-s），它会自动帮你编译成class文件，放到相应的工程目录下（如果是web，它会自动把class文件放到webroot/web-inf/classes下）。<br/>

&nbsp;
但是有时会遇到这种情况，在编辑完一个类保存后，去找相应的.class文件却怎么也找不到。这说明eclipse并没有帮你编译这个类（.java文件），这种情况确实很烦人。<br/>

&nbsp; 产生这种问题的原因我遇到的有两种：<br/>
<br/>
<OL>
<LI>
你所编辑的这个类，即.java文件有错误，eclipse会提醒你错误的位置，改掉就行了</LI>
<LI>
另一种可能是你的classpath中有重复的jar包，只要找出，把它去掉。</LI>
</OL>
上面是我遇到过的两种情况及解决办法。以后遇到再补充吧<IMG SRC="http://blog.sina.com.cn/images/face/001.gif"><br/>
]]></description>
            <author>小杨</author>
            <category>java基础技术</category>
            <comments>http://blog.sina.com.cn/s/blog_4b86f2ee0100077k.html#comment</comments>
            <pubDate>Mon, 12 Feb 2007 09:11:39 GMT+8</pubDate>
            <guid>http://blog.sina.com.cn/s/blog_4b86f2ee0100077k.html</guid>
        </item>
    </channel>
</rss>
