﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-心情小站-随笔分类-工作日志</title><link>http://www.blogjava.net/RongHao/category/4425.html</link><description>勤学、勤思</description><language>zh-cn</language><lastBuildDate>Fri, 14 Dec 2012 08:19:34 GMT</lastBuildDate><pubDate>Fri, 14 Dec 2012 08:19:34 GMT</pubDate><ttl>60</ttl><item><title>少年Pi的奇幻漂流-我们的后台自动化发布方案</title><link>http://www.blogjava.net/RongHao/archive/2012/12/14/393002.html</link><dc:creator>ronghao</dc:creator><author>ronghao</author><pubDate>Fri, 14 Dec 2012 07:54:00 GMT</pubDate><guid>http://www.blogjava.net/RongHao/archive/2012/12/14/393002.html</guid><wfw:comment>http://www.blogjava.net/RongHao/comments/393002.html</wfw:comment><comments>http://www.blogjava.net/RongHao/archive/2012/12/14/393002.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/RongHao/comments/commentRss/393002.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/RongHao/services/trackbacks/393002.html</trackback:ping><description><![CDATA[<p><strong style="text-indent: -22.5pt;">一、</strong><strong style="text-indent: -22.5pt;"><span style="font-family:宋体;">我们要解决的问题</span></strong></p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">无论是什么样的解决方案，一定要牢记我们要解决的问题是什么，切不能将解决方案当做问题本身。具体到过程改进，不管是何种方式的改进，它们所要解决的问题永远只有一个：<strong>缩短从产品想法到可用软件之间的时间周期</strong>。自动化发布正是如此，如果软件发布只做一次，我们说根本不需要自动化，但如果三次以上，那么软件开发的黄金法则</span>DRY<span style="font-family:宋体;">就必须遵守，让时间真正用到开发当中去。</span></p>  <p style="margin-left:22.5pt;text-indent:0cm;">&nbsp;</p>  <p style="margin-left:22.5pt;text-indent:-22.5pt;"><strong>二、</strong><strong><span style="font-family:宋体;">与发布相关的问题</span></strong></p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">所谓自动化只不过是将原先手工做的工作谦让给机器做，所以自动化之前一定要先清楚与发布相关的问题有哪些，即使不自动化，这些工作也一个也不能少：</span></p>  <ol style="margin-top:0cm" start="1" type="1">  <li><span style="font-family:宋体;">应用程序如何打包？</span>      <span style="font-family:宋体;">发布包能否追踪到</span>SVN<span style="font-family:宋体;">版本号？</span></li>  <li><span style="font-family:宋体;">对目标机器环境有什么样的要求？</span>      </li>  <li><span style="font-family:宋体;">配置信息是否需要根据目标机器信息做出调整？</span>      </li>  <li><span style="font-family:宋体;">应用程序如何安装和启动？</span></li>  <li><span style="font-family:宋体;">应用程序启动后如何切流量？</span>      </li>  <li><span style="font-family:宋体;">应用程序如何升级？</span>      <span style="font-family:宋体;">旧版本程序数据如何迁移？</span></li>  <li><span style="font-family:宋体;">升级过程中和结束后如何切流量？</span>      </li>  <li><span style="font-family:宋体;">应用程序如何卸载？</span>      </li> </ol>  <p><strong>&nbsp;</strong></p>  <p style="margin-left:22.5pt;text-indent:-22.5pt;"><strong>三、</strong><strong><span style="font-family:宋体;">我们的方案</span></strong></p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">李安的少年</span>Pi<span style="font-family:宋体;">正在狂刷票房，我们的自动化发布方案也要跟上潮流：</span><span style="color:red">P</span>uppet+C<span style="color:red">I</span><span style="font-family:宋体;">我们的少年</span>Pi<span style="font-family:宋体;">。</span></p>  <p style="margin-left:42.0pt;text-indent:-21.0pt;"><span style="font-family:Wingdings;">&#216;&nbsp;</span><span style="font-family:宋体;">使用</span>CI<span style="font-family:宋体;">自动化打包，追踪每个发布包的</span>SVN<span style="font-family:宋体;">版本；</span></p>  <p style="margin-left:42.0pt;text-indent:-21.0pt;"><span style="font-family:Wingdings;">&#216;&nbsp;</span><span style="font-family:宋体;">使用</span>Puppet<span style="font-family:宋体;">管理发布包、目标机器环境、应用程序配置信息以及应用程序线上生命周期；</span></p>  <p style="margin-left:42.0pt;text-indent:-21.0pt;"><span style="font-family:Wingdings;">&#216;&nbsp;</span><span style="font-family:宋体;">使用伽利略系统提供应用程序的命名服务和进行流量切换。</span></p>  <p style="margin-left:21.0pt"><span style="font-family:宋体;">现在应用程序的发布需要两步：</span>CI<span style="font-family:宋体;">一键打包、</span>puppet<span style="font-family:宋体;">指定应用程序版本</span>SVN<span style="font-family:宋体;">提交。</span></p>  <p style="margin-left:21.0pt"><img src="http://www.blogjava.net/images/blogjava_net/ronghao/CI打包1.png" border="0" alt="" width="472" height="180" /><br /><br /></p>  <p style="margin-left:21.0pt"><img src="http://www.blogjava.net/images/blogjava_net/ronghao/puppet-svn1.png" border="0" alt="" width="651" height="207" /><br /></p>  <p><strong>&nbsp;</strong></p>  <p style="margin-left:22.5pt;text-indent:-22.5pt;"><strong>四、</strong><strong><span style="font-family:宋体;">具体方案</span></strong></p>  <p style="margin-left:22.5pt;text-indent:0cm;"><span style="font-family:宋体;">具体方案也就是如何解决与发布相关八个问题的过程。</span></p>  <p style="margin-left:43.5pt;text-indent:-21.0pt;"><strong>1.<span style="font-weight: normal; font-size: 7pt; line-height: normal; font-family: 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></strong><strong><span style="font-family:宋体;">如何安装、升级和卸载应用程序</span></strong></p>  <p style="margin-left:22.5pt;text-indent:19.5pt"><span style="font-family:宋体;">我们使用操作系统原生包管理系统来安装、升级和卸载应用程序，我们的应用程序打出</span>RPM<span style="font-family:宋体;">二进制包。免安装，所有机器自带，绿色的，有机的。</span></p>  <p style="margin-left:21.0pt;text-indent:21.0pt"><span style="font-family:宋体;">打包：</span>rpm -ba ./team_member-1.spec</p>  <p style="margin-left:21.0pt;text-indent:21.0pt"><span style="font-family:宋体;">安装：</span>rpm &#8211;ivh team_ member-2.0.1-48.x86_64.rpm</p>  <p style="margin-left:21.0pt;text-indent:21.0pt"><span style="font-family:宋体;">升级：</span>rpm &#8211;U team_ member-2.0.1-49.x86_64.rpm</p>  <p style="margin-left:22.5pt;text-indent:19.5pt"><span style="font-family:宋体;">卸载：</span>rpm &#8211;e team_ member-2.0.1-48.x86_64.rpm</p>  <p style="margin-left:22.5pt;text-indent:19.5pt"><span style="font-family:宋体;">程序升级前要停旧版本服务怎么办？旧版本数据要做处理怎么办？</span>RPM<span style="font-family:宋体;">已经帮我们料理好这一切，只要写出</span>spec<span style="font-family:宋体;">文件，剩下的交给我们。尽情的插入吧：</span></p>  <p align="left" style="margin-left: 22.5pt; text-indent: 19.5pt;"><img src="http://www.blogjava.net/images/blogjava_net/ronghao/spec文件.png" border="0" alt="" width="612" height="412" /><br /></p>  <p style="margin-left:22.5pt;text-indent:19.5pt">&nbsp;</p>  <p style="margin-left:43.5pt;text-indent:-21.0pt;"><strong>2.<span style="font-weight: normal; font-size: 7pt; line-height: normal; font-family: 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></strong><strong><span style="font-family:宋体;">如何管理目标机器环境和应用配置信息</span></strong></p>  <p style="margin-left:22.5pt;text-indent:19.5pt"><span style="font-family:宋体;">应用程序已经打好</span>rpm<span style="font-family:宋体;">包了，但这还不够，应用程序发布到哪台机器上？应用程序对目标机器有什么要求？发布时需要修改哪些配置和参数？实际发布如何执行，难道需要登陆到每台目标机器运行</span>rpm<span style="font-family:宋体;">命令吗？</span></p>  <p style="margin-left:21.0pt;text-indent:21.0pt"><span style="font-family:宋体;">我们使用</span>Puppet<span style="font-family:宋体;">来搞定这一切，</span>Puppet<span style="font-family:宋体;">是现在应用第一的</span>devops<span style="font-family:宋体;">工具，它通过</span>master/agent<span style="font-family:宋体;">的工作模式管理机器。我们通过声明来控制我们的机器达到目标状态。同时，所有</span>puppet<span style="font-family:宋体;">文件全部在</span>SVN<span style="font-family:宋体;">里，所有对机器的修改全部</span>codereview<span style="font-family:宋体;">和可审计。</span></p>  <p style="margin-left:22.5pt;text-indent:19.5pt">&nbsp;</p>  <p style="margin-left:22.5pt;text-indent:19.5pt"><img src="http://www.blogjava.net/images/blogjava_net/ronghao/puppet结构.png" border="0" alt="" width="351" height="178" /><br /></p>  <p style="margin-left:21.0pt;text-indent:21.0pt"><span style="font-family:宋体;">如何管理应用程序发布到哪台机器上？在回答这个问题前我们必须将应用程序在线上的生命周期再进行一次封装。</span></p>  <p style="margin-left:21.0pt;text-indent:21.0pt"><img src="http://www.blogjava.net/images/blogjava_net/ronghao/应用生命周期.png" border="0" alt="" width="501" height="307" /><br /></p>  <p style="margin-left:21.0pt;text-indent:21.0pt"><span style="font-family:宋体;">应用程序</span>TeamMember<span style="font-family:宋体;">被我们封装成一个</span>puppet module<span style="font-family:宋体;">，配置文件和参数被封装在对应</span>templates<span style="font-family:宋体;">和</span>files<span style="font-family:宋体;">里，每次发布前都要修改配置文件和传递不同的参数？</span>out<span style="font-family:宋体;">了吧，</span>puppet<span style="font-family:宋体;">帮你传参搞定</span>:</p>  <p style="margin-left:21.0pt;text-indent:21.0pt"><img src="http://www.blogjava.net/images/blogjava_net/ronghao/puppet代码结构.png" border="0" alt="" width="737" height="371" /><br /></p>  <p style="margin-left:21.0pt;text-indent:21.0pt">Teammember.conf<span style="font-family:宋体;">文件内容：</span></p>  <p style="margin-left:21.0pt;text-indent:21.0pt"><img src="http://www.blogjava.net/images/blogjava_net/ronghao/puppet管理配置文件.png" border="0" alt="" width="494" height="353" /><br /></p>  <p style="margin-left:21.0pt;text-indent:21.0pt"><span style="font-family:宋体;">封装完成后的效果是这样的：</span></p>  <p style="margin-left:21.0pt;text-indent:21.0pt"><img src="http://www.blogjava.net/images/blogjava_net/ronghao/puppet封装应用程序.png" border="0" alt="" width="623" height="342" /><br /></p>  <p style="margin-left:21.0pt;text-indent:21.0pt"><span style="font-family:宋体;">最后在管理部署的</span>site.pp<span style="font-family:宋体;">文件里声明一下，应用程序</span>TeamMember<span style="font-family:宋体;">的</span>2146<span style="font-family:宋体;">版本就被自动部署到</span>10.128.34.141.test.back.shequ<span style="font-family:宋体;">这台机器上了，我们后续的工作也就是维护这个</span>site.pp<span style="font-family:宋体;">文件了，所有应用程序的部署信息都在</span>SVN<span style="font-family:宋体;">被集中管理起来：</span></p>  <p style="margin-left:21.0pt;text-indent:21.0pt"><img src="http://www.blogjava.net/images/blogjava_net/ronghao/puppet管理部署.png" border="0" alt="" width="687" height="258" /><br /></p>  <p style="margin-left:21.0pt;text-indent:21.0pt"><span style="font-family:宋体;">登陆到每台目标机器运行</span>rpm<span style="font-family:宋体;">命令？</span>No!<span style="font-family:宋体;">现在</span>TeamMember<span style="font-family:宋体;">已经被封装，我们修改完毕</span>site.pp<span style="font-family:宋体;">并提交后，</span>puppet<span style="font-family:宋体;">就自动执行命令了，要不怎么说是自动化呢。（现在</span>puppet<span style="font-family:宋体;">默认在</span>agent<span style="font-family:宋体;">每半小时同步一次，但同时支持马上触发执行）。</span></p>  <p style="margin-left:21.0pt;text-indent:21.0pt">&nbsp;</p>  <p style="margin-left:43.5pt;text-indent:-21.0pt;"><strong>3.<span style="font-weight: normal; font-size: 7pt; line-height: normal; font-family: 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></strong><strong><span style="font-family:宋体;">如何追踪每次发布的</span>SVN</strong><strong><span style="font-family:宋体;">版本号</span></strong></p>  <p style="margin-left:43.5pt;text-indent:0cm;"><span style="font-family:宋体;">我们使用</span>CI<span style="font-family:宋体;">进行应用程序的打包，将</span>build<span style="font-family:宋体;">号作为包命名的一部分：</span></p>  <p style="margin-left:43.5pt;text-indent:0cm;"><img src="http://www.blogjava.net/images/blogjava_net/ronghao/安装包格式.png" border="0" alt="" width="497" height="330" /><br /></p>  <p style="margin-left:43.5pt;text-indent:0cm;">&nbsp;</p>  <p style="margin-left:43.5pt;text-indent:-21.0pt;"><strong>4.<span style="font-weight: normal; font-size: 7pt; line-height: normal; font-family: 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></strong><strong><span style="font-family:宋体;">如何在发布过程中切换流量</span></strong></p>  <p style="margin-left:43.5pt;text-indent:0cm;"><span style="font-family:宋体;">这是另外一个很大的话题，参见伽利略计划。</span></p>  <p style="margin-left:22.5pt;text-indent:0cm;"><strong>&nbsp;</strong></p>  <p style="margin-left:22.5pt;text-indent:-22.5pt;"><strong>五、</strong><strong><span style="font-family:宋体;">下一步工作</span></strong></p>  <p style="text-indent:21.0pt"><span style="text-indent: 21pt; font-family: 宋体;">使用</span><span style="text-indent: 21pt;">CI</span><span style="text-indent: 21pt; font-family: 宋体;">将环境的自动化部署与自动化测试串联起来，搭建起整个研发流程自动化平台：</span></p>  <p style="text-indent:21.0pt"><img src="http://www.blogjava.net/images/blogjava_net/ronghao/持续交付.png" border="0" alt="" width="651" height="464" /><br /></p>  <p style="margin-left:22.5pt;text-indent:0cm;"><strong>&nbsp;</strong></p>  <p style="margin-left:22.5pt;text-indent:-22.5pt;"><strong>六、</strong><strong><span style="font-family:宋体;">小结</span></strong></p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">没有银弹，自动化所做的只是将之前手工工作交给计算机完成，需要做的工作一个都不能少，此外，我们还要多做一些封装或脚本工作，但是，当我们需要重复做这些事情的时候，价值就出现了。我们的目标永远是缩短从产品想法到可用软件之间的时间周期。让时间真正用到开发当中去。</span></p><img src ="http://www.blogjava.net/RongHao/aggbug/393002.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/RongHao/" target="_blank">ronghao</a> 2012-12-14 15:54 <a href="http://www.blogjava.net/RongHao/archive/2012/12/14/393002.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>一个项目的自动化测试实践</title><link>http://www.blogjava.net/RongHao/archive/2010/06/16/323669.html</link><dc:creator>ronghao</dc:creator><author>ronghao</author><pubDate>Wed, 16 Jun 2010 13:13:00 GMT</pubDate><guid>http://www.blogjava.net/RongHao/archive/2010/06/16/323669.html</guid><wfw:comment>http://www.blogjava.net/RongHao/comments/323669.html</wfw:comment><comments>http://www.blogjava.net/RongHao/archive/2010/06/16/323669.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/RongHao/comments/commentRss/323669.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/RongHao/services/trackbacks/323669.html</trackback:ping><description><![CDATA[<p style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-family: Arial, sans-serif, Helvetica, Tahoma; font-size: 12px; line-height: 18px; ">项目上线，有时间总结一下当前的项目，对自己而言，一直是一个学习的过程。本篇总结我们的测试实践。本文分5部分，分别是：项目背景、系统架构与模块划分、我们的测试实践、自动化测试在项目中的价值与对自动化测试的进一步思考。<br />
<br />
<strong style="font-weight: bold; ">一、项目背景</strong><br />
所有对项目的介绍一定是从客户开始。<br />
<strong style="font-weight: bold; ">客户</strong>：我们的客户是一家全球领先的时尚内容提供商，通过遍布全球的员工，客户每天获取大量关于时装发布、产品设计、街边流行、城市热点等信息，这些信息的绝大部分以图片的形式上传到公司服务器，然后由专职编辑对这些图片进行整理和归类（打标签），最后再由设计人员根据这些信息书写分析报表。<br />
<strong style="font-weight: bold; ">关键内容</strong>：分类细致的海量高清图片和具有前瞻性的分析报表。<br />
<strong style="font-weight: bold; ">商业模式</strong>：网站，行业内用户订阅-付费。<br />
<strong style="font-weight: bold; ">客户面临的问题</strong>：同质化竞争、客户流失。<br />
<strong style="font-weight: bold; ">新系统的关键词</strong>：CMS、更精确的内容分类、更好的全文检索、更好的用户体验（更有表现力的内容展现）、更快的内容发布。<br />
<br />
<strong style="font-weight: bold; ">二、系统架构与模块划分</strong><br />
<strong style="font-weight: bold; ">1、REST的架构风格</strong><br />
系统采用了Sling作为WEB框架，JCR作为了底层内容存储框架。<br />
系统的特点：<br />
<strong style="font-weight: bold; ">URI唯一标识资源</strong><br />
通过URI能够直接映射到JCR节点，例如http://localhost:80/content/section/news.html能够映射到JCR里的/content/section/news节点<br />
<br />
<strong style="font-weight: bold; ">GET/POST/DELETE标准方法对资源进行操作</strong><br />
支持标准方法对资源的直接操作<br />
<br />
<strong style="font-weight: bold; ">资源的多重表述</strong><br />
同一资源可以存在多种表述形式，例如http://localhost:80/content/section/news.html展现网页，</p>
<p style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-family: Arial, sans-serif, Helvetica, Tahoma; font-size: 12px; line-height: 18px; ">http://localhost:80/content/section/news.json展现资源信息的JSON描述，<br />
http://localhost:80/content/section/news.pdf展现网页的PDF。<br />
<br />
<strong style="font-weight: bold; ">服务器端的无状态</strong><br />
通过JS获取当前用户信息并缓存在客户端。<br />
<br />
2、<strong style="font-weight: bold; ">系统分层</strong><br />
系统分为四层：JS、Servlet、Domain Model和JCR。<br />
因为JCR提供了一套节点模型，所以Domain Model是在节点模型上的行为增强，例如所有对图片节点的操作我们封装在Asset领域模型里。<br />
<img src="http://dl.javaeye.com/upload/attachment/260171/a677675f-f108-3d61-b522-1b435ec0e6f4.png" alt="系统分层" width="625" height="401" style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-width: 0px; border-style: initial; border-color: initial; " /><br />
<br />
<strong style="font-weight: bold; ">3、程序划分</strong><br />
程序分为两个大的模块：<strong style="font-weight: bold; ">Migration和Bundles</strong>。为什么叫Bundles？因为Sling使用了OSGI框架Felix。<br />
Migration负责导入客户的遗留数据到新系统。之前客户的CMS运行已有10多年的时间，积累有大量数据。主要是各种类型的报表和图片。<br />
Bundles实现系统功能。主要包括了定义报表模板、定义报表各种所见即所得的展现组件、实现对图片的管理、搜索（包括基于图片的可视化搜索）和其他七七八八。<br />
<br />
<strong style="font-weight: bold; ">三、测试实践<br />
1、Migration的测试<br />
&nbsp; 自动化测试</strong><br />
对Migration，我们采用了TDD的方式。<br />
输入是客户实际提供的xml文件，输出是JCR里的节点。测试环境的搭建主要是在本地建立起Jackrabbit实例。我们的工作方式是这样：每天早上领到一张migration故事卡，然后先写一个xml到jcr节点的集成测试描述出该类型报表的功能需求，接下来就是让这个测试通过。经过开始阶段的熟悉过程，我们的速度保持在一对Pair一天一种报表类型的导入。<br />
<br />
&nbsp;&nbsp;&nbsp;<strong style="font-weight: bold; ">手工测试</strong><br />
将xml文件内容正常解析并导入JCR只是第一步，第二步我们需要在Bundles里为该类型的报表编写模板使之正常展现。由于涉及到报表样式，这个测试我们采用手工测试，这个工作也是QA工作的重要一部分。<br />
在最开始的开发中，我们没有导入所有报表数据进行测试。这带来了问题，由于客户遗留数据跨越10多年，各种数据形式都可能存在（特别是04年以前数据，给UI带来了很大挑战），而最开始的开发中，我们只是使用了部分数据（09、10年数据）进行测试，这个导致了建立UAT环境时程序的很多返工以及QA的测试压力。<br />
<br />
<strong style="font-weight: bold; ">2、Bundles的测试<br />
自动化测试</strong><br />
对领域模型，我们采用了TDD的方式进行单元测试；在本地Jackrabbit实例里建立数据，领域模型封装数据测试行为。<br />
对servlet，我们采用了TDD的方式进行集成测试（同时测试了servlet和领域模型），在测试中对request和response进行mock；<br />
对JS，我们使用数据驱动的selenium功能测试进行覆盖。</p>
<p style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-family: Arial, sans-serif, Helvetica, Tahoma; font-size: 12px; line-height: 18px; "><img src="http://dl.javaeye.com/upload/attachment/260173/14882852-ea15-3302-929f-708e92146619.png" alt="测试覆盖" width="631" height="405" style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-width: 0px; border-style: initial; border-color: initial; " /><br />
<br />
我们最后的自动化测试结构：<br />
<img src="http://dl.javaeye.com/upload/attachment/260175/ece5188b-cef1-31cd-a490-574db3d22fc5.png" alt="测试的分层" width="490" height="335" style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-width: 0px; border-style: initial; border-color: initial; " /><br />
<br />
<strong style="font-weight: bold; ">手工测试</strong><br />
手工测试内容主要是功能测试。<br />
自动化测试价值低的部分我们采用手工测试，这部分内容包括报表模板，相对独立，内容不多，一次测试处处通过；<br />
自动化测试成本高的部分我们采用手工测试，这部分内容包括报表展现组件的编辑功能，因为采用了Ext JS，所以自动化测试困难；<br />
无法自动化的测试：报表在线生成PDF，报表样式，WEBDEV批量上传图片等;<br />
<br />
<strong style="font-weight: bold; ">我们与QA的约定：</strong><br />
每完成一个用户故事，我们会与QA、BA一起mini showcase；<br />
QA验收完成后编写功能测试用例；<br />
我们对QA编写的功能测试用例进行自动化，共同维护一个功能测试列表；<br />
对于不能自动化或自动化价值不高的测试用例QA继续使用手工测试。<br />
<br />
<strong style="font-weight: bold; ">四、我们感受到的自动化测试价值</strong><br />
1、通过自动化功能测试，我们使得需求对客户可视化；<br />
2、QA的回归测试成本降低，尽管目前频繁的向客户实际使用环境部署，但QA每次部署只需要做一些简单的冒烟测试；<br />
3、测试即需求，这点在TDD的开发过程中感觉非常明显，今天开发的目的就是使这个测试通过，避免了频繁部署到应用中进行测试，最快的电梯不是速度最快的电梯，而是中间停的楼层最少的电梯；<br />
4、与持续集成一起，及时反馈。这点在进行JS代码编写时，心理上都非常依赖于selenium测试，对于没有测试覆盖的地方，没有安全感；<br />
5、足够的单元、集成测试保证了频繁重构，没有人愿意引入BUG，没有足够的测试，没人愿意重构；<br />
6、测试即文档，良好的测试和命名，使得新加入的成员非常容易理解当前代码的功能。<br />
<br />
<strong style="font-weight: bold; ">五、思考和讨论</strong><br />
<strong style="font-weight: bold; ">1、自动化功能测试做到多少才合适？</strong><br />
当然是越多越合适，问题在于自动化功能测试成本要大大高于单元测试和集成测试，这些成本反映在测试环境的搭建、数据的准备，需要准备其他很多关联数据例如用户信息和权限信息、自动化功能测试的运行时间长、稳定性（随机成功/失败）、编写等等，需要权衡成本与收益。<br />
个人认为，自动化功能测试需要考虑的着重点包括：页面是否包含大量功能交互性JS（与展现性JS相对）？当前功能是否与其他功能共享一些代码？即独立性，独立性越低越需要着重覆盖（这里又涉及到另外一个问题，即从各个模块里重构出共用代码是否总是合适？）。QA每次冒烟测试时是否需要重复回归（重复回归次数越多，自动化越有价值）？经常失败的测试一定比不失败的测试价值更高？或者从未失败过的测试就没有价值？<br />
<br />
<strong style="font-weight: bold; ">2、单元测试？功能单元测试？</strong><br />
TDD的测试粒度多大才合适？从我个人而言，几乎天然的反对mock，为了满足测试覆盖率的追求，强制将两个联系很紧密的类分开，做出各式各样mock，这真让人气馁；stub也不是什么好东西，在一个曾经的spring项目里，在测试目录里，一堆一堆的测试xml配置文件让人有种呕吐的感觉。那就做集成测试吧，两个类关系很好，那么就整体测试它们的输入和输出，看起来很不错，功能实现了，测试也没多写，也不用准备太多其他东西，但是打开实现代码，你就发现那个丑陋，到处是复制和粘贴，是啊，不管黑猫白猫抓住老鼠就是好猫，只关注了当前结果，完全失去了TDD驱动简单设计的好处。<br />
<br />
<strong style="font-weight: bold; ">3、测试的重点是测试用例的设计，它反映出对需求的理解</strong><br />
那么结论就很明显，我们如果连需求就没有理解，如何进行实现，所以测试驱动是很自然的。</p>
<img src ="http://www.blogjava.net/RongHao/aggbug/323669.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/RongHao/" target="_blank">ronghao</a> 2010-06-16 21:13 <a href="http://www.blogjava.net/RongHao/archive/2010/06/16/323669.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>心理学，再谈好代码</title><link>http://www.blogjava.net/RongHao/archive/2010/03/21/316100.html</link><dc:creator>ronghao</dc:creator><author>ronghao</author><pubDate>Sun, 21 Mar 2010 14:27:00 GMT</pubDate><guid>http://www.blogjava.net/RongHao/archive/2010/03/21/316100.html</guid><wfw:comment>http://www.blogjava.net/RongHao/comments/316100.html</wfw:comment><comments>http://www.blogjava.net/RongHao/archive/2010/03/21/316100.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/RongHao/comments/commentRss/316100.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/RongHao/services/trackbacks/316100.html</trackback:ping><description><![CDATA[<p>什么代码才是好代码？这真是个老得能拔掉牙齿的话题。好吧，那让我们再在这刮沙尘暴的无聊时光里重复一次。好的代码要是易读的代码、要做到职责分离、要做到单一职责、要有高的执行效率....<br />
<br />
等等，等等，这才抽象了，太书面化了。我只是一个菜鸟，刚写代码几年，也没念过什么书，能不能说得通俗易懂一些？<br />
<br />
好吧，我停下来，想，这真是个难缠的家伙。我说，这样吧，我推荐几本书你去看吧，《重构》熊节最近再版了，建议你去买一本。恩，等等，有个省钱的招，去图灵俱乐部讨论组注册下，蹭赠书也很爽，哈哈。<br />
<br />
可是，你还没告诉我什么代码才是好代码呢？知道你也没什么好答案，我自己来说好了。<br />
<br />
省略掉此时我内心花花的汗水，下面是菜鸟的叙述：<br />
<br />
<strong>1.一致</strong><br />
我发现自己有轻微的强迫症，当我碰到以下代码时，我就会冲动。<br />
冲动前代码：<br />
def imgName<br />
if(XXX){<br />
&nbsp;&nbsp; imgName="meigui"<br />
}else{<br />
&nbsp;&nbsp; imgName=""<br />
}<br />
冲动后代码：<br />
def imgName=XXX?"meigui":""<br />
<br />
尽管两段代码功能一致，但一旦我发现出现冲动前代码时，我就会感到不舒服，感到难受，就好像看到阅兵正步走不齐一样。方法名也是一样：<br />
冲动前：<br />
def testXXX(){}<br />
冲动后：<br />
def should_XXX_when_XXX(){}<br />
<br />
变量亦是如此:<br />
冲动前：<br />
def imgNode=resouce.adoptTo(Node)<br />
冲动后：<br />
def node=resouce.adoptTo(Node)<br />
<br />
总之，我不愿意看到同一个事情有两种实现方式，如果功能类似，那么不管是逻辑还是变量、方法名，我会强迫一致，整齐划一。<br />
<br />
关于一致，从调试代码的角度看，零星的不一致比大量的不一致更加糟糕，因为这时大部分地方的一致性会令人麻痹大意。在实现查询分页功能时，我们有这样一行代码：<br />
nodeIterator.size<br />
这行代码的意思是获取查询结果的总数，大部分情况下它工作良好，但是在一种特殊情况下它返回了-1。这对我当时几乎是灾难性的，因为调试过程中我们始终相信这行代码的行为一致，结果是花费了一个下午才找到这个问题。<br />
<br />
<strong>2.简洁</strong><br />
我喜欢短的代码，对我而言，短的程序总是比更长一些的代码容易理解，小学时学课文就已经这样了，一看到大段的段落我总是会晕过去（特别是文言文，首先我就
对自己理解这段文字失去了信心）。这里要提到注释，即是这些注释明确是为了提高代码的可读性，也会增加我阅读代码的困难，所以我不会在方法里的任何位置添
加注释，撑死在个别方法声明前添加，并且这种情况也尽量避免，如果这个类确实包含了重要的不易理解的算法，我也只会在类声明前添加注释。<br />
<br />
关于自然语言，有一个基于经验的结论被称为Zipf定律，即：自然语言中最常用到的单词，其长度会趋于最短。<br />
<br />
我写代码的时候，能够简写尽量简写，例如，变量名，imageNode，我一定会写成imgNode；方法名procedureXXX,我一定会写成
procXXX,和讨厌大段代码一样，我非常讨厌命名很长的方法名和变量名，尽管这些名称这么长是为了更好的增加可读性，但可读性不是这样增加的。<br />
<br />
在我的第一份代码工作里，我们使用拼音来命名方法和变量（还好，没有包括类名），我讨厌这种命名方式的原因并不是因为我的语文老师不好以至于我前后鼻音不分，而是这种写法根本排除了简写的可能性，甚至，为了避免歧义，有时不得不变得更长。<br />
<br />
<strong>3.联觉和顺序</strong><br />
关于记忆，人类有两种重要的记忆能力：联觉和顺序记忆。<br />
<br />
关于联觉，一个例子是：你总可以一眼记住一个人的脸，比如范冰冰，尽管我到现在也不清楚她到底是单眼皮还是双眼皮，也不清楚她到底是厚嘴唇还是薄嘴唇。<br />
<br />
那么，在代码里，这里的表现就是局部，即一个功能的所有相关代码都集中在一个地方。我最讨厌的代码是这样的：最开始我打开一个文件，在阅读的过程中，我发
现一个不清楚的方法，于是我按下ctrl并点击鼠标，于是我跳到另外一个文件；接下来，在阅读另外一个方法里，我再次发现了一个不清楚的方法，于是我再次
按下ctrl并点击鼠标，哇哈，新的文件打开了....如此反复，终于当我打开最后一个文件时，我发现IDE的文件条里已经密密麻麻的排满了好几排文件，
于是，我移动鼠标，右键，弹出一个关闭菜单，我选择了close others，瞬间，哦米拖佛，整个世界清静了，但是，等等，我最初是打算干嘛来着？<br />
<br />
所以，请把所有相关联的代码都集中在一个地方，求您了。哦，对了，能不用接口请不要用接口，总会碰到这样的情况，打开好几排的文件，接口文件占了一半，我靠，少几个接口会死啊。对了，这可能是您的一致性心理在作怪，对不起，对不起。<br />
<br />
关于局部，一个范例同样与调试有关，在很久之前的一次调试中，我们始终找不到一个变量错误的原因，因为在这段代码里，根本找不到任何错误，很久以后，终于
发现，这个变量竟然是个全局变量，嘿嘿，告诉你吧，这个变量在servlet里，04年的时候，网上很火的一篇文章，标题就是：不要在servlet里使
用全局变量！<br />
<br />
关于顺序，最典型的例子出现在高中化学里，我总也不能瞬间说出第12、13个化学元素是什么，我通常会这样记忆：氢氦锂铍硼碳氮氧氟氖钠镁铝硅磷，啊哈，第12个元素是镁，第13个元素时铝，合起来就是--美女！<br />
<br />
所以，在代码里，请将互相调用的方法按顺序摆放，方法1先调用了方法2，那么请将方法2紧放在方法1后边。我讨厌这样的配置：打开方法1，发现其调用了方
法2，点击方法2，编辑器里的滚动条瞬间从最上端滚到最下端，紧接着，滚动条又从最下端滚动到中间，再接着，又是最下端，接着，归零到最上端....人生
经不起这样的大起大落，真的，那得要多么大的心脏啊，麦蒂才有过那么一次，13秒....<br />
<br />
还有，知道为什么goto为什么那么臭名昭著了吧。<br />
<br />
<strong>4.自然</strong><br />
使得代码具有轻松的表达方式，同时把错误率降到最低，一种最重要的方法就是代码变得&#8220;自然&#8221;，即向自然语言靠拢。因为代码并不仅仅是与机器交流的，更重要的是，需要在人之间交流。<br />
<br />
机器语言到高级语言，面向过程语言到面向对象语言，jdbc到hibernate，java到动态语言，这些都促使代码变得更加自然。<br />
<br />
Ruby里有个不起眼的特性，就是方法调用不用再写括号，这一特性是如此的微不足道但是却被很多人津津乐道，原因就是它更加自然，更加贴近我们的自然语言。于是，我看到，我的同事晓娜，在groovy里，一遍遍的将她力所能及的括号去掉。<br />
<br />
此外，程序语言和自然语言是有区别的，除了不能在代码里利用感情词抒发情感之外（我想，如果可以，一定会看到很多的冯特），程序语言没有口语。很少看到程
序员之间这样交流，来吧，我们来说段代码（当然也有，徐昊就可以，哈哈），他们更多的会使用白板和笔或者直接是编辑器。所以，结束招聘时是否需要笔试的争
论吧，我真为那些不经过笔试就直接招人的公司感到羞愧，因为他们根本就不懂程序语言。<br />
<br />
此处省略华丽的分割线。<br />
<br />
此文谢谢我们项目组WGSN的激烈讨论，谢谢讨论中徐昊的精彩点评。</p>
<img src ="http://www.blogjava.net/RongHao/aggbug/316100.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/RongHao/" target="_blank">ronghao</a> 2010-03-21 22:27 <a href="http://www.blogjava.net/RongHao/archive/2010/03/21/316100.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>数据驱动测试</title><link>http://www.blogjava.net/RongHao/archive/2010/01/17/309837.html</link><dc:creator>ronghao</dc:creator><author>ronghao</author><pubDate>Sun, 17 Jan 2010 04:08:00 GMT</pubDate><guid>http://www.blogjava.net/RongHao/archive/2010/01/17/309837.html</guid><wfw:comment>http://www.blogjava.net/RongHao/comments/309837.html</wfw:comment><comments>http://www.blogjava.net/RongHao/archive/2010/01/17/309837.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/RongHao/comments/commentRss/309837.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/RongHao/services/trackbacks/309837.html</trackback:ping><description><![CDATA[我们从一个最简单的登录例子开始。<br />
<br />
最开始我们需要验证在用户名和密码都正确的情况下，能够正常登录系统，我们这样编写测试代码（以下都是伪代码，使用TestNG和Selenium）：<br />
<br />
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000;">@Test<br />
def&nbsp;should_login_success_with_exist_username_and_correct_password(){<br />
&nbsp;&nbsp;&nbsp;&nbsp;LoginPage&nbsp;page&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;user.open(LoginPage,</span><span style="color: #000000;">"</span><span style="color: #000000;">/login.html</span><span style="color: #000000;">"</span><span style="color: #000000;">)<br />
&nbsp;&nbsp;&nbsp;&nbsp;user.perform(</span><span style="color: #000000;">"</span><span style="color: #000000;">login</span><span style="color: #000000;">"</span><span style="color: #000000;">,[</span><span style="color: #000000;">'</span><span style="color: #000000;">user1</span><span style="color: #000000;">'</span><span style="color: #000000;">,</span><span style="color: #000000;">'</span><span style="color: #000000;">1234</span><span style="color: #000000;">'</span><span style="color: #000000;">],on(page))<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">assert</span><span style="color: #000000;">&nbsp;page.successLogin<br />
}</span></div>
<br />
<br />
恩，很不错，运行一下，出现红条。为什么呢？原来测试数据库里没有用户名为user1的用户，好吧，写个数据库数据初始化脚本。再运行，OK，绿条！<br />
<br />
那么，接下来我们再增加一个测试，需要覆盖密码错误时不能登录系统的情况，很快测试就完成了：<br />
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000;"><br />
@Test<br />
def&nbsp;should_login_success_with_exist_username_and_incorrect_password(){<br />
&nbsp;&nbsp;&nbsp;&nbsp;LoginPage&nbsp;page&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;user.open(LoginPage,</span><span style="color: #000000;">"</span><span style="color: #000000;">/login.html</span><span style="color: #000000;">"</span><span style="color: #000000;">)<br />
&nbsp;&nbsp;&nbsp;&nbsp;user.perform(</span><span style="color: #000000;">"</span><span style="color: #000000;">login</span><span style="color: #000000;">"</span><span style="color: #000000;">,[</span><span style="color: #000000;">'</span><span style="color: #000000;">user1</span><span style="color: #000000;">'</span><span style="color: #000000;">,</span><span style="color: #000000;">'</span><span style="color: #000000;">4321</span><span style="color: #000000;">'</span><span style="color: #000000;">],on(page))<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">assert</span><span style="color: #000000;">&nbsp;page.successLogin,</span><span style="color: #0000ff;">false</span><span style="color: #000000;"><br />
}</span></div>
<br />
<br />
再运行一下测试，绿条。好啦，现在可以看下这段代码，恩，有些重复，重构一下：<br />
<br />
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000;">@Test<br />
def&nbsp;should_login_success_with_exist_username_and_correct_password(){<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">assert</span><span style="color: #000000;">&nbsp;login(</span><span style="color: #000000;">'</span><span style="color: #000000;">user1</span><span style="color: #000000;">'</span><span style="color: #000000;">,</span><span style="color: #000000;">'</span><span style="color: #000000;">1234</span><span style="color: #000000;">'</span><span style="color: #000000;">)<br />
}<br />
<br />
@Test<br />
def&nbsp;should_login_success_with_exist_username_and_incorrect_password(){<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">assert</span><span style="color: #000000;">&nbsp;login(</span><span style="color: #000000;">'</span><span style="color: #000000;">user1</span><span style="color: #000000;">'</span><span style="color: #000000;">,</span><span style="color: #000000;">'</span><span style="color: #000000;">4321</span><span style="color: #000000;">'</span><span style="color: #000000;">),</span><span style="color: #0000ff;">false</span><span style="color: #000000;"><br />
}<br />
<br />
def&nbsp;login(username,password){<br />
&nbsp;&nbsp;&nbsp;&nbsp;LoginPage&nbsp;page&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;user.open(LoginPage,</span><span style="color: #000000;">"</span><span style="color: #000000;">/login.html</span><span style="color: #000000;">"</span><span style="color: #000000;">)<br />
&nbsp;&nbsp;&nbsp;&nbsp;user.perform(</span><span style="color: #000000;">"</span><span style="color: #000000;">login</span><span style="color: #000000;">"</span><span style="color: #000000;">,[username,password],on(page))<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;page.successLogin<br />
}</span></div>
<br />
<br />
重构完成，可以看到，我们的测试方法里现在没有了任何行为，仅仅是数据！这样让我感觉有点怪，不管了，先用TestNG提供的@dataProvider整理一下：<br />
<br />
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000;">@Test(dataProvider</span><span style="color: #000000;">=</span><span style="color: #000000;">"</span><span style="color: #000000;">testdata</span><span style="color: #000000;">"</span><span style="color: #000000;">)<br />
def&nbsp;testLogin(username,password,expected){<br />
&nbsp;&nbsp;&nbsp;&nbsp;LoginPage&nbsp;page&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;user.open(LoginPage,</span><span style="color: #000000;">"</span><span style="color: #000000;">/login.html</span><span style="color: #000000;">"</span><span style="color: #000000;">)<br />
&nbsp;&nbsp;&nbsp;&nbsp;user.perform(</span><span style="color: #000000;">"</span><span style="color: #000000;">login</span><span style="color: #000000;">"</span><span style="color: #000000;">,[username,password],on(page))<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">assert</span><span style="color: #000000;">&nbsp;page.successLogin,expected<br />
}<br />
<br />
@DataProvider(name</span><span style="color: #000000;">=</span><span style="color: #000000;">"</span><span style="color: #000000;">testdata</span><span style="color: #000000;">"</span><span style="color: #000000;">)<br />
def&nbsp;Object[][]&nbsp;dataForLogin(){<br />
&nbsp;&nbsp;&nbsp;&nbsp;def&nbsp;data</span><span style="color: #000000;">=</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;Object[</span><span style="color: #000000;">2</span><span style="color: #000000;">][]<br />
&nbsp;&nbsp;&nbsp;&nbsp;data[</span><span style="color: #000000;">0</span><span style="color: #000000;">]</span><span style="color: #000000;">=</span><span style="color: #000000;">[</span><span style="color: #000000;">'</span><span style="color: #000000;">user1</span><span style="color: #000000;">'</span><span style="color: #000000;">,</span><span style="color: #000000;">'</span><span style="color: #000000;">1234</span><span style="color: #000000;">'</span><span style="color: #000000;">,</span><span style="color: #0000ff;">true</span><span style="color: #000000;">]&nbsp;as&nbsp;Object[]<br />
&nbsp;&nbsp;&nbsp;&nbsp;data[</span><span style="color: #000000;">1</span><span style="color: #000000;">]</span><span style="color: #000000;">=</span><span style="color: #000000;">[</span><span style="color: #000000;">'</span><span style="color: #000000;">user1</span><span style="color: #000000;">'</span><span style="color: #000000;">,</span><span style="color: #000000;">'</span><span style="color: #000000;">4321</span><span style="color: #000000;">'</span><span style="color: #000000;">,</span><span style="color: #0000ff;">false</span><span style="color: #000000;">]&nbsp;as&nbsp;Object[]<br />
}</span></div>
<br />
<br />
测试方法只剩下了一个！如果要测试不存在的用户不能登录系统呢？很简单，增加数据即可！<br />
<br />
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000;">@DataProvider(name</span><span style="color: #000000;">=</span><span style="color: #000000;">"</span><span style="color: #000000;">testdata</span><span style="color: #000000;">"</span><span style="color: #000000;">)<br />
def&nbsp;Object[][]&nbsp;dataForLogin(){<br />
&nbsp;&nbsp;&nbsp;&nbsp;def&nbsp;data</span><span style="color: #000000;">=</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;Object[</span><span style="color: #000000;">2</span><span style="color: #000000;">][]<br />
&nbsp;&nbsp;&nbsp;&nbsp;data[</span><span style="color: #000000;">0</span><span style="color: #000000;">]</span><span style="color: #000000;">=</span><span style="color: #000000;">[</span><span style="color: #000000;">'</span><span style="color: #000000;">user1</span><span style="color: #000000;">'</span><span style="color: #000000;">,</span><span style="color: #000000;">'</span><span style="color: #000000;">1234</span><span style="color: #000000;">'</span><span style="color: #000000;">,</span><span style="color: #0000ff;">true</span><span style="color: #000000;">]&nbsp;as&nbsp;Object[]<br />
&nbsp;&nbsp;&nbsp;&nbsp;data[</span><span style="color: #000000;">1</span><span style="color: #000000;">]</span><span style="color: #000000;">=</span><span style="color: #000000;">[</span><span style="color: #000000;">'</span><span style="color: #000000;">user1</span><span style="color: #000000;">'</span><span style="color: #000000;">,</span><span style="color: #000000;">'</span><span style="color: #000000;">4321</span><span style="color: #000000;">'</span><span style="color: #000000;">,</span><span style="color: #0000ff;">false</span><span style="color: #000000;">]&nbsp;as&nbsp;Object[]<br />
&nbsp;&nbsp;&nbsp;&nbsp;data[</span><span style="color: #000000;">1</span><span style="color: #000000;">]</span><span style="color: #000000;">=</span><span style="color: #000000;">[</span><span style="color: #000000;">'</span><span style="color: #000000;">inexistuser</span><span style="color: #000000;">'</span><span style="color: #000000;">,</span><span style="color: #000000;">'</span><span style="color: #000000;">1234</span><span style="color: #000000;">'</span><span style="color: #000000;">,</span><span style="color: #0000ff;">false</span><span style="color: #000000;">]&nbsp;as&nbsp;Object[]<br />
}</span></div>
<br />
<br />
在我们的测试方法里，测试数据和测试的行为进行了完全的分离。从系统的功能来说，功能一旦实现，那么就是一个黑盒，我们只要提供数据即可进行测试，这个数据包括两部分：输入和期待的输出。我开始暗自嘀咕：难道我以前那么多的洋洋得意测试方法很多都是不需要的吗？这些方式为什么会存在呢？恩，想起来了，这些方法是为了覆盖功能的各个路径的，是提高测试覆盖率的。那么为什么会产生这么多的测试方法呢？哦，在这些测试方法里，测试数据和测试行为是耦合在一起的！<br />
<br />
我伸了个懒腰，突然想，这下好了，我已经封装好了功能行为，QA想增加测试用例只需要自己增加数据就可以了，嘿嘿，爽啊。说曹操，QA到。QA mm说，你的某个功能实现有问题。我瞅了一眼，说，不可能啊（这是dev面对bug的第一反应），俺有测试的，持续集成一直是绿条的。QA mm说，在你的开发环境下测试是没有问题的，但是在QA环境，因为数据库变了，数据变了，应用服务器变了，所以会有些问题。我极不情愿的登录到QA环境，一测试，还真是，郁闷。<br />
<br />
怎么办？修复完BUG，第一反应就是自动化测试能不能跑在QA环境呢？一般情况下，这些测试需要干净的测试环境，我们会制造很多的测试数据，可是在QA环境下，QA有她自己的测试数据，这些数据都不存在了哈。恩，看看刚才的测试代码，哈，就用QA的数据也可以啊，心情愉悦的改下：<br />
<br />
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000;">@DataProvider(name</span><span style="color: #000000;">=</span><span style="color: #000000;">"</span><span style="color: #000000;">testdata</span><span style="color: #000000;">"</span><span style="color: #000000;">)<br />
def&nbsp;Object[][]&nbsp;dataForLogin(){<br />
&nbsp;&nbsp;&nbsp;&nbsp;def&nbsp;data</span><span style="color: #000000;">=</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;Object[</span><span style="color: #000000;">2</span><span style="color: #000000;">][]<br />
&nbsp;&nbsp;&nbsp;&nbsp;data[</span><span style="color: #000000;">0</span><span style="color: #000000;">]</span><span style="color: #000000;">=</span><span style="color: #000000;">[</span><span style="color: #000000;">'</span><span style="color: #000000;">hrong</span><span style="color: #000000;">'</span><span style="color: #000000;">,</span><span style="color: #000000;">'</span><span style="color: #000000;">1234</span><span style="color: #000000;">'</span><span style="color: #000000;">,</span><span style="color: #0000ff;">true</span><span style="color: #000000;">]&nbsp;as&nbsp;Object[]<br />
&nbsp;&nbsp;&nbsp;&nbsp;data[</span><span style="color: #000000;">1</span><span style="color: #000000;">]</span><span style="color: #000000;">=</span><span style="color: #000000;">[</span><span style="color: #000000;">'</span><span style="color: #000000;">hrong</span><span style="color: #000000;">'</span><span style="color: #000000;">,</span><span style="color: #000000;">'</span><span style="color: #000000;">4321</span><span style="color: #000000;">'</span><span style="color: #000000;">,</span><span style="color: #0000ff;">false</span><span style="color: #000000;">]&nbsp;as&nbsp;Object[]<br />
&nbsp;&nbsp;&nbsp;&nbsp;data[</span><span style="color: #000000;">1</span><span style="color: #000000;">]</span><span style="color: #000000;">=</span><span style="color: #000000;">[</span><span style="color: #000000;">'</span><span style="color: #000000;">rhao</span><span style="color: #000000;">'</span><span style="color: #000000;">,</span><span style="color: #000000;">'</span><span style="color: #000000;">1234</span><span style="color: #000000;">'</span><span style="color: #000000;">,</span><span style="color: #0000ff;">false</span><span style="color: #000000;">]&nbsp;as&nbsp;Object[]<br />
}</span></div>
<br />
<br />
OK,完成！为了该测试既能在开发测试环境运行又能在QA环境下运行，我们可以引入一个环境变量，将测试数据扔到文件里，通过环境变量来加载不同的测试数据（测试文件）。<br />
<br />
好吧，喝点东西（甲流很厉害，喝板蓝根好了），总结一下：<br />
<br />
数据驱动测试：<strong>测试数据与测试行为分离，通过数据来驱动测试。</strong><br />
<br />
好处：<strong>在对测试行为封装好的情况下，QA mm能够自己通过数据修改自动化测试；<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 自动化测试能够运行在多个环境下（开发环境、QA环境、产品环境）<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 测试的可读性<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 测试方法大量压缩</strong><br />
<br />
适用范围：<strong>功能测试（selenium测试）<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 通过环境准备测试数据（非测试用例自己准备数据）</strong><br />
<br />
可能存在的问题：<strong>比一般的测试编写困难，特别是在静态语言里</strong><br />
<br />
最后：该文章的思考来自于徐昊在团队内部的相应Session.<br />
<br />
<br />
<br />
<br />
<img src ="http://www.blogjava.net/RongHao/aggbug/309837.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/RongHao/" target="_blank">ronghao</a> 2010-01-17 12:08 <a href="http://www.blogjava.net/RongHao/archive/2010/01/17/309837.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>使用Selenium测试showModalDialog模态对话框</title><link>http://www.blogjava.net/RongHao/archive/2009/07/27/288640.html</link><dc:creator>ronghao</dc:creator><author>ronghao</author><pubDate>Mon, 27 Jul 2009 13:17:00 GMT</pubDate><guid>http://www.blogjava.net/RongHao/archive/2009/07/27/288640.html</guid><wfw:comment>http://www.blogjava.net/RongHao/comments/288640.html</wfw:comment><comments>http://www.blogjava.net/RongHao/archive/2009/07/27/288640.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/RongHao/comments/commentRss/288640.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/RongHao/services/trackbacks/288640.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 使用Selenium测试showModalDialog模态对话框一直是一件困难的事情，本文提出一种hack解决的方法。&nbsp;&nbsp;<a href='http://www.blogjava.net/RongHao/archive/2009/07/27/288640.html'>阅读全文</a><img src ="http://www.blogjava.net/RongHao/aggbug/288640.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/RongHao/" target="_blank">ronghao</a> 2009-07-27 21:17 <a href="http://www.blogjava.net/RongHao/archive/2009/07/27/288640.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>（Multi-stage Continuous Integration）多阶段持续集成</title><link>http://www.blogjava.net/RongHao/archive/2009/05/26/278114.html</link><dc:creator>ronghao</dc:creator><author>ronghao</author><pubDate>Tue, 26 May 2009 15:13:00 GMT</pubDate><guid>http://www.blogjava.net/RongHao/archive/2009/05/26/278114.html</guid><wfw:comment>http://www.blogjava.net/RongHao/comments/278114.html</wfw:comment><comments>http://www.blogjava.net/RongHao/archive/2009/05/26/278114.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/RongHao/comments/commentRss/278114.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/RongHao/services/trackbacks/278114.html</trackback:ping><description><![CDATA[<strong>一、目前的情况</strong><br />
目前我们要进行持续集成的对象是一个有着100人左右的开发团队，他们开发着一套很庞大的系统。整个开发团队划分为多个开发小组进行协同开发，每个开发小组负责2-3个模块的开发，实际这里的模块已经相当于一个中小型系统。各模块所有的类都通过eclipse整体编译在一起，直接放置在WEB-INF/classes下。本地是无法启动整个系统的，需要耗费大量的资源。<br />
<br />
<strong>二、碰到的问题</strong><br />
在了解具体情况之前，我们最初的想法是为整个产品做一个持续集成，但是很快就发现这一想法存在很多的问题：<br />
1、整个产品每次构建的时间会很长，这个时间包括代码的编译、启动Weblogic，完成自动化测试，同时对服务器的硬件要求非常高<br />
2、因为构建时间长，所以如果本地构建通过后再提交会严重影响开发效率，况且本地的硬件条件很可能启动不了<br />
3、如果本地不构建提交，则由于开发人数众多，构建会非常不稳定，会经常处于失败状态。而构建失败会导致后续提交的阻塞。<br />
4、作为一个100人的开发团队，代码提交会引发频繁的服务器构建，服务器无法负担。<br />
<br />
同时作为客户，他们有这样一种想法：敏捷开发是好的，但是不适合于大的项目和大的团队。<br />
<br />
<strong>最重要的问题集中在两个方面</strong>：<br />
1、启动整个产品过于重量级（不包括自动化测试的情况下已经如此）<br />
2、如何不影响开发人员的频繁提交<br />
<br />
<strong>三、我们的想法</strong><br />
我们现在的想法是做多阶段的持续集成（multi-stage CI）<br />
<br />
可以参考这里<a href="http://www.ddj.com/development-tools/212201506" target="_blank">http://www.ddj.com/development-tools/212201506</a>
<br />
<br />
具体而言：<br />
1、各个开发小组内做小组内的持续集成<br />
2、开发小组间集成做整个产品的持续集成<br />
<img src="http://www.blogjava.net/images/blogjava_net/ronghao/ci.gif" alt="" border="0" height="183" width="400" /><br />
<br />
大概：<br />
1、每个开发小组一个分支，整个产品一条主线<br />
2、在小组分支上搭建持续集成环境，小组内的开发向该分支上提交，各个小组可以并发开发，互不影响<br />
3、小组完成一个完整的功能后，从主线更新合并代码，本地构建通过，提交，触发整个产品的持续集成<br />
<br />
为使小组内持续集成构建加快，小组内尽量划分清楚对其他模块的依赖，不必要的模块（这里的模块包括基础模块，例如工作流模块）不必加载。<br />
同时推荐轻量级的web服务器例如Tomcat来完成小组内的测试环境。需要启动weblogic的情况或功能依赖过多的情况下，建议在产品持续集成时进行测试。<br />
同时保留原有的启动单独测试服务器进行手工测试的习惯。<br />
<img src ="http://www.blogjava.net/RongHao/aggbug/278114.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/RongHao/" target="_blank">ronghao</a> 2009-05-26 23:13 <a href="http://www.blogjava.net/RongHao/archive/2009/05/26/278114.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>基于memcached的SNA实现</title><link>http://www.blogjava.net/RongHao/archive/2008/10/28/237207.html</link><dc:creator>ronghao</dc:creator><author>ronghao</author><pubDate>Tue, 28 Oct 2008 12:41:00 GMT</pubDate><guid>http://www.blogjava.net/RongHao/archive/2008/10/28/237207.html</guid><wfw:comment>http://www.blogjava.net/RongHao/comments/237207.html</wfw:comment><comments>http://www.blogjava.net/RongHao/archive/2008/10/28/237207.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.blogjava.net/RongHao/comments/commentRss/237207.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/RongHao/services/trackbacks/237207.html</trackback:ping><description><![CDATA[<p>系统要集群，使用SNA方案。<br />
<strong>一、	缓存的处理</strong><br />
缓存要使用统一的缓存服务器，集中式缓存。<br />
原先的实现采用ehcache。<br />
在spring里的配置，以资源缓存为例：</p>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #008000;">&lt;!--</span><span style="color: #008000;">&nbsp;EhCache&nbsp;Manager&nbsp;</span><span style="color: #008000;">--&gt;</span><span style="color: #000000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">&lt;</span><span style="color: #800000;">bean&nbsp;</span><span style="color: #ff0000;">id</span><span style="color: #0000ff;">="cacheManager"</span><span style="color: #ff0000;">&nbsp;class</span><span style="color: #0000ff;">="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"</span><span style="color: #0000ff;">&gt;</span><span style="color: #000000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">&lt;</span><span style="color: #800000;">property&nbsp;</span><span style="color: #ff0000;">name</span><span style="color: #0000ff;">="configLocation"</span><span style="color: #0000ff;">&gt;</span><span style="color: #000000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">&lt;</span><span style="color: #800000;">value</span><span style="color: #0000ff;">&gt;</span><span style="color: #000000;">classpath:ehcache.xml</span><span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">value</span><span style="color: #0000ff;">&gt;</span><span style="color: #000000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">property</span><span style="color: #0000ff;">&gt;</span><span style="color: #000000;"><br />
</span><span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">bean</span><span style="color: #0000ff;">&gt;</span><span style="color: #000000;"><br />
<br />
</span><span style="color: #0000ff;">&lt;</span><span style="color: #800000;">bean&nbsp;</span><span style="color: #ff0000;">id</span><span style="color: #0000ff;">="resourceCacheBackend"</span><span style="color: #ff0000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;class</span><span style="color: #0000ff;">="org.springframework.cache.ehcache.EhCacheFactoryBean"</span><span style="color: #0000ff;">&gt;</span><span style="color: #000000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">&lt;</span><span style="color: #800000;">property&nbsp;</span><span style="color: #ff0000;">name</span><span style="color: #0000ff;">="cacheManager"</span><span style="color: #ff0000;">&nbsp;ref</span><span style="color: #0000ff;">="cacheManager"</span><span style="color: #0000ff;">/&gt;</span><span style="color: #000000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">&lt;</span><span style="color: #800000;">property&nbsp;</span><span style="color: #ff0000;">name</span><span style="color: #0000ff;">="cacheName"</span><span style="color: #ff0000;">&nbsp;value</span><span style="color: #0000ff;">="resourceCache"</span><span style="color: #0000ff;">/&gt;</span><span style="color: #000000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">bean</span><span style="color: #0000ff;">&gt;</span><span style="color: #000000;"><br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">&lt;</span><span style="color: #800000;">bean&nbsp;</span><span style="color: #ff0000;">id</span><span style="color: #0000ff;">="resourceCache"</span><span style="color: #ff0000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;class</span><span style="color: #0000ff;">="com.framework.extcomponent.security.authentication.services.acegi.cache.EhCacheBasedResourceCache"</span><span style="color: #ff0000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;autowire</span><span style="color: #0000ff;">="byName"</span><span style="color: #0000ff;">&gt;</span><span style="color: #000000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">&lt;</span><span style="color: #800000;">property&nbsp;</span><span style="color: #ff0000;">name</span><span style="color: #0000ff;">="cache"</span><span style="color: #ff0000;">&nbsp;ref</span><span style="color: #0000ff;">="resourceCacheBackend"</span><span style="color: #0000ff;">/&gt;</span><span style="color: #000000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">bean</span><span style="color: #0000ff;">&gt;</span><span style="color: #000000;"><br />
<br />
</span></div>
<pre name="code" class="xml"></pre>
<p>cacheManager负责对ehcache进行管理，初始化、启动、停止。<br />
resourceCacheBackend负责实际执行缓存操作，put 、get、remove。<br />
resourceCache实现具有业务语义的业务应用层面的缓存操作，内部调用resourceCacheBackend操作。<br />
<br />
现在采用memcached。<br />
关于客户端，采用文初封装的客户端，地址在<a href="http://code.google.com/p/memcache-client-forjava/" mce_href="http://code.google.com/p/memcache-client-forjava/">http://code.google.com/p/memcache-client-forjava/</a>。<br />
使用spring的FactoryBean进行二次封装。同理：<br />
memcachedManager负责对memcached进行管理，初始化、启动、停止。<br />
代码：</p>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #008000;">/**</span><span style="color: #008000;"><br />
*&nbsp;User:&nbsp;ronghao<br />
*&nbsp;Date:&nbsp;2008-10-14<br />
*&nbsp;Time:&nbsp;10:36:30<br />
*&nbsp;管理Memcached&nbsp;的CacheManager<br />
</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br />
</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;MemcachedCacheManagerFactoryBean&nbsp;</span><span style="color: #0000ff;">implements</span><span style="color: #000000;">&nbsp;FactoryBean,&nbsp;InitializingBean,&nbsp;DisposableBean&nbsp;{<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">protected</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">final</span><span style="color: #000000;">&nbsp;Log&nbsp;logger&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;LogFactory.getLog(getClass());<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;ICacheManager</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">IMemcachedCache</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">&nbsp;cacheManager;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;Object&nbsp;getObject()&nbsp;</span><span style="color: #0000ff;">throws</span><span style="color: #000000;">&nbsp;Exception&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;cacheManager;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;Class&nbsp;getObjectType()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">this</span><span style="color: #000000;">.cacheManager.getClass();<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">boolean</span><span style="color: #000000;">&nbsp;isSingleton()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">true</span><span style="color: #000000;">;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;afterPropertiesSet()&nbsp;</span><span style="color: #0000ff;">throws</span><span style="color: #000000;">&nbsp;Exception&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;logger.info(</span><span style="color: #000000;">"</span><span style="color: #000000;">Initializing&nbsp;Memcached&nbsp;CacheManager</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cacheManager&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;CacheUtil.getCacheManager(IMemcachedCache.</span><span style="color: #0000ff;">class</span><span style="color: #000000;">,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MemcachedCacheManager.</span><span style="color: #0000ff;">class</span><span style="color: #000000;">.getName());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cacheManager.start();<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;destroy()&nbsp;</span><span style="color: #0000ff;">throws</span><span style="color: #000000;">&nbsp;Exception&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;logger.info(</span><span style="color: #000000;">"</span><span style="color: #000000;">Shutting&nbsp;down&nbsp;Memcached&nbsp;CacheManager</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cacheManager.stop();<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}</span></div>
&nbsp;
<br />
<p>配置：</p>
<pre name="code" class="xml"><br />
</pre>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000ff;">&lt;</span><span style="color: #800000;">bean&nbsp;</span><span style="color: #ff0000;">id</span><span style="color: #0000ff;">="memcachedManager"</span><span style="color: #ff0000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;class</span><span style="color: #0000ff;">="com.framework.extcomponent.cache.MemcachedCacheManagerFactoryBean"</span><span style="color: #0000ff;">/&gt;</span></div>
<pre name="code" class="xml"></pre>
&nbsp;
<br />
<p>resourceCacheBackend负责实际执行缓存操作，put 、get、remove。<br />
代码：</p>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #008000;">/**</span><span style="color: #008000;"><br />
*&nbsp;User:&nbsp;ronghao<br />
*&nbsp;Date:&nbsp;2008-10-14<br />
*&nbsp;Time:&nbsp;10:37:16<br />
*&nbsp;返回&nbsp;&nbsp;MemcachedCache<br />
</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br />
</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;MemcachedCacheFactoryBean&nbsp;</span><span style="color: #0000ff;">implements</span><span style="color: #000000;">&nbsp;FactoryBean,&nbsp;BeanNameAware,&nbsp;InitializingBean&nbsp;{<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">protected</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">final</span><span style="color: #000000;">&nbsp;Log&nbsp;logger&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;LogFactory.getLog(getClass());<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;ICacheManager</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">IMemcachedCache</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">&nbsp;cacheManager;<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;String&nbsp;cacheName;<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;String&nbsp;beanName;<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;IMemcachedCache&nbsp;cache;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;setCacheManager(ICacheManager</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">IMemcachedCache</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">&nbsp;cacheManager)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">this</span><span style="color: #000000;">.cacheManager&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;cacheManager;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;setCacheName(String&nbsp;cacheName)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">this</span><span style="color: #000000;">.cacheName&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;cacheName;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;Object&nbsp;getObject()&nbsp;</span><span style="color: #0000ff;">throws</span><span style="color: #000000;">&nbsp;Exception&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;cache;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;Class&nbsp;getObjectType()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">this</span><span style="color: #000000;">.cache.getClass();<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">boolean</span><span style="color: #000000;">&nbsp;isSingleton()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">true</span><span style="color: #000000;">;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;setBeanName(String&nbsp;name)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">this</span><span style="color: #000000;">.beanName</span><span style="color: #000000;">=</span><span style="color: #000000;">name;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;afterPropertiesSet()&nbsp;</span><span style="color: #0000ff;">throws</span><span style="color: #000000;">&nbsp;Exception&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;If&nbsp;no&nbsp;cache&nbsp;name&nbsp;given,&nbsp;use&nbsp;bean&nbsp;name&nbsp;as&nbsp;cache&nbsp;name.</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(</span><span style="color: #0000ff;">this</span><span style="color: #000000;">.cacheName&nbsp;</span><span style="color: #000000;">==</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">null</span><span style="color: #000000;">)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">this</span><span style="color: #000000;">.cacheName&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">this</span><span style="color: #000000;">.beanName;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cache&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;cacheManager.getCache(cacheName);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}</span></div>
<p>配置：</p>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000ff;">&lt;</span><span style="color: #800000;">bean&nbsp;</span><span style="color: #ff0000;">id</span><span style="color: #0000ff;">="resourceCacheBackend"</span><span style="color: #ff0000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;class</span><span style="color: #0000ff;">="com.framework.extcomponent.cache.MemcachedCacheFactoryBean"</span><span style="color: #0000ff;">&gt;</span><span style="color: #000000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">&lt;</span><span style="color: #800000;">property&nbsp;</span><span style="color: #ff0000;">name</span><span style="color: #0000ff;">="cacheManager"</span><span style="color: #ff0000;">&nbsp;ref</span><span style="color: #0000ff;">="memcachedManager"</span><span style="color: #0000ff;">/&gt;</span><span style="color: #000000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">&lt;</span><span style="color: #800000;">property&nbsp;</span><span style="color: #ff0000;">name</span><span style="color: #0000ff;">="cacheName"</span><span style="color: #ff0000;">&nbsp;value</span><span style="color: #0000ff;">="memcache"</span><span style="color: #0000ff;">/&gt;</span><span style="color: #000000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">bean</span><span style="color: #0000ff;">&gt;</span></div>
&nbsp;
resourceCache同上，替换新的实现类MemcachedBasedResourceCache即可。<br />
<p><strong>二、	Session失效的处理</strong><br />
采用memcached作为httpsession的存储，并不直接保存httpsession对象，自定义SessionMap，SessionMap直接继承HashMap，保存SessionMap。<br />
<br />
会话胶粘：未失败转发的情况下没必要在memcached保存的SessionMap和httpsession之间复制来复制去，眉来眼去。<br />
<br />
利用memcached计数器保存在线人数。<br />
<br />
系统权限采用了acegi，在acegi的拦截器链里配置snaFilter</p>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000ff;">&lt;</span><span style="color: #800000;">bean&nbsp;</span><span style="color: #ff0000;">id</span><span style="color: #0000ff;">="filterChainProxy"</span><span style="color: #ff0000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;class</span><span style="color: #0000ff;">="org.acegisecurity.util.FilterChainProxy"</span><span style="color: #0000ff;">&gt;</span><span style="color: #000000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">&lt;</span><span style="color: #800000;">property&nbsp;</span><span style="color: #ff0000;">name</span><span style="color: #0000ff;">="filterInvocationDefinitionSource"</span><span style="color: #0000ff;">&gt;</span><span style="color: #000000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">&lt;</span><span style="color: #800000;">value</span><span style="color: #0000ff;">&gt;</span><span style="color: #000000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;PATTERN_TYPE_APACHE_ANT<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/**=snaFilter,httpSessionContextIntegrationFilter,logoutFilter,authenticationProcessingFilter,basicProcessingFilter,securityContextHolderAwareRequestFilter,exceptionTranslationFilter,filterInvocationInterceptor<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">value</span><span style="color: #0000ff;">&gt;</span><span style="color: #000000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">property</span><span style="color: #0000ff;">&gt;</span><span style="color: #000000;"><br />
</span><span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">bean</span><span style="color: #0000ff;">&gt;</span></div>
<p><br />
注意需要配置在第一个。<br />
snaFilter的职责：<br />
1、	没有HttpSession时，创建HttpSession;<br />
2、	创建Cookie保存HttpSession id;<br />
3、	如果Cookie保存的HttpSession id与当前HttpSession id一致，说明是正常请求；<br />
4、	如果Cookie保存的HttpSession id与当前HttpSession id不一致，说明是失败转发；失败转发的处理：<br />
&nbsp;&nbsp;&nbsp;&nbsp; 4.1、根据Cookie保存的HttpSession id从memcached获取SessionMap；<br />
&nbsp;&nbsp;&nbsp;&nbsp; 4.2、SessionMap属性复制到当前HttpSession；<br />
&nbsp;&nbsp;&nbsp;&nbsp; 4.3、memcached删除SessionMap。<br />
5、	判断当前请求url是否是登出url，是则删除SessionMap，在线人数减1.<br />
<br />
代码：</p>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;doFilter(ServletRequest&nbsp;servletRequest,&nbsp;ServletResponse&nbsp;servletResponse,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;FilterChain&nbsp;filterChain)&nbsp;</span><span style="color: #0000ff;">throws</span><span style="color: #000000;">&nbsp;IOException,&nbsp;ServletException&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">final</span><span style="color: #000000;">&nbsp;HttpServletRequest&nbsp;hrequest&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;(HttpServletRequest)&nbsp;servletRequest;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">final</span><span style="color: #000000;">&nbsp;HttpServletResponse&nbsp;hresponse&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;(HttpServletResponse)&nbsp;servletResponse;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;uri&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;hrequest.getRequestURI();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;logger.debug(</span><span style="color: #000000;">"</span><span style="color: #000000;">开始SNA拦截-----------------</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">+</span><span style="color: #000000;">&nbsp;uri);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HttpSession&nbsp;httpSession&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;hrequest.getSession();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;sessionId&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;httpSession.getId();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">如果是登出,则直接干掉sessionMap</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(uri.equals(logoutUrl))&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;logger.debug(</span><span style="color: #000000;">"</span><span style="color: #000000;">remove&nbsp;sessionmap:</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">+</span><span style="color: #000000;">&nbsp;sessionId);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">在线人数减1</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;getCache().addOrDecr(</span><span style="color: #000000;">"</span><span style="color: #000000;">userCount</span><span style="color: #000000;">"</span><span style="color: #000000;">,</span><span style="color: #000000;">1</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;getCache().remove(sessionId);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</span><span style="color: #0000ff;">else</span><span style="color: #000000;">&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;cookiesessionid&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;getSessionIdFromCookie(hrequest,&nbsp;hresponse);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(</span><span style="color: #000000;">!</span><span style="color: #000000;">sessionId.equals(cookiesessionid))&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;createCookie(sessionId,&nbsp;hresponse);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SessionMap&nbsp;sessionMap&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;getSessionMap(cookiesessionid);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(sessionMap&nbsp;</span><span style="color: #000000;">!=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">null</span><span style="color: #000000;">)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;logger.debug(</span><span style="color: #000000;">"</span><span style="color: #000000;">fail&nbsp;over--------sessionid:</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">+</span><span style="color: #000000;">&nbsp;sessionId&nbsp;</span><span style="color: #000000;">+</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">"</span><span style="color: #000000;">cookiesessionid:</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">+</span><span style="color: #000000;">&nbsp;cookiesessionid);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;initialHttpSession(sessionMap,&nbsp;httpSession);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cache.remove(cookiesessionid);<br />
&nbsp;&nbsp;&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;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;filterChain.doFilter(hrequest,&nbsp;hresponse);<br />
}</span></div>
<br />
利用HttpSessionAttributeListener监听httpsession的属性变化,同步到memecached中的sessionmap。
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;attributeAdded(HttpSessionBindingEvent&nbsp;event)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HttpSession&nbsp;httpSession&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;event.getSession();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;attrName&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;event.getName();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Object&nbsp;attrValue&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;event.getValue();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;sessionId&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;httpSession.getId();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;logger.debug(</span><span style="color: #000000;">"</span><span style="color: #000000;">attributeAdded&nbsp;sessionId:</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">+</span><span style="color: #000000;">&nbsp;sessionId&nbsp;</span><span style="color: #000000;">+</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">"</span><span style="color: #000000;">name:</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">+</span><span style="color: #000000;">&nbsp;attrName&nbsp;</span><span style="color: #000000;">+</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">"</span><span style="color: #000000;">,value:</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">+</span><span style="color: #000000;">&nbsp;attrValue);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SessionMap&nbsp;sessionMap&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;getSessionMap(sessionId);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(sessionMap&nbsp;</span><span style="color: #000000;">==</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">null</span><span style="color: #000000;">){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">在线人数加1</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;getCache().addOrIncr(</span><span style="color: #000000;">"</span><span style="color: #000000;">userCount</span><span style="color: #000000;">"</span><span style="color: #000000;">,</span><span style="color: #000000;">1</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sessionMap&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;SessionMap();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;logger.debug(</span><span style="color: #000000;">"</span><span style="color: #000000;">name:</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">+</span><span style="color: #000000;">&nbsp;attrName&nbsp;</span><span style="color: #000000;">+</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">"</span><span style="color: #000000;">,value:</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">+</span><span style="color: #000000;">&nbsp;attrValue);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sessionMap.put(attrName,&nbsp;attrValue);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;getCache().put(sessionId,&nbsp;sessionMap);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;attributeRemoved(HttpSessionBindingEvent&nbsp;event)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HttpSession&nbsp;httpSession&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;event.getSession();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;attrName&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;event.getName();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;sessionId&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;httpSession.getId();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;logger.debug(</span><span style="color: #000000;">"</span><span style="color: #000000;">attributeRemoved&nbsp;sessionId:</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">+</span><span style="color: #000000;">&nbsp;sessionId&nbsp;</span><span style="color: #000000;">+</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">"</span><span style="color: #000000;">name:</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">+</span><span style="color: #000000;">&nbsp;attrName);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SessionMap&nbsp;sessionMap&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;getSessionMap(sessionId);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(sessionMap&nbsp;</span><span style="color: #000000;">!=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">null</span><span style="color: #000000;">)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;logger.debug(</span><span style="color: #000000;">"</span><span style="color: #000000;">remove:</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">+</span><span style="color: #000000;">&nbsp;attrName);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sessionMap.remove(attrName);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;getCache().put(sessionId,&nbsp;sessionMap);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;attributeReplaced(HttpSessionBindingEvent&nbsp;event)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;attributeAdded(event);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}</span></div>
<pre name="code" class="java"></pre>
&nbsp;
利用HttpSessionListener，sessionDestroyed事件时根据sessionid删除memcached里的sessionMap（如果存在）。不再担心httpsession的过期问题。
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;sessionDestroyed(HttpSessionEvent&nbsp;event)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HttpSession&nbsp;httpSession&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;event.getSession();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;sessionId&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;httpSession.getId();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;logger.debug(</span><span style="color: #000000;">"</span><span style="color: #000000;">session&nbsp;Removed&nbsp;sessionId:</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">+</span><span style="color: #000000;">&nbsp;sessionId);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SessionMap&nbsp;sessionMap&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;getSessionMap(sessionId);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(sessionMap&nbsp;</span><span style="color: #000000;">!=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">null</span><span style="color: #000000;">)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;logger.debug(</span><span style="color: #000000;">"</span><span style="color: #000000;">remove&nbsp;sessionmap:</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">+</span><span style="color: #000000;">&nbsp;sessionId);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">在线人数减1</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;getCache().addOrDecr(</span><span style="color: #000000;">"</span><span style="color: #000000;">userCount</span><span style="color: #000000;">"</span><span style="color: #000000;">,</span><span style="color: #000000;">1</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;getCache().remove(sessionId);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}</span></div>
<br />
&nbsp;<strong>
三、	文件保存的处理</strong><br />
<p>和缓存类似，采用集中式的文件服务。对于linux，采用nfs。参考文档<a href="http://linux.vbird.org/linux_server/0330nfs.php#What_NFS_perm" mce_href="http://linux.vbird.org/linux_server/0330nfs.php#What_NFS_perm">http://linux.vbird.org/linux_server/0330nfs.php#What_NFS_perm</a>。关键在于对权限的分配。<br />
应用程序本身不用修改。</p>
<p><br />
</p>
<img src ="http://www.blogjava.net/RongHao/aggbug/237207.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/RongHao/" target="_blank">ronghao</a> 2008-10-28 20:41 <a href="http://www.blogjava.net/RongHao/archive/2008/10/28/237207.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>SNA方案之session炒冷饭</title><link>http://www.blogjava.net/RongHao/archive/2008/09/04/226940.html</link><dc:creator>ronghao</dc:creator><author>ronghao</author><pubDate>Thu, 04 Sep 2008 06:31:00 GMT</pubDate><guid>http://www.blogjava.net/RongHao/archive/2008/09/04/226940.html</guid><wfw:comment>http://www.blogjava.net/RongHao/comments/226940.html</wfw:comment><comments>http://www.blogjava.net/RongHao/archive/2008/09/04/226940.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/RongHao/comments/commentRss/226940.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/RongHao/services/trackbacks/226940.html</trackback:ping><description><![CDATA[<p style="margin-left: 24pt; text-indent: 0cm;"><span style="font-size: 12pt; font-family: 宋体;">在集群部署的情况下，应用程序需要做出调整，主要集中在四个方面：对</span><span style="font-size: 12pt;">httpsession</span><span style="font-size: 12pt; font-family: 宋体;">的处理、对缓存的处理、共享的文件系统、</span><span style="font-size: 12pt;">synchronized</span><span style="font-size: 12pt; font-family: 宋体;">关键字的失效。</span></p>
<p style="margin-left: 42pt; text-indent: -18pt;"><strong><span style="font-size: 12pt; font-family: 宋体;">对</span><span style="font-size: 12pt;">httpsession</span><span style="font-size: 12pt; font-family: 宋体;">的处理</span></strong></p>
<p style="margin-left: 42pt; text-indent: 0cm;"><span style="font-size: 12pt; font-family: 宋体;">对</span><span style="font-size: 12pt;">httpsession</span><span style="font-size: 12pt; font-family: 宋体;">的处理最为重要，因为对</span><span style="font-size: 12pt;">WEB</span><span style="font-size: 12pt; font-family: 宋体;">程序而言，</span><span style="font-size: 12pt;">httpsession</span><span style="font-size: 12pt; font-family: 宋体;">无疑是最重要的全局资源，它需要被多个</span><span style="font-size: 12pt;">web</span><span style="font-size: 12pt; font-family: 宋体;">服务器所共享。</span></p>
<p style="margin-left: 42pt; text-indent: 0cm;"><strong><span style="font-size: 12pt; font-family: 宋体; font-weight: normal;">无共享的集群架构（</span></strong><strong><span style="font-size: 12pt; font-family: &quot;Calibri&quot;,&quot;sans-serif&quot;; font-weight: normal;">SNA</span></strong><strong><span style="font-size: 12pt; font-family: 宋体; font-weight: normal;">），</span></strong><span style="font-size: 12pt; font-family: 宋体;">在这样的集群中，每个节点具备完全相同的功能，并且不需要知道其他节点存在与否。每个节点</span><span style="font-size: 12pt;">JVM</span><span style="font-size: 12pt; font-family: 宋体;">进程不保持全局状态，才能够保证</span><span style="font-size: 12pt;">n</span><span style="font-size: 12pt; font-family: 宋体;">个</span><span style="font-size: 12pt;">JVM</span><span style="font-size: 12pt; font-family: 宋体;">节点的幂等性，那些所有涉及到全局状态的，必须放在</span><span style="font-size: 12pt;">JVM</span><span style="font-size: 12pt; font-family: 宋体;">进程之外，例如用户</span><span style="font-size: 12pt;">ID</span><span style="font-size: 12pt; font-family: 宋体;">可以使用</span><span style="font-size: 12pt;">cookie</span><span style="font-size: 12pt; font-family: 宋体;">，</span><span style="font-size: 12pt;">session</span><span style="font-size: 12pt; font-family: 宋体;">可以放入数据库（这并不是一个好的选择），文件可以放在共享存储系统中。</span></p>
<p style="margin-left: 42pt; text-indent: 0cm;"><span style="font-size: 12pt; font-family: 宋体;">也就是说</span><span style="font-size: 12pt;">httpsession</span><span style="font-size: 12pt; font-family: 宋体;">的信息需要被保存在</span><span style="font-size: 12pt;">JVM</span><span style="font-size: 12pt; font-family: 宋体;">进程之外，例如分布式缓存、数据库。</span></p>
<p style="margin-left: 42pt; text-indent: 0cm;"><span style="font-size: 12pt; font-family: 宋体;"><br />
</span></p>
<p style="margin-left: 42pt; text-indent: 0cm;"><span style="font-size: 12pt; font-family: 宋体;">这里是方案：</span></p>
1、使用会话cookie保存web服务器产生的sessionid<br />
&nbsp;&nbsp; 为什么是sessionid而不是userid，原因在于谁也不知道除去登录外其他人会在httpsession里干些什么<br />
<br />
2、自定义SessionMap&lt;String,Serializable&gt;同步保存httpsession内的信息<br />
&nbsp;&nbsp; 自定义SessionMap同步httpsession，在操作httpsession时不用改变调用接口，不用东张西望<br />
<br />
3、使用分布式缓存memcached保存自定义SessionMap&lt;String,Serializable&gt;<br />
<br />
4、会话胶粘<br />
&nbsp;&nbsp; 未失败转发的情况下没必要在memcached和httpsession之间复制来复制去，眉来眼去<br />
<br />
5、使用SnaFilter处理失败转发<br />
<br />
6、使用HttpSessionListener实现SessionMap&lt;String,Serializable&gt;的过期<br />
&nbsp;&nbsp; 利用容器session 机制的好处,httpsession过期的时候干掉memecached里的SessionMap
<p style="margin-left: 60.75pt; text-indent: -18.75pt;"><span style="font-family: 宋体;"><br />
</span></p>
<p style="margin-left: 42pt; text-indent: 0cm;"><span style="font-family: 宋体;">下面根据</span>web<span style="font-family: 宋体;">请求的过程分情况讨论该方案：</span></p>
<p style="margin-left: 60pt; text-indent: -18pt;"><strong><span style="font-family: 宋体;">A、登录</span></strong></p>
<p style="margin-left: 60pt; text-indent: -18pt;"><strong><span style="font-family: 宋体;"><img src="http://www.blogjava.net/images/blogjava_net/ronghao/login1.gif" alt="" border="0" height="420" width="567" /><br />
</span></strong></p>
<p style="margin-left: 60pt; text-indent: 0cm;"><span><!--[if gte vml 1]>
<![endif]--></span></p>
<p style="margin-left: 60pt; text-indent: 0cm;"><span style="font-family: 宋体;">根据请求的</span>url<span style="font-family: 宋体;">判断是否是登录请求</span></p>
<p style="margin-left: 60pt; text-indent: 0cm;"><span style="font-family: 宋体;">在线人数保存在</span>memcached<span style="font-family: 宋体;">里</span></p>
<p style="margin-left: 60pt; text-indent: -18pt;"><strong><span style="font-size: 12pt;">B、&nbsp;</span><span style="font-family: 宋体;">正常请求</span></strong></p>
<p style="margin-left: 60pt; text-indent: -18pt;"><strong><span style="font-family: 宋体;"><img src="http://www.blogjava.net/images/blogjava_net/ronghao/normal1.gif" alt="" border="0" height="445" width="567" /><br />
</span></strong></p>
<p style="margin-left: 60pt; text-indent: 0cm;"><span><!--[if gte vml 1]>
<![endif]--></span></p>
<p style="margin-left: 60pt; text-indent: -18pt;"><strong><span style="font-size: 12pt;">C、&nbsp;</span><span style="font-family: 宋体;">失败转发</span></strong></p>
<p style="margin-left: 60pt; text-indent: -18pt;"><strong><span style="font-family: 宋体;"><img src="http://www.blogjava.net/images/blogjava_net/ronghao/fail1.gif" alt="" border="0" height="424" width="581" /><br />
</span></strong></p>
<p style="margin-left: 60pt; text-indent: 0cm;"><span><!--[if gte vml 1]>
<![endif]--></span></p>
<p style="margin-left: 60pt; text-indent: -18pt;"><strong><span style="font-size: 12pt;">D、</span><span style="font-family: 宋体;">登出</span></strong></p>
<p style="margin-left: 60pt; text-indent: -18pt;"><strong><span style="font-family: 宋体;"><img src="http://www.blogjava.net/images/blogjava_net/ronghao/logout1.gif" alt="" border="0" height="458" width="567" /><br />
</span></strong></p>
<p style="margin-left: 60pt; text-indent: 0cm;"><span><!--[if gte vml 1]>
<![endif]--></span></p>
<p style="margin-left: 60pt; text-indent: 0cm;"><span style="font-family: 宋体;">根据请求的</span>url<span style="font-family: 宋体;">判断是否是登出请求</span></p>
<p style="margin-left: 60pt; text-indent: -18pt;"><strong>E、HttpSession<span style="font-family: 宋体;">过期</span></strong></p>
<p style="margin-left: 60pt; text-indent: 0cm;"><span style="font-family: 宋体;">不</span>hack memcached<span style="font-family: 宋体;">，使用</span>HttpSessionListener<span style="font-family: 宋体;">，</span>sessionDestroyed<span style="font-family: 宋体;">事件时根据</span>sessionid<span style="font-family: 宋体;">删除</span>memcached<span style="font-family: 宋体;">里的</span>sessionMap<span style="font-family: 宋体;">（如果存在）</span></p>
<p style="margin-left: 21pt; text-indent: 0cm;"><span style="font-family: 宋体;">关于在线人数的统计：在线人数存储在</span>memcached<span style="font-family: 宋体;">里，将在线人数与</span>sessionMap<span style="font-family: 宋体;">绑定，往</span>memcached<span style="font-family: 宋体;">里增加</span>sessionMap<span style="font-family: 宋体;">时在线人数</span>+1<span style="font-family: 宋体;">，删除时</span>-1.</p>
<img src ="http://www.blogjava.net/RongHao/aggbug/226940.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/RongHao/" target="_blank">ronghao</a> 2008-09-04 14:31 <a href="http://www.blogjava.net/RongHao/archive/2008/09/04/226940.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>一次性能调优的实战</title><link>http://www.blogjava.net/RongHao/archive/2008/09/01/226068.html</link><dc:creator>ronghao</dc:creator><author>ronghao</author><pubDate>Mon, 01 Sep 2008 05:49:00 GMT</pubDate><guid>http://www.blogjava.net/RongHao/archive/2008/09/01/226068.html</guid><wfw:comment>http://www.blogjava.net/RongHao/comments/226068.html</wfw:comment><comments>http://www.blogjava.net/RongHao/archive/2008/09/01/226068.html#Feedback</comments><slash:comments>10</slash:comments><wfw:commentRss>http://www.blogjava.net/RongHao/comments/commentRss/226068.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/RongHao/services/trackbacks/226068.html</trackback:ping><description><![CDATA[项目情况：是一个大型公司的内部办公系统，该系统有两个和一般企业应用不太一样的特点：一是用户量非常多，人员数达到2W左右，另一个是采用分级管理的形式，各个分公司数据分开管理。
<br />
<br />
我们的定位：我们是作为业务平台的提供商参与这个项目的，我们提供底层的开发平台，系统集成商在此基础上进行二次开发。
<br />
<br />
在项目从开发到部署的过程中遇到了很多的问题，也反映出很多问题。
<br />
<br />
<strong>一、怎么回事，跑得比猫还慢</strong>
<br />
项目开发完毕后部署在Ibm aix
小型机上，32G内存，16个cpu。应用服务器采用的是weblogic9.2，数据库是oracle10.0.2。上线后发现系统运行的非常缓慢，甚
至比开发环境下的tomcat还要慢。于是开始排查原因，最开始是对SQL进行监控，优先考虑是数据库访问性能产生瓶颈。通过监控，发现很多业务需要执行
大量的SQL语句，查看客户编写的相关代码，发现在查询数据时循环执行了大量SQL。主要原因在于他们在代码中循环调用了我们相关API，一个最典型的例
子是通过用户ID查找用户NAME，他们在业务表格里没有保存用户name，而是在查询的时候通过用户ID查找用户name填充到页面，几乎每一个查询都
是n+1。<br />
&nbsp;<br />
另外由于平台使用了hibernate，使得oo编程得非常爽快，导致开发人员完全忽略了相应的数据库操作所带来的压力。很多业务逻辑直接通过PO叠加完成，把一些可以通过很少SQL完成的逻辑全部分散放置到PO里，导致了大量PO的交互和SQL语句。
<br />
<br />
开始优化SQL，优化的同时增加大量业务缓存。但优化完毕后运行缓慢的现象依旧存在，性能有了一定的提升但是不是非常明显。继续优化，其中考虑过
多频繁访问的数据使用内存数据库的方式。但是优化过后在tomcat上效果明显，部署到生产环境就问题依旧。于是考虑weblogic的配置问题，作为开
发平台提供商，我们只是提供系统开发相关方面的支持，对于应用服务器和数据库服务器只是做基本的配置系统可运行即可。但是在这个问题上系统集成商咬定是我
们平台的问题不放，并且存在一个很严重的问题：他们使用的是盗版的weblogic，这样根本就没有相应的技术支持。
<br />
<br />
问题的解决：最后是找了一个BEA曾经的开发人员，问题实际非常的简单，现场部署的weblogic默认是运行在32位机器上，与64位机器存
在一定的不兼容。通过替换相应的jar包，问题得到了解决，主要是IO方面。替换完毕后，速度提升了进30%
。该开发人员说，如果没有lisence，根本就不会得到这些替换的jar包。
<br />
<br />
<strong>二、内存耗尽了</strong>
<br />
访问速度的问题解决了，系统的使用量很快上来，马上遇到新的问题：内存耗尽了。严重到几乎每天都要out of memory一次。这种问题在客户现场频繁出现。
<br />
<br />
本地测试，tomcat，sun jdk
通过Jprofiler监测内存使用情况。在并发访问门户的情况下，内存确实存在暴涨的情况，100并发，内存使用立刻上升了150m左右，继续并发
100，再增长150m。但是很快在抵达高峰时会有一次gc发生，内存使用稳定在200m，内存里大量char[]数组对象。疲劳测试，内存使用曲线并没
有出现逐渐上升泄露的情况。换weblogic和jrocket测试，gc发生的更加频繁，内存使用稳定。<br />
&nbsp;<br />
但是现场依旧频繁当机，内存根本释放不了，一直逐渐增长，典型的内存泄露。对系统缓存、单态对象包括spring管理的对象、IO流进行了统一
排查，依旧没有找到内存泄露的原因。使用IBM
工具分析heapdump文件，结果还是大量的char[]数组对象占据内存，查找应用，找不到相关业务对象引用。<br />
&nbsp;<br />
问题解决：问题解决是一篇偶尔搜到的oracle论坛的帖子，这里<a href="http://forums.oracle.com/forums/message.jspa?messageID=1040570 ">http://forums.oracle.com/forums/message.jspa?messageID=1040570
</a>。原因在于oracle10的数据库驱动对statement最后执行的结果集有着引用，并且不会释放，目的在于通过内存而换取更好的性能。数据库连接采
用的是weblogic的连接池，关于connection有个相关的statement
cache设定，设定一个connection能够被缓存的statement个数，最大是1024，而现场就被设定为了1024！connection
pool的connection个数被设置为了500
。真是个恐怖的设置。在将1024改为10后，内存使用量轰然倒地，稳定在1g左右。这个设置是在前面系统访问速度存在问题时由系统集成商的开发人员设置
上去的，他们将所有和优化相关的参数全部开到了最大。这个问题要是用户购买的是正版的weblogic和oracle的话，相信也会很快得到解决。
<br />
<br />
<strong>三、线程阻塞</strong>
<br />
内存泄露的问题解决后，线程阻塞的问题浮出水面。系统集成商报告是线程死锁，通过分析工具其实是线程阻塞，主要问题在于系统用到了
synchronized关键字，对工作流相关API全部使用了synchronized，原因在这里：<a href="http: //ronghao.javaeye.com/blog/205731">http:
//ronghao.javaeye.com/blog/205731</a>
。分析发现一个工作项提交的操作在连接数据库时被挂起了20分钟！造成了大量线程的排队阻塞。被挂起的原因有很多种。我们采用的方法是将接口拆分和设置事
务timeout时间。但是这显然不是一个好方法。最后是去掉所有的synchronized关键字，将同步的问题交由数据库解决，问题解决。
<br />
<br />
<strong>四、反思</strong>
<br />
1、系统集成商为什么不购买正版？
<br />
2、开发平台提供商究竟在项目开发中处于一种什么样的位置？开发平台是否对所有软件开发问题都要负责？
<br />
3、开发平台是越封装越快乐吗？还是越封装越丑陋？<br />
<br />
更具体的细节在这里：<br />
<h1>&nbsp;<a href="http://blog.csdn.net/snow_fox_yaya/archive/2008/07/24/2704336.aspx"><a href="http://blog.csdn.net/snow_fox_yaya/archive/2008/07/24/2704336.aspx">AIX+weblogic性能诊断记录</a></a><a href="http://blog.csdn.net/snow_fox_yaya/archive/2008/07/24/2704336.aspx"></a></h1>
<img src ="http://www.blogjava.net/RongHao/aggbug/226068.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/RongHao/" target="_blank">ronghao</a> 2008-09-01 13:49 <a href="http://www.blogjava.net/RongHao/archive/2008/09/01/226068.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>从贫血到充血Domain Model</title><link>http://www.blogjava.net/RongHao/archive/2008/07/03/212402.html</link><dc:creator>ronghao</dc:creator><author>ronghao</author><pubDate>Thu, 03 Jul 2008 10:23:00 GMT</pubDate><guid>http://www.blogjava.net/RongHao/archive/2008/07/03/212402.html</guid><wfw:comment>http://www.blogjava.net/RongHao/comments/212402.html</wfw:comment><comments>http://www.blogjava.net/RongHao/archive/2008/07/03/212402.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/RongHao/comments/commentRss/212402.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/RongHao/services/trackbacks/212402.html</trackback:ping><description><![CDATA[关于Domain Model的讨论已经非常多了，炒炒冷饭，这里是自己的一些做法。<br />
以Workitem（工作流里的工作项）作为例子<br />
<br />
<strong>最开始的做法：</strong><br />
一个实体类叫做Workitem，指的是一个工作项或者称为任务项<br />
一个DAO类叫做WorkitemDao<br />
一个业务逻辑类叫做WorkitemManager(或者叫做WorkitemService)<br />
<br />
主要看看WorkitemManager，因为主要逻辑集中在这里<br />
<br />
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;WorkitemManager&nbsp;{<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;WorkItemDAO&nbsp;workItemDAO;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;setWorkItemDAO(WorkItemDAO&nbsp;workItemDAO)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">this</span><span style="color: #000000;">.workItemDAO&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;workItemDAO;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">/**</span><span style="color: #008000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;提交工作项<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;</span><span style="color: #808080;">@param</span><span style="color: #008000;">&nbsp;workitemId&nbsp;工作项ID<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;commitWorkitem(String&nbsp;workitemId){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;WorkItem&nbsp;workitem&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;workItemDAO.getWorkItem(workitemId);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">当前工作项结束</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;workitem.complete();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;sID&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;workitem.getSequenceId();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">找到所对应的节点</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;InstActivity&nbsp;instActivity</span><span style="color: #000000;">=</span><span style="color: #000000;">workitem.getInstActivity();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">查找是否存在下一工作项</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;WorkItem&nbsp;sequenceWorkitem&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;workItemDAO.findSequenceWorkItem(instActivity.getId(),&nbsp;sID&nbsp;</span><span style="color: #000000;">+</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">1</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">如果不存在则触发节点流转</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(sequenceWorkitem&nbsp;</span><span style="color: #000000;">==</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">null</span><span style="color: #000000;">)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;instActivity.signal();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">否则把下一工作项激活</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">else</span><span style="color: #000000;">&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sequenceWorkitem.setExecutive();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
}</span></div>
<br />
<br />
Workitem类里有一些状态转换的逻辑，这样避免直接调用get/set属性方法<br />
<br />
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;Workitem{<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;state&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;WorkitemInfo.PREPARE;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">/**</span><span style="color: #008000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;委派工作项<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;commission()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(state&nbsp;</span><span style="color: #000000;">!=</span><span style="color: #000000;">&nbsp;WorkitemInfo.EXECUTE&nbsp;</span><span style="color: #000000;">&amp;&amp;</span><span style="color: #000000;">&nbsp;state&nbsp;</span><span style="color: #000000;">!=</span><span style="color: #000000;">&nbsp;WorkitemInfo.SIGNINED<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">&amp;&amp;</span><span style="color: #000000;">&nbsp;state&nbsp;</span><span style="color: #000000;">!=</span><span style="color: #000000;">&nbsp;WorkitemInfo.TOREAD</span><span style="color: #000000;">&amp;&amp;</span><span style="color: #000000;">&nbsp;state&nbsp;</span><span style="color: #000000;">!=</span><span style="color: #000000;">&nbsp;WorkitemInfo.SUSPEND)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">throw</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;WorkflowException(Messages.CANNOT_ALTER_WORKITEM_STATE);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setState(WorkitemInfo.COMMISSIONED);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setCommitted(</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;Timestamp(System.currentTimeMillis()));<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">/**</span><span style="color: #008000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;完成工作项<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;complete()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(state&nbsp;</span><span style="color: #000000;">!=</span><span style="color: #000000;">&nbsp;WorkitemInfo.SIGNINED)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">throw</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;WorkflowException(Messages.CANNOT_ALTER_WORKITEM_STATE);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setState(WorkitemInfo.COMPLETE);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setCompleted(</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;Timestamp(System.currentTimeMillis()));<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}</span></div>
<br />
<br />
<strong>接下来的做法：</strong><br />
三个类不变，将WorkitemManager打平，将逻辑移动到Workitem<br />
<br />
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;WorkitemManager&nbsp;{<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;WorkItemDAO&nbsp;workItemDAO;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;setWorkItemDAO(WorkItemDAO&nbsp;workItemDAO)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">this</span><span style="color: #000000;">.workItemDAO&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;workItemDAO;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">/**</span><span style="color: #008000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;提交工作项<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;</span><span style="color: #808080;">@param</span><span style="color: #008000;">&nbsp;workitemId&nbsp;工作项ID<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;commitWorkitem(String&nbsp;workitemId){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;WorkItem&nbsp;workitem&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;workItemDAO.getWorkItem(workitemId);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">当前工作项提交</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;workitem.commit();<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
}</span></div>
<br />
实际上此时WorkitemManager的功能非常有限，仅仅是事务边界和获取workitem对象，甚至在一些情况下可以省略。<br />
<br />
通过一个Container类将spring的applicationContext进行封装，然后通过getBean()的静态方法即可访问被spring所管理的bean。实际是将workItemDAO隐式注入了Workitem。<br />
<br />
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;Workitem{<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">/**</span><span style="color: #008000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;提交工作项<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;commit()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(state&nbsp;</span><span style="color: #000000;">!=</span><span style="color: #000000;">&nbsp;WorkitemInfo.EXECUTE&nbsp;</span><span style="color: #000000;">&amp;&amp;</span><span style="color: #000000;">&nbsp;state&nbsp;</span><span style="color: #000000;">!=</span><span style="color: #000000;">&nbsp;WorkitemInfo.SIGNINED<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">&amp;&amp;</span><span style="color: #000000;">&nbsp;state&nbsp;</span><span style="color: #000000;">!=</span><span style="color: #000000;">&nbsp;WorkitemInfo.TOREAD</span><span style="color: #000000;">&amp;&amp;</span><span style="color: #000000;">&nbsp;state&nbsp;</span><span style="color: #000000;">!=</span><span style="color: #000000;">&nbsp;WorkitemInfo.SUSPEND)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">throw</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;WorkflowException(Messages.CANNOT_ALTER_WORKITEM_STATE);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setState(WorkitemInfo.COMMISSIONED);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setCommitted(</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;Timestamp(System.currentTimeMillis()));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;sID&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;workitem.getSequenceId();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;WorkItemDAO&nbsp;workItemDAO</span><span style="color: #000000;">=</span><span style="color: #000000;">(WorkItemDAO)Container.getBean(</span><span style="color: #000000;">"</span><span style="color: #000000;">workItemDAO</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">查找是否存在下一工作项</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;WorkItem&nbsp;sequenceWorkitem&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;workItemDAO.findSequenceWorkItem(instActivity.getId(),&nbsp;sID&nbsp;</span><span style="color: #000000;">+</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">1</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">如果不存在则触发节点流转</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(sequenceWorkitem&nbsp;</span><span style="color: #000000;">==</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">null</span><span style="color: #000000;">)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;instActivity.signal();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">否则把下一工作项激活</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">else</span><span style="color: #000000;">&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sequenceWorkitem.setExecutive();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
}</span></div>
<br />
<br />
这样带来的好处是业务逻辑全部被封装到Domain Model，Domain Model之间的交互变得非常的简单，没有频繁的set/get，直接调用有业务语义的Domain Model的方法即可。问题在于单元测试时脱离不了spring的容器，workItemDAO需要stub。我觉得这个问题不大，问题是Domain Model开始变得臃肿，在业务逻辑复杂时代码行急剧膨胀。<br />
<br />
<strong>现在的做法</strong><br />
以上三个类保持不变，增加一个类WorkitemExecutor，将业务逻辑移步。<br />
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000;"><br />
</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;Workitem{<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">/**</span><span style="color: #008000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;提交工作项<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;commit()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(state&nbsp;</span><span style="color: #000000;">!=</span><span style="color: #000000;">&nbsp;WorkitemInfo.EXECUTE&nbsp;</span><span style="color: #000000;">&amp;&amp;</span><span style="color: #000000;">&nbsp;state&nbsp;</span><span style="color: #000000;">!=</span><span style="color: #000000;">&nbsp;WorkitemInfo.SIGNINED<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">&amp;&amp;</span><span style="color: #000000;">&nbsp;state&nbsp;</span><span style="color: #000000;">!=</span><span style="color: #000000;">&nbsp;WorkitemInfo.TOREAD</span><span style="color: #000000;">&amp;&amp;</span><span style="color: #000000;">&nbsp;state&nbsp;</span><span style="color: #000000;">!=</span><span style="color: #000000;">&nbsp;WorkitemInfo.SUSPEND)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">throw</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;WorkflowException(Messages.CANNOT_ALTER_WORKITEM_STATE);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setState(WorkitemInfo.COMMISSIONED);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setCommitted(</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;Timestamp(System.currentTimeMillis()));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;WorkitemExecutor&nbsp;workitemExecutor</span><span style="color: #000000;">=</span><span style="color: #000000;">(WorkitemExecutor)Container.getBean(</span><span style="color: #000000;">"</span><span style="color: #000000;">workitemExecutor</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;workitemExecutor.commitWorkitem(</span><span style="color: #0000ff;">this</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
}<br />
<br />
</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;WorkitemExecutor&nbsp;{<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;WorkItemDAO&nbsp;workItemDAO;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;setWorkItemDAO(WorkItemDAO&nbsp;workItemDAO)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">this</span><span style="color: #000000;">.workItemDAO&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;workItemDAO;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">/**</span><span style="color: #008000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;提交工作项<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;</span><span style="color: #808080;">@param</span><span style="color: #008000;">&nbsp;workitemId&nbsp;工作项ID<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;commitWorkitem(Workitem&nbsp;workitem){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;sID&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;workitem.getSequenceId();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">找到所对应的节点</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;InstActivity&nbsp;instActivity</span><span style="color: #000000;">=</span><span style="color: #000000;">workitem.getInstActivity();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">查找是否存在下一工作项</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;WorkItem&nbsp;sequenceWorkitem&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;workItemDAO.findSequenceWorkItem(instActivity.getId(),&nbsp;sID&nbsp;</span><span style="color: #000000;">+</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">1</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">如果不存在则触发节点流转</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(sequenceWorkitem&nbsp;</span><span style="color: #000000;">==</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">null</span><span style="color: #000000;">)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;instActivity.signal();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">否则把下一工作项激活</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">else</span><span style="color: #000000;">&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sequenceWorkitem.setExecutive();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
}</span></div>
<br />
<br />
将业务逻辑拆分成两部分，一部分在Workitem，另一部分委托给WorkitemExecutor。实际上是Domain Model将复杂逻辑的情况重新外包出去。调用的时候，面向的接口还是Domain Model的方法。注意到WorkitemExecutor和WorkitemManager的API是非常相似的。实际可以这样认为，传统的方式 <br />
Client-&gt;(Business Facade)-&gt;service(Business Logic 部分依赖Domain Model)-&gt;Data Access(DAO)。<br />
现在的方式<br />
Client-&gt;(Business Facade)-&gt;Domain Model-&gt;service-&gt;Data Access(DAO)。<br />
<br />
另外，在返回client端的查询的时候还是倾向于直接调用DAO，而不是通过Domain Model。<br />
<br />
改进：<br />
注意到代码中有这么一行<br />
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000;">WorkItemDAO&nbsp;workItemDAO</span><span style="color: #000000;">=</span><span style="color: #000000;">(WorkItemDAO)Container.getBean(</span><span style="color: #000000;">"</span><span style="color: #000000;">workItemDAO</span><span style="color: #000000;">"</span><span style="color: #000000;">);</span></div>
<br />
确实是一个bad smell.当代码中大量出现后，这种造型是很恐怖的。所以采取了一种处理方式：给所有Domain Model继承一个父类，在父类里集中管理所有Domain Model所依赖的services，在父类里进行造型。<br />
<br />
<br />
<img src ="http://www.blogjava.net/RongHao/aggbug/212402.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/RongHao/" target="_blank">ronghao</a> 2008-07-03 18:23 <a href="http://www.blogjava.net/RongHao/archive/2008/07/03/212402.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>高并发测试下的一些问题及解决</title><link>http://www.blogjava.net/RongHao/archive/2008/06/19/209140.html</link><dc:creator>ronghao</dc:creator><author>ronghao</author><pubDate>Thu, 19 Jun 2008 05:34:00 GMT</pubDate><guid>http://www.blogjava.net/RongHao/archive/2008/06/19/209140.html</guid><wfw:comment>http://www.blogjava.net/RongHao/comments/209140.html</wfw:comment><comments>http://www.blogjava.net/RongHao/archive/2008/06/19/209140.html#Feedback</comments><slash:comments>22</slash:comments><wfw:commentRss>http://www.blogjava.net/RongHao/comments/commentRss/209140.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/RongHao/services/trackbacks/209140.html</trackback:ping><description><![CDATA[测试在sqlserver2000上进行，对工作流操作的相关方法在单元测试里进行多线程并发。测试发现sqlserver出现死锁的情况相当多，一些典型的情况：<br />
<br />
1、对同一张表先insert再update是很快会引起死锁的，不管操作的是否是同一记录<br />
解决方法：对于同一记录，需要调整hibernate的映射策略，使得一次insert完成操作。对于不同的记录需要在代码中手动flush，使得update先于insert。<br />
<br />
2、对两张表进行多次update操作时，两张表交替update也会很快引起死锁<br />
解决方法：在代码中手动flush，保证对两张表的update不会出现交替的情况。<br />
<br />
3、部分大范围扫描的select和update混合也会导致死锁<br />
解决方法：优化sql，尽量减少sql语句，通过给po增加持久化字段的方式减少关联查询<br />
<br />
经过优化，大部分情况下数据库死锁的情况得以避免，另外奇怪的是通过事件探查器在死锁时并未发现锁升级的事件。但是在一些特殊情况下（例如多个并发汇聚的直接联合），死锁依旧发生。最后不得不对方法进行synchronized关键字同步，这个通过synchronized flush完成。业务方法不必同步，最后批量操作数据库时进行同步。<br />
<br />
换oracle进行测试，在未synchronized的情况下，未发生死锁情况。由此可见sqlserver与oracle锁实现机制存在很大的差别。对sqlserver鄙视之。另，同事说，sqlserver2005后性能和机制发生了很大的变化，未测试。<br />
<br />
补充一下我的一个最简单情况下的测试用例：<br />
PO：<br />
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;TestPO&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;id;<br />
&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;name;<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;num;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<img src="http://www.blogjava.net/Images/dot.gif"  alt="" />.<br />
}</span></div>
<br />
映射文件 hibernate3：<br />
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000;">&lt;</span><span style="color: #000000;">hibernate</span><span style="color: #000000;">-</span><span style="color: #000000;">mapping&nbsp;</span><span style="color: #0000ff;">default</span><span style="color: #000000;">-</span><span style="color: #000000;">access</span><span style="color: #000000;">=</span><span style="color: #000000;">"</span><span style="color: #000000;">field</span><span style="color: #000000;">"</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"><br />
&nbsp;&nbsp;</span><span style="color: #000000;">&lt;</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;table</span><span style="color: #000000;">=</span><span style="color: #000000;">"</span><span style="color: #000000;">WFMS_TESTPO</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;name</span><span style="color: #000000;">=</span><span style="color: #000000;">"</span><span style="color: #000000;">com.eway.workflow.test.po.TestPO</span><span style="color: #000000;">"</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"><br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">id&nbsp;name</span><span style="color: #000000;">=</span><span style="color: #000000;">"</span><span style="color: #000000;">id</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;column</span><span style="color: #000000;">=</span><span style="color: #000000;">"</span><span style="color: #000000;">ID</span><span style="color: #000000;">"</span><span style="color: #000000;">&gt;&lt;</span><span style="color: #000000;">generator&nbsp;</span><span style="color: #0000ff;">class</span><span style="color: #000000;">=</span><span style="color: #000000;">"</span><span style="color: #000000;">uuid</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">/&gt;&lt;/</span><span style="color: #000000;">id</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"><br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">property&nbsp;name</span><span style="color: #000000;">=</span><span style="color: #000000;">"</span><span style="color: #000000;">name</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;column</span><span style="color: #000000;">=</span><span style="color: #000000;">"</span><span style="color: #000000;">NAME</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;type</span><span style="color: #000000;">=</span><span style="color: #000000;">"</span><span style="color: #000000;">string</span><span style="color: #000000;">"</span><span style="color: #000000;">/&gt;</span><span style="color: #000000;"><br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">property&nbsp;name</span><span style="color: #000000;">=</span><span style="color: #000000;">"</span><span style="color: #000000;">num</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;column</span><span style="color: #000000;">=</span><span style="color: #000000;">"</span><span style="color: #000000;">NUM</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;type</span><span style="color: #000000;">=</span><span style="color: #000000;">"</span><span style="color: #000000;">integer</span><span style="color: #000000;">"</span><span style="color: #000000;">/&gt;</span><span style="color: #000000;"><br />
<br />
&nbsp;&nbsp;</span><span style="color: #000000;">&lt;/</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"><br />
</span><span style="color: #000000;">&lt;/</span><span style="color: #000000;">hibernate</span><span style="color: #000000;">-</span><span style="color: #000000;">mapping</span><span style="color: #000000;">&gt;</span></div>
<br />
被测试方法（都配置有事务）：<br />
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;testSave(</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;num)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TestPO&nbsp;po&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;TestPO();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;po.setName(</span><span style="color: #000000;">"</span><span style="color: #000000;">ronghao</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;po.setNum(num);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;theadTestDao.save(po);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;po.setName(</span><span style="color: #000000;">"</span><span style="color: #000000;">haorong</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;testSaveByJdbc(</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;num)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;sql&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">"</span><span style="color: #000000;">insert&nbsp;into&nbsp;WFMS_TESTPO&nbsp;(ID,NAME,NUM)&nbsp;values&nbsp;(?,'RONGHAO',?)</span><span style="color: #000000;">"</span><span style="color: #000000;">;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Object[]&nbsp;params&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;Object[]{num,num};<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;jdbcTemplate.update(sql,&nbsp;params);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sql</span><span style="color: #000000;">=</span><span style="color: #000000;">"</span><span style="color: #000000;">update&nbsp;WFMS_TESTPO&nbsp;set&nbsp;name='haorong'&nbsp;where&nbsp;id=?</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;&nbsp;;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;params&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;Object[]{num};<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;jdbcTemplate.update(sql,&nbsp;params);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}</span></div>
<br />
测试用例：<br />
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;testSave()&nbsp;</span><span style="color: #0000ff;">throws</span><span style="color: #000000;">&nbsp;Exception&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TheadtestTemplate&nbsp;template&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;TheadtestTemplate();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;template.execute(</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;TheadtestCallback()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;doInThead(</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;suquence)&nbsp;{<br />
</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;theadTestManager.testSave(suquence);</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;theadTestManager.testSaveByJdbc(suquence);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;},&nbsp;</span><span style="color: #000000;">10</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}</span></div>
<br />
测试结果：不论是hibernate还是jdbc，并发情况下都很快就会引起sqlserver2000的死锁，换用两种数据库驱动jtds和jturbo死锁的情况没有变化。<br />
<br />
<strong>结论</strong>：sqlserver2000数据库的lock配置策略，不支持，或者数据库本身，就不支持对不同的行做同时操作（或者支持不完善），所谓的行锁支持很不完善，死锁情况非常容易发生。<br />
<br />
补充：我对数据库的一些实现机制也并不是很了解，所以这里也只能列出现象而不能解释死锁的根本原因。另外感谢Alex的讨论。<br />
<img src ="http://www.blogjava.net/RongHao/aggbug/209140.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/RongHao/" target="_blank">ronghao</a> 2008-06-19 13:34 <a href="http://www.blogjava.net/RongHao/archive/2008/06/19/209140.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>failed batch HSQLDB </title><link>http://www.blogjava.net/RongHao/archive/2008/05/30/204166.html</link><dc:creator>ronghao</dc:creator><author>ronghao</author><pubDate>Fri, 30 May 2008 10:40:00 GMT</pubDate><guid>http://www.blogjava.net/RongHao/archive/2008/05/30/204166.html</guid><wfw:comment>http://www.blogjava.net/RongHao/comments/204166.html</wfw:comment><comments>http://www.blogjava.net/RongHao/archive/2008/05/30/204166.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/RongHao/comments/commentRss/204166.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/RongHao/services/trackbacks/204166.html</trackback:ping><description><![CDATA[今天用hsqldb做单元测试时碰到这么个异常<br />
failed batch; nested exception is java.sql.BatchUpdateException: failed batch<br />
经过检查发现是HSQLDB的问题<br />
The bug is in HSQLDB - a well known one (any Google search for HSQLDB and that "failed batch" <br />
message would have told you it).<br />
https://sourceforge.net/tracker/?func=detail&amp;atid=378131&amp;aid=1407528&amp;group_id=23316<br />
解决方法 : turn off batching with HSQLDB it doesnt work.<br />
设置&lt;prop key="hibernate.jdbc.batch_size"&gt;0&lt;/prop&gt;即可
<img src ="http://www.blogjava.net/RongHao/aggbug/204166.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/RongHao/" target="_blank">ronghao</a> 2008-05-30 18:40 <a href="http://www.blogjava.net/RongHao/archive/2008/05/30/204166.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>使用solr搭建你的全文检索</title><link>http://www.blogjava.net/RongHao/archive/2007/11/06/158621.html</link><dc:creator>ronghao</dc:creator><author>ronghao</author><pubDate>Tue, 06 Nov 2007 10:03:00 GMT</pubDate><guid>http://www.blogjava.net/RongHao/archive/2007/11/06/158621.html</guid><wfw:comment>http://www.blogjava.net/RongHao/comments/158621.html</wfw:comment><comments>http://www.blogjava.net/RongHao/archive/2007/11/06/158621.html#Feedback</comments><slash:comments>10</slash:comments><wfw:commentRss>http://www.blogjava.net/RongHao/comments/commentRss/158621.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/RongHao/services/trackbacks/158621.html</trackback:ping><description><![CDATA[<p><span style="font-size: 9pt; font-family: 宋体;">Solr </span><span style="font-size: 9pt; font-family: 宋体;">是一个可供企业使用的、基于 Lucene 的开箱即用的搜索服务器。对Lucene不熟？那么建议先看看下面两篇文档：</span></p>
<p><span style="font-size: 9pt; font-family: 宋体;">实战</span><span style="font-size: 9pt; font-family: 宋体;">Lucene</span><span style="font-size: 9pt; font-family: 宋体;">，第 1 部分: 初识 Lucene：<a href="http://www.ibm.com/developerworks/cn/java/j-lo-lucene1/">http://www.ibm.com/developerworks/cn/java/j-lo-lucene1/</a></span></p>
<p><span style="font-size: 9pt; font-family: 宋体;">用</span><span style="font-size: 9pt; font-family: 宋体;">Lucene</span><span style="font-size: 9pt; font-family: 宋体;">加速Web搜索应用程序的开发：<a href="http://www.ibm.com/developerworks/cn/web/wa-lucene2/">http://www.ibm.com/developerworks/cn/web/wa-lucene2/</a></span></p>
<p style="margin-left: 25.5pt; text-indent: -25.5pt;"><strong><span style="font-size: 9pt; font-family: 宋体;">一、&nbsp;</span></strong><strong><span style="font-size: 9pt; font-family: 宋体;">solr</span></strong><strong><span style="font-size: 9pt; font-family: 宋体;">介绍</span></strong></p>
<p style="margin-left: 25.5pt;"><span style="font-size: 9pt; font-family: 宋体; color: #cc0033;">solr</span><span style="font-size: 9pt; font-family: 宋体;">是基于Lucene Java搜索库的企业级全文搜索引擎，目前是apache的一个项目。它的官方网址在<a href="http://lucene.apache.org/solr/">http://lucene.apache.org/solr/</a> 。solr需要运行在一个</span><span style="font-size: 9pt;">servlet
</span><span style="font-size: 9pt; font-family: 宋体;">容器里，例如</span><span style="font-size: 9pt;">tomcat5.5</span><span style="font-size: 9pt; font-family: 宋体;">。</span><span style="font-size: 9pt;">solr</span><span style="font-size: 9pt; font-family: 宋体;">在</span><span style="font-size: 9pt;">lucene</span><span style="font-size: 9pt; font-family: 宋体;">的上层提供了一个基于</span><span style="font-size: 9pt;">HTTP/XML</span><span style="font-size: 9pt; font-family: 宋体;">的</span><span style="font-size: 9pt;">Web Services</span><span style="font-size: 9pt; font-family: 宋体;">，我们的应用需要通过这个服务与</span><span style="font-size: 9pt;">solr</span><span style="font-size: 9pt; font-family: 宋体;">进行交互。</span></p>
<p style="margin-left: 25.5pt; text-indent: -25.5pt;"><strong><span style="font-size: 9pt; font-family: 宋体;">二、&nbsp;</span></strong><strong><span style="font-size: 9pt; font-family: 宋体;">solr</span></strong><strong><span style="font-size: 9pt; font-family: 宋体;">安装和配置</span></strong></p>
<p style="margin-left: 25.5pt;"><span style="font-size: 9pt; font-family: 宋体;">关于solr的安装和配置，这里也有两篇非常好的文档，作者同时也</span><span style="font-size: 9pt; font-family: 宋体;">是</span><span style="font-size: 9pt;"> Lucene Java </span><span style="font-size: 9pt; font-family: 宋体;">项目的提交人和发言人：</span></p>
<p style="margin-left: 25.5pt;"><span style="font-size: 9pt; font-family: 宋体;">使用</span><span style="font-size: 9pt;">Apache Solr</span><span style="font-size: 9pt; font-family: 宋体;">实现更加灵巧的搜索：</span><span style="font-size: 9pt;"><a href="http://www.ibm.com/developerworks/cn/java/j-solr1/index.html">http://www.ibm.com/developerworks/cn/java/j-solr1/index.html</a></span></p>
<p style="margin-left: 25.5pt;"><span style="font-size: 9pt;"><a href="http://www.ibm.com/developerworks/cn/java/j-solr2/index.html">http://www.ibm.com/developerworks/cn/java/j-solr2/index.html</a></span></p>
<p style="margin-left: 25.5pt;"><span style="font-size: 9pt; font-family: 宋体;">下面主要说说需要注意的地方。</span></p>
<p style="margin-left: 25.5pt;"><span style="font-size: 9pt;">Solr</span><span style="font-size: 9pt; font-family: 宋体;">的安装非常简单，下载</span><span style="font-size: 9pt;">solr</span><span style="font-size: 9pt; font-family: 宋体;">的</span><span style="font-size: 9pt;">zip</span><span style="font-size: 9pt; font-family: 宋体;">包后解压缩将</span><span style="font-size: 9pt;">dist</span><span style="font-size: 9pt; font-family: 宋体;">目录下的</span><span style="font-size: 9pt;">war</span><span style="font-size: 9pt; font-family: 宋体;">文件改名为</span><span style="font-size: 9pt;">solr.war</span><span style="font-size: 9pt; font-family: 宋体;">直接复制到</span><span style="font-size: 9pt;">tomcat5.5</span><span style="font-size: 9pt; font-family: 宋体;">的</span><span style="font-size: 9pt;">webapps</span><span style="font-size: 9pt; font-family: 宋体;">目录即可。注意一定要设置</span><span style="font-size: 9pt;">solr</span><span style="font-size: 9pt; font-family: 宋体;">的主位置。有三种方法。我采用的是</span><span style="font-size: 9pt; font-family: 宋体;">在tomcat里配置java:comp/env/solr/home的一个JNDI指向solr的主目录（example目录下），建立/tomcat55/conf/Catalina/localhost/solr.xml文件。</span></p>
<br />
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000ff;">&lt;</span><span style="color: #800000;">Context&nbsp;</span><span style="color: #ff0000;">docBase</span><span style="color: #0000ff;">="D:/solr.war"</span><span style="color: #ff0000;">&nbsp;debug</span><span style="color: #0000ff;">="0"</span><span style="color: #ff0000;">&nbsp;crossContext</span><span style="color: #0000ff;">="true"</span><span style="color: #ff0000;">&nbsp;</span><span style="color: #0000ff;">&gt;</span><span style="color: #000000;"><br />
<br />
&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">&lt;</span><span style="color: #800000;">Environment&nbsp;</span><span style="color: #ff0000;">name</span><span style="color: #0000ff;">="solr/home"</span><span style="color: #ff0000;">&nbsp;type</span><span style="color: #0000ff;">="java.lang.String"</span><span style="color: #ff0000;">&nbsp;value</span><span style="color: #0000ff;">="D:/solr/solr"</span><span style="color: #ff0000;">&nbsp;override</span><span style="color: #0000ff;">="true"</span><span style="color: #ff0000;">&nbsp;</span><span style="color: #0000ff;">/&gt;</span><span style="color: #000000;"><br />
<br />
</span><span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">Context</span><span style="color: #0000ff;">&gt;</span><span style="color: #000000;"><br />
</span></div>
<p style="margin-left: 22.5pt; text-indent: -22.5pt;"><span style="font-size: 9pt; font-family: 宋体;">&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-size: 9pt; font-family: 宋体;">观察这个指定的solr主位置，里面存在两个文件夹：conf和data。其中conf里存放了对solr而言最为重要的两个配置文件schema.xml和solrconfig.xml。data则用于存放索引文件。</span></p>
<p style="margin-left: 22.5pt; text-indent: -22.5pt;"><span style="font-size: 9pt; font-family: 宋体;">&nbsp;&nbsp;&nbsp;&nbsp; schema.xml</span><span style="font-size: 9pt; font-family: 宋体;">主要包括</span><span style="font-size: 9pt;">types</span><span style="font-size: 9pt; font-family: 宋体;">、</span><span style="font-size: 9pt;">fields</span><span style="font-size: 9pt; font-family: 宋体;">和其他的一些缺省设置。</span></p>
<p style="margin-left: 22.55pt;"><span style="font-size: 9pt; font-family: 宋体;">solrconfig.xml</span><span style="font-size: 9pt; font-family: 宋体;">用来配置</span><span style="font-size: 9pt;">Solr</span><span style="font-size: 9pt; font-family: 宋体;">的一些系统属性，例如与索引和查询处理有关的一些常见的配置选项，以及缓存、扩展等等。</span></p>
<p style="margin-left: 22.55pt;"><span style="font-size: 9pt; font-family: 宋体;">上面的文档对这两个文件有比较详细的说明，非常容易上手。注意到</span><span style="font-size: 9pt; font-family: 宋体;">schema.xml</span><span style="font-size: 9pt; font-family: 宋体;">里有一个</span></p>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000ff;">&lt;</span><span style="color: #800000;">uniqueKey</span><span style="color: #0000ff;">&gt;</span><span style="color: #000000;">url</span><span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">uniqueKey</span><span style="color: #0000ff;">&gt;</span></div>
<p style="margin-left: 22.55pt;"><span style="font-size: 9pt; font-family: 宋体;">的配置，这里将</span><span style="font-size: 9pt;">url</span><span style="font-size: 9pt; font-family: 宋体;">字段作为索引文档的唯一标识符，非常重要。</span></p>
<p style="margin-left: 25.5pt; text-indent: -25.5pt;"><strong><span style="font-size: 9pt; font-family: 宋体;">三、&nbsp;</span></strong><strong><span style="font-size: 9pt; font-family: 宋体;">加入中文分词</span></strong></p>
<p style="margin-left: 25.5pt;"><span style="font-size: 9pt; font-family: 宋体;">对全文检索而言，中文分词非常的重要，这里采用了qieqie庖丁分词（非常不错：））。集成非常的容易，我下载的是2.0.4-alpha2版本，其中它支持最多切分和按最大切分。创建自己的一个中文TokenizerFactory继承自solr的BaseTokenizerFactory。</span></p>
<br />
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #008000;">/**</span><span style="color: #008000;"><br />
<br />
&nbsp;*&nbsp;Created&nbsp;by&nbsp;IntelliJ&nbsp;IDEA.<br />
<br />
&nbsp;*&nbsp;User:&nbsp;ronghao<br />
<br />
&nbsp;*&nbsp;Date:&nbsp;2007-11-3<br />
<br />
&nbsp;*&nbsp;Time:&nbsp;14:40:59<br />
<br />
&nbsp;*&nbsp;中文切词&nbsp;对庖丁切词的封装<br />
<br />
&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br />
<br />
</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;ChineseTokenizerFactory&nbsp;</span><span style="color: #0000ff;">extends</span><span style="color: #000000;">&nbsp;BaseTokenizerFactory&nbsp;{<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">/**</span><span style="color: #008000;"><br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;最多切分&nbsp;&nbsp;&nbsp;默认模式<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">static</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">final</span><span style="color: #000000;">&nbsp;String&nbsp;MOST_WORDS_MODE&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">"</span><span style="color: #000000;">most-words</span><span style="color: #000000;">"</span><span style="color: #000000;">;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">/**</span><span style="color: #008000;"><br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;按最大切分<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">static</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">final</span><span style="color: #000000;">&nbsp;String&nbsp;MAX_WORD_LENGTH_MODE&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">"</span><span style="color: #000000;">max-word-length</span><span style="color: #000000;">"</span><span style="color: #000000;">;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;String&nbsp;mode&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">null</span><span style="color: #000000;">;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;setMode(String&nbsp;mode)&nbsp;{<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(mode</span><span style="color: #000000;">==</span><span style="color: #0000ff;">null</span><span style="color: #000000;">||</span><span style="color: #000000;">MOST_WORDS_MODE.equalsIgnoreCase(mode)<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">||</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">"</span><span style="color: #000000;">default</span><span style="color: #000000;">"</span><span style="color: #000000;">.equalsIgnoreCase(mode))&nbsp;{<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">this</span><span style="color: #000000;">.mode</span><span style="color: #000000;">=</span><span style="color: #000000;">MOST_WORDS_MODE;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</span><span style="color: #0000ff;">else</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(MAX_WORD_LENGTH_MODE.equalsIgnoreCase(mode))&nbsp;{<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">this</span><span style="color: #000000;">.mode</span><span style="color: #000000;">=</span><span style="color: #000000;">MAX_WORD_LENGTH_MODE;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">else</span><span style="color: #000000;">&nbsp;{<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">throw</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;IllegalArgumentException(</span><span style="color: #000000;">"</span><span style="color: #000000;">不合法的分析器Mode参数设置:</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">+</span><span style="color: #000000;">&nbsp;mode);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;@Override<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;init(Map</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">String,&nbsp;String</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">&nbsp;args)&nbsp;{<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">super</span><span style="color: #000000;">.init(args);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setMode(args.get(</span><span style="color: #000000;">"</span><span style="color: #000000;">mode</span><span style="color: #000000;">"</span><span style="color: #000000;">));<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;TokenStream&nbsp;create(Reader&nbsp;input)&nbsp;{<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;PaodingTokenizer(input,&nbsp;PaodingMaker.make(),<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;createTokenCollector());<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;TokenCollector&nbsp;createTokenCollector()&nbsp;{<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">(&nbsp;MOST_WORDS_MODE.equals(mode))<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;MostWordsTokenCollector();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">(&nbsp;MAX_WORD_LENGTH_MODE.equals(mode))<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;MaxWordLengthTokenCollector();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">throw</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;Error(</span><span style="color: #000000;">"</span><span style="color: #000000;">never&nbsp;happened</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
}<br />
</span></div>
<p style="margin-left: 25.5pt;"><span style="font-size: 9pt; font-family: 宋体;">在schema.xml的字段text配置里加入该分词器。</span></p>
<br />
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000ff;">&lt;</span><span style="color: #800000;">fieldtype&nbsp;</span><span style="color: #ff0000;">name</span><span style="color: #0000ff;">="text"</span><span style="color: #ff0000;">&nbsp;class</span><span style="color: #0000ff;">="solr.TextField"</span><span style="color: #ff0000;">&nbsp;positionIncrementGap</span><span style="color: #0000ff;">="100"</span><span style="color: #0000ff;">&gt;</span><span style="color: #000000;"><br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">&lt;</span><span style="color: #800000;">analyzer&nbsp;</span><span style="color: #ff0000;">type</span><span style="color: #0000ff;">="index"</span><span style="color: #0000ff;">&gt;</span><span style="color: #000000;"><br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">&lt;</span><span style="color: #800000;">tokenizer&nbsp;</span><span style="color: #ff0000;">class</span><span style="color: #0000ff;">="com.ronghao.fulltextsearch.analyzer.ChineseTokenizerFactory"</span><span style="color: #ff0000;">&nbsp;mode</span><span style="color: #0000ff;">="most-words"</span><span style="color: #0000ff;">/&gt;</span><span style="color: #000000;"><br />
</span><span style="color: #000000;"><br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">&lt;</span><span style="color: #800000;">filter&nbsp;</span><span style="color: #ff0000;">class</span><span style="color: #0000ff;">="solr.StopFilterFactory"</span><span style="color: #ff0000;">&nbsp;ignoreCase</span><span style="color: #0000ff;">="true"</span><span style="color: #ff0000;">&nbsp;words</span><span style="color: #0000ff;">="stopwords.txt"</span><span style="color: #0000ff;">/&gt;</span><span style="color: #000000;"><br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">&lt;</span><span style="color: #800000;">filter&nbsp;</span><span style="color: #ff0000;">class</span><span style="color: #0000ff;">="solr.WordDelimiterFilterFactory"</span><span style="color: #ff0000;">&nbsp;generateWordParts</span><span style="color: #0000ff;">="1"</span><span style="color: #ff0000;">&nbsp;generateNumberParts</span><span style="color: #0000ff;">="1"</span><span style="color: #ff0000;">&nbsp;catenateWords</span><span style="color: #0000ff;">="1"</span><span style="color: #ff0000;">&nbsp;catenateNumbers</span><span style="color: #0000ff;">="1"</span><span style="color: #ff0000;">&nbsp;catenateAll</span><span style="color: #0000ff;">="0"</span><span style="color: #0000ff;">/&gt;</span><span style="color: #000000;"><br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">&lt;</span><span style="color: #800000;">filter&nbsp;</span><span style="color: #ff0000;">class</span><span style="color: #0000ff;">="solr.LowerCaseFilterFactory"</span><span style="color: #0000ff;">/&gt;</span><span style="color: #000000;"><br />
</span><span style="color: #000000;"><br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">&lt;</span><span style="color: #800000;">filter&nbsp;</span><span style="color: #ff0000;">class</span><span style="color: #0000ff;">="solr.RemoveDuplicatesTokenFilterFactory"</span><span style="color: #0000ff;">/&gt;</span><span style="color: #000000;"><br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">analyzer</span><span style="color: #0000ff;">&gt;</span><span style="color: #000000;"><br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">&lt;</span><span style="color: #800000;">analyzer&nbsp;</span><span style="color: #ff0000;">type</span><span style="color: #0000ff;">="query"</span><span style="color: #0000ff;">&gt;</span><span style="color: #000000;"><br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">&lt;</span><span style="color: #800000;">tokenizer&nbsp;</span><span style="color: #ff0000;">class</span><span style="color: #0000ff;">="com.ronghao.fulltextsearch.analyzer.ChineseTokenizerFactory"</span><span style="color: #ff0000;">&nbsp;mode</span><span style="color: #0000ff;">="most-words"</span><span style="color: #0000ff;">/&gt;</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">&lt;</span><span style="color: #800000;">filter&nbsp;</span><span style="color: #ff0000;">class</span><span style="color: #0000ff;">="solr.SynonymFilterFactory"</span><span style="color: #ff0000;">&nbsp;synonyms</span><span style="color: #0000ff;">="synonyms.txt"</span><span style="color: #ff0000;">&nbsp;ignoreCase</span><span style="color: #0000ff;">="true"</span><span style="color: #ff0000;">&nbsp;expand</span><span style="color: #0000ff;">="true"</span><span style="color: #0000ff;">/&gt;</span><span style="color: #000000;"><br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">&lt;</span><span style="color: #800000;">filter&nbsp;</span><span style="color: #ff0000;">class</span><span style="color: #0000ff;">="solr.StopFilterFactory"</span><span style="color: #ff0000;">&nbsp;ignoreCase</span><span style="color: #0000ff;">="true"</span><span style="color: #ff0000;">&nbsp;words</span><span style="color: #0000ff;">="stopwords.txt"</span><span style="color: #0000ff;">/&gt;</span><span style="color: #000000;"><br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">&lt;</span><span style="color: #800000;">filter&nbsp;</span><span style="color: #ff0000;">class</span><span style="color: #0000ff;">="solr.WordDelimiterFilterFactory"</span><span style="color: #ff0000;">&nbsp;generateWordParts</span><span style="color: #0000ff;">="1"</span><span style="color: #ff0000;">&nbsp;generateNumberParts</span><span style="color: #0000ff;">="1"</span><span style="color: #ff0000;">&nbsp;catenateWords</span><span style="color: #0000ff;">="0"</span><span style="color: #ff0000;">&nbsp;catenateNumbers</span><span style="color: #0000ff;">="0"</span><span style="color: #ff0000;">&nbsp;catenateAll</span><span style="color: #0000ff;">="0"</span><span style="color: #0000ff;">/&gt;</span><span style="color: #000000;"><br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">&lt;</span><span style="color: #800000;">filter&nbsp;</span><span style="color: #ff0000;">class</span><span style="color: #0000ff;">="solr.LowerCaseFilterFactory"</span><span style="color: #0000ff;">/&gt;</span><span style="color: #000000;"><br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">&lt;</span><span style="color: #800000;">filter&nbsp;</span><span style="color: #ff0000;">class</span><span style="color: #0000ff;">="solr.RemoveDuplicatesTokenFilterFactory"</span><span style="color: #0000ff;">/&gt;</span><span style="color: #000000;"><br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">analyzer</span><span style="color: #0000ff;">&gt;</span><span style="color: #000000;"><br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">fieldtype</span><span style="color: #0000ff;">&gt;</span><span style="color: #000000;"><br />
<br />
</span><span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">types</span><span style="color: #0000ff;">&gt;</span><span style="color: #000000;"><br />
</span></div>
<p style="margin-left: 18pt; text-indent: -18pt;"><span style="font-size: 9pt; font-family: 宋体;">&nbsp;&nbsp;&nbsp; </span><span style="font-size: 9pt; font-family: 宋体;">完成后重启tomcat，即可在<a href="http://localhost:8080/solr/admin/analysis.jsp">http://localhost:8080/solr/admin/analysis.jsp</a></span></p>
<p style="margin-left: 18.05pt;"><span style="font-size: 9pt; font-family: 宋体;">体验到庖丁的中文分词。注意要将paoding-analysis.jar复制到solr的lib下，注意修改jar包里字典的home。</span></p>
<p style="margin-left: 25.5pt; text-indent: -25.5pt;"><strong><span style="font-size: 9pt; font-family: 宋体;">四、&nbsp;</span></strong><strong><span style="font-size: 9pt; font-family: 宋体;">与自己应用进行集成</span></strong></p>
<p style="margin-left: 25.5pt;"><span style="font-size: 9pt; font-family: 宋体;">Solr</span><span style="font-size: 9pt; font-family: 宋体;">安装完毕，现在可以将自己的应用与solr集成。其实过程非常的简单，应用增加数据--&gt;</span><span style="font-size: 9pt; font-family: 宋体;">根据配置的字段构建add的xml文档</span><span style="font-size: 9pt; font-family: Wingdings;">--&gt;</span><span style="font-size: 9pt; font-family: 宋体;">post</span><span style="font-size: 9pt; font-family: 宋体;">至solr/update。</span></p>
<p style="margin-left: 25.5pt;"><span style="font-size: 9pt; font-family: 宋体;">应用删除数据</span><span style="font-size: 9pt; font-family: Wingdings;">&#224;</span><span style="font-size: 9pt; font-family: 宋体;">根据配置的索引文档唯一标识符构建delete的xml文档</span><span style="font-size: 9pt; font-family: Wingdings;">--&gt;</span><span style="font-size: 9pt; font-family: 宋体;">post</span><span style="font-size: 9pt; font-family: 宋体;">至solr/update。</span></p>
<p style="margin-left: 25.5pt;"><span style="font-size: 9pt; font-family: 宋体;">检索数据</span><span style="font-size: 9pt; font-family: Wingdings;">&#224;</span><span style="font-size: 9pt; font-family: 宋体;">构建查询xml—&gt;get至/solr/select/--&gt;</span><span style="font-size: 9pt; font-family: 宋体;">对solr返回的xml进行处理</span><span style="font-size: 9pt; font-family: Wingdings;">--&gt;</span><span style="font-size: 9pt; font-family: 宋体;">页面展现。</span></p>
<p style="margin-left: 25.5pt;"><span style="font-size: 9pt; font-family: 宋体;">具体的xml格式可以在solr网站找到。另外就是solr支持高亮显示，非常方便。</span></p>
<p style="margin-left: 25.5pt;"><span style="font-size: 9pt; font-family: 宋体;">关于中文，solr</span><span style="font-size: 9pt; font-family: 宋体;">内核支持</span><span style="font-size: 9pt;">UTF-8</span><span style="font-size: 9pt; font-family: 宋体;">编码，所以在</span><span style="font-size: 9pt;">tomcat</span><span style="font-size: 9pt; font-family: 宋体;">里的</span><span style="font-size: 9pt;">server.xml</span><span style="font-size: 9pt; font-family: 宋体;">需要进行配置</span></p>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000ff;">&lt;</span><span style="color: #800000;">Connector&nbsp;</span><span style="color: #ff0000;">port</span><span style="color: #0000ff;">="8080"</span><span style="color: #ff0000;">&nbsp;maxHttpHeaderSize</span><span style="color: #0000ff;">="8192"</span><span style="color: #ff0000;">&nbsp;URIEncoding</span><span style="color: #0000ff;">="UTF-8"</span><span style="color: #ff0000;">&nbsp;&#8230;</span><span style="color: #0000ff;">/&gt;</span></div>
<p style="margin-left: 25.5pt;"><span style="font-size: 9pt; font-family: 宋体;">另外，向solr Post请求的时候需要转为utf-8编码。对solr 返回的查询结果也需要进行一次utf-8的转码。检索数据时对查询的关键字也需要转码，然后用&#8220;+&#8221;连接。</span></p>
<br />
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000;">String[]&nbsp;array&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;StringUtils.split(query,&nbsp;</span><span style="color: #0000ff;">null</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">0</span><span style="color: #000000;">);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">for</span><span style="color: #000000;">&nbsp;(String&nbsp;str&nbsp;:&nbsp;array)&nbsp;{<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;result&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;result&nbsp;</span><span style="color: #000000;">+</span><span style="color: #000000;">&nbsp;URLEncoder.encode(str,&nbsp;</span><span style="color: #000000;">"</span><span style="color: #000000;">UTF-8</span><span style="color: #000000;">"</span><span style="color: #000000;">)&nbsp;</span><span style="color: #000000;">+</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">"</span><span style="color: #000000;">+</span><span style="color: #000000;">"</span><span style="color: #000000;">;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
</span></div>
<img src ="http://www.blogjava.net/RongHao/aggbug/158621.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/RongHao/" target="_blank">ronghao</a> 2007-11-06 18:03 <a href="http://www.blogjava.net/RongHao/archive/2007/11/06/158621.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>结合spring+hibernate与jdbc的事务</title><link>http://www.blogjava.net/RongHao/archive/2007/10/09/151411.html</link><dc:creator>ronghao</dc:creator><author>ronghao</author><pubDate>Tue, 09 Oct 2007 07:11:00 GMT</pubDate><guid>http://www.blogjava.net/RongHao/archive/2007/10/09/151411.html</guid><wfw:comment>http://www.blogjava.net/RongHao/comments/151411.html</wfw:comment><comments>http://www.blogjava.net/RongHao/archive/2007/10/09/151411.html#Feedback</comments><slash:comments>5</slash:comments><wfw:commentRss>http://www.blogjava.net/RongHao/comments/commentRss/151411.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/RongHao/services/trackbacks/151411.html</trackback:ping><description><![CDATA[<strong>问题背景</strong>：我们是一家工作流公司，客户采购我们的产品后，将其嵌入其项目中。我们的工作流采用的是&nbsp;&nbsp; spring+hibernate的方式，客户项目则是jdbc直接进行数据库操作。<br />
<strong>问题</strong>：客户在其数据库操作过程中需要调用我们的工作流接口，这样就需要将我们的工作流操作与他们的业&nbsp; 务操作置于同一个事务中。我们的服务采用的都是spring的声明式事务，而客户采用的是对&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; connection进行事务处理。如何保证事务的一致性？<br />
<strong>想到的解决方案一</strong>：使用jta事务，用tomcat+jotm提供事务管理器。为什么一开始就想到要使用jta事务？？实际上我们和客户都是使用的同一个数据库，为了方便，各自使用了不同的数据库连接方式，使用jta的话确实有bt的意思在里面。但是事实上是我们的第一反应都是jta。最后没有采用该方法的原因也很简单：我没有将jotm配置成功！汗一个。<br />
<strong>想到的解决方案二</strong>：将客户的这些特定代码用spring管理起来。因为要修改客户部分代码，这个方案遭到了客户的强烈反对。于是放弃。<br />
<strong>想到的解决方案三</strong>：客户数据库操作与我们的服务使用同一个数据库连接。然后编程处理事务。存在两种方式：一种是把客户的连接传给我们，另一种则是把我们的连接传给客户。第一种方式对我们的影响太大，所以最后决定采用后一种方式：从hibernate session中获取connection然后传递给客户。接下来查看一下HibernateTemplate的execute()方法，思路就很简单了：获取定义的sessionFactory--&gt;创建一个新的session并打开--&gt;将session与当前线程绑定--&gt;给客户代码返回connection--&gt;打开事务--&gt;客户使用我们传递的connection进行数据库操作--&gt;我们不带声明事务的服务操作--&gt;提交事务--&gt;解除绑定。<br />
<strong>实际要注意的地方是</strong>：1、将session与当前线程绑定使用的TransactionSynchronizationManager.bindResource()方法，这样在HibernateTemplate里才能找到session；<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2、我们的服务一定要把声明式事务彻底干掉，否则会有commit;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3、我们服务调用完毕后一定要flush session，否则客户代码不会感知数据库里的数据变化。<br />
最终解决：使用了spring里常用的模板和回调。代码如下：<br />
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;TransactionTemplate&nbsp;{<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">protected</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">final</span><span style="color: #000000;">&nbsp;Log&nbsp;logger&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;LogFactory.getLog(TransactionTemplate.</span><span style="color: #0000ff;">class</span><span style="color: #000000;">);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;FlushMode&nbsp;flushMode&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;FlushMode.ALWAYS;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;Object&nbsp;execute(TransactionCallback&nbsp;callback)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">首先获取sessionFactory</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SessionFactory&nbsp;sessionFactory&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;(SessionFactory)&nbsp;Framework.getEngine()<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.getContainer().getComponent(</span><span style="color: #000000;">"</span><span style="color: #000000;">sessionFactory</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">创建一个新的session并打开</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;logger.debug(</span><span style="color: #000000;">"</span><span style="color: #000000;">Opening&nbsp;single&nbsp;Hibernate&nbsp;Session&nbsp;in&nbsp;TransactionTemplate</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Session&nbsp;session&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;getSession(sessionFactory);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">将session与当前线程绑定</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TransactionSynchronizationManager.bindResource(sessionFactory,&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;SessionHolder(session));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">获取数据库连接</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Connection&nbsp;conn&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;session.connection();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Object&nbsp;result&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">null</span><span style="color: #000000;">;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Transaction&nbsp;transaction&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">null</span><span style="color: #000000;">;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">try</span><span style="color: #000000;">&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">开始处理事务</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;transaction&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;session.beginTransaction();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">try</span><span style="color: #000000;">&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;result&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;callback.doInTransaction(conn);<br />
&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;</span><span style="color: #0000ff;">catch</span><span style="color: #000000;">&nbsp;(RuntimeException&nbsp;ex)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;doRollback(session,&nbsp;transaction);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">throw</span><span style="color: #000000;">&nbsp;ex;<br />
&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;</span><span style="color: #0000ff;">catch</span><span style="color: #000000;">&nbsp;(Error&nbsp;err)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;doRollback(session,&nbsp;transaction);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">throw</span><span style="color: #000000;">&nbsp;err;<br />
&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;</span><span style="color: #008000;">//</span><span style="color: #008000;">如果数据库操作过程中没有发生异常则提交事务</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;transaction.commit();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</span><span style="color: #0000ff;">catch</span><span style="color: #000000;">&nbsp;(WorkflowException&nbsp;e)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;logger.error(</span><span style="color: #000000;">"</span><span style="color: #000000;">数据库操作失败，事务回滚也失败！</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">throw</span><span style="color: #000000;">&nbsp;e;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</span><span style="color: #0000ff;">catch</span><span style="color: #000000;">&nbsp;(RuntimeException&nbsp;ex)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;logger.error(</span><span style="color: #000000;">"</span><span style="color: #000000;">数据库操作失败，事务被回滚！</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">throw</span><span style="color: #000000;">&nbsp;ex;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</span><span style="color: #0000ff;">catch</span><span style="color: #000000;">&nbsp;(Error&nbsp;err)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;logger.error(</span><span style="color: #000000;">"</span><span style="color: #000000;">数据库操作失败，事务被回滚！</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">throw</span><span style="color: #000000;">&nbsp;err;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</span><span style="color: #0000ff;">finally</span><span style="color: #000000;">&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;将session与当前线程解除绑定</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TransactionSynchronizationManager.unbindResource(sessionFactory);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;doClose(session);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;result;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">protected</span><span style="color: #000000;">&nbsp;Session&nbsp;getSession(SessionFactory&nbsp;sessionFactory)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Session&nbsp;session&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;SessionFactoryUtils.getSession(sessionFactory,&nbsp;</span><span style="color: #0000ff;">true</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;FlushMode&nbsp;flushMode&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;getFlushMode();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(flushMode&nbsp;</span><span style="color: #000000;">!=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">null</span><span style="color: #000000;">)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;session.setFlushMode(flushMode);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;session;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;doRollback(Session&nbsp;session,&nbsp;Transaction&nbsp;transaction)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;logger.debug(</span><span style="color: #000000;">"</span><span style="color: #000000;">数据库操作异常，开始回滚事务</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">try</span><span style="color: #000000;">&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;transaction.rollback();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;logger.debug(</span><span style="color: #000000;">"</span><span style="color: #000000;">回滚事务成功！</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">catch</span><span style="color: #000000;">&nbsp;(Exception&nbsp;e)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;logger.error(</span><span style="color: #000000;">"</span><span style="color: #000000;">回滚事务失败！</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">throw</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;WorkflowException(</span><span style="color: #000000;">"</span><span style="color: #000000;">回滚事务失败！</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</span><span style="color: #0000ff;">finally</span><span style="color: #000000;">&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;session.clear();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;doClose(Session&nbsp;session)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;logger.debug(</span><span style="color: #000000;">"</span><span style="color: #000000;">开始关闭连接</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">try</span><span style="color: #000000;">&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;session.close();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">catch</span><span style="color: #000000;">&nbsp;(Exception&nbsp;e)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;logger.error(</span><span style="color: #000000;">"</span><span style="color: #000000;">关闭连接失败！</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">throw</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;WorkflowException(</span><span style="color: #000000;">"</span><span style="color: #000000;">关闭连接失败！</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;FlushMode&nbsp;getFlushMode()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;flushMode;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;setFlushMode(FlushMode&nbsp;flushMode)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">this</span><span style="color: #000000;">.flushMode&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;flushMode;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}</span></div>
<br />
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">interface</span><span style="color: #000000;">&nbsp;TransactionCallback&nbsp;{<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;Object&nbsp;doInTransaction(Connection&nbsp;conn);<br />
}</span></div>
调用伪代码：<br />
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;methodA(){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TransactionTemplate&nbsp;transactionTemplate</span><span style="color: #000000;">=</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;TransactionTemplate();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;transactionTemplate.execute(</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;TransactionCallback(){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;Object&nbsp;doInTransaction(Connection&nbsp;conn)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">客户代码</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;client.method1(</span><span style="color: #000000;">"</span><span style="color: #000000;">1</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">我们代码&nbsp;直接使用</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;our.method2();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">客户代码</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;client.method3(</span><span style="color: #000000;">"</span><span style="color: #000000;">l</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">null</span><span style="color: #000000;">;&nbsp;&nbsp;<br />
&nbsp;&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;}</span></div>
<br />
<br />
<img src ="http://www.blogjava.net/RongHao/aggbug/151411.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/RongHao/" target="_blank">ronghao</a> 2007-10-09 15:11 <a href="http://www.blogjava.net/RongHao/archive/2007/10/09/151411.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>REST个人认知（一）</title><link>http://www.blogjava.net/RongHao/archive/2007/07/11/129662.html</link><dc:creator>ronghao</dc:creator><author>ronghao</author><pubDate>Wed, 11 Jul 2007 09:37:00 GMT</pubDate><guid>http://www.blogjava.net/RongHao/archive/2007/07/11/129662.html</guid><wfw:comment>http://www.blogjava.net/RongHao/comments/129662.html</wfw:comment><comments>http://www.blogjava.net/RongHao/archive/2007/07/11/129662.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/RongHao/comments/commentRss/129662.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/RongHao/services/trackbacks/129662.html</trackback:ping><description><![CDATA[今天终于有空看看了Fielding的rest论文，没有看完，很多文字确实难懂，但有些还是很有感触的，做个记号。<br>一个<span style="font-weight: bold;">软件架构</span>是一个软件系统在其操作的某个阶段的运行时元素的抽象。<br><span style="font-weight: bold;">架构元素</span>：组件，连接器，数据，配置。<br><span style="font-weight: bold;">架构风格</span>：一组协作的架构约束。<br>一种特定的架构可以由多种架构风格组成。<br><span style="font-weight: bold;">关键关注点的架构属性</span><br><span style="font-weight: bold;">性能</span><br>最佳的应用性能是通过不使用网络而获得的。这意味着对于一个基于网络的应用，最高效的架构风格是在可能的情况下能够将对于网络使用减少到最少的架构风格。<br><span style="font-weight: bold;">可伸缩性</span><br>表示在一个主动的配置中，架构支持大量的组件或大量的组件之间交互的能力。<br><span style="font-weight: bold;">简单性</span><br>对组件之间的功能分配应用分离关注点原则。使得单个的组件足够简单，更容易被理解和实现。<br><span style="font-weight: bold;">可修改性</span><br>基于网络的系统的一个特殊的关注点是动态的可修改性，它要求在对一个已部署的应用做出修改时，无需停止和重启整个系统。包括：可进化性，可扩展性，可定制性，可配置性，可重用性。<br><span style="font-weight: bold;">可见性</span><br>能够通过限制必须使用通用性的接口，或者提供访问监视功能的方法，来影响基于网络的应用中交互的可见性。在这种情况下，可见性是指一个组件对于其他两个组件之间的交互进行监视或仲裁的能力。<br><span style="font-weight: bold;">可移植性</span><br>能够在不同的环境下运行。<br><span style="font-weight: bold;">可靠性</span><br>当在组件、连接器或数据之中出现部分故障时，一个架构容易受到系统层面故障影响的程度。<br> <img src ="http://www.blogjava.net/RongHao/aggbug/129662.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/RongHao/" target="_blank">ronghao</a> 2007-07-11 17:37 <a href="http://www.blogjava.net/RongHao/archive/2007/07/11/129662.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>sqlserver2000下USER表名与系统表重名</title><link>http://www.blogjava.net/RongHao/archive/2007/03/11/103086.html</link><dc:creator>ronghao</dc:creator><author>ronghao</author><pubDate>Sun, 11 Mar 2007 03:29:00 GMT</pubDate><guid>http://www.blogjava.net/RongHao/archive/2007/03/11/103086.html</guid><wfw:comment>http://www.blogjava.net/RongHao/comments/103086.html</wfw:comment><comments>http://www.blogjava.net/RongHao/archive/2007/03/11/103086.html#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://www.blogjava.net/RongHao/comments/commentRss/103086.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/RongHao/services/trackbacks/103086.html</trackback:ping><description><![CDATA[用hibernate做持久层工具，建立User对象，映射到sqlserver的USER表，但在做查询的时候不断的报sql错误，打印hibernate产生的sql语句到sqlserver查询工具执行，报同样的错误。给表名加上[]后sql可以正常执行。例如：select * from USER错误。select * from [USER]正确。开始以为是什么地方配置的问题。因为以前用过达梦数据库，它默认也是必须给表名加[]的，可以通过设置数据库属性解决这个问题。但也没有发现什么配置。后来在SecuritySite群提问，illusion提示是与系统表重名，更改表名后果然正常。于是感到很奇怪：USER这么常用的表名怎么会与系统表重名呢？<br /><img src ="http://www.blogjava.net/RongHao/aggbug/103086.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/RongHao/" target="_blank">ronghao</a> 2007-03-11 11:29 <a href="http://www.blogjava.net/RongHao/archive/2007/03/11/103086.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>关于集群的补课</title><link>http://www.blogjava.net/RongHao/archive/2007/02/12/99560.html</link><dc:creator>ronghao</dc:creator><author>ronghao</author><pubDate>Mon, 12 Feb 2007 15:18:00 GMT</pubDate><guid>http://www.blogjava.net/RongHao/archive/2007/02/12/99560.html</guid><wfw:comment>http://www.blogjava.net/RongHao/comments/99560.html</wfw:comment><comments>http://www.blogjava.net/RongHao/archive/2007/02/12/99560.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/RongHao/comments/commentRss/99560.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/RongHao/services/trackbacks/99560.html</trackback:ping><description><![CDATA[昨天发了个很什么的随笔，今天把与集群有关的东西搜了搜。整理一下。<br />什么是集群，集群的概念。下面这个BLOG讲的非常清楚：<br /><a href="http://blog.csdn.net/ESoftWind/archive/2006/10/19/1341089.aspx">http://blog.csdn.net/ESoftWind/archive/2006/10/19/1341089.aspx</a><br />web层次的集群方案讨论，看完javaeye相关的讨论，你会大概了解：<br /><a href="http://www.javaeye.com/topic/20298">http://www.javaeye.com/topic/20298</a><br />注意里面robbin的无共享架构（Share Nothing Architecture）SNA。<br /><b>web层次的集群主要技术就是：负载均衡和http session的失败转移。</b><br />负载均衡不再多说，焦点在于http session的失败转移。各个节点的http session复制会极大的影响性能。如何避免，robbin提出保持每个节点的无状态性，不再使用Session来保持全局状态。用户标示从cookie取得，假设不使用分布式Cache，session直接放在数据库中。他推荐了memcached作为分布式Cache，这样在从数据库读取session时中间又隔了一层Cache来提高性能。<br /><b>大致的方法是这样：</b>用户登陆的时候给他一个cookie，存放userId，同时给这个用户分配一个Session，存放user对象，然后
把这个session保存到数据库和分布式 Cache里。黏性会话。写一个filter或者
webwork拦截器对用户请求进行拦截，如果他有cookie，但是session里面没有user对象，说明前一个节点down掉了，就根据
cookie里面的userId查数据库或者是分布式
Cache获得先前保存的session，把原先的session复制到他的新session里面。这样各个节点间的 session就不用复制，因为
session是没有状态的。我们的程序对使用session不受影响，只是session里的对象要可序列化，当改变session里的对象时需要同步
到cache和数据库。当然，效率的原因，session里面东西越少越好，越稳定越好。<br />谁有这方面的经验？<br /><img src ="http://www.blogjava.net/RongHao/aggbug/99560.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/RongHao/" target="_blank">ronghao</a> 2007-02-12 23:18 <a href="http://www.blogjava.net/RongHao/archive/2007/02/12/99560.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>探讨一个可能的大访问量交易网站的开发注意事项</title><link>http://www.blogjava.net/RongHao/archive/2007/02/10/99106.html</link><dc:creator>ronghao</dc:creator><author>ronghao</author><pubDate>Sat, 10 Feb 2007 03:41:00 GMT</pubDate><guid>http://www.blogjava.net/RongHao/archive/2007/02/10/99106.html</guid><wfw:comment>http://www.blogjava.net/RongHao/comments/99106.html</wfw:comment><comments>http://www.blogjava.net/RongHao/archive/2007/02/10/99106.html#Feedback</comments><slash:comments>9</slash:comments><wfw:commentRss>http://www.blogjava.net/RongHao/comments/commentRss/99106.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/RongHao/services/trackbacks/99106.html</trackback:ping><description><![CDATA[最近要开发一个与拍卖有关的大访问量交易网站。一直做电子政务，对这方面没有任何经验。一开始考虑用php,mysql开发，后来由于觉得和交易相关，数据的一致性和安全一定很重要，最后考虑用java开发。
<br />我不清楚在做这个开发时和平时相比有哪些需要注意的地方，我想到的有：
<br />1.webwork+spring+hibernate这种组合方式是否可行。据说tobao用了ejb，虽说个人并未觉得ejb哪点好，但别人    既然用了就肯定有它的一定道理；
<br />2.数据的缓存肯定是必须的，但哪些是最需要被缓存的数据？
<br />3.dba肯定需要，在没有dba的情况下，涉及数据库时应该注意什么；
<br />4.这样的一个系统，它的性能肯定非常主要。它最有可能的瓶颈会发生在什么地方？
<br />5.我们的理想工期会有多长，3到5个开发人员。
<br />6.jms远程异步调用的支持。
<br />目前想到的大概这些，希望有经验的朋友给些建议。我对这个项目目前还没有很大的把握。<img src ="http://www.blogjava.net/RongHao/aggbug/99106.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/RongHao/" target="_blank">ronghao</a> 2007-02-10 11:41 <a href="http://www.blogjava.net/RongHao/archive/2007/02/10/99106.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>什么是JAVA内容仓库(Java Content Repository）(2)</title><link>http://www.blogjava.net/RongHao/archive/2007/01/23/95637.html</link><dc:creator>ronghao</dc:creator><author>ronghao</author><pubDate>Tue, 23 Jan 2007 15:57:00 GMT</pubDate><guid>http://www.blogjava.net/RongHao/archive/2007/01/23/95637.html</guid><wfw:comment>http://www.blogjava.net/RongHao/comments/95637.html</wfw:comment><comments>http://www.blogjava.net/RongHao/archive/2007/01/23/95637.html#Feedback</comments><slash:comments>25</slash:comments><wfw:commentRss>http://www.blogjava.net/RongHao/comments/commentRss/95637.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/RongHao/services/trackbacks/95637.html</trackback:ping><description><![CDATA[
		<b>内容仓库模型</b>
		<br />JSR-170 是这样定义内容仓库的，内容仓库由一组 workspace（工作空间）组成，这些workspace通常应该包含相似的内容。一个内容仓库有一个到多个 workspace。每个workspace都是一个树状结构，都有一个唯一的树根节点（root node）。树上的item（元素）或者是个node（节点）或者是个property（属性）。每个node都可以有零个到多个子节点和零个到多个子属性。只有根节点没有父节点，其余所有的节点都有一个父节点。property 也必须有一个父节点，但它没有子节点或是子属性，property 是叶子元素。property是真正存储数据的元素。<br /><br />下图描述了一个blog应用程序的内容仓库模型。每个root node（根节点）的子节点都代表了一个blog实体。与这个blog实体有关的数据都存储在 bolgEntry 节点的属性里，其中一个 blogAttachment property 存储了一个二进制图片文件。<br /><img src="http://www.blogjava.net/images/blogjava_net/ronghao/repositorymodel3.gif" alt="repositorymodel3.gif" border="0" height="263" width="550" /><br />根据内容仓库实现的功能，JSR-170定义了三种级别：<br />Level 1：定义了一个只读的内容仓库。功能包括读取内容，将内容导出为XML和查找内容。<br />Level 2：定义了可写的内容仓库。Level 2是Level 1的扩展，新增的功能包括往内容仓库里写入内容，和从XML导入数据到仓库。<br />Advanced options：定义实现五种附加功能，版本控制、JTA、SQL查询、清晰的内容锁定和监视。<br /><br /><b>什么是Apache JackRabbit？</b><br />Apache JackRabbit是一个开放源码的JSR-170 实现，实现了Level 2，但它还有许多扩展的功能。详细可以去它的官方网站。<br /><br />下面我们决定用Apache JackRabbit来作为我们示例程序的内容仓库。<br /><br /><b>如何配置Apache JackRabbit</b><br />JackRabbit需要两个参数来配置一个内容仓库实例。<br />1.内容仓库主目录：这个文件目录下通常包含了所有的内容，搜索索引，内部配置文件和其他持久化信息。它的结构看起来会像下面这个样子：<br /><div style="border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; background-color: rgb(238, 238, 238); font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: rgb(0, 0, 0);">   c:</span><span style="color: rgb(0, 0, 0);">/</span><span style="color: rgb(0, 0, 0);">temp<br />        </span><span style="color: rgb(0, 0, 0);">|</span><span style="color: rgb(0, 0, 0);"><br />        </span><span style="color: rgb(0, 0, 0);">|--</span><span style="color: rgb(0, 0, 0);">Blogging<br />                </span><span style="color: rgb(0, 0, 0);">|</span><span style="color: rgb(0, 0, 0);"><br />                </span><span style="color: rgb(0, 0, 0);">|-</span><span style="color: rgb(0, 0, 0);">repository<br />                </span><span style="color: rgb(0, 0, 0);">|</span><span style="color: rgb(0, 0, 0);">       </span><span style="color: rgb(0, 0, 0);">|</span><span style="color: rgb(0, 0, 0);"><br />                </span><span style="color: rgb(0, 0, 0);">|</span><span style="color: rgb(0, 0, 0);">       </span><span style="color: rgb(0, 0, 0);">|-</span><span style="color: rgb(0, 0, 0);">index<br />                </span><span style="color: rgb(0, 0, 0);">|</span><span style="color: rgb(0, 0, 0);">       </span><span style="color: rgb(0, 0, 0);">|-</span><span style="color: rgb(0, 0, 0);">meta<br />                </span><span style="color: rgb(0, 0, 0);">|</span><span style="color: rgb(0, 0, 0);">       </span><span style="color: rgb(0, 0, 0);">|-</span><span style="color: rgb(0, 0, 0);">namespaces<br />                </span><span style="color: rgb(0, 0, 0);">|</span><span style="color: rgb(0, 0, 0);">       </span><span style="color: rgb(0, 0, 0);">|-</span><span style="color: rgb(0, 0, 0);">nodetypes             <br />                </span><span style="color: rgb(0, 0, 0);">|</span><span style="color: rgb(0, 0, 0);"><br />                </span><span style="color: rgb(0, 0, 0);">|-</span><span style="color: rgb(0, 0, 0);">version<br />                </span><span style="color: rgb(0, 0, 0);">|</span><span style="color: rgb(0, 0, 0);"><br />                </span><span style="color: rgb(0, 0, 0);">|-</span><span style="color: rgb(0, 0, 0);">workspace<br />                        </span><span style="color: rgb(0, 0, 0);">|</span><span style="color: rgb(0, 0, 0);"><br />                        </span><span style="color: rgb(0, 0, 0);">|--</span><span style="color: rgb(0, 0, 255);">default</span></div><br />  在上面的情况下，内容仓库主目录是c:/temp/Blogging.<br />2.内容仓库配置文件：一个典型的配置文件如下：<br /><div style="border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; background-color: rgb(238, 238, 238); font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: rgb(0, 0, 0);">&lt;</span><span style="color: rgb(0, 0, 0);">Repository</span><span style="color: rgb(0, 0, 0);">&gt;</span><span style="color: rgb(0, 0, 0);"><br /> </span><span style="color: rgb(0, 0, 0);">&lt;</span><span style="color: rgb(0, 0, 0);">FileSystem </span><span style="color: rgb(0, 0, 255);">class</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">org.apache.jackrabbit.core.fs.local.LocalFileSystem</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">&gt;</span><span style="color: rgb(0, 0, 0);"><br />  </span><span style="color: rgb(0, 0, 0);">&lt;</span><span style="color: rgb(0, 0, 0);">param name</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">path</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);"> value</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">${rep.home}/repository</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">/&gt;</span><span style="color: rgb(0, 0, 0);"><br /> </span><span style="color: rgb(0, 0, 0);">&lt;/</span><span style="color: rgb(0, 0, 0);">FileSystem</span><span style="color: rgb(0, 0, 0);">&gt;</span><span style="color: rgb(0, 0, 0);"><br /> </span><span style="color: rgb(0, 0, 0);">&lt;</span><span style="color: rgb(0, 0, 0);">Security appName</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">Jackrabbit</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">&gt;</span><span style="color: rgb(0, 0, 0);"><br />  </span><span style="color: rgb(0, 0, 0);">&lt;</span><span style="color: rgb(0, 0, 0);">AccessManager </span><span style="color: rgb(0, 0, 255);">class</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">org.apache.jackrabbit.core.security.SimpleAccessManager</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">/&gt;</span><span style="color: rgb(0, 0, 0);"><br />  </span><span style="color: rgb(0, 0, 0);">&lt;</span><span style="color: rgb(0, 0, 0);">LoginModule </span><span style="color: rgb(0, 0, 255);">class</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">org.apache.jackrabbit.core.security.SimpleLoginModule</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">&gt;</span><span style="color: rgb(0, 0, 0);"><br />    </span><span style="color: rgb(0, 0, 0);">&lt;</span><span style="color: rgb(0, 0, 0);">param name</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">anonymousId</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);"> value</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">anonymous</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">/&gt;</span><span style="color: rgb(0, 0, 0);"><br />  </span><span style="color: rgb(0, 0, 0);">&lt;/</span><span style="color: rgb(0, 0, 0);">LoginModule</span><span style="color: rgb(0, 0, 0);">&gt;</span><span style="color: rgb(0, 0, 0);"><br /> </span><span style="color: rgb(0, 0, 0);">&lt;/</span><span style="color: rgb(0, 0, 0);">Security</span><span style="color: rgb(0, 0, 0);">&gt;</span><span style="color: rgb(0, 0, 0);"><br /> </span><span style="color: rgb(0, 0, 0);">&lt;</span><span style="color: rgb(0, 0, 0);">Workspaces rootPath</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">${rep.home}/workspaces</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);"> defaultWorkspace</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">default</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">/&gt;</span><span style="color: rgb(0, 0, 0);"><br /> </span><span style="color: rgb(0, 0, 0);">&lt;</span><span style="color: rgb(0, 0, 0);">Workspace name</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">${wsp.name}</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">&gt;</span><span style="color: rgb(0, 0, 0);"><br />  </span><span style="color: rgb(0, 0, 0);">&lt;</span><span style="color: rgb(0, 0, 0);">FileSystem </span><span style="color: rgb(0, 0, 255);">class</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">org.apache.jackrabbit.core.fs.local.LocalFileSystem</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">&gt;</span><span style="color: rgb(0, 0, 0);"><br />   </span><span style="color: rgb(0, 0, 0);">&lt;</span><span style="color: rgb(0, 0, 0);">param name</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">path</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);"> value</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">${wsp.home}</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">/&gt;</span><span style="color: rgb(0, 0, 0);"><br />  </span><span style="color: rgb(0, 0, 0);">&lt;/</span><span style="color: rgb(0, 0, 0);">FileSystem</span><span style="color: rgb(0, 0, 0);">&gt;</span><span style="color: rgb(0, 0, 0);"><br />  </span><span style="color: rgb(0, 0, 0);">&lt;</span><span style="color: rgb(0, 0, 0);">PersistenceManager <br />        </span><span style="color: rgb(0, 0, 255);">class</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">org.apache.jackrabbit.core.state.db.DerbyPersistenceManager</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">&gt;</span><span style="color: rgb(0, 0, 0);"><br />   </span><span style="color: rgb(0, 0, 0);">&lt;</span><span style="color: rgb(0, 0, 0);">param name</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">url</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);"> value</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">jdbc:derby:${wsp.home}/db;create=true</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">/&gt;</span><span style="color: rgb(0, 0, 0);"><br />   </span><span style="color: rgb(0, 0, 0);">&lt;</span><span style="color: rgb(0, 0, 0);">param name</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">schemaObjectPrefix</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);"> value</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">${wsp.name}_</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">/&gt;</span><span style="color: rgb(0, 0, 0);"><br />  </span><span style="color: rgb(0, 0, 0);">&lt;/</span><span style="color: rgb(0, 0, 0);">PersistenceManager</span><span style="color: rgb(0, 0, 0);">&gt;</span><span style="color: rgb(0, 0, 0);"><br />  </span><span style="color: rgb(0, 0, 0);">&lt;</span><span style="color: rgb(0, 0, 0);">SearchIndex </span><span style="color: rgb(0, 0, 255);">class</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">org.apache.jackrabbit.core.query.lucene.SearchIndex</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">&gt;</span><span style="color: rgb(0, 0, 0);"><br />   </span><span style="color: rgb(0, 0, 0);">&lt;</span><span style="color: rgb(0, 0, 0);">param name</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">path</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);"> value</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">${wsp.home}/index</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">/&gt;</span><span style="color: rgb(0, 0, 0);"><br />  </span><span style="color: rgb(0, 0, 0);">&lt;/</span><span style="color: rgb(0, 0, 0);">SearchIndex</span><span style="color: rgb(0, 0, 0);">&gt;</span><span style="color: rgb(0, 0, 0);"><br /> </span><span style="color: rgb(0, 0, 0);">&lt;/</span><span style="color: rgb(0, 0, 0);">Workspace</span><span style="color: rgb(0, 0, 0);">&gt;</span><span style="color: rgb(0, 0, 0);"><br /> </span><span style="color: rgb(0, 0, 0);">&lt;</span><span style="color: rgb(0, 0, 0);">Versioning rootPath</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">${rep.home}/version</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">&gt;</span><span style="color: rgb(0, 0, 0);"><br />  </span><span style="color: rgb(0, 0, 0);">&lt;</span><span style="color: rgb(0, 0, 0);">FileSystem </span><span style="color: rgb(0, 0, 255);">class</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">org.apache.jackrabbit.core.fs.local.LocalFileSystem</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">&gt;</span><span style="color: rgb(0, 0, 0);"><br />   </span><span style="color: rgb(0, 0, 0);">&lt;</span><span style="color: rgb(0, 0, 0);">param name</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">path</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);"> value</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">${rep.home}/version</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 0);">/&gt;</span><span style="color: rgb(0, 0, 0);"><br />  </span><span style="color: rgb(0, 0, 0);">&lt;/</span><span style="color: rgb(0, 0, 0);">FileSystem</span><span style="color: rgb(0, 0, 0);">&gt;</span><span style="color: rgb(0, 0, 0);"><br />  </span><span style="color: rgb(0, 0, 0);">&lt;</span><span style="color: rgb(0, 0, 0);">PersistenceManager <br />        </span><span style="color: rgb(0, 0, 255);">class</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">org.apache.jackrabbit.core.state.db.DerbyPersistenceManager</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">&gt;</span><span style="color: rgb(0, 0, 0);"><br />   </span><span style="color: rgb(0, 0, 0);">&lt;</span><span style="color: rgb(0, 0, 0);">param name</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">url</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);"> value</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">jdbc:derby:${rep.home}/version/db;create=true</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">/&gt;</span><span style="color: rgb(0, 0, 0);"><br />   </span><span style="color: rgb(0, 0, 0);">&lt;</span><span style="color: rgb(0, 0, 0);">param name</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">schemaObjectPrefix</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);"> value</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">version_</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">/&gt;</span><span style="color: rgb(0, 0, 0);"><br />  </span><span style="color: rgb(0, 0, 0);">&lt;/</span><span style="color: rgb(0, 0, 0);">PersistenceManager</span><span style="color: rgb(0, 0, 0);">&gt;</span><span style="color: rgb(0, 0, 0);"><br />  </span><span style="color: rgb(0, 0, 0);">&lt;/</span><span style="color: rgb(0, 0, 0);">Versioning</span><span style="color: rgb(0, 0, 0);">&gt;</span><span style="color: rgb(0, 0, 0);"><br />  </span><span style="color: rgb(0, 0, 0);">&lt;</span><span style="color: rgb(0, 0, 0);">SearchIndex </span><span style="color: rgb(0, 0, 255);">class</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">org.apache.jackrabbit.core.query.lucene.SearchIndex</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">&gt;</span><span style="color: rgb(0, 0, 0);"><br />   </span><span style="color: rgb(0, 0, 0);">&lt;</span><span style="color: rgb(0, 0, 0);">param name</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">path</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);"> value</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">${rep.home}/repository/index</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">/&gt;</span><span style="color: rgb(0, 0, 0);"><br />  </span><span style="color: rgb(0, 0, 0);">&lt;/</span><span style="color: rgb(0, 0, 0);">SearchIndex</span><span style="color: rgb(0, 0, 0);">&gt;</span><span style="color: rgb(0, 0, 0);"><br /></span><span style="color: rgb(0, 0, 0);">&lt;/</span><span style="color: rgb(0, 0, 0);">Repository</span><span style="color: rgb(0, 0, 0);">&gt;</span></div><br />  <br />  在这个配置文件里，&lt;Repository&gt;元素是根元素，它包含了下面这些元素：<br />  <b>a</b>，&lt;FileSystem&gt;: 该元素配置了内容仓库的全局数据存储位置，这些全局数据包括已注册的命名空间，定制的节点类型等等。        JackRabbit 提供了几种选择，一种是像上面例子里配置的存储在本地文件里，LocalFileSystem. 如果你想把它们存储在数据库里，你可以使用 DbFileSystem.<br />  <b>b</b>，&lt;Security&gt;:内容仓库的安全配置，它有两个子元素：&lt;AccessManager&gt;和&lt;LoginModule&gt;。&lt;AccessManager&gt;配置的类用来判断用户有没有权限来对特定数据执行特定的操作。<br />  <b>c</b>，&lt;Workspaces&gt;:这个元素的配置对所有的workspace都通用。它的rootPath 属性是所有workspace文件夹的根目录，在我们的例子里它是c:/temp/Blogging/Workspace；defaultWorkspace 属性则包含了workspace的默认名。<br />  <b>d</b>，&lt;Workspace&gt;:这个元素是所有workspace的默认配置模板。去每个workspace文件夹下你都会发现一个workspace.xml文件，这个文件和这个元素的配置一模一样。三个子元素：&lt;FileSystem&gt;，和这个workspace相关数据的存储位置；&lt;PersistenceManager&gt; ，这个workspace内容节点存储策略；&lt;SearchIndex&gt;，可选，全文检索。<br />  <b>e</b>，&lt;Versioning&gt;:配置一个版本相关的对象。其实JackRabbit也是把它作为节点来处理的。<br /><br />这两个参数可以通过两种方式设置，一种是在仓库实例创建时直接传到Jackrabbit里去，一种是间接的通过设置JNDI object factory。<br />你可以设置org.apache.jackrabbit.repository.home 这个系统属性的值来指定你的内容仓库主目录；也可以设置<br />org.apache.jackrabbit.repository.conf 这个系统属性的值来指定你的内容仓库配置文件repository.xml。如果你不设定这两个<br />参数，Jackrabbit会把当前目录作为内容仓库主目录，同时，它有一个默认的内容仓库配置文件。<img src ="http://www.blogjava.net/RongHao/aggbug/95637.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/RongHao/" target="_blank">ronghao</a> 2007-01-23 23:57 <a href="http://www.blogjava.net/RongHao/archive/2007/01/23/95637.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>webwork拦截与common-fileupload冲突</title><link>http://www.blogjava.net/RongHao/archive/2006/11/23/83122.html</link><dc:creator>ronghao</dc:creator><author>ronghao</author><pubDate>Thu, 23 Nov 2006 14:02:00 GMT</pubDate><guid>http://www.blogjava.net/RongHao/archive/2006/11/23/83122.html</guid><wfw:comment>http://www.blogjava.net/RongHao/comments/83122.html</wfw:comment><comments>http://www.blogjava.net/RongHao/archive/2006/11/23/83122.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/RongHao/comments/commentRss/83122.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/RongHao/services/trackbacks/83122.html</trackback:ping><description><![CDATA[CMS包括了网页的发布,在编辑网页内容的时候用到了FCK.网上的教程是很多的.一切刚开始都很顺利.问题出在对图片\FLASH的上传和浏览支持上.对JAVA来说是需要编码的.这里用到了FCK-java.2.2这个包.其实里面除了两个servlet其他的都可以干掉的,似乎用不上它提供的标签.<br />两个servlet里用到了common-fileupload.jar.一切看起来都很不错,可是就是上传不了文件.debug发现common-fileupload得不到fileItem.很是郁闷,因为提供的sample是可以正常跑的.上了common-fileupload的官方网站,才发现是有别的进程拦截request的缘故.于是开始调试.我靠,系统里的过滤器真TM的多,最后是把webwork\webwork-cleanup拦截/*变为*.action这才正常.<br /> 问题:我们真的需要这么多的filter吗?这些filter过滤的范围认真考虑过吗,可以缩小吗?<img src ="http://www.blogjava.net/RongHao/aggbug/83122.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/RongHao/" target="_blank">ronghao</a> 2006-11-23 22:02 <a href="http://www.blogjava.net/RongHao/archive/2006/11/23/83122.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ibatis DAO 事务探索</title><link>http://www.blogjava.net/RongHao/archive/2006/01/20/28817.html</link><dc:creator>ronghao</dc:creator><author>ronghao</author><pubDate>Fri, 20 Jan 2006 09:50:00 GMT</pubDate><guid>http://www.blogjava.net/RongHao/archive/2006/01/20/28817.html</guid><wfw:comment>http://www.blogjava.net/RongHao/comments/28817.html</wfw:comment><comments>http://www.blogjava.net/RongHao/archive/2006/01/20/28817.html#Feedback</comments><slash:comments>6</slash:comments><wfw:commentRss>http://www.blogjava.net/RongHao/comments/commentRss/28817.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/RongHao/services/trackbacks/28817.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: ibatis DAO 框架提供了事务管理模块。而这个事务管理可以应用到很多场合，包括JDBC、Hibernate、JTA、SQLMAP等。下面以最简单的JDBC来分析一下其如何实现事务管理。首先来看一段代码：public&nbsp;class&nbsp;OrderService&nbsp;{&nbsp;&nbsp;private&nbsp;DaoManager&nbsp;daoManager;...&nbsp;&nbsp;<a href='http://www.blogjava.net/RongHao/archive/2006/01/20/28817.html'>阅读全文</a><img src ="http://www.blogjava.net/RongHao/aggbug/28817.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/RongHao/" target="_blank">ronghao</a> 2006-01-20 17:50 <a href="http://www.blogjava.net/RongHao/archive/2006/01/20/28817.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>为什么会重复的造轮子？？</title><link>http://www.blogjava.net/RongHao/archive/2005/12/28/25763.html</link><dc:creator>ronghao</dc:creator><author>ronghao</author><pubDate>Wed, 28 Dec 2005 10:19:00 GMT</pubDate><guid>http://www.blogjava.net/RongHao/archive/2005/12/28/25763.html</guid><wfw:comment>http://www.blogjava.net/RongHao/comments/25763.html</wfw:comment><comments>http://www.blogjava.net/RongHao/archive/2005/12/28/25763.html#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://www.blogjava.net/RongHao/comments/commentRss/25763.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/RongHao/services/trackbacks/25763.html</trackback:ping><description><![CDATA[<P>&nbsp;项目终于上线运行了，但是还是问题多多，但还好都不是涉及到逻辑的大问题。这两天把数据备份这块重做了一下，原先是自己写的一个线程池，功能其实就是每天对mysql进行备份（当初设计为什么会把这个功能放到程序里实现？？）。这完全是重复的造轮子！开始是用Jrontab重构了下，后来讨论后决定在程序里把这项功能删除，改在Linux里写个备份脚本。每天的晚上12点进行备份，然后本机一份，ftp到另一台服务器一份。有段时间没操作Linux了，结果好多命令都忘了，寒！为什么会重复的造轮子？？</P><img src ="http://www.blogjava.net/RongHao/aggbug/25763.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/RongHao/" target="_blank">ronghao</a> 2005-12-28 18:19 <a href="http://www.blogjava.net/RongHao/archive/2005/12/28/25763.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>对原有系统进行重构</title><link>http://www.blogjava.net/RongHao/archive/2005/11/15/19933.html</link><dc:creator>ronghao</dc:creator><author>ronghao</author><pubDate>Tue, 15 Nov 2005 10:04:00 GMT</pubDate><guid>http://www.blogjava.net/RongHao/archive/2005/11/15/19933.html</guid><wfw:comment>http://www.blogjava.net/RongHao/comments/19933.html</wfw:comment><comments>http://www.blogjava.net/RongHao/archive/2005/11/15/19933.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/RongHao/comments/commentRss/19933.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/RongHao/services/trackbacks/19933.html</trackback:ping><description><![CDATA[最近一直打算对原有的一套业务管理系统进行重构。原因很简单，原来的系统采用的是JSP+JAVABEAN+MYSQL的两层架构，在JSP中直接调用JAVABEAN，并且包含了太多的业务逻辑，在维护的时候很困难。<BR>初步的打算是采用jstl+struts+spring+hibernate。<BR>struts仅仅用于表现层，它的Action中不可以涉及到业务逻辑<BR>最近看了看jbpm的源代码，但是还没有在具体的项目中实践。也决定先不在程序中包含工作流。<img src ="http://www.blogjava.net/RongHao/aggbug/19933.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/RongHao/" target="_blank">ronghao</a> 2005-11-15 18:04 <a href="http://www.blogjava.net/RongHao/archive/2005/11/15/19933.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>在Tomcat中配置运行jbpm自带的websale </title><link>http://www.blogjava.net/RongHao/archive/2005/11/10/19192.html</link><dc:creator>ronghao</dc:creator><author>ronghao</author><pubDate>Thu, 10 Nov 2005 09:31:00 GMT</pubDate><guid>http://www.blogjava.net/RongHao/archive/2005/11/10/19192.html</guid><wfw:comment>http://www.blogjava.net/RongHao/comments/19192.html</wfw:comment><comments>http://www.blogjava.net/RongHao/archive/2005/11/10/19192.html#Feedback</comments><slash:comments>5</slash:comments><wfw:commentRss>http://www.blogjava.net/RongHao/comments/commentRss/19192.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/RongHao/services/trackbacks/19192.html</trackback:ping><description><![CDATA[<P>1. 从 http://www.jboss.com/products/jbpm/downloads 下载 jbpm-3.0.zip </P>
<P>2. 解压缩 jbpm-3.0.zip 到 'temp' 目录 </P>
<P>3. 使用 eclipse, 将 'temp\jbpm-3.0' 作为 an existing project into workspace 导入 </P>
<P><STRONG>配置连接 MySQL</STRONG> </P>
<P>1. 在 'jbpm-3.0\lib' 目录下 创建 'mysql' 目录 </P>
<P>2. 将 mysql数据库驱动 (mysql-connector-java-3.1.7-bin.jar) 拷贝到 'mysql' 目录 </P>
<P>3. 在 mysql 中创建一个数据库，数据库名字 </P>
<P><DB-NAME>4. 在 'jbpm-3.0\src\resources'目录下创建 'mysql' 目录 </P>
<P>5. 把两个配置文件 (create.db.hibernate.properties, identity.db.xml) 从 'hsqldb' 目录下 拷贝到 'mysql' 目录 </P>
<P>6. 按下面所示编辑 'create.db.hibernate.properties' 文件: hibernate.dialect=org.hibernate.dialect.MySQLDialect <BR><BR>hibernate.connection.driver_class=com.mysql.jdbc.Driver <BR><BR>hibernate.connection.url=jdbc:mysql://localhost:3306/<DB-NAME> <BR><BR>hibernate.connection.username=<USERNAME> hibernate.connection.password=<PASSWORD> <BR><BR>hibernate.show_sql=true hibernate.query.substitutions=true 1, false 0 <BR><BR>hibernate.c3p0.min_size=1 hibernate.c3p0.max_size=3 </P>
<P>7. 另一个文件 'identity.db.xml'不做改动 </P>
<P>8. 在 'jbpm-3.0' 根目录, 编辑ANT的脚本 'build.deploy.xml' 找到 target name="create.db", 删除 db.start, db.stop 在这个目标块中将所有的'hsqldb' 替换为 'mysql' </P>
<P>9. 运行ANT ant create.db -buildfile build.deploy.xml 运行完毕后就会发现mysql中多出很多表，这是jbpm保持状态用的 </P>
<P><STRONG>创建 jbpm.war 使其在tomcat中运行</STRONG> </P>
<P>默认的打war包时，掉了一些库文件 </P>
<P>1. 在 eclipse中, 编辑ant脚本 'build.deploy.xml' 在目标块 target name="build.webapp" 中在</P>
<P>&lt;copy todir="build/jbpm.war.dir/WEB-INF/lib"&gt;<COPY todir="build/jbpm.war.dir/WEB-INF/lib">&nbsp;下将</P>
<P>&lt;fileset dir="build" includes="jbpm-webapp-${jbpm.version}.jar" /&gt; <FILESET dir=build includes="jbpm-webapp-${jbpm.version}.jar" />替换为</P>
<P>&lt;fileset dir="build" includes="jbpm*.jar" /&gt; </P>
<P>&nbsp;<FILESET dir=build includes="jbpm*.jar" />另外加入新的两行</P>
<P>&lt;fileset dir="lib/hibernate" includes="*.jar" /&gt; <BR>&lt;fileset dir="lib/bsh" includes="*.jar" /&gt; </P>
<P><FILESET dir=lib/hibernate includes="*.jar" /><FILESET dir=lib/bsh includes="*.jar" />2.因为 Hibernate 不能将它的SessionFactory与tomcat的jndi 绑定 , 我们直接在源码中修改 </P>
<P>3. 打开源文件 JbpmSessionFactory.java, 在 getInstance() 方法里, 删除下面代码 </P>
<P>InitialContext initialContext = new InitialContext(); Object o = initialContext.lookup(jndiName); </P>
<P>将下面这行 </P>
<P>instance = (JbpmSessionFactory) PortableRemoteObject.narrow<BR>(o, JbpmSessionFactory.class); </P>
<P>替换为 instance = (JbpmSessionFactory) PortableRemoteObject.narrow<BR>(new JbpmSessionFactory(createConfiguration()), JbpmSessionFactory.class); </P>
<P>4.在 createConfiguration(String configResource) 方法里, 注释掉这段代码 </P>
<P>String hibernatePropertiesResource = JbpmConfiguration.getString("jbpm.hibernate.properties"); </P>
<P>if (hibernatePropertiesResource!=null) { Properties hibernateProperties = <BR>new Properties(); </P>
<P>try { hibernateProperties.load( ClassLoaderUtil.getStream(hibernatePropertiesResource) ); } </P>
<P>catch (IOException e) { <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; e.printStackTrace(); <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throw new RuntimeException<BR>&nbsp;&nbsp; ("couldn't load the hibernate properties from resource&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; '"hibernatePropertiesResource"'", e); <BR>} <BR>log.debug("overriding hibernate properties with "+ hibernateProperties); configuration.setProperties(hibernateProperties); <BR>} <BR>同时加入下面的代码 </P>
<P>configuration.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQLDialect"); <BR><BR>configuration.setProperty("hibernate.connection.driver_class", "com.mysql.jdbc.Driver"); <BR><BR>configuration.setProperty("hibernate.connection.url", "jdbc:mysql://localhost:3306/<DB-NAME>";); <BR><BR>configuration.setProperty("hibernate.connection.username", "<USERNAME>"); </P>
<P>configuration.setProperty("hibernate.connection.password", "<PASSWORD>"); </P>
<P>configuration.setProperty("hibernate.connection.pool_size", "15"); </P>
<P>5. 运行脚本命令 ant build ant build.webapp -buildfile build.deploy.xml </P>
<P>6. 将jbpm.war 从 'jbpm-3.0\build' 下拷贝到 'tomcat.home\webapps' </P>
<P>7. 启动 tomcat </P>
<P>8. 打开浏览器 'http://localhost:8080/jbpm' </P><img src ="http://www.blogjava.net/RongHao/aggbug/19192.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/RongHao/" target="_blank">ronghao</a> 2005-11-10 17:31 <a href="http://www.blogjava.net/RongHao/archive/2005/11/10/19192.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>