<?xml version="1.0" encoding="GB2312"?>
<rss version="2.0">
<channel>
<title><![CDATA[JavaEE开源博客－阿Q]]></title>
<link>http://zhan.zhmy.com/index.html</link>
<description><![CDATA[JavaEE开源博客－阿Q]]></description>
<item>
<title><![CDATA[XP系统启动后，显示桌面慢、开机慢(正在加载个人设置)的解决方法]]></title>
<link>http://zhan.zhmy.com/archives/2008/151055.html</link>
<description><![CDATA[<P><BR>xp开机后：启动正常，滚动条以及在登陆界面（加载个人设置）的地方等待的时间并不长，但是进入系统，桌面背景出来以后，要等待很久图标和任务栏才能显示出来。</P>
<P>网上的很多资料都说是IP的问题（自动取得IP，非固定IP）、开机加载的自动运行项目过多等，但事实上并非是大家所说的原因。</P>
<P>这里的开机慢并非是电脑处理的慢，事实上我怀疑其实系统在后台早已启动完毕了，因为等够了时间之后桌面图标和任务栏一下子就出来了，所有的加载项也都启动完毕了，不像平时那样桌面显示出来了还要等杀毒软件等启动。我遇到的这种情况仅仅是桌面图标和任务栏迟迟不显示而已，类似于explorer没有加载。而且这种情况都是在我禁用了某些系统服务之后出现的，所以我判断是跟某个系统服务有关，但是由于一次更改了好几个系统服务，所以不知道具体是哪个服务造成的。</P>
<P>经过不懈的努力，终于找到了病源——Shell Hardware Detection服务。经过验证，此服务被禁用，甚至是启动类型设为手动，都会引发此故障，将其设为自动启动后，问题解决。</P>
<P>具体设置是在 开始-设置-控制面板-管理工具-服务 找到Shell Hardware Detection服务后在右键属性里选择“自动”即可。</P>
<P>不信大家试试，可以解决进入系统慢&nbsp; 开机慢&nbsp; 桌面显示慢等问题<BR></P>
<P>来源: <A href="http://i4n.com.cn/read.php/556.htm">http://i4n.com.cn/read.php/556.htm</A></P>]]></description>
<author>sam</author>
<pubDate>2008-9-28 16:39:00</pubDate>
</item>
<item>
<title><![CDATA[真正匹配IP地址的正则表达式]]></title>
<link>http://zhan.zhmy.com/archives/2008/150423.html</link>
<description><![CDATA[一直想找个严格匹配IP地址的正则表达式, 但一直找不到自己想要的<br>今晚, 突然心血来潮, 测试了本地链接里面的ip地址格式, 认识到IP格式为:<br>[1~223]<span style="font-weight: bold;">.</span>[0~255]<span style="font-weight: bold;">.</span>[0~255]<span style="font-weight: bold;">.</span>[0~255]<br><br>于是自己写了一个, 经测试通过<br>匹配1~223：var regex1 = /^([1-9]|[1-9]\d|1\d{2}|2[01]\d|22[0-3])$/;<br>匹配0~255：var regex2 = /^(\d|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])$/;<br>匹配IP地址：var regex = /^([1-9]|[1-9]\d|1\d{2}|2[01]\d|22[0-3])\.(\d|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])$/;<br><br>如果还有问题可以随时找我^_^<br><br><br>个人博客: http://zhan.zhmy.com/<br>]]></description>
<author>sam</author>
<pubDate>2008-8-29 1:31:00</pubDate>
</item>
<item>
<title><![CDATA[关于卡巴斯基误杀后的恢复方法]]></title>
<link>http://zhan.zhmy.com/archives/2008/150350.html</link>
<description><![CDATA[<H2>关于卡巴斯基误杀后的恢复方法</H2>
<DIV class=t_msgfont id=postmessage_738836>
<P>最近似乎看到很多网友谈卡巴色变，总是担心卡巴斯基误杀！其实卡巴斯基误杀并没有网上流传的那么严重。不过误杀是有的。前些天就误杀了网络游戏梦幻西游。<BR><BR>　　其实即便卡巴斯基真的误杀并没有什么可怕的，卡巴斯基有完善的误杀备份还原系统，可以将误杀的文件还原！<BR><BR>　　首先，我们来看看卡巴斯基的备份系统，如图：卡巴斯基主界面的报告和数据文件有一个备份区域这里就是卡巴斯基存放被删除的病毒文件的地方<BR></P>
<P>&nbsp;</P>
<P><SPAN id=attach_239898 onmouseover="showMenu(this.id, 0, 1)" style="LEFT: 215px; POSITION: absolute; TOP: 529px"><IMG src="http://style60.googletounion.com/images/default/attachimg.gif" border=0></SPAN> <IMG onmouseover="http://www.zhmy.com/attachimginfo(this, 'attach_239898', 1);attachimg(this, 'mouseover')" onclick="if(vouching(this.src)){zoom(this, 'http://up1.googletounion.com/data/qq/36/33/8/a/20080503_0a3cfd84ab4d34f2ba6d37sNt57HgkWl.jpg');}" onmouseout="http://www.zhmy.com/attachimginfo(this, 'attach_239898', 0, event)" alt="" src="http://up1.googletounion.com/data/qq/36/33/8/a/20080503_0a3cfd84ab4d34f2ba6d37sNt57HgkWl.jpg" onload="http://www.zhmy.com/attachimg(this, 'load')" border=0> </P>
<DIV class=t_attach id=attach_239898_menu style="DISPLAY: none; POSITION: absolute"><IMG class=absmiddle alt="" src="http://sclt.qq.topzj.com/images/attachicons/image.gif" border=0> <A href="http://sclt.qq.topzj.com/attachment.php?aid=239898&amp;nothumb=yes" target=_blank><STRONG><FONT color=#006699>1.jpg</FONT></STRONG></A> (40.52 KB)<BR>
<DIV class=t_smallfont>2008-5-3 20:13</DIV></DIV>
<P></P>
<P>&nbsp;</P>
<P>我们点击右边的设置即可设置卡巴斯基保留备份的时间，卡巴斯基默认为30天</P>
<P>&nbsp;</P>
<P>&nbsp;</P>
<P><SPAN id=attach_239899 onmouseover="showMenu(this.id, 0, 1)" style="DISPLAY: none; LEFT: 215px; POSITION: absolute; TOP: 1011px"><IMG src="http://style60.googletounion.com/images/default/attachimg.gif" border=0></SPAN> <IMG onmouseover="http://www.zhmy.com/attachimginfo(this, 'attach_239899', 1);attachimg(this, 'mouseover')" onclick="if(vouching(this.src)){zoom(this, 'http://up1.googletounion.com/data/qq/36/33/8/a/20080503_48e379c08566ffe263camjbLeiTUK2bh.jpg');}" onmouseout="http://www.zhmy.com/attachimginfo(this, 'attach_239899', 0, event)" alt="" src="http://up1.googletounion.com/data/qq/36/33/8/a/20080503_48e379c08566ffe263camjbLeiTUK2bh.jpg" onload="http://www.zhmy.com/attachimg(this, 'load')" border=0> </P>
<DIV class=t_attach id=attach_239899_menu style="DISPLAY: none; POSITION: absolute"><IMG class=absmiddle alt="" src="http://sclt.qq.topzj.com/images/attachicons/image.gif" border=0> <A href="http://sclt.qq.topzj.com/attachment.php?aid=239899&amp;nothumb=yes" target=_blank><STRONG><FONT color=#006699>2.jpg</FONT></STRONG></A> (35.29 KB)<BR>
<DIV class=t_smallfont>2008-5-3 20:13</DIV></DIV>
<P></P>
<P>&nbsp;</P>
<P>&nbsp;</P>
<P>下面我们来使用一下还原功能，首先我先用释放一个机器狗病毒让卡巴斯基删除并备份到备份区域<BR></P>
<P>&nbsp;</P>
<P><SPAN id=attach_239900 onmouseover="showMenu(this.id, 0, 1)" style="DISPLAY: none; LEFT: 215px; POSITION: absolute; TOP: 1540px"><IMG src="http://style60.googletounion.com/images/default/attachimg.gif" border=0></SPAN> <IMG onmouseover="http://www.zhmy.com/attachimginfo(this, 'attach_239900', 1);attachimg(this, 'mouseover')" onclick="if(vouching(this.src)){zoom(this, 'http://up1.googletounion.com/data/qq/36/33/8/a/20080503_ae58d1128d3f31ec94d7TglhQjB3Zbbc.jpg');}" onmouseout="http://www.zhmy.com/attachimginfo(this, 'attach_239900', 0, event)" alt="" src="http://up1.googletounion.com/data/qq/36/33/8/a/20080503_ae58d1128d3f31ec94d7TglhQjB3Zbbc.jpg" onload="http://www.zhmy.com/attachimg(this, 'load')" border=0> </P>
<DIV class=t_attach id=attach_239900_menu style="DISPLAY: none; POSITION: absolute"><IMG class=absmiddle alt="" src="http://sclt.qq.topzj.com/images/attachicons/image.gif" border=0> <A href="http://sclt.qq.topzj.com/attachment.php?aid=239900&amp;nothumb=yes" target=_blank><STRONG><FONT color=#006699>3.jpg</FONT></STRONG></A> (26.37 KB)<BR>
<DIV class=t_smallfont>2008-5-3 20:13</DIV></DIV>
<P></P>
<P>&nbsp;</P>
<P>下面我们来使用还原功能，这里要说明如果这个文件是被误杀的那么即便还原卡巴斯基还会再报毒，所以在还原前我们需要把误杀的文件添加至信任区域，这里我们首先打开卡巴斯基的主界面点击保护，右边有一个计算机保护状态，点击他：<BR></P>
<P>&nbsp;</P>
<P><SPAN id=attach_239901 onmouseover="showMenu(this.id, 0, 1)" style="DISPLAY: none; LEFT: 215px; POSITION: absolute; TOP: 2011px"><IMG src="http://style60.googletounion.com/images/default/attachimg.gif" border=0></SPAN> <IMG onmouseover="http://www.zhmy.com/attachimginfo(this, 'attach_239901', 1);attachimg(this, 'mouseover')" onclick="if(vouching(this.src)){zoom(this, 'http://up1.googletounion.com/data/qq/36/33/8/a/20080503_cd92895f66e5a8cfc7ecE23GbwCOttaJ.jpg');}" onmouseout="http://www.zhmy.com/attachimginfo(this, 'attach_239901', 0, event)" alt="" src="http://up1.googletounion.com/data/qq/36/33/8/a/20080503_cd92895f66e5a8cfc7ecE23GbwCOttaJ.jpg" onload="http://www.zhmy.com/attachimg(this, 'load')" border=0> </P>
<DIV class=t_attach id=attach_239901_menu style="DISPLAY: none; POSITION: absolute"><IMG class=absmiddle alt="" src="http://sclt.qq.topzj.com/images/attachicons/image.gif" border=0> <A href="http://sclt.qq.topzj.com/attachment.php?aid=239901&amp;nothumb=yes" target=_blank><STRONG><FONT color=#006699>4.jpg</FONT></STRONG></A> (42.13 KB)<BR>
<DIV class=t_smallfont>2008-5-3 20:13</DIV></DIV>
<P></P>
<P>&nbsp;</P>
<P>在出现的对话框中选择检测到，在这里有所有被卡巴斯基检测到的风险软件以及病毒的记录</P>
<P>&nbsp;</P>
<P>&nbsp;</P>
<P><SPAN class="" id=attach_239902 style="DISPLAY: none; LEFT: 215px; POSITION: absolute; TOP: 2483px" unselectable="true" h="0" w="0" Y="2513" X="215" outfunc="null" initialized="true"><IMG src="http://style60.googletounion.com/images/default/attachimg.gif" border=0></SPAN> <IMG onmouseover="http://www.zhmy.com/attachimginfo(this, 'attach_239902', 1);attachimg(this, 'mouseover')" onclick="if(vouching(this.src)){zoom(this, 'http://up1.googletounion.com/data/qq/36/33/8/a/20080503_3ea76ba2a3f19253ea35eu1UB6CSj6v5.jpg');}" onmouseout="http://www.zhmy.com/attachimginfo(this, 'attach_239902', 0, event)" alt="" src="http://up1.googletounion.com/data/qq/36/33/8/a/20080503_3ea76ba2a3f19253ea35eu1UB6CSj6v5.jpg" onload="http://www.zhmy.com/attachimg(this, 'load')" border=0> </P>
<DIV class=t_attach id=attach_239902_menu style="DISPLAY: none; Z-INDEX: 50; FILTER: progid:DXImageTransform.Microsoft.shadow(direction=135,color=#CCCCCC,strength=2); LEFT: 215px; CLIP: rect(auto auto auto auto); POSITION: absolute; TOP: 2513px" h="45" w="121" initialized="true" ctrlkey="attach_239902"><IMG class=absmiddle alt="" src="http://sclt.qq.topzj.com/images/attachicons/image.gif" border=0> <A href="http://sclt.qq.topzj.com/attachment.php?aid=239902&amp;nothumb=yes" target=_blank><STRONG><FONT color=#006699>5.jpg</FONT></STRONG></A> (56.23 KB)<BR>
<DIV class=t_smallfont>2008-5-3 20:13</DIV></DIV>
<P></P>
<P>&nbsp;</P>
<P>&nbsp;</P>
<P>我们找到与备份区同样路径的误杀文件，右键单击或者点击文件后选择操作，选择添加至信任区域，在出现的对话框中的点击组件，会出现排除范围，勾选扫描和文件反病毒，文件反病毒就是实时监控。而扫描则是全盘扫描，如果只选择文件反病毒卡巴斯基则会在下次全盘扫描时再次删除</P>
<P>&nbsp;</P>
<P>&nbsp;</P>
<P><SPAN id=attach_239903 onmouseover="showMenu(this.id, 0, 1)" style="DISPLAY: none; LEFT: 215px; POSITION: absolute; TOP: 2983px"><IMG src="http://style60.googletounion.com/images/default/attachimg.gif" border=0></SPAN> <IMG onmouseover="http://www.zhmy.com/attachimginfo(this, 'attach_239903', 1);attachimg(this, 'mouseover')" onclick="if(vouching(this.src)){zoom(this, 'http://up1.googletounion.com/data/qq/36/33/8/a/20080503_94bfc379afbb14c816f4UHBuLuA51YfP.jpg');}" onmouseout="http://www.zhmy.com/attachimginfo(this, 'attach_239903', 0, event)" alt="" src="http://up1.googletounion.com/data/qq/36/33/8/a/20080503_94bfc379afbb14c816f4UHBuLuA51YfP.jpg" onload="http://www.zhmy.com/attachimg(this, 'load')" border=0> </P>
<DIV class=t_attach id=attach_239903_menu style="DISPLAY: none; POSITION: absolute"><IMG class=absmiddle alt="" src="http://sclt.qq.topzj.com/images/attachicons/image.gif" border=0> <A href="http://sclt.qq.topzj.com/attachment.php?aid=239903&amp;nothumb=yes" target=_blank><STRONG><FONT color=#006699>6.jpg</FONT></STRONG></A> (42.46 KB)<BR>
<DIV class=t_smallfont>2008-5-3 20:13</DIV></DIV>
<P></P>
<P>&nbsp;</P>
<P>&nbsp;</P>
<P>&nbsp;</P>
<P>好了添加完毕即可执行还原，还原方法和添加至信任区域的方法差不多在备份区域点击恢复选择是，卡巴斯基会自动给出文件被删除的路径：</P>
<P>&nbsp;</P>
<P>&nbsp;</P>
<P><SPAN class="" id=attach_239904 style="DISPLAY: none; LEFT: 215px; POSITION: absolute; TOP: 3488px" unselectable="true" h="0" w="0" Y="3518" X="215" outfunc="null" initialized="true"><IMG src="http://style60.googletounion.com/images/default/attachimg.gif" border=0></SPAN> <IMG onmouseover="http://www.zhmy.com/attachimginfo(this, 'attach_239904', 1);attachimg(this, 'mouseover')" onclick="if(vouching(this.src)){zoom(this, 'http://up1.googletounion.com/data/qq/36/33/8/a/20080503_d582a7bb8f2a4630705f4aNi1znmHCwG.jpg');}" onmouseout="http://www.zhmy.com/attachimginfo(this, 'attach_239904', 0, event)" alt="" src="http://up1.googletounion.com/data/qq/36/33/8/a/20080503_d582a7bb8f2a4630705f4aNi1znmHCwG.jpg" onload="http://www.zhmy.com/attachimg(this, 'load')" border=0> </P>
<DIV class=t_attach id=attach_239904_menu style="DISPLAY: none; Z-INDEX: 50; FILTER: progid:DXImageTransform.Microsoft.shadow(direction=135,color=#CCCCCC,strength=2); LEFT: 215px; CLIP: rect(auto auto auto auto); POSITION: absolute; TOP: 3518px" h="45" w="114" initialized="true" ctrlkey="attach_239904"><IMG class=absmiddle alt="" src="http://sclt.qq.topzj.com/images/attachicons/image.gif" border=0> <A href="http://sclt.qq.topzj.com/attachment.php?aid=239904&amp;nothumb=yes" target=_blank><STRONG><FONT color=#006699>7.jpg</FONT></STRONG></A> (30.9 KB)<BR>
<DIV class=t_smallfont>2008-5-3 20:13</DIV></DIV>
<P></P>
<P>&nbsp;</P>
<P>&nbsp;</P>
<P>选择保存即可完全恢复。</P>
<P>&nbsp;</P>
<P>&nbsp;</P>
<P><SPAN id=attach_239905 onmouseover="showMenu(this.id, 0, 1)" style="DISPLAY: none; LEFT: 215px; POSITION: absolute; TOP: 3982px"><IMG src="http://style60.googletounion.com/images/default/attachimg.gif" border=0></SPAN> <IMG onmouseover="http://www.zhmy.com/attachimginfo(this, 'attach_239905', 1);attachimg(this, 'mouseover')" onclick="if(vouching(this.src)){zoom(this, 'http://up1.googletounion.com/data/qq/36/33/8/a/20080503_a494e5832e7f832e2cefvRdga5xt64oa.jpg');}" onmouseout="http://www.zhmy.com/attachimginfo(this, 'attach_239905', 0, event)" alt="" src="http://up1.googletounion.com/data/qq/36/33/8/a/20080503_a494e5832e7f832e2cefvRdga5xt64oa.jpg" onload="http://www.zhmy.com/attachimg(this, 'load')" border=0> </P>
<DIV class=t_attach id=attach_239905_menu style="DISPLAY: none; POSITION: absolute"><IMG class=absmiddle alt="" src="http://sclt.qq.topzj.com/images/attachicons/image.gif" border=0> <A href="http://sclt.qq.topzj.com/attachment.php?aid=239905&amp;nothumb=yes" target=_blank><STRONG><FONT color=#006699>8.jpg</FONT></STRONG></A> (33.31 KB)<BR>
<DIV class=t_smallfont>2008-5-3 20:13</DIV></DIV>
<P></P>
<P>&nbsp;</P>
<P>&nbsp;</P>
<P>如果卡巴斯基更新病毒库取消误报后我们要删除此规则可以打开卡巴斯基的设置——威胁和排除——排除——信任区域——排掩码里删除该规则即可. </P>
<P>&nbsp;</P>
<P>来源: <A href="http://sclt.qq.topzj.com/viewthread.php?tid=418563">http://sclt.qq.topzj.com/viewthread.php?tid=418563</A></P></DIV>]]></description>
<author>sam</author>
<pubDate>2008-8-24 16:04:00</pubDate>
</item>
<item>
<title><![CDATA[JavaMail邮件发送程序(多个接收人, 多附件发送)]]></title>
<link>http://zhan.zhmy.com/archives/2008/150198.html</link>
<description><![CDATA[<SPAN lang=EN-US style="FONT-SIZE: 10pt; COLOR: rgb(63,95,191); FONT-FAMILY: 'Courier New'"></SPAN><SPAN lang=EN-US style="FONT-SIZE: 10pt; FONT-FAMILY: 'Courier New'"><?xml:namespace prefix = o /><o:p> 
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT size=3><FONT face="Times New Roman"></FONT></FONT></SPAN></P><SPAN lang=EN-US><FONT face="Times New Roman" size=3>package org.javaee.mail;</FONT></SPAN> 
<P></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><o:p><FONT face="Times New Roman" size=3>&nbsp;</FONT></o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT face="Times New Roman" size=3>import java.util.Date;</FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT face="Times New Roman" size=3>import java.util.Properties;</FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><o:p><FONT face="Times New Roman" size=3>&nbsp;</FONT></o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT face="Times New Roman" size=3>import javax.activation.DataHandler;</FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT face="Times New Roman" size=3>import javax.activation.FileDataSource;</FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT face="Times New Roman" size=3>import javax.mail.Address;</FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT face="Times New Roman" size=3>import javax.mail.Message;</FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT face="Times New Roman" size=3>import javax.mail.Multipart;</FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT face="Times New Roman" size=3>import javax.mail.Session;</FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT face="Times New Roman" size=3>import <SPAN style="BACKGROUND: silver; moz-background-clip: -moz-initial; moz-background-origin: -moz-initial; moz-background-inline-policy: -moz-initial">javax.mail.Transport</SPAN>;</FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT face="Times New Roman" size=3>import javax.mail.internet.AddressException;</FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT face="Times New Roman" size=3>import javax.mail.internet.InternetAddress;</FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT face="Times New Roman" size=3>import javax.mail.internet.MimeBodyPart;</FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT face="Times New Roman" size=3>import javax.mail.internet.MimeMessage;</FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT face="Times New Roman" size=3>import javax.mail.internet.MimeMultipart;</FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><o:p><FONT face="Times New Roman" size=3>&nbsp;</FONT></o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT face="Times New Roman" size=3>import org.apache.commons.logging.Log;</FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT face="Times New Roman" size=3>import org.apache.commons.logging.LogFactory;</FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><o:p><FONT face="Times New Roman" size=3>&nbsp;</FONT></o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT face="Times New Roman" size=3>/**</FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT size=3><FONT face="Times New Roman"><SPAN>&nbsp;</SPAN>* @author zhanjia</FONT></FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT size=3><FONT face="Times New Roman"><SPAN>&nbsp;</SPAN>* </FONT></FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT size=3><FONT face="Times New Roman"><SPAN>&nbsp;</SPAN>*/</FONT></FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT face="Times New Roman" size=3>public class SendMail {</FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><o:p><FONT face="Times New Roman" size=3>&nbsp;</FONT></o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT size=3><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>private final static Log log = LogFactory.getLog(SendMail.class);</FONT></FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><o:p><FONT face="Times New Roman" size=3>&nbsp;</FONT></o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT size=3><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>/**</FONT></FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT size=3><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN><SPAN>&nbsp;</SPAN>* @param args</FONT></FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT size=3><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN><SPAN>&nbsp;</SPAN>*/</FONT></FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT size=3><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>public static void main(String[] args) {</FONT></FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><FONT size=3><SPAN lang=EN-US><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>String to1 = "681519@qq.com,zhanji@163.com,279679@qq.com"; // </FONT></SPAN><SPAN style="FONT-FAMILY: 宋体">多个接收邮件由逗号隔开</SPAN></FONT></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><FONT size=3><SPAN lang=EN-US><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>String from = "zhan@163.com"; // </FONT></SPAN><SPAN style="FONT-FAMILY: 宋体">发送人邮箱</SPAN></FONT></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><FONT size=3><SPAN lang=EN-US><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>String host = "smtp.163.com"; // </FONT></SPAN><SPAN style="FONT-FAMILY: 宋体">发送人邮箱服务器主机名</SPAN></FONT></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><FONT size=3><SPAN lang=EN-US><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>String user = "zhan"; // </FONT></SPAN><SPAN style="FONT-FAMILY: 宋体">发送人邮箱用户名</SPAN></FONT></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><FONT size=3><SPAN lang=EN-US><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>String password = "jsj"; // </FONT></SPAN><SPAN style="FONT-FAMILY: 宋体">发送人邮箱密码</SPAN></FONT></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><FONT size=3><SPAN lang=EN-US><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>String protocol = "smtp"; // </FONT></SPAN><SPAN style="FONT-FAMILY: 宋体">发送协议</SPAN></FONT></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><FONT size=3><SPAN lang=EN-US><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>String personal = "</FONT></SPAN><SPAN style="FONT-FAMILY: 宋体">山姆</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">"; // </FONT></SPAN><SPAN style="FONT-FAMILY: 宋体">发件人名称</SPAN></FONT></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><FONT size=3><SPAN lang=EN-US><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>String subject = "</FONT></SPAN><SPAN style="FONT-FAMILY: 宋体">最优邮件发送程序测试</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">..."; // </FONT></SPAN><SPAN style="FONT-FAMILY: 宋体">邮件主题</SPAN></FONT></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><FONT size=3><SPAN lang=EN-US><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>String content = "</FONT></SPAN><SPAN style="FONT-FAMILY: 宋体">你好</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">, </FONT></SPAN><SPAN style="FONT-FAMILY: 宋体">这是我的测试邮件内容</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">!!"; // </FONT></SPAN><SPAN style="FONT-FAMILY: 宋体">邮件内容</SPAN></FONT></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><FONT size=3><SPAN lang=EN-US><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>String[] accessories = { "E:/a.txt", "E:/b.txt", "E:/c.txt" }; // </FONT></SPAN><SPAN style="FONT-FAMILY: 宋体">附件路径</SPAN></FONT></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><o:p><FONT face="Times New Roman" size=3>&nbsp;</FONT></o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT size=3><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>SendMail obj = new SendMail();</FONT></FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT size=3><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>obj.sendMail(from, personal, user, password, subject, content,</FONT></FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><FONT size=3><SPAN lang=EN-US><FONT face="Times New Roman"><SPAN>&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;&nbsp;&nbsp; </SPAN>accessories, to1, host, protocol); // </FONT></SPAN><SPAN style="FONT-FAMILY: 宋体">发送</SPAN></FONT></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><FONT size=3><SPAN lang=EN-US><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>log.info("</FONT></SPAN><SPAN style="FONT-FAMILY: 宋体">第一次发送完成</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">");</FONT></SPAN></FONT></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT size=3><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>log.info("");</FONT></FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><SPAN><FONT face="Times New Roman" size=3>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </FONT></SPAN></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT size=3><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>try {</FONT></FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT size=3><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>InternetAddress[] to2 = { new InternetAddress("681519@qq.com"),</FONT></FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT size=3><FONT face="Times New Roman"><SPAN>&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>new InternetAddress("zhanji@163.com"),</FONT></FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><FONT size=3><SPAN lang=EN-US><FONT face="Times New Roman"><SPAN>&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>new InternetAddress("279679@qq.com") }; // </FONT></SPAN><SPAN style="FONT-FAMILY: 宋体">接收邮件数组</SPAN></FONT></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT size=3><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>obj.sendMail(from, personal, user, password, subject, content,</FONT></FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><FONT size=3><SPAN lang=EN-US><FONT face="Times New Roman"><SPAN>&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>accessories, to2, host, protocol); // </FONT></SPAN><SPAN style="FONT-FAMILY: 宋体">发送</SPAN></FONT></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT size=3><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>} catch (AddressException e) {</FONT></FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT size=3><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>e.printStackTrace();</FONT></FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT size=3><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>}</FONT></FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><FONT size=3><SPAN lang=EN-US><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>log.info("</FONT></SPAN><SPAN style="FONT-FAMILY: 宋体">第二次发送完成</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">");</FONT></SPAN></FONT></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><o:p><FONT face="Times New Roman" size=3>&nbsp;</FONT></o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT size=3><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>}</FONT></FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><o:p><FONT face="Times New Roman" size=3>&nbsp;</FONT></o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT size=3><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>public void sendMail(String from, String personal, String user,</FONT></FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT size=3><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>String password, String subject, String content,</FONT></FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT size=3><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>String[] accessories, Object to, String host, String protocol) {</FONT></FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT size=3><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>try {</FONT></FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><FONT size=3><SPAN lang=EN-US><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>// </FONT></SPAN><SPAN style="FONT-FAMILY: 宋体">初始化邮件发送属性</SPAN></FONT></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT size=3><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>Properties props = new Properties();</FONT></FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT size=3><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>props.put("mail.smtp.host", host);</FONT></FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT size=3><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>props.put("mail.smtp.auth", "true");</FONT></FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT size=3><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>// props.put("mail.bebug", "true");</FONT></FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><o:p><FONT face="Times New Roman" size=3>&nbsp;</FONT></o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><FONT size=3><SPAN lang=EN-US><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>// </FONT></SPAN><SPAN style="FONT-FAMILY: 宋体">初始化发件人地址及个人姓名信息</SPAN></FONT></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT size=3><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>Address fromAddress = null;</FONT></FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT size=3><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>if (personal == null || "".equals(personal.trim())) {</FONT></FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT size=3><FONT face="Times New Roman"><SPAN>&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;&nbsp;&nbsp; </SPAN>fromAddress = new InternetAddress(from);</FONT></FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT size=3><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>} else {</FONT></FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT size=3><FONT face="Times New Roman"><SPAN>&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;&nbsp;&nbsp; </SPAN>fromAddress = new InternetAddress(from, personal, "GBK");</FONT></FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT size=3><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>}</FONT></FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><o:p><FONT face="Times New Roman" size=3>&nbsp;</FONT></o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><FONT size=3><SPAN lang=EN-US><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>// </FONT></SPAN><SPAN style="FONT-FAMILY: 宋体">初始化接收人邮件地址信息</SPAN></FONT></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT size=3><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>Address[] address = null;</FONT></FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT size=3><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>if (to != null) {</FONT></FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT size=3><FONT face="Times New Roman"><SPAN>&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;&nbsp;&nbsp; </SPAN>if (to instanceof String) {</FONT></FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT size=3><FONT face="Times New Roman"><SPAN>&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>log</FONT></FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT size=3><FONT face="Times New Roman"><SPAN>&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>.info("--------------to is instanceof String-------------");</FONT></FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT size=3><FONT face="Times New Roman"><SPAN>&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>address = InternetAddress.parse((String) to);</FONT></FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT size=3><FONT face="Times New Roman"><SPAN>&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;&nbsp;&nbsp; </SPAN>} else if (to instanceof InternetAddress[]) {</FONT></FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT size=3><FONT face="Times New Roman"><SPAN>&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>log</FONT></FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT size=3><FONT face="Times New Roman"><SPAN>&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>.info("-----------to is instanceof InternetAddress[]-----");</FONT></FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT size=3><FONT face="Times New Roman"><SPAN>&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>address = (InternetAddress[]) to;</FONT></FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT size=3><FONT face="Times New Roman"><SPAN>&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;&nbsp;&nbsp; </SPAN>}</FONT></FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT size=3><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>}</FONT></FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><o:p><FONT face="Times New Roman" size=3>&nbsp;</FONT></o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><FONT size=3><SPAN lang=EN-US><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>log.info("---------------------</FONT></SPAN><SPAN style="FONT-FAMILY: 宋体">获取</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">session-------------------");</FONT></SPAN></FONT></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><FONT size=3><SPAN lang=EN-US><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>// </FONT></SPAN><SPAN style="FONT-FAMILY: 宋体">获取默认</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">session</FONT></SPAN></FONT></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT size=3><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>Session sendMailSession = Session.getDefaultInstance(props, null);</FONT></FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT size=3><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>// sendMailSession.setDebug(true);</FONT></FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><o:p><FONT face="Times New Roman" size=3>&nbsp;</FONT></o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><FONT size=3><SPAN lang=EN-US><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>// </FONT></SPAN><SPAN style="FONT-FAMILY: 宋体">获取指定协议的传输对象</SPAN></FONT></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT size=3><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN><SPAN style="BACKGROUND: silver; moz-background-clip: -moz-initial; moz-background-origin: -moz-initial; moz-background-inline-policy: -moz-initial">Transport</SPAN> transport = sendMailSession.getTransport(protocol);</FONT></FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><o:p><FONT face="Times New Roman" size=3>&nbsp;</FONT></o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><FONT size=3><SPAN lang=EN-US><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>log.info("---------------------</FONT></SPAN><SPAN style="FONT-FAMILY: 宋体">创建消息对象</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">-------------------");</FONT></SPAN></FONT></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><FONT size=3><SPAN lang=EN-US><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>// </FONT></SPAN><SPAN style="FONT-FAMILY: 宋体">根据邮件会话创建消息对象</SPAN></FONT></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT size=3><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></FONT></FONT></SPAN><SPAN lang=EN-US style="FONT-SIZE: 10pt; FONT-FAMILY: 'Courier New'"><SPAN lang=EN-US><FONT size=3><FONT face="Times New Roman">MimeMessage </FONT></FONT></SPAN></SPAN><SPAN lang=EN-US><FONT size=3><FONT face="Times New Roman"><SPAN></SPAN>msg = new MimeMessage(sendMailSession);</FONT></FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><FONT size=3><SPAN lang=EN-US><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>msg.setFrom(fromAddress); // </FONT></SPAN><SPAN style="FONT-FAMILY: 宋体">设置邮件发件人</SPAN></FONT></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><FONT size=3><SPAN lang=EN-US><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>msg.setRecipients(Message.RecipientType.TO, address); // </FONT></SPAN><SPAN style="FONT-FAMILY: 宋体">设置邮件接收人</SPAN></FONT></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><FONT size=3><SPAN lang=EN-US><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>msg.setSubject(subject, "GBK"); // </FONT></SPAN><SPAN style="FONT-FAMILY: 宋体">设置邮件主题</SPAN></FONT></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><FONT size=3><SPAN lang=EN-US><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>msg.setHeader("X-Mailer", "msgsend"); // </FONT></SPAN><SPAN style="FONT-FAMILY: 宋体">头部信息</SPAN></FONT></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><FONT size=3><SPAN lang=EN-US><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>msg.setSentDate(new Date()); // </FONT></SPAN><SPAN style="FONT-FAMILY: 宋体">发送日期</SPAN></FONT></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><o:p><FONT face="Times New Roman" size=3>&nbsp;</FONT></o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><FONT size=3><SPAN lang=EN-US><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>log.info("---------------------</FONT></SPAN><SPAN style="FONT-FAMILY: 宋体">创建</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">Multipart-------------------");</FONT></SPAN></FONT></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><FONT size=3><SPAN lang=EN-US><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>// </FONT></SPAN><SPAN style="FONT-FAMILY: 宋体">创建多部分邮件发送对象</SPAN></FONT></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT size=3><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>Multipart mp = new MimeMultipart();</FONT></FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><o:p><FONT face="Times New Roman" size=3>&nbsp;</FONT></o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><FONT size=3><SPAN lang=EN-US><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>// </FONT></SPAN><SPAN style="FONT-FAMILY: 宋体">邮件正文内容</SPAN></FONT></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT size=3><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>MimeBodyPart mbp1 = new MimeBodyPart();</FONT></FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT size=3><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>mbp1.setText(content, "GBK");</FONT></FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT size=3><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>mp.addBodyPart(mbp1);</FONT></FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><o:p><FONT face="Times New Roman" size=3>&nbsp;</FONT></o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><FONT size=3><SPAN lang=EN-US><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>// </FONT></SPAN><SPAN style="FONT-FAMILY: 宋体">添加附件</SPAN></FONT></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT size=3><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>for (int i = 0; i &lt; accessories.length; i++) {</FONT></FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT size=3><FONT face="Times New Roman"><SPAN>&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;&nbsp;&nbsp; </SPAN>MimeBodyPart mbp2 = new MimeBodyPart();</FONT></FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT size=3><FONT face="Times New Roman"><SPAN>&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;&nbsp;&nbsp; </SPAN>FileDataSource fds = new FileDataSource(accessories[i]);</FONT></FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT size=3><FONT face="Times New Roman"><SPAN>&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;&nbsp;&nbsp; </SPAN>mbp2.setDataHandler(new DataHandler(fds));</FONT></FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT size=3><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>mbp2.setFileName(fds.getName());</FONT></FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT size=3><FONT face="Times New Roman"><SPAN>&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;&nbsp;&nbsp; </SPAN>mp.addBodyPart(mbp2);</FONT></FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT size=3><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>}</FONT></FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><SPAN><FONT face="Times New Roman" size=3>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </FONT></SPAN></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><FONT size=3><SPAN lang=EN-US><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>// </FONT></SPAN><SPAN style="FONT-FAMILY: 宋体">将多部分的邮件内容添加到</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">Message</FONT></SPAN><SPAN style="FONT-FAMILY: 宋体">中</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">, </FONT></SPAN><SPAN style="FONT-FAMILY: 宋体">最终的邮件内容都要放到</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">Message</FONT></SPAN><SPAN style="FONT-FAMILY: 宋体">中来发送</SPAN></FONT></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT size=3><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>msg.setContent(mp);</FONT></FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><o:p><FONT face="Times New Roman" size=3>&nbsp;</FONT></o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><FONT size=3><SPAN lang=EN-US><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>log.info("--------------------</FONT></SPAN><SPAN style="FONT-FAMILY: 宋体">正式发送</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">--------------------");</FONT></SPAN></FONT></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><FONT size=3><SPAN lang=EN-US><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>// </FONT></SPAN><SPAN style="FONT-FAMILY: 宋体">发送人邮件连接并发送邮件</SPAN></FONT></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT size=3><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>transport.connect(host, user, password);</FONT></FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT size=3><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>transport.sendMessage(msg, msg</FONT></FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT size=3><FONT face="Times New Roman"><SPAN>&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>.getRecipients(Message.RecipientType.TO));</FONT></FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT size=3><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>transport.close();</FONT></FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><FONT size=3><SPAN lang=EN-US><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>log.info("</FONT></SPAN><SPAN style="FONT-FAMILY: 宋体">恭喜</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">, </FONT></SPAN><SPAN style="FONT-FAMILY: 宋体">邮件发送成功</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">!");</FONT></SPAN></FONT></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT size=3><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>} catch (Exception ex) {</FONT></FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT size=3><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>ex.printStackTrace();</FONT></FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT size=3><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>}</FONT></FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT size=3><FONT face="Times New Roman"><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>}</FONT></FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><FONT face="Times New Roman" size=3>}</FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><o:p><FONT face="Times New Roman" size=3>&nbsp;</FONT></o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left" align=left></P></o:p></SPAN>
<P></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left" align=left>&nbsp;</P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><o:p><FONT face="Times New Roman">&nbsp;</FONT></o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><o:p><FONT face="Times New Roman"></FONT></o:p></SPAN>&nbsp;</P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><o:p><FONT face="Times New Roman">个人博客：<A href="http://zhan.zhmy.com/">http://zhan.zhmy.com/</A></FONT></o:p></SPAN></P>]]></description>
<author>sam</author>
<pubDate>2008-8-13 22:49:00</pubDate>
</item>
<item>
<title><![CDATA[领域模型设计讨论与研究]]></title>
<link>http://zhan.zhmy.com/archives/2008/149770.html</link>
<description><![CDATA[<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US style="FONT-FAMILY: 宋体">------------------------<BR></SPAN><SPAN style="FONT-FAMILY: 宋体">内容转自<SPAN lang=EN-US>ROBBIN</SPAN>的<SPAN lang=EN-US> <BR>&nbsp;&nbsp;&nbsp;&nbsp;<A title=http://forum.javaeye.com href="http://forum.javaeye.com/" target=_blank><FONT color=#333333>http://forum.javaeye.com/</FONT></A><BR></SPAN>总结转自<SPAN lang=EN-US>dev2dev</SPAN>版主<SPAN lang=EN-US>whx1977</SPAN>：<SPAN lang=EN-US><BR>&nbsp;&nbsp;&nbsp;&nbsp;<A title=http://dev2dev.bea.com.cn/bbs/thread.jspa?forumID=125&amp;threadID=31360 href="http://dev2dev.bea.com.cn/bbs/thread.jspa?forumID=125&amp;threadID=31360" target=_blank><FONT color=#333333>http://dev2dev.bea.com.cn/bbs/thread.jspa?forumID=125&amp;threadID=31360</FONT></A><BR>------------------------<BR><BR></SPAN>为了补大家的遗憾，在此总结下<SPAN lang=EN-US>ROBBIN</SPAN>的领域模型的一些观点和大家的补充，在网站和演讲中，<SPAN lang=EN-US>robbin</SPAN>将领域模型初步分为<SPAN lang=EN-US>4</SPAN>大类：<SPAN lang=EN-US><BR><B style="http://www.zhmy.com/mso-bidi-font-weight: normal">1</B></SPAN><B style="mso-bidi-font-weight: normal">，失血模型<SPAN lang=EN-US><BR>2</SPAN>，贫血模型<SPAN lang=EN-US><BR>3</SPAN>，充血模型<SPAN lang=EN-US><BR>4</SPAN>，胀血模型</B><SPAN lang=EN-US><BR></SPAN>那么让我们看看究竟有这些领域模型的具体内容，以及他们的优缺点：<SPAN lang=EN-US> <BR><BR></SPAN><B style="mso-bidi-font-weight: normal">一、失血模型</B><SPAN lang=EN-US> <BR><BR></SPAN>失血模型简单来说，就是<SPAN lang=EN-US>domain object</SPAN>只有属性的<SPAN lang=EN-US>getter/setter</SPAN>方法的纯数据类，所有的业务逻辑完全由<SPAN lang=EN-US>business object</SPAN>来完成<SPAN lang=EN-US>(</SPAN>又称<SPAN lang=EN-US>TransactionScript)</SPAN>，这种模型下的<SPAN lang=EN-US>domain object</SPAN>被<SPAN lang=EN-US>Martin Fowler</SPAN>称之为<SPAN lang=EN-US>“</SPAN>贫血的<SPAN lang=EN-US>domain object”</SPAN>。下面用举一个具体的代码来说明，代码来自<SPAN lang=EN-US>Hibernate</SPAN>的<SPAN lang=EN-US>caveatemptor</SPAN>，但经过我的改写：<SPAN lang=EN-US> <BR><BR></SPAN><SPAN style="COLOR: blue">一个实体类叫做<SPAN lang=EN-US>Item</SPAN>，指的是一个拍卖项目<SPAN lang=EN-US> <BR></SPAN>一个<SPAN lang=EN-US>DAO</SPAN>接口类叫做<SPAN lang=EN-US>ItemDao <BR></SPAN>一个<SPAN lang=EN-US>DAO</SPAN>接口实现类叫做<SPAN lang=EN-US>ItemDaoHibernateImpl <BR></SPAN>一个业务逻辑类叫做<SPAN lang=EN-US>ItemManager(</SPAN>或者叫做<SPAN lang=EN-US>ItemService) </SPAN></SPAN><SPAN lang=EN-US><?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /><o:p></o:p></SPAN></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US style="FONT-FAMILY: 宋体">java</SPAN><SPAN style="FONT-FAMILY: 宋体">代码<SPAN lang=EN-US>:&nbsp;&nbsp;<BR><BR>public class Item implements Serializable { <BR>&nbsp;&nbsp;&nbsp;&nbsp;private Long id = null; <BR>&nbsp;&nbsp;&nbsp;&nbsp;private int version; <BR>&nbsp;&nbsp;&nbsp;&nbsp;private String name; <BR>&nbsp;&nbsp;&nbsp;&nbsp;private User seller; <BR>&nbsp;&nbsp;&nbsp;&nbsp;private String description; <BR>&nbsp;&nbsp;&nbsp;&nbsp;private MonetaryAmount initialPrice; <BR>&nbsp;&nbsp;&nbsp;&nbsp;private MonetaryAmount reservePrice; <BR>&nbsp;&nbsp;&nbsp;&nbsp;private Date startDate; <BR>&nbsp;&nbsp;&nbsp;&nbsp;private Date endDate; <BR>&nbsp;&nbsp;&nbsp;&nbsp;private Set categorizedItems = new HashSet(); <BR>&nbsp;&nbsp;&nbsp;&nbsp;private Collection bids = new ArrayList(); <BR>&nbsp;&nbsp;&nbsp;&nbsp;private Bid successfulBid; <BR>&nbsp;&nbsp;&nbsp;&nbsp;private ItemState state; <BR>&nbsp;&nbsp;&nbsp;&nbsp;private User approvedBy; <BR>&nbsp;&nbsp;&nbsp;&nbsp;private Date approvalDatetime; <BR>&nbsp;&nbsp;&nbsp;&nbsp;private Date created = new Date(); <BR>&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp;getter/setter</SPAN>方法省略不写，避免篇幅太长<SPAN lang=EN-US> <BR>}<o:p></o:p></SPAN></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US style="FONT-FAMILY: 宋体">java</SPAN><SPAN style="FONT-FAMILY: 宋体">代码<SPAN lang=EN-US>:&nbsp;&nbsp;<BR><BR>public interface ItemDao { <BR>&nbsp;&nbsp;&nbsp;&nbsp;public Item getItemById(Long id); <BR>&nbsp;&nbsp;&nbsp;&nbsp;public Collection findAll(); <BR>&nbsp;&nbsp;&nbsp;&nbsp;public void updateItem(Item item); <BR>}<o:p></o:p></SPAN></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US style="FONT-FAMILY: 宋体"><BR>ItemDao</SPAN><SPAN style="FONT-FAMILY: 宋体">定义持久化操作的接口，用于隔离持久化代码。<SPAN lang=EN-US> <o:p></o:p></SPAN></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US style="FONT-FAMILY: 宋体">java</SPAN><SPAN style="FONT-FAMILY: 宋体">代码<SPAN lang=EN-US>:&nbsp;&nbsp;<BR><BR>public class ItemDaoHibernateImpl implements ItemDao extends HibernateDaoSupport { <BR>&nbsp;&nbsp;&nbsp;&nbsp;public Item getItemById(Long id) { <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return (Item) getHibernateTemplate().load(Item.class, id); <BR>&nbsp;&nbsp;&nbsp;&nbsp;} <BR>&nbsp;&nbsp;&nbsp;&nbsp;public Collection findAll() { <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return (List) getHibernateTemplate().find("from Item"); <BR>&nbsp;&nbsp;&nbsp;&nbsp;} <BR>&nbsp;&nbsp;&nbsp;&nbsp;public void updateItem(Item item) { <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;getHibernateTemplate().update(item); <BR>&nbsp;&nbsp;&nbsp;&nbsp;} <BR>}<o:p></o:p></SPAN></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US style="FONT-FAMILY: 宋体">ItemDaoHibernateImpl</SPAN><SPAN style="FONT-FAMILY: 宋体">完成具体的持久化工作，请注意，数据库资源的获取和释放是在<SPAN lang=EN-US>ItemDaoHibernateImpl</SPAN>里面处理的，每个<SPAN lang=EN-US>DAO</SPAN>方法调用之<SPAN lang=EN-US><BR><BR></SPAN>前打开<SPAN lang=EN-US>Session</SPAN>，<SPAN lang=EN-US>DAO</SPAN>方法调用之后，关闭<SPAN lang=EN-US>Session</SPAN>。<SPAN lang=EN-US>(Session</SPAN>放在<SPAN lang=EN-US>ThreadLocal</SPAN>中，保证一次调用只打开关闭一次<SPAN lang=EN-US>) <o:p></o:p></SPAN></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US style="FONT-FAMILY: 宋体">java</SPAN><SPAN style="FONT-FAMILY: 宋体">代码<SPAN lang=EN-US>:&nbsp;&nbsp;<BR><BR>public class ItemManager { <BR>&nbsp;&nbsp;&nbsp;&nbsp;private ItemDao itemDao; <BR>&nbsp;&nbsp;&nbsp;&nbsp;public void setItemDao(ItemDao itemDao) { this.itemDao = itemDao;} <BR>&nbsp;&nbsp;&nbsp;&nbsp;public Bid loadItemById(Long id) { <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;itemDao.loadItemById(id); <BR>&nbsp;&nbsp;&nbsp;&nbsp;} <BR>&nbsp;&nbsp;&nbsp;&nbsp;public Collection listAllItems() { <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;&nbsp;itemDao.findAll(); <BR>&nbsp;&nbsp;&nbsp;&nbsp;} <BR>&nbsp;&nbsp;&nbsp;&nbsp;public Bid placeBid(Item item, User bidder, MonetaryAmount bidAmount, <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;&nbsp;&nbsp;&nbsp;Bid currentMaxBid, Bid currentMinBid) throws BusinessException { <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (currentMaxBid != null &amp;&amp; currentMaxBid.getAmount().compareTo(bidAmount) &gt; 0) { <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throw new BusinessException("Bid too low."); <BR>&nbsp;&nbsp;&nbsp;&nbsp;} <BR>&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;// Auction is active <BR>&nbsp;&nbsp;&nbsp;&nbsp;if ( !state.equals(ItemState.ACTIVE) ) <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throw new BusinessException("Auction is not active yet."); <BR>&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;// Auction still valid <BR>&nbsp;&nbsp;&nbsp;&nbsp;if ( item.getEndDate().before( new Date() ) ) <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throw new BusinessException("Can't place new bid, auction already ended."); <BR>&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;// Create new Bid <BR>&nbsp;&nbsp;&nbsp;&nbsp;Bid newBid = new Bid(bidAmount, item, bidder); <BR>&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;// Place bid for this Item <BR>&nbsp;&nbsp;&nbsp;&nbsp;item.getBids().add(newBid); <BR>&nbsp;&nbsp;&nbsp;&nbsp;itemDao.update(item);&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp;&nbsp;</SPAN>调用<SPAN lang=EN-US>DAO</SPAN>完成持久化操作<SPAN lang=EN-US> <BR>&nbsp;&nbsp;&nbsp;&nbsp;return newBid; <BR>&nbsp;&nbsp;&nbsp;&nbsp;} <BR>}<o:p></o:p></SPAN></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US style="FONT-FAMILY: 宋体"><BR></SPAN><SPAN style="FONT-FAMILY: 宋体">事务的管理是在<SPAN lang=EN-US>ItemManger</SPAN>这一层完成的，<SPAN lang=EN-US>ItemManager</SPAN>实现具体的业务逻辑。除了常见的和<SPAN lang=EN-US>CRUD</SPAN>有关的简单逻辑之外，这里还有一个<SPAN lang=EN-US>placeBid</SPAN>的逻辑，即项目的竞标。<SPAN lang=EN-US> <BR><BR></SPAN>以上是一个完整的第一种模型的示例代码。在这个示例中，<SPAN lang=EN-US>placeBid</SPAN>，<SPAN lang=EN-US>loadItemById</SPAN>，<SPAN lang=EN-US>findAll</SPAN>等等业务逻辑统统放在<SPAN lang=EN-US>ItemManager</SPAN>中实现，而<SPAN lang=EN-US>Item</SPAN>只有<SPAN lang=EN-US>getter/setter</SPAN>方法。<SPAN lang=EN-US><BR><BR></SPAN><B style="mso-bidi-font-weight: normal">二、贫血模型 </B><SPAN lang=EN-US><BR><BR></SPAN>简单来说，就是<SPAN lang=EN-US>domain ojbect</SPAN>包含了不依赖于持久化的领域逻辑，而那些依赖持久化的领域逻辑被分离到<SPAN lang=EN-US>Service</SPAN>层。<SPAN lang=EN-US> <BR>Service(</SPAN>业务逻辑，事务封装<SPAN lang=EN-US>) --&gt; DAO ---&gt; domain object <BR></SPAN>这也就是<SPAN lang=EN-US>Martin Fowler</SPAN>指的<SPAN lang=EN-US>rich domain object <BR><BR></SPAN><SPAN style="COLOR: blue">一个带有业务逻辑的实体类，即<SPAN lang=EN-US>domain object</SPAN>是<SPAN lang=EN-US>Item <BR></SPAN>一个<SPAN lang=EN-US>DAO</SPAN>接口<SPAN lang=EN-US>ItemDao <BR></SPAN>一个<SPAN lang=EN-US>DAO</SPAN>实现<SPAN lang=EN-US>ItemDaoHibernateImpl <BR></SPAN>一个业务逻辑对象<SPAN lang=EN-US>ItemManager </SPAN></SPAN><SPAN lang=EN-US><o:p></o:p></SPAN></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US style="FONT-FAMILY: 宋体">java</SPAN><SPAN style="FONT-FAMILY: 宋体">代码<SPAN lang=EN-US>:&nbsp;&nbsp;<BR><BR>public class Item implements Serializable { <BR>&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp;</SPAN>所有的属性和<SPAN lang=EN-US>getter/setter</SPAN>方法同上，省略<SPAN lang=EN-US> <BR>&nbsp;&nbsp;&nbsp;&nbsp;public Bid placeBid(User bidder, MonetaryAmount bidAmount, <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;Bid currentMaxBid, Bid currentMinBid) <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throws BusinessException { <BR>&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Check highest bid (can also be a different Strategy (pattern)) <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (currentMaxBid != null &amp;&amp; currentMaxBid.getAmount().compareTo(bidAmount) &gt; 0) { <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throw new BusinessException("Bid too low."); <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} <BR>&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Auction is active <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if ( !state.equals(ItemState.ACTIVE) ) <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throw new BusinessException("Auction is not active yet."); <BR>&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Auction still valid <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if ( this.getEndDate().before( new Date() ) ) <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throw new BusinessException("Can't place new bid, auction already ended."); <BR>&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Create new Bid <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Bid newBid = new Bid(bidAmount, this, bidder); <BR>&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Place bid for this Item <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.getBids.add(newBid);&nbsp;&nbsp;// </SPAN>请注意这一句，透明的进行了持久化，但是不能在这里调用<SPAN lang=EN-US>ItemDao</SPAN>，<SPAN lang=EN-US>Item</SPAN>不能对<SPAN lang=EN-US>ItemDao</SPAN>产生<SPAN lang=EN-US><BR><BR></SPAN>依赖！<SPAN lang=EN-US> <BR>&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return newBid; <BR>&nbsp;&nbsp;&nbsp;&nbsp;} <BR>}<o:p></o:p></SPAN></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US style="FONT-FAMILY: 宋体"><BR></SPAN><SPAN style="FONT-FAMILY: 宋体">竞标这个业务逻辑被放入到<SPAN lang=EN-US>Item</SPAN>中来。请注意<SPAN lang=EN-US>this.getBids.add(newBid); </SPAN>如果没有<SPAN lang=EN-US>Hibernate</SPAN>或者<SPAN lang=EN-US>JDO</SPAN>这种<SPAN lang=EN-US>O/R Mapping</SPAN>的支持，我们是无法实现这种透明的持久化行为的。但是请注意，<SPAN lang=EN-US>Item</SPAN>里面不能去调用<SPAN lang=EN-US>ItemDAO</SPAN>，对<SPAN lang=EN-US>ItemDAO</SPAN>产生依赖！<SPAN lang=EN-US> <BR><BR>ItemDao</SPAN>和<SPAN lang=EN-US>ItemDaoHibernateImpl</SPAN>的代码同上，省略。<SPAN lang=EN-US> <o:p></o:p></SPAN></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US style="FONT-FAMILY: 宋体">java</SPAN><SPAN style="FONT-FAMILY: 宋体">代码<SPAN lang=EN-US>:&nbsp;&nbsp;<BR><BR>public class ItemManager { <BR>&nbsp;&nbsp;&nbsp;&nbsp;private ItemDao itemDao; <BR>&nbsp;&nbsp;&nbsp;&nbsp;public void setItemDao(ItemDao itemDao) { this.itemDao = itemDao;} <BR>&nbsp;&nbsp;&nbsp;&nbsp;public Bid loadItemById(Long id) { <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;itemDao.loadItemById(id); <BR>&nbsp;&nbsp;&nbsp;&nbsp;} <BR>&nbsp;&nbsp;&nbsp;&nbsp;public Collection listAllItems() { <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;&nbsp;itemDao.findAll(); <BR>&nbsp;&nbsp;&nbsp;&nbsp;} <BR>&nbsp;&nbsp;&nbsp;&nbsp;public Bid placeBid(Item item, User bidder, MonetaryAmount bidAmount, <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;&nbsp;&nbsp;&nbsp;Bid currentMaxBid, Bid currentMinBid) throws BusinessException { <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;item.placeBid(bidder, bidAmount, currentMaxBid, currentMinBid); <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;itemDao.update(item);&nbsp;&nbsp;&nbsp;&nbsp;// </SPAN>必须显式的调用<SPAN lang=EN-US>DAO</SPAN>，保持持久化<SPAN lang=EN-US> <BR>&nbsp;&nbsp;&nbsp;&nbsp;} <BR>}<o:p></o:p></SPAN></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US style="FONT-FAMILY: 宋体"><BR></SPAN><SPAN style="FONT-FAMILY: 宋体">在第二种模型中，<SPAN lang=EN-US>placeBid</SPAN>业务逻辑是放在<SPAN lang=EN-US>Item</SPAN>中实现的，而<SPAN lang=EN-US>loadItemById</SPAN>和<SPAN lang=EN-US>findAll</SPAN>业务逻辑是放在<SPAN lang=EN-US>ItemManager</SPAN>中实现的。不过值得注意的是，即使<SPAN lang=EN-US>placeBid</SPAN>业务逻辑放在<SPAN lang=EN-US>Item</SPAN>中，你仍然需要在<SPAN lang=EN-US>ItemManager</SPAN>中简单的封装一层，以保证对<SPAN lang=EN-US>placeBid</SPAN>业务逻辑进行事务的管理和持久化的触发。<SPAN lang=EN-US> <BR><BR></SPAN>这种模型是<SPAN lang=EN-US>Martin Fowler</SPAN>所指的真正的<SPAN lang=EN-US>domain model</SPAN>。在这种模型中，有三个业务逻辑方法：<SPAN lang=EN-US>placeBid</SPAN>，<SPAN lang=EN-US>loadItemById</SPAN>和<SPAN lang=EN-US>findAll</SPAN>，现在的问题是哪个逻辑应该放在<SPAN lang=EN-US>Item</SPAN>中，哪个逻辑应该放在<SPAN lang=EN-US>ItemManager</SPAN>中。在我们这个例子中，<SPAN lang=EN-US>placeBid</SPAN>放在<SPAN lang=EN-US>Item</SPAN>中<SPAN lang=EN-US>(</SPAN>但是<SPAN lang=EN-US>ItemManager</SPAN>也需要对它进行简单的封装<SPAN lang=EN-US>)</SPAN>，<SPAN lang=EN-US>loadItemById</SPAN>和<SPAN lang=EN-US>findAll</SPAN>是放在<SPAN lang=EN-US>ItemManager</SPAN>中的。<SPAN lang=EN-US> <BR><BR></SPAN>切分的原则是什么呢？<SPAN lang=EN-US> Rod Johnson</SPAN>提出原则是<SPAN lang=EN-US>“case by case”</SPAN>，可重用度高的，和<SPAN lang=EN-US>domain object</SPAN>状态密切关联的放在<SPAN lang=EN-US>Item</SPAN>中，可重用度低的，和<SPAN lang=EN-US>domain object</SPAN>状态没有密切关联的放在<SPAN lang=EN-US>ItemManager</SPAN>中。<SPAN lang=EN-US> <BR><BR></SPAN>经过上面的讨论，如何区分<SPAN lang=EN-US>domain logic</SPAN>和<SPAN lang=EN-US>business logic</SPAN>，我想提出一个改进的区分原则：<SPAN lang=EN-US> <BR><BR>domain logic</SPAN>只应该和这一个<SPAN lang=EN-US>domain object</SPAN>的实例状态有关，而不应该和一批<SPAN lang=EN-US>domain object</SPAN>的状态有关；<SPAN lang=EN-US> <BR><BR></SPAN>当你把一个<SPAN lang=EN-US>logic</SPAN>放到<SPAN lang=EN-US>domain object</SPAN>中以后，这个<SPAN lang=EN-US>domain object</SPAN>应该仍然独立于持久层框架之外<SPAN lang=EN-US>(Hibernate,JDO)</SPAN>，这个<SPAN lang=EN-US>domain object</SPAN>仍然可<SPAN lang=EN-US><BR><BR></SPAN>以脱离持久层框架进行单元测试，这个<SPAN lang=EN-US>domain object</SPAN>仍然是一个完备的，自包含的，不依赖于外部环境的领域对象，这种情况下，这个<SPAN lang=EN-US>logic</SPAN>才是<SPAN lang=EN-US>domain logic</SPAN>。<SPAN lang=EN-US> <BR></SPAN>这里有一个很确定的原则：<SPAN lang=EN-US>logic</SPAN>是否只和这个<SPAN lang=EN-US>object</SPAN>的状态有关，如果只和这个<SPAN lang=EN-US>object</SPAN>有关，就是<SPAN lang=EN-US>domain logic</SPAN>；如果<SPAN lang=EN-US>logic</SPAN>是和一批<SPAN lang=EN-US>domain <SPAN style="mso-spacerun: yes">&nbsp;</SPAN>object</SPAN>的状态有关，就不是<SPAN lang=EN-US>domain logic</SPAN>，而是<SPAN lang=EN-US>business logic</SPAN>。<SPAN lang=EN-US> <BR><BR>Item</SPAN>的<SPAN lang=EN-US>placeBid</SPAN>这个业务逻辑方法没有显式的对持久化<SPAN lang=EN-US>ItemDao</SPAN>接口产生依赖，所以要放在<SPAN lang=EN-US>Item</SPAN>中。请注意，如果脱离了<SPAN lang=EN-US>Hibernate</SPAN>这个持久化框架，<SPAN lang=EN-US>Item</SPAN>这个<SPAN lang=EN-US>domain object</SPAN>是可以进行单元测试的，他不依赖于<SPAN lang=EN-US>Hibernate</SPAN>的持久化机制。它是一个独立的，可移植的，完整的，自包含的域对象。<SPAN lang=EN-US> <BR><BR></SPAN>而<SPAN lang=EN-US>loadItemById</SPAN>和<SPAN lang=EN-US>findAll</SPAN>这两个业务逻辑方法是必须显式的对持久化<SPAN lang=EN-US>ItemDao</SPAN>接口产生依赖，否则这个业务逻辑就无法完成。如果你要把这两个方法放在<SPAN lang=EN-US>Item</SPAN>中，那么<SPAN lang=EN-US>Item</SPAN>就无法脱离<SPAN lang=EN-US>Hibernate</SPAN>框架，无法在<SPAN lang=EN-US>Hibernate</SPAN>框架之外独立存在。<SPAN lang=EN-US><BR><BR></SPAN>这种模型的优点：<SPAN lang=EN-US> <BR>1</SPAN>、各层单向依赖，结构清楚，易于实现和维护<SPAN lang=EN-US> <BR>2</SPAN>、设计简单易行，底层模型非常稳定<SPAN lang=EN-US> <BR></SPAN>这种模型的缺点：<SPAN lang=EN-US> <BR>1</SPAN>、<SPAN lang=EN-US>domain object</SPAN>的部分比较紧密依赖的持久化<SPAN lang=EN-US>domain logic</SPAN>被分离到<SPAN lang=EN-US>Service</SPAN>层，显得不够<SPAN lang=EN-US>OO <BR>2</SPAN>、<SPAN lang=EN-US>Service</SPAN>层过于厚重<SPAN lang=EN-US> <BR><BR></SPAN><B style="mso-bidi-font-weight: normal">三、充血模型</B><SPAN lang=EN-US> <BR><BR></SPAN>充血模型和第二种模型差不多，所不同的就是如何划分业务逻辑，即认为，绝大多业务逻辑都应该被放在<SPAN lang=EN-US>domain object</SPAN>里面<SPAN lang=EN-US>(</SPAN>包括持久化逻辑<SPAN lang=EN-US>)</SPAN>，而<SPAN lang=EN-US>Service</SPAN>层应该是很薄的一层，仅仅封装事务和少量逻辑，不和<SPAN lang=EN-US>DAO</SPAN>层打交道。<SPAN lang=EN-US> <BR>Service(</SPAN>事务封装<SPAN lang=EN-US>) ---&gt; domain object &lt;---&gt; DAO <BR></SPAN>这种模型就是把第二种模型的<SPAN lang=EN-US>domain object</SPAN>和<SPAN lang=EN-US>business object</SPAN>合二为一了。所以<SPAN lang=EN-US>ItemManager</SPAN>就不需要了，在这种模型下面，只有三个类，他们分别是：<SPAN lang=EN-US> <BR><BR><SPAN style="COLOR: blue">Item</SPAN></SPAN><SPAN style="COLOR: blue">：包含了实体类信息，也包含了所有的业务逻辑<SPAN lang=EN-US> <BR>ItemDao</SPAN>：持久化<SPAN lang=EN-US>DAO</SPAN>接口类<SPAN lang=EN-US> <BR>ItemDaoHibernateImpl</SPAN>：<SPAN lang=EN-US>DAO</SPAN>接口的实现类 </SPAN><SPAN lang=EN-US><BR><BR></SPAN>由于<SPAN lang=EN-US>ItemDao</SPAN>和<SPAN lang=EN-US>ItemDaoHibernateImpl</SPAN>和上面完全相同，就省略了。<SPAN lang=EN-US> <o:p></o:p></SPAN></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US style="FONT-FAMILY: 宋体">java</SPAN><SPAN style="FONT-FAMILY: 宋体">代码<SPAN lang=EN-US>:&nbsp;&nbsp;<BR><BR>public class Item implements Serializable { <BR>&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp;</SPAN>所有的属性和<SPAN lang=EN-US>getter/setter</SPAN>方法都省略<SPAN lang=EN-US> <BR>&nbsp;&nbsp; private static ItemDao itemDao; <BR>&nbsp;&nbsp;&nbsp;&nbsp;public void setItemDao(ItemDao itemDao) {this.itemDao = itemDao;} <BR>&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;public static Item loadItemById(Long id) { <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return (Item) itemDao.loadItemById(id); <BR>&nbsp;&nbsp;&nbsp;&nbsp;} <BR>&nbsp;&nbsp;&nbsp;&nbsp;public static Collection findAll() { <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return (List) itemDao.findAll(); <BR>&nbsp;&nbsp;&nbsp;&nbsp;} <BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;public Bid placeBid(User bidder, MonetaryAmount bidAmount, <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Bid currentMaxBid, Bid currentMinBid) <BR>&nbsp;&nbsp;&nbsp;&nbsp;throws BusinessException { <BR>&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Check highest bid (can also be a different Strategy (pattern)) <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (currentMaxBid != null &amp;&amp; currentMaxBid.getAmount().compareTo(bidAmount) &gt; 0) { <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throw new BusinessException("Bid too low."); <BR>&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;// Auction is active <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if ( !state.equals(ItemState.ACTIVE) ) <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throw new BusinessException("Auction is not active yet."); <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Auction still valid <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if ( this.getEndDate().before( new Date() ) ) <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throw new BusinessException("Can't place new bid, auction already ended."); <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Create new Bid <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Bid newBid = new Bid(bidAmount, this, bidder); <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Place bid for this Item <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.addBid(newBid); <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;itemDao.update(this);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp;</SPAN>调用<SPAN lang=EN-US>DAO</SPAN>进行显式持久化<SPAN lang=EN-US> <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return newBid; <BR>&nbsp;&nbsp;&nbsp;&nbsp;} <BR>}<o:p></o:p></SPAN></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US style="FONT-FAMILY: 宋体"><BR></SPAN><SPAN style="FONT-FAMILY: 宋体">在这种模型中，所有的业务逻辑全部都在<SPAN lang=EN-US>Item</SPAN>中，事务管理也在<SPAN lang=EN-US>Item</SPAN>中实现。<SPAN lang=EN-US><BR></SPAN>这种模型的优点：<SPAN lang=EN-US> <BR>1</SPAN>、更加符合<SPAN lang=EN-US>OO</SPAN>的原则<SPAN lang=EN-US> <BR>2</SPAN>、<SPAN lang=EN-US>Service</SPAN>层很薄，只充当<SPAN lang=EN-US>Facade</SPAN>的角色，不和<SPAN lang=EN-US>DAO</SPAN>打交道。<SPAN lang=EN-US> <BR></SPAN>这种模型的缺点：<SPAN lang=EN-US> <BR>1</SPAN>、<SPAN lang=EN-US>DAO</SPAN>和<SPAN lang=EN-US>domain object</SPAN>形成了双向依赖，复杂的双向依赖会导致很多潜在的问题。<SPAN lang=EN-US> <BR>2</SPAN>、如何划分<SPAN lang=EN-US>Service</SPAN>层逻辑和<SPAN lang=EN-US>domain</SPAN>层逻辑是非常含混的，在实际项目中，由于设计和开发人员的水平差异，可能导致整个结构的混乱无序。<SPAN lang=EN-US> <BR>3</SPAN>、考虑到<SPAN lang=EN-US>Service</SPAN>层的事务封装特性，<SPAN lang=EN-US>Service</SPAN>层必须对所有的<SPAN lang=EN-US>domain object</SPAN>的逻辑提供相应的事务封装方法，其结果就是<SPAN lang=EN-US>Service</SPAN>完全重定义一遍所有的<SPAN lang=EN-US>domain logic</SPAN>，非常烦琐，而且<SPAN lang=EN-US>Service</SPAN>的事务化封装其意义就等于把<SPAN lang=EN-US>OO</SPAN>的<SPAN lang=EN-US>domain logic</SPAN>转换为过程的<SPAN lang=EN-US>Service TransactionScript</SPAN>。该充血模型辛辛苦苦在<SPAN lang=EN-US>domain</SPAN>层实现的<SPAN lang=EN-US>OO</SPAN>在<SPAN lang=EN-US>Service</SPAN>层又变成了过程式，对于<SPAN lang=EN-US>Web</SPAN>层程序员的角度来看，和贫血模型没有什么区别了。<SPAN lang=EN-US> <BR><BR>1.</SPAN>事务我是不希望由<SPAN lang=EN-US>Item</SPAN>管理的，而是由容器或更高一层的业务类来管理。<SPAN lang=EN-US> <BR><BR>2.</SPAN>如果<SPAN lang=EN-US>Item</SPAN>不脱离持久层的管理，如<SPAN lang=EN-US>JDO</SPAN>的<SPAN lang=EN-US>pm</SPAN>，那么<SPAN lang=EN-US>itemDao.update(this); </SPAN>是不需要的，也就是说<SPAN lang=EN-US>Item</SPAN>是在事务过程中从数据库拿出来的，并且声明周期不超出当前事务的范围。<SPAN lang=EN-US> <BR><BR>3.</SPAN>如果<SPAN lang=EN-US>Item</SPAN>是脱离持久层，也就是在<SPAN lang=EN-US>Item</SPAN>的生命周期超出了事务的范围，那就要必须显示调用<SPAN lang=EN-US>update</SPAN>或<SPAN lang=EN-US>attach</SPAN>之类的持久化方法的，这种时候就应该是按<SPAN lang=EN-US>robbin</SPAN>所说的第<SPAN lang=EN-US>2</SPAN>种模型来做。<SPAN lang=EN-US><BR><BR></SPAN><B style="mso-bidi-font-weight: normal">四、胀血模型 </B><SPAN lang=EN-US><BR></SPAN><SPAN style="COLOR: blue">基于充血模型的第三个缺点，有同学提出，干脆取消<SPAN lang=EN-US>Service</SPAN>层，只剩下<SPAN lang=EN-US>domain object</SPAN>和<SPAN lang=EN-US>DAO</SPAN>两层，在<SPAN lang=EN-US>domain object</SPAN>的<SPAN lang=EN-US>domain logic</SPAN>上面封装事务。<SPAN lang=EN-US> <BR>domain object(</SPAN>事务封装，业务逻辑<SPAN lang=EN-US>) &lt;---&gt; DAO <BR></SPAN>似乎<SPAN lang=EN-US>ruby on rails</SPAN>就是这种模型，他甚至把<SPAN lang=EN-US>domain object</SPAN>和<SPAN lang=EN-US>DAO</SPAN>都合并了。<SPAN lang=EN-US> <BR></SPAN>该模型优点：<SPAN lang=EN-US> <BR>1</SPAN>、简化了分层<SPAN lang=EN-US> <BR>2</SPAN>、也算符合<SPAN lang=EN-US>OO <BR></SPAN>该模型缺点：<SPAN lang=EN-US> <BR>1</SPAN>、很多不是<SPAN lang=EN-US>domain logic</SPAN>的<SPAN lang=EN-US>service</SPAN>逻辑也被强行放入<SPAN lang=EN-US>domain object </SPAN>，引起了<SPAN lang=EN-US>domain ojbect</SPAN>模型的不稳定<SPAN lang=EN-US> <BR>2</SPAN>、<SPAN lang=EN-US>domain object</SPAN>暴露给<SPAN lang=EN-US>web</SPAN>层过多的信息，可能引起意想不到的副作用。<SPAN lang=EN-US><BR><BR>[b][color=Blue]</SPAN>评价： </SPAN><SPAN lang=EN-US><BR><BR></SPAN>在这四种模型当中，失血模型和胀血模型应该是不被提倡的。而贫血模型和充血模型从技术上来说，都已经是可行的了。但是我个人仍然主张<SPAN lang=EN-US><BR><BR></SPAN>使用贫血模型。其理由：<SPAN lang=EN-US> <BR><BR>1</SPAN>、参考充血模型第三个缺点，由于暴露给<SPAN lang=EN-US>web</SPAN>层程序拿到的还是<SPAN lang=EN-US>Service Transaction Script</SPAN>，对于<SPAN lang=EN-US>web</SPAN>层程序员来说，底层<SPAN lang=EN-US>OO</SPAN>意义丧失了。<SPAN lang=EN-US> <BR><BR>2</SPAN>、参考充血模型第三个缺点，为了事务封装，<SPAN lang=EN-US>Service</SPAN>层要给每个<SPAN lang=EN-US>domain logic</SPAN>提供一个过程化封装，这对于编程来说，做了多余的工作，非常烦琐。<SPAN lang=EN-US> <BR><BR>3</SPAN>、<SPAN lang=EN-US>domain object</SPAN>和<SPAN lang=EN-US>DAO</SPAN>的双向依赖在做大项目中，考虑到团队成员的水平差异，很容易引入不可预知的潜在<SPAN lang=EN-US>bug</SPAN>。<SPAN lang=EN-US> <BR><BR>4</SPAN>、如何划分<SPAN lang=EN-US>domain logic</SPAN>和<SPAN lang=EN-US>service logic</SPAN>的标准是不确定的，往往要根据个人经验，有些人就是觉得某个业务他更加贴近<SPAN lang=EN-US>domain</SPAN>，也有人认<SPAN lang=EN-US><BR><BR></SPAN>为这个业务是贴近<SPAN lang=EN-US>service</SPAN>的。由于划分标准的不确定性，带来的后果就是实际项目中会产生很多这样的争议和纠纷，不同的人会有不同的划分方法，最后就会造成整个项目的逻辑分层混乱。这不像贫血模型中我提出的按照是否依赖持久化进行划分，这种标准是非常确定的，不会引起争议，因此团队开发中，不会产生此类问题。<SPAN lang=EN-US> <BR><BR>5</SPAN>、贫血模型的<SPAN lang=EN-US>domain object</SPAN>确实不够<SPAN lang=EN-US>rich</SPAN>，但是我们是做项目，不是做研究，好用就行了，管它是不是那么纯的<SPAN lang=EN-US>OO</SPAN>呢？其实我不同意<SPAN lang=EN-US>firebody</SPAN>认为的贫血模型在设计模型和实现代码中有很大跨越的说法。一个设计模型到实现的时候，你直接得到两个类：一个实体类，一个控制类就行了，没有什么跨越。<SPAN lang=EN-US> <BR><BR></SPAN><B style="mso-bidi-font-weight: normal">简单评价一下：</B><SPAN lang=EN-US> <BR><BR></SPAN><B style="mso-bidi-font-weight: normal">第一种模型</B>绝大多数人都反对，因此反对理由我也不多讲了。但遗憾的是，我观察到的实际情形是，很多使用<SPAN lang=EN-US>Hibernate</SPAN>的公司最后都是这种模<SPAN lang=EN-US><BR><BR></SPAN>型，这里面有很大的原因是很多公司的技术水平没有达到这种层次，所以导致了这种贫血模型的出现。从这一点来说，<SPAN lang=EN-US>Martin Fowler</SPAN>的批评声音不是太响了，而是太弱了，还需要再继续呐喊。<SPAN lang=EN-US> <BR><BR></SPAN><B style="mso-bidi-font-weight: normal">第二种模型</B>就是<SPAN lang=EN-US>Martin Fowler</SPAN>一直主张的模型，实际上也是我一直在实际项目中采用这种模型。我没有看过<SPAN lang=EN-US>Martin</SPAN>的<SPAN lang=EN-US>POEAA</SPAN>，之所以能够自己摸索到这种模型，也是因为从<SPAN lang=EN-US>02</SPAN>年我已经开始思考这个问题并且寻求解决方案了，但是当时没有看到<SPAN lang=EN-US>Hibernate</SPAN>，那时候做的一个小型项目我已经按照这种模型来做了，但是由于没有<SPAN lang=EN-US>O/R Mapping</SPAN>的支持，写到后来又不得不全部改成贫血的<SPAN lang=EN-US>domain object</SPAN>，项目做完以后再继续找，随后就发现了<SPAN lang=EN-US>Hibernate</SPAN>。当然，现在很多人一开始就是用<SPAN lang=EN-US>Hibernate</SPAN>做项目，没有经历过我经历的那个阶段。<SPAN lang=EN-US> <BR><BR></SPAN>不过我觉得这种模型仍然不够完美，因为你还是需要一个业务逻辑层来封装所有的<SPAN lang=EN-US>domain logic</SPAN>，这显得非常罗嗦，并且业务逻辑对象的接口也不够稳定。如果不考虑业务逻辑对象的重用性的话<SPAN lang=EN-US>(</SPAN>业务逻辑对象的可重用性也不可能好<SPAN lang=EN-US>)</SPAN>，很多人干脆就去掉了<SPAN lang=EN-US>xxxManager</SPAN>这一层，在<SPAN lang=EN-US>Web</SPAN>层的<SPAN lang=EN-US>Action</SPAN>代码直接调用<SPAN lang=EN-US>xxxDao</SPAN>，同时容器事务管理配置到<SPAN lang=EN-US>Action</SPAN>这一层上来。<SPAN lang=EN-US>Hibernate</SPAN>的<SPAN lang=EN-US>caveatemptor</SPAN>就是这样架构的一个典型应用。<SPAN lang=EN-US> <BR><BR></SPAN><B style="mso-bidi-font-weight: normal">第三种模型</B>是我很反对的一种模型，这种模型下面，<SPAN lang=EN-US>Domain Object</SPAN>和<SPAN lang=EN-US>DAO</SPAN>形成了双向依赖关系，无法脱离框架测试，并且业务逻辑层的服务也和持久层对象的状态耦合到了一起，会造成程序的高度的复杂性，很差的灵活性和糟糕的可维护性。也许将来技术进步导致的<SPAN lang=EN-US>O/R Mapping</SPAN>管理下的<SPAN lang=EN-US>domain object</SPAN>发展到足够的动态持久透明化的话，这种模型才会成为一个理想的选择。就像<SPAN lang=EN-US>O/R Mapping</SPAN>的流行使得第二种模型成为了可能<SPAN lang=EN-US>Martin Fowler</SPAN>的<SPAN lang=EN-US>Domain Model</SPAN>，或者说我们的第二种模型难道是完美无缺的吗？当然不是，接下来我就要分析一下它的不足，以及可能的解决办法，而这些都来源于我个人的实践探索。<SPAN lang=EN-US> <BR><BR></SPAN>在第二种模型中，我们可以清楚的把这<SPAN lang=EN-US>4</SPAN>个类分为三层：<SPAN lang=EN-US> <BR><BR>1</SPAN>、实体类层，即<SPAN lang=EN-US>Item</SPAN>，带有<SPAN lang=EN-US>domain logic</SPAN>的<SPAN lang=EN-US>domain object <BR>2</SPAN>、<SPAN lang=EN-US>DAO</SPAN>层，即<SPAN lang=EN-US>ItemDao</SPAN>和<SPAN lang=EN-US>ItemDaoHibernateImpl</SPAN>，抽象持久化操作的接口和实现类<SPAN lang=EN-US> <BR>3</SPAN>、业务逻辑层，即<SPAN lang=EN-US>ItemManager</SPAN>，接受容器事务控制，向<SPAN lang=EN-US>Web</SPAN>层提供统一的服务调用<SPAN lang=EN-US> <BR><BR></SPAN>在这三层中我们大家可以看到，<SPAN lang=EN-US>domain object</SPAN>和<SPAN lang=EN-US>DAO</SPAN>都是非常稳定的层，其实原因也很简单，因为<SPAN lang=EN-US>domain object</SPAN>是映射数据库字段的，数据库字段不会频繁变动，所以<SPAN lang=EN-US>domain object</SPAN>也相对稳定，而面向数据库持久化编程的<SPAN lang=EN-US>DAO</SPAN>层也不过就是<SPAN lang=EN-US>CRUD</SPAN>而已，不会有更多的花样，所以也很稳定。<SPAN lang=EN-US> <BR><BR></SPAN>问题就在于这个充当<SPAN lang=EN-US>business workflow facade</SPAN>的业务逻辑对象，它的变动是相当频繁的。业务逻辑对象通常都是无状态的、受事务控制的、<SPAN lang=EN-US>Singleton</SPAN>类，我们可以考察一下业务逻辑对象都有哪几类业务逻辑方法：<SPAN lang=EN-US> <BR><BR></SPAN>第一类：<SPAN lang=EN-US>DAO</SPAN>接口方法的代理，就是上面例子中的<SPAN lang=EN-US>loadItemById</SPAN>方法和<SPAN lang=EN-US>findAll</SPAN>方法。<SPAN lang=EN-US> <BR><BR>ItemManager</SPAN>之所以要代理这种类，目的有两个：向<SPAN lang=EN-US>Web</SPAN>层提供统一的服务调用入口点和给持久化方法增加事务控制功能。这两点都很容易理解，你不能既给<SPAN lang=EN-US>Web</SPAN>层程序员提供<SPAN lang=EN-US>xxxManager</SPAN>，也给他提供<SPAN lang=EN-US>xxxDao</SPAN>，所以你需要用<SPAN lang=EN-US>xxxManager</SPAN>封装<SPAN lang=EN-US>xxxDao</SPAN>，在这里，充当了一个简单代理功能；而事务控制也是持久化方法必须的，事务可能需要跨越多个<SPAN lang=EN-US>DAO</SPAN>方法调用，所以必须放在业务逻辑层，而不能放在<SPAN lang=EN-US>DAO</SPAN>层。<SPAN lang=EN-US> <BR><BR></SPAN>但是必须看到，对于一个典型的<SPAN lang=EN-US>web</SPAN>应用来说，绝大多数的业务逻辑都是简单的<SPAN lang=EN-US>CRUD</SPAN>逻辑，所以这种情况下，针对每个<SPAN lang=EN-US>DAO</SPAN>方法，<SPAN lang=EN-US>xxxManager</SPAN>都需要提供一个对应的封装方法，这不但是非常枯燥的，也是令人感觉非常不好的。<SPAN lang=EN-US> <BR><BR></SPAN>第二类：<SPAN lang=EN-US>domain logic</SPAN>的方法代理。就是上面例子中<SPAN lang=EN-US>placeBid</SPAN>方法。虽然<SPAN lang=EN-US>Item</SPAN>已经有了<SPAN lang=EN-US>placeBid</SPAN>方法，但是<SPAN lang=EN-US>ItemManager</SPAN>仍然需要封装一下<SPAN lang=EN-US>Item</SPAN>的<SPAN lang=EN-US>placeBid</SPAN>，然后再提供一个简单封装之后的代理方法。<SPAN lang=EN-US> <BR><BR></SPAN>这和第一种情况类似，其原因也一样，也是为了给<SPAN lang=EN-US>Web</SPAN>层提供一个统一的服务调用入口点和给隐式的持久化动作提供事务控制。<SPAN lang=EN-US> <BR><BR></SPAN>同样，和第一种情况一样，针对每个<SPAN lang=EN-US>domain logic</SPAN>方法，<SPAN lang=EN-US>xxxManager</SPAN>都需要提供一个对应的封装方法，同样是枯燥的，令人不爽的。<SPAN lang=EN-US> <BR><BR></SPAN>第三类：需要多个<SPAN lang=EN-US>domain object</SPAN>和<SPAN lang=EN-US>DAO</SPAN>参与协作的<SPAN lang=EN-US>business workflow</SPAN>。这种情况是业务逻辑对象真正应该完成的职责。<SPAN lang=EN-US> <BR><BR></SPAN>在这个简单的例子中，没有涉及到这种情况，不过大家都可以想像的出来这种应用场景，因此不必举例说明了。<SPAN lang=EN-US> <BR><BR></SPAN>通过上面的分析可以看出，只有第三类业务逻辑方法才是业务逻辑对象真正应该承担的职责，而前两类业务逻辑方法都是<SPAN lang=EN-US>“</SPAN>无奈之举<SPAN lang=EN-US>”</SPAN>，不得不为之的事情，不但枯燥，而且令人沮丧。<SPAN lang=EN-US> <BR><BR></SPAN>分析完了业务逻辑对象，我们再回头看一下<SPAN lang=EN-US>domain object</SPAN>，我们要仔细考察一下<SPAN lang=EN-US>domain logic</SPAN>的话，会发现<SPAN lang=EN-US>domain logic</SPAN>也分为两类：<SPAN lang=EN-US> <BR><BR></SPAN>第一类：需要持久层框架隐式的实现透明持久化的<SPAN lang=EN-US>domain logic</SPAN>，例如<SPAN lang=EN-US>Item</SPAN>的<SPAN lang=EN-US>placeBid</SPAN>方法中的这一句：<SPAN lang=EN-US> <BR>java</SPAN>代码<SPAN lang=EN-US>:&nbsp;&nbsp;<BR><BR>this.getBids().add(newBid);<BR><BR></SPAN>上面已经着重提到，虽然这仅仅只是一个<SPAN lang=EN-US>Java</SPAN>集合的添加新元素的操作，但是实际上通过事务的控制，会潜在的触发两条<SPAN lang=EN-US>SQL</SPAN>：一条是<SPAN lang=EN-US>insert</SPAN>一条记录到<SPAN lang=EN-US>bid</SPAN>表，一条是更新<SPAN lang=EN-US>item</SPAN>表相应的记录。如果我们让<SPAN lang=EN-US>Item</SPAN>脱离<SPAN lang=EN-US>Hibernate</SPAN>进行单元测试，它就是一个单纯的<SPAN lang=EN-US>Java</SPAN>集合操作，如果我们把他加入到<SPAN lang=EN-US>Hibernate</SPAN>框架中，他就会潜在的触发两条<SPAN lang=EN-US>SQL</SPAN>，这就是隐式的依赖于持久化的<SPAN lang=EN-US>domain logic</SPAN>。<SPAN lang=EN-US> <BR></SPAN>特别请注意的一点是：在没有<SPAN lang=EN-US>Hibernate/JDO</SPAN>这类可以实现<SPAN lang=EN-US>“</SPAN>透明的持久化<SPAN lang=EN-US>”</SPAN>工具出现之前，这类<SPAN lang=EN-US>domain logic</SPAN>是无法实现的。<SPAN lang=EN-US> <BR><BR></SPAN>对于这一类<SPAN lang=EN-US>domain logic</SPAN>，业务逻辑对象必须提供相应的封装方法，以实现事务控制。<SPAN lang=EN-US> <BR><BR></SPAN>第二类：完全不依赖持久化的<SPAN lang=EN-US>domain logic</SPAN>，例如<SPAN lang=EN-US>readonly</SPAN>例子中的<SPAN lang=EN-US>Topic</SPAN>，如下：<SPAN lang=EN-US> <o:p></o:p></SPAN></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US style="FONT-FAMILY: 宋体">java</SPAN><SPAN style="FONT-FAMILY: 宋体">代码<SPAN lang=EN-US>:&nbsp;&nbsp;<BR><BR>class Topic { <BR>&nbsp;&nbsp;&nbsp;&nbsp;boolean isAllowReply() { <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Calendar dueDate = Calendar.getInstance(); <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dueDate.setTime(lastUpdatedTime); <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dueDate.add(Calendar.DATE, forum.timeToLive); <BR>&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Date now = new Date(); <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return now.after(dueDate.getTime()); <BR>&nbsp;&nbsp;&nbsp;&nbsp;} <BR>}<o:p></o:p></SPAN></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US style="FONT-FAMILY: 宋体"><BR></SPAN><SPAN style="FONT-FAMILY: 宋体">注意这个<SPAN lang=EN-US>isAllowReply</SPAN>方法，他和持久化完全不发生一丁点关系。在实际的开发中，我们同样会遇到很多这种不需要持久化的业务逻辑<SPAN lang=EN-US>(</SPAN>主要发生在日期运算、数值运算和枚举运算方面<SPAN lang=EN-US>)</SPAN>，这种<SPAN lang=EN-US>domain logic</SPAN>不管脱离不脱离所在的框架，它的行为都是一致的。对于这种<SPAN lang=EN-US>domain logic</SPAN>，业务逻辑层并不需要提供封装方法，它可以适用于任何场合。<SPAN lang=EN-US><BR></SPAN>概括说：<SPAN lang=EN-US>action</SPAN>做为控制器 ，<SPAN lang=EN-US>service</SPAN>面向<SPAN lang=EN-US>use case</SPAN>，<SPAN lang=EN-US>domain object</SPAN>是中间稳定的一层，<SPAN lang=EN-US>dao</SPAN>是作为下层的服务，提供持久化服务，可以被<SPAN lang=EN-US>domain object</SPAN>所使用。<SPAN lang=EN-US> <BR></SPAN>针对上面帖子中分析的业务逻辑对象的方法有三类的情况，我们在实际的项目中会遇到一些困扰。主要的困扰就是业务逻辑对象的方法会变动的相当频繁，并且业务逻辑对象的方法数量会非常庞大。针对这个问题，我所知道的有两种解决方案，我姑且称之为第二种模型的两类变种：<SPAN lang=EN-US> <BR><BR></SPAN>第一类变种就是<SPAN lang=EN-US>partech</SPAN>的那种模型，简单的来说，就是把业务逻辑对象层和<SPAN lang=EN-US>DAO</SPAN>层合二为一；第二类变种就是干脆取消业务逻辑层，把事务控制前推至<SPAN lang=EN-US>Web</SPAN>层的<SPAN lang=EN-US>Action</SPAN>层来处理，下面分别分析一下两类变种的优缺点<SPAN lang=EN-US>:<BR><BR></SPAN>第一类变种是合并业务逻辑对象和<SPAN lang=EN-US>DAO</SPAN>层，这种设计代码简化为<SPAN lang=EN-US>3</SPAN>个类，如下所示：<SPAN lang=EN-US> <BR><BR></SPAN>一个<SPAN lang=EN-US>domain object</SPAN>：<SPAN lang=EN-US>Item(</SPAN>同第二种模型的代码，省略<SPAN lang=EN-US>) <BR></SPAN>一个业务层接口：<SPAN lang=EN-US>ItemManager(</SPAN>合并原来的<SPAN lang=EN-US>ItemManager</SPAN>方法签名和<SPAN lang=EN-US>ItemDao</SPAN>接口而来<SPAN lang=EN-US>) <BR></SPAN>一个业务层实现类：<SPAN lang=EN-US>ItemManagerHibernateImpl(</SPAN>合并原来的<SPAN lang=EN-US>ItemManager</SPAN>方法实现和<SPAN lang=EN-US>ItemDaoHibernateImpl) <o:p></o:p></SPAN></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US style="FONT-FAMILY: 宋体">java</SPAN><SPAN style="FONT-FAMILY: 宋体">代码<SPAN lang=EN-US>:&nbsp;&nbsp;<BR><BR>public interface ItemManager { <BR>&nbsp;&nbsp;&nbsp;&nbsp;public Item loadItemById(Long id); <BR>&nbsp;&nbsp;&nbsp;&nbsp;public Collection findAll(); <BR>&nbsp;&nbsp;&nbsp;&nbsp;public void updateItem(Item item); <BR>&nbsp;&nbsp;&nbsp;&nbsp;public Bid placeBid(Item item, User bidder, MonetaryAmount bidAmount, Bid currentMaxBid, Bid currentMinBid) throws <BR><BR>BusinessException; <BR>}<o:p></o:p></SPAN></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US style="FONT-FAMILY: 宋体">java</SPAN><SPAN style="FONT-FAMILY: 宋体">代码<SPAN lang=EN-US>:&nbsp;&nbsp;<BR><BR>public class ItemManagerHibernateImpl implements ItemManager extends HibernateDaoSupport { <BR>&nbsp;&nbsp;&nbsp;&nbsp;public Item loadItemById(Long id) { <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return (Item) getHibernateTemplate().load(Item.class, id); <BR>&nbsp;&nbsp;&nbsp;&nbsp;} <BR>&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;&nbsp;Collection findAll() { <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return (List) getHibernateTemplate().find("from Item"); <BR>&nbsp;&nbsp;&nbsp;&nbsp;} <BR>&nbsp;&nbsp;&nbsp;&nbsp;public void updateItem(Item item) { <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;getHibernateTemplate().update(item); <BR>&nbsp;&nbsp;&nbsp;&nbsp;} <BR>&nbsp;&nbsp;&nbsp;&nbsp;public Bid placeBid(Item item, User bidder, MonetaryAmount bidAmount, Bid currentMaxBid, Bid currentMinBid) throws <BR><BR>BusinessException { <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;item.placeBid(bidder, bidAmount, currentMaxBid, currentMinBid); <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;updateItem(item);&nbsp;&nbsp; //&nbsp;&nbsp;</SPAN>确保持久化<SPAN lang=EN-US>item <BR>&nbsp;&nbsp;&nbsp;&nbsp;} <BR>}<o:p></o:p></SPAN></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US style="FONT-FAMILY: 宋体"><BR></SPAN><SPAN style="FONT-FAMILY: 宋体">第二种模型的第一类变种把业务逻辑对象和<SPAN lang=EN-US>DAO</SPAN>层合并到了一起。<SPAN lang=EN-US> <BR></SPAN>考虑到典型的<SPAN lang=EN-US>web</SPAN>应用中，简单的<SPAN lang=EN-US>CRUD</SPAN>操作占据了业务逻辑的绝大多数比例，因此第一类变种的优点是：避免了业务逻辑不得不大量封装<SPAN lang=EN-US>DAO</SPAN>接口的问题，简化了软件架构设计，节省了大量的业务层代码量。<SPAN lang=EN-US> <BR></SPAN>这种方案的缺点是：把<SPAN lang=EN-US>DAO</SPAN>接口方法和业务逻辑方法混合到了一起，显得职责不够单一化，软件分层结构不够清晰；此外这种方案仍然不得不对隐式依赖持久化的<SPAN lang=EN-US>domain logic</SPAN>提供封装方法，未能做到彻底的简化。<SPAN lang=EN-US> <BR><BR></SPAN>总体而言，个人认为这种变种各方面权衡下来，是目前相对最为合理方案，这也是我目前项目中采用的架构<SPAN lang=EN-US><BR></SPAN>第二种模型的第二类变种就是干脆取消<SPAN lang=EN-US>ItemManager</SPAN>，保留原来的<SPAN lang=EN-US>Item</SPAN>，<SPAN lang=EN-US>ItemDao</SPAN>，<SPAN lang=EN-US>ItemDaoHibernateImpl</SPAN>这<SPAN lang=EN-US>3</SPAN>个类。在这种情况下把事务控制前推至<SPAN lang=EN-US>Web</SPAN>层的<SPAN lang=EN-US>Action</SPAN>去控制，具体来说，就是直接对<SPAN lang=EN-US>Action</SPAN>的<SPAN lang=EN-US>execute()</SPAN>方法进行容器事务声明。<SPAN lang=EN-US> <BR><BR></SPAN>这种方式的优点是：极大的简化了业务逻辑层，避免了业务逻辑对象不得不大量封装<SPAN lang=EN-US>DAO</SPAN>接口方法和大量封装<SPAN lang=EN-US>domain logic</SPAN>的问题。对于业务逻辑非常简单的项目，采用这种方案是一个非常合适的选择。<SPAN lang=EN-US> <BR><BR></SPAN>这种方式的缺点主要有<SPAN lang=EN-US>3</SPAN>个：<SPAN lang=EN-US> <BR><BR>1) </SPAN>由于彻底取消了业务逻辑对象层，对于那些有重用需要的、多个<SPAN lang=EN-US>domain object</SPAN>和多个<SPAN lang=EN-US>DAO</SPAN>参与的、复杂业务逻辑流程来说，你不得不在<SPAN lang=EN-US>Action</SPAN>中一遍又一遍的重复实现这部分代码，效率既低，也不利于软件重用。<SPAN lang=EN-US> <BR><BR>2) Web</SPAN>层程序员需要对持久层机制有相当高程度的了解和掌握，必须知道什么时候应该调用什么<SPAN lang=EN-US>DAO</SPAN>方法进行必要的持久化。<SPAN lang=EN-US> <BR><BR>3) </SPAN>事务的范围被扩大了。假设你在一个<SPAN lang=EN-US>Action</SPAN>中，首先需要插入一条记录，然后再需要查询数据库，显示一个记录列表，对于这种情况，事务的作用范围应该是在插入记录的前后，但是现在扩大到了整个<SPAN lang=EN-US>execute</SPAN>执行期间。如果插入动作完毕，查询动作过程中出现通往数据库服务器的网络异常，那么前面的插入动作将回滚，但是实际上我们期望的是插入应该被提交。<SPAN lang=EN-US> <BR><BR></SPAN>总体而言，这种变种的缺陷比较大，只适合在业务逻辑非常简单的小型项目中，值得一提的是<SPAN lang=EN-US>Hibernate</SPAN>的<SPAN lang=EN-US>caveatemptor</SPAN>就是采用这种变种的架构，大家可以参考一下。<SPAN lang=EN-US> <BR><BR></SPAN>综上所述，在采用<SPAN lang=EN-US>Rich Domain Object</SPAN>模型的三种解决方案中<SPAN lang=EN-US>(</SPAN>第二模型，第二模型第一变种，第二模型第二变种<SPAN lang=EN-US>)</SPAN>，我认为权衡下来，第二模型的第一变种是相对最好的解决方案，不过它仍然有一定的不足，在这里我也希望大家能够提出更好的解决方案。<SPAN lang=EN-US><BR></SPAN>，<SPAN lang=EN-US>partech </SPAN>提出了 实体控制对象 和 实体对象 两种不同层次的<SPAN lang=EN-US> Domain Object </SPAN>，由于<SPAN lang=EN-US> Domain Object </SPAN>可以依赖于<SPAN lang=EN-US> XXXFinderDAO</SPAN>，因此，也就不存在<SPAN lang=EN-US>“</SPAN>大数据量问题<SPAN lang=EN-US>”</SPAN>，因此，整个<SPAN lang=EN-US> Domain </SPAN>体系，对于实际业务表述的更为完整，更为一体化。我非常倾向这种方式。<SPAN lang=EN-US><BR></SPAN>一般是这样的顺序：<SPAN lang=EN-US> <BR>Client--&gt;Service--&gt;D Object--&gt;DAO--&gt;DB <BR></SPAN>至于哪些该放在哪里，基本有这样的原则：（就是<SPAN lang=EN-US>robbin</SPAN>的第二种了）<SPAN lang=EN-US> <BR>DO</SPAN>封装内在的业务逻辑<SPAN lang=EN-US> <BR>Service </SPAN>封装外在于<SPAN lang=EN-US>DO</SPAN>的业务逻辑<SPAN lang=EN-US> <BR><BR></SPAN>当然如果业务逻辑简单或者没有的话也可以：<SPAN lang=EN-US> <BR>Client--&gt;D Object--&gt;DAO--&gt;DB <BR><BR></SPAN>对于第二种的第一个变种固然是个好办法，但如<SPAN lang=EN-US>Robbin</SPAN>所说也有缺陷如果有多个<SPAN lang=EN-US>Servcie</SPAN>要调用<SPAN lang=EN-US>DAO</SPAN>的话，就有问题了。合并也意味中不能很好的重用<SPAN lang=EN-US> <BR><BR></SPAN>说到底就是粒度的问题，分得细重用好，但类多、结构复杂、繁琐。分得粗（干脆用一个类干所有的事）重用差，但类少、结构简单。<SPAN lang=EN-US><o:p></o:p></SPAN></SPAN></P>
<P>&nbsp;</P>]]></description>
<author>sam</author>
<pubDate>2008-7-22 1:12:00</pubDate>
</item>
<item>
<title><![CDATA[(转)线程同步]]></title>
<link>http://zhan.zhmy.com/archives/2008/149601.html</link>
<description><![CDATA[<P>线程同步 <BR>作者 : buaawhl <BR><BR>我们可以在计算机上运行各种计算机软件程序。每一个运行的程序可能包括多个独立运行的线程（Thread）。 <BR>线程（Thread）是一份独立运行的程序，有自己专用的运行栈。线程有可能和其他线程共享一些资源，比如，内存，文件，数据库等。 <BR>当多个线程同时读写同一份共享资源的时候，可能会引起冲突。这时候，我们需要引入线程“同步”机制，即各位线程之间要有个先来后到，不能一窝蜂挤上去抢作一团。 <BR>同步这个词是从英文synchronize（使同时发生）翻译过来的。我也不明白为什么要用这个很容易引起误解的词。既然大家都这么用，咱们也就只好这么将就。 <BR>线程同步的真实意思和字面意思恰好相反。线程同步的真实意思，其实是“排队”：几个线程之间要排队，一个一个对共享资源进行操作，而不是同时进行操作。 <BR><BR>因此，关于线程同步，需要牢牢记住的第一点是：线程同步就是线程排队。同步就是排队。线程同步的目的就是避免线程“同步”执行。这可真是个无聊的绕口令。 <BR>关于线程同步，需要牢牢记住的第二点是 “共享”这两个字。只有共享资源的读写访问才需要同步。如果不是共享资源，那么就根本没有同步的必要。 <BR>关于线程同步，需要牢牢记住的第三点是，只有“变量”才需要同步访问。如果共享的资源是固定不变的，那么就相当于“常量”，线程同时读取常量也不需要同步。至少一个线程修改共享资源，这样的情况下，线程之间就需要同步。 <BR>关于线程同步，需要牢牢记住的第四点是：多个线程访问共享资源的代码有可能是同一份代码，也有可能是不同的代码；无论是否执行同一份代码，只要这些线程的代码访问同一份可变的共享资源，这些线程之间就需要同步。 <BR><BR>为了加深理解，下面举几个例子。 <BR>有两个采购员，他们的工作内容是相同的，都是遵循如下的步骤： <BR>（1）到市场上去，寻找并购买有潜力的样品。 <BR>（2）回到公司，写报告。 <BR>这两个人的工作内容虽然一样，他们都需要购买样品，他们可能买到同样种类的样品，但是他们绝对不会购买到同一件样品，他们之间没有任何共享资源。所以，他们可以各自进行自己的工作，互不干扰。 <BR>这两个采购员就相当于两个线程；两个采购员遵循相同的工作步骤，相当于这两个线程执行同一段代码。 <BR><BR>下面给这两个采购员增加一个工作步骤。采购员需要根据公司的“布告栏”上面公布的信息，安排自己的工作计划。 <BR>这两个采购员有可能同时走到布告栏的前面，同时观看布告栏上的信息。这一点问题都没有。因为布告栏是只读的，这两个采购员谁都不会去修改布告栏上写的信息。 <BR><BR>下面增加一个角色。一个办公室行政人员这个时候，也走到了布告栏前面，准备修改布告栏上的信息。 <BR>如果行政人员先到达布告栏，并且正在修改布告栏的内容。两个采购员这个时候，恰好也到了。这两个采购员就必须等待行政人员完成修改之后，才能观看修改后的信息。 <BR>如果行政人员到达的时候，两个采购员已经在观看布告栏了。那么行政人员需要等待两个采购员把当前信息记录下来之后，才能够写上新的信息。 <BR>上述这两种情况，行政人员和采购员对布告栏的访问就需要进行同步。因为其中一个线程（行政人员）修改了共享资源（布告栏）。而且我们可以看到，行政人员的工作流程和采购员的工作流程（执行代码）完全不同，但是由于他们访问了同一份可变共享资源（布告栏），所以他们之间需要同步。 <BR><BR>同步锁 <BR><BR>前面讲了为什么要线程同步，下面我们就来看如何才能线程同步。 <BR>线程同步的基本实现思路还是比较容易理解的。我们可以给共享资源加一把锁，这把锁只有一把钥匙。哪个线程获取了这把钥匙，才有权利访问该共享资源。 <BR>生活中，我们也可能会遇到这样的例子。一些超市的外面提供了一些自动储物箱。每个储物箱都有一把锁，一把钥匙。人们可以使用那些带有钥匙的储物箱，把东西放到储物箱里面，把储物箱锁上，然后把钥匙拿走。这样，该储物箱就被锁住了，其他人不能再访问这个储物箱。（当然，真实的储物箱钥匙是可以被人拿走复制的，所以不要把贵重物品放在超市的储物箱里面。于是很多超市都采用了电子密码锁。） <BR>线程同步锁这个模型看起来很直观。但是，还有一个严峻的问题没有解决，这个同步锁应该加在哪里？ <BR>当然是加在共享资源上了。反应快的读者一定会抢先回答。 <BR>没错，如果可能，我们当然尽量把同步锁加在共享资源上。一些比较完善的共享资源，比如，文件系统，数据库系统等，自身都提供了比较完善的同步锁机制。我们不用另外给这些资源加锁，这些资源自己就有锁。 <BR>但是，大部分情况下，我们在代码中访问的共享资源都是比较简单的共享对象。这些对象里面没有地方让我们加锁。 <BR>读者可能会提出建议：为什么不在每一个对象内部都增加一个新的区域，专门用来加锁呢？这种设计理论上当然也是可行的。问题在于，线程同步的情况并不是很普遍。如果因为这小概率事件，在所有对象内部都开辟一块锁空间，将会带来极大的空间浪费。得不偿失。 <BR>于是，现代的编程语言的设计思路都是把同步锁加在代码段上。确切的说，是把同步锁加在“访问共享资源的代码段”上。这一点一定要记住，同步锁是加在代码段上的。 <BR>同步锁加在代码段上，就很好地解决了上述的空间浪费问题。但是却增加了模型的复杂度，也增加了我们的理解难度。 <BR>现在我们就来仔细分析“同步锁加在代码段上”的线程同步模型。 <BR>首先，我们已经解决了同步锁加在哪里的问题。我们已经确定，同步锁不是加在共享资源上，而是加在访问共享资源的代码段上。 <BR>其次，我们要解决的问题是，我们应该在代码段上加什么样的锁。这个问题是重点中的重点。这是我们尤其要注意的问题：访问同一份共享资源的不同代码段，应该加上同一个同步锁；如果加的是不同的同步锁，那么根本就起不到同步的作用，没有任何意义。 <BR>这就是说，同步锁本身也一定是多个线程之间的共享对象。 <BR><BR>Java语言的synchronized关键字 <BR><BR>为了加深理解，举几个代码段同步的例子。 <BR>不同语言的同步锁模型都是一样的。只是表达方式有些不同。这里我们以当前最流行的Java语言为例。Java语言里面用synchronized关键字给代码段加锁。整个语法形式表现为 <BR>synchronized(同步锁) { <BR>// 访问共享资源，需要同步的代码段 <BR>} <BR><BR>这里尤其要注意的就是，同步锁本身一定要是共享的对象。 <BR><BR>… f1() { <BR><BR>Object lock1 = new Object(); // 产生一个同步锁 <BR><BR>synchronized(lock1){ <BR>// 代码段 A <BR>// 访问共享资源 resource1 <BR>// 需要同步 <BR>} <BR>} <BR><BR>上面这段代码没有任何意义。因为那个同步锁是在函数体内部产生的。每个线程调用这段代码的时候，都会产生一个新的同步锁。那么多个线程之间，使用的是不同的同步锁。根本达不到同步的目的。 <BR>同步代码一定要写成如下的形式，才有意义。 <BR><BR>public static final Object lock1 = new Object(); <BR><BR>… f1() { <BR><BR>synchronized(lock1){ // lock1 是公用同步锁 <BR>// 代码段 A <BR>// 访问共享资源 resource1 <BR>// 需要同步 <BR>} <BR><BR>你不一定要把同步锁声明为static或者public，但是你一定要保证相关的同步代码之间，一定要使用同一个同步锁。 <BR>讲到这里，你一定会好奇，这个同步锁到底是个什么东西。为什么随便声明一个Object对象，就可以作为同步锁？ <BR>在Java里面，同步锁的概念就是这样的。任何一个Object Reference都可以作为同步锁。我们可以把Object Reference理解为对象在内存分配系统中的内存地址。因此，要保证同步代码段之间使用的是同一个同步锁，我们就要保证这些同步代码段的synchronized关键字使用的是同一个Object Reference，同一个内存地址。这也是为什么我在前面的代码中声明lock1的时候，使用了final关键字，这就是为了保证lock1的Object Reference在整个系统运行过程中都保持不变。 <BR>一些求知欲强的读者可能想要继续深入了解synchronzied(同步锁)的实际运行机制。Java虚拟机规范中（你可以在google用“JVM Spec”等关键字进行搜索），有对synchronized关键字的详细解释。synchronized会编译成 monitor enter, … monitor exit之类的指令对。Monitor就是实际上的同步锁。每一个Object Reference在概念上都对应一个monitor。 <BR>这些实现细节问题，并不是理解同步锁模型的关键。我们继续看几个例子，加深对同步锁模型的理解。 <BR><BR>public static final Object lock1 = new Object(); <BR><BR>… f1() { <BR><BR>synchronized(lock1){ // lock1 是公用同步锁 <BR>// 代码段 A <BR>// 访问共享资源 resource1 <BR>// 需要同步 <BR>} <BR>} <BR><BR>… f2() { <BR><BR>synchronized(lock1){ // lock1 是公用同步锁 <BR>// 代码段 B <BR>// 访问共享资源 resource1 <BR>// 需要同步 <BR>} <BR>} <BR><BR>上述的代码中，代码段A和代码段B就是同步的。因为它们使用的是同一个同步锁lock1。 <BR>如果有10个线程同时执行代码段A，同时还有20个线程同时执行代码段B，那么这30个线程之间都是要进行同步的。 <BR>这30个线程都要竞争一个同步锁lock1。同一时刻，只有一个线程能够获得lock1的所有权，只有一个线程可以执行代码段A或者代码段B。其他竞争失败的线程只能暂停运行，进入到该同步锁的就绪（Ready）队列。 <BR>每一个同步锁下面都挂了几个线程队列，包括就绪（Ready）队列，待召（Waiting）队列等。比如，lock1对应的就绪队列就可以叫做lock1 - ready queue。每个队列里面都可能有多个暂停运行的线程。 <BR>注意，竞争同步锁失败的线程进入的是该同步锁的就绪（Ready）队列，而不是后面要讲述的待召队列（Waiting Queue，也可以翻译为等待队列）。就绪队列里面的线程总是时刻准备着竞争同步锁，时刻准备着运行。而待召队列里面的线程则只能一直等待，直到等到某个信号的通知之后，才能够转移到就绪队列中，准备运行。 <BR>成功获取同步锁的线程，执行完同步代码段之后，会释放同步锁。该同步锁的就绪队列中的其他线程就继续下一轮同步锁的竞争。成功者就可以继续运行，失败者还是要乖乖地待在就绪队列中。 <BR>因此，线程同步是非常耗费资源的一种操作。我们要尽量控制线程同步的代码段范围。同步的代码段范围越小越好。我们用一个名词“同步粒度”来表示同步代码段的范围。 <BR>同步粒度 <BR>在Java语言里面，我们可以直接把synchronized关键字直接加在函数的定义上。 <BR>比如。 <BR>… synchronized … f1() { <BR>// f1 代码段 <BR>} <BR><BR>这段代码就等价于 <BR>… f1() { <BR>synchronized(this){ // 同步锁就是对象本身 <BR>// f1 代码段 <BR>} <BR>} <BR><BR>同样的原则适用于静态（static）函数 <BR>比如。 <BR>… static synchronized … f1() { <BR>// f1 代码段 <BR>} <BR><BR>这段代码就等价于 <BR>…static … f1() { <BR>synchronized(Class.forName(…)){ // 同步锁是类定义本身 <BR>// f1 代码段 <BR>} <BR>} <BR><BR>但是，我们要尽量避免这种直接把synchronized加在函数定义上的偷懒做法。因为我们要控制同步粒度。同步的代码段越小越好。synchronized控制的范围越小越好。 <BR>我们不仅要在缩小同步代码段的长度上下功夫，我们同时还要注意细分同步锁。 <BR>比如，下面的代码 <BR><BR>public static final Object lock1 = new Object(); <BR><BR>… f1() { <BR><BR>synchronized(lock1){ // lock1 是公用同步锁 <BR>// 代码段 A <BR>// 访问共享资源 resource1 <BR>// 需要同步 <BR>} <BR>} <BR><BR>… f2() { <BR><BR>synchronized(lock1){ // lock1 是公用同步锁 <BR>// 代码段 B <BR>// 访问共享资源 resource1 <BR>// 需要同步 <BR>} <BR>} <BR><BR>… f3() { <BR><BR>synchronized(lock1){ // lock1 是公用同步锁 <BR>// 代码段 C <BR>// 访问共享资源 resource2 <BR>// 需要同步 <BR>} <BR>} <BR><BR>… f4() { <BR><BR>synchronized(lock1){ // lock1 是公用同步锁 <BR>// 代码段 D <BR>// 访问共享资源 resource2 <BR>// 需要同步 <BR>} <BR>} <BR><BR>上述的4段同步代码，使用同一个同步锁lock1。所有调用4段代码中任何一段代码的线程，都需要竞争同一个同步锁lock1。 <BR>我们仔细分析一下，发现这是没有必要的。 <BR>因为f1()的代码段A和f2()的代码段B访问的共享资源是resource1，f3()的代码段C和f4()的代码段D访问的共享资源是resource2，它们没有必要都竞争同一个同步锁lock1。我们可以增加一个同步锁lock2。f3()和f4()的代码可以修改为： <BR>public static final Object lock2 = new Object(); <BR><BR>… f3() { <BR><BR>synchronized(lock2){ // lock2 是公用同步锁 <BR>// 代码段 C <BR>// 访问共享资源 resource2 <BR>// 需要同步 <BR>} <BR>} <BR><BR>… f4() { <BR><BR>synchronized(lock2){ // lock2 是公用同步锁 <BR>// 代码段 D <BR>// 访问共享资源 resource2 <BR>// 需要同步 <BR>} <BR>} <BR><BR>这样，f1()和f2()就会竞争lock1，而f3()和f4()就会竞争lock2。这样，分开来分别竞争两个锁，就可以大大较少同步锁竞争的概率，从而减少系统的开销。 <BR><BR>信号量 <BR><BR>同步锁模型只是最简单的同步模型。同一时刻，只有一个线程能够运行同步代码。 <BR>有的时候，我们希望处理更加复杂的同步模型，比如生产者/消费者模型、读写同步模型等。这种情况下，同步锁模型就不够用了。我们需要一个新的模型。这就是我们要讲述的信号量模型。 <BR>信号量模型的工作方式如下：线程在运行的过程中，可以主动停下来，等待某个信号量的通知；这时候，该线程就进入到该信号量的待召（Waiting）队列当中；等到通知之后，再继续运行。 <BR>很多语言里面，同步锁都由专门的对象表示，对象名通常叫Monitor。 <BR>同样，在很多语言中，信号量通常也有专门的对象名来表示，比如，Mutex，Semphore。 <BR>信号量模型要比同步锁模型复杂许多。一些系统中，信号量甚至可以跨进程进行同步。另外一些信号量甚至还有计数功能，能够控制同时运行的线程数。 <BR>我们没有必要考虑那么复杂的模型。所有那些复杂的模型，都是最基本的模型衍生出来的。只要掌握了最基本的信号量模型——“等待/通知”模型，复杂模型也就迎刃而解了。 <BR>我们还是以Java语言为例。Java语言里面的同步锁和信号量概念都非常模糊，没有专门的对象名词来表示同步锁和信号量，只有两个同步锁相关的关键字——volatile和synchronized。 <BR>这种模糊虽然导致概念不清，但同时也避免了Monitor、Mutex、Semphore等名词带来的种种误解。我们不必执着于名词之争，可以专注于理解实际的运行原理。 <BR>在Java语言里面，任何一个Object Reference都可以作为同步锁。同样的道理，任何一个Object Reference也可以作为信号量。 <BR>Object对象的wait()方法就是等待通知，Object对象的notify()方法就是发出通知。 <BR>具体调用方法为 <BR>（1）等待某个信号量的通知 <BR>public static final Object signal = new Object(); <BR><BR>… f1() { <BR>synchronized(singal) { // 首先我们要获取这个信号量。这个信号量同时也是一个同步锁 <BR><BR>// 只有成功获取了signal这个信号量兼同步锁之后，我们才可能进入这段代码 <BR>signal.wait(); // 这里要放弃信号量。本线程要进入signal信号量的待召（Waiting）队列 <BR><BR>// 可怜。辛辛苦苦争取到手的信号量，就这么被放弃了 <BR><BR>// 等到通知之后，从待召（Waiting）队列转到就绪（Ready）队列里面 <BR>// 转到了就绪队列中，离CPU核心近了一步，就有机会继续执行下面的代码了。 <BR>// 仍然需要把signal同步锁竞争到手，才能够真正继续执行下面的代码。命苦啊。 <BR>… <BR>} <BR>} <BR><BR>需要注意的是，上述代码中的signal.wait()的意思。signal.wait()很容易导致误解。signal.wait()的意思并不是说，signal开始wait，而是说，运行这段代码的当前线程开始wait这个signal对象，即进入signal对象的待召（Waiting）队列。 <BR><BR>（2）发出某个信号量的通知 <BR>… f2() { <BR>synchronized(singal) { // 首先，我们同样要获取这个信号量。同时也是一个同步锁。 <BR><BR>// 只有成功获取了signal这个信号量兼同步锁之后，我们才可能进入这段代码 <BR>signal.notify(); // 这里，我们通知signal的待召队列中的某个线程。 <BR><BR>// 如果某个线程等到了这个通知，那个线程就会转到就绪队列中 <BR>// 但是本线程仍然继续拥有signal这个同步锁，本线程仍然继续执行 <BR>// 嘿嘿，虽然本线程好心通知其他线程， <BR>// 但是，本线程可没有那么高风亮节，放弃到手的同步锁 <BR>// 本线程继续执行下面的代码 <BR>… <BR>} <BR>} <BR><BR>需要注意的是，signal.notify()的意思。signal.notify()并不是通知signal这个对象本身。而是通知正在等待signal信号量的其他线程。 <BR><BR>以上就是Object的wait()和notify()的基本用法。 <BR>实际上，wait()还可以定义等待时间，当线程在某信号量的待召队列中，等到足够长的时间，就会等无可等，无需再等，自己就从待召队列转移到就绪队列中了。 <BR>另外，还有一个notifyAll()方法，表示通知待召队列里面的所有线程。 <BR>这些细节问题，并不对大局产生影响。 <BR><BR>绿色线程 <BR><BR>绿色线程（Green Thread）是一个相对于操作系统线程（Native Thread）的概念。 <BR>操作系统线程（Native Thread）的意思就是，程序里面的线程会真正映射到操作系统的线程，线程的运行和调度都是由操作系统控制的 <BR>绿色线程（Green Thread）的意思是，程序里面的线程不会真正映射到操作系统的线程，而是由语言运行平台自身来调度。 <BR>当前版本的Python语言的线程就可以映射到操作系统线程。当前版本的Ruby语言的线程就属于绿色线程，无法映射到操作系统的线程，因此Ruby语言的线程的运行速度比较慢。 <BR>难道说，绿色线程要比操作系统线程要慢吗？当然不是这样。事实上，情况可能正好相反。Ruby是一个特殊的例子。线程调度器并不是很成熟。 <BR>目前，线程的流行实现模型就是绿色线程。比如，stackless Python，就引入了更加轻量的绿色线程概念。在线程并发编程方面，无论是运行速度还是并发负载上，都优于Python。 <BR>另一个更著名的例子就是ErLang（爱立信公司开发的一种开源语言）。 <BR>ErLang的绿色线程概念非常彻底。ErLang的线程不叫Thread，而是叫做Process。这很容易和进程混淆起来。这里要注意区分一下。 <BR>ErLang Process之间根本就不需要同步。因为ErLang语言的所有变量都是final的，不允许变量的值发生任何变化。因此根本就不需要同步。 <BR>final变量的另一个好处就是，对象之间不可能出现交叉引用，不可能构成一种环状的关联，对象之间的关联都是单向的，树状的。因此，内存垃圾回收的算法效率也非常高。这就让ErLang能够达到Soft Real Time（软实时）的效果。这对于一门支持内存垃圾回收的语言来说，可不是一件容易的事情。 </P>
<P>&nbsp;</P>
<P>//////////////////////////////////////////////////////////////////////////////</P>
<P>yield 的实现代码其实就是 sleep ( 0 ). 即暂停一下, 然后进入就绪队列(Ready Queue). 随时可以恢复运行. <BR>yield, sleep 用 pause 这个词, 其实更合适. <BR>yield 虽然名为放弃,其实却并没有放弃同步锁的争夺权.暂停几秒之后, 照样生龙活虎, 跃跃欲试.而不象 lock.wait那样自暴自弃,把命运控制权拱手相让. <BR>lock.wait是把自己彻底打入冷宫(Waiting Queue, 待诏队列).除非有其他线程发扬风格,次奥用lock.notify,否则调用了lock.wait的线程,将永远不见天日,呆在 waiting queue里面发霉. <BR><BR>-------------------------- <BR><BR>终止线程, 最好正常结束Thread或者Runnable的run方法. <BR>比如,可以设一个标志位 stop.这个标志位可以在外部设置.比如, thread.setStop(). <BR>在thread/runnable的run方法的程序中的关键点(比如循环体内的第一条指令,或者循环条件)加上这样的判断语句 <BR>if(stop) <BR>return </P>
<P>&nbsp;</P>
<P>//////////////////////////////////////////////////////////////////////////////</P>
<P>太好的文章了，把几个基本概念和线程的一些机制说的很明白。<BR><BR>首先是几个概念，线程，同步资源，同步锁。其中同步资源,同步锁都是线程需要竞争的资源。但是线程首先得到同步锁，才能得到同步资源（即线程到达运行态）。<BR><BR>其次是线程的一些机制。线程有几个状态，这里涉及三个主要的：就绪态，阻塞态和运行态。这三个状态有转换关系，此文涉及其中几个。就绪态到运行态，运行态到阻塞态，阻塞态到就绪态。但是必须注意，阻塞态不能直接到运行态。<BR><BR>java中object reference可以是同步锁和信号量。线程要想执行，首先必须处于就绪态中，其次得到同步锁。于是同步锁后面就有几个线程队列，比如就绪线程的队列，阻塞线程的队列。就绪队列很简单，关键是阻塞队列。<BR><BR>首先线程A从就绪态得到对象锁（同时也得到信号量），线程A进入运行态。当线程A执行到同步锁对象的wait（）时，线程A放弃信号量（同时放弃同步锁），线程A从运行态到阻塞态（不是就绪态）。这时其它在同步锁就绪队列里的线程B就有机会得到同步锁，进入运行态，得以执行同步对象的notify（）（此时线程A从阻塞态到就绪态，有机会得到同步锁进入运行态）。线程B继续执行，直到他放弃同步锁。这时线程A（已经在就绪队列里等待了）就有机会得到同步锁，从而继续刚才没有执行完的代码。<BR><BR>希望楼主多发此类好文章！ </P>
<P>&nbsp;</P>
<P>-------------------------------------------------------------------------</P>
<P>文章写得太好了，非常佩服</P>
<P>来源：<A href="http://www.javaeye.com/topic/164905?page=1">http://www.javaeye.com/topic/164905?page=1</A></P>]]></description>
<author>sam</author>
<pubDate>2008-7-13 0:23:00</pubDate>
</item>
<item>
<title><![CDATA[工作办事的几点总结]]></title>
<link>http://zhan.zhmy.com/archives/2008/149447.html</link>
<description><![CDATA[<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt 18pt; TEXT-INDENT: -18pt; mso-list: l0 level1 lfo1; tab-stops: list 18.0pt"><SPAN lang=EN-US style="mso-fareast-font-family: 'Times New Roman'"><SPAN style="mso-list: Ignore"><FONT face="Times New Roman">1、<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp; </SPAN></FONT></SPAN></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">做事的方法与思路很重要</SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt 18pt"><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">当你接到一个新的工作任务时，不要急于着手去办。</SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt 18pt"><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">首先，应该分析问题，分清主次。</SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt 18pt"><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">其次，罗列出解决问题的几种方案，逐一淘汰，选择一种最佳的解决方案。</SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 18pt"><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">最后，对于选定后的方案，针对问题计划好解决问题的步骤与流程。</SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 18pt"><SPAN lang=EN-US><?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /><o:p><FONT face="Times New Roman">&nbsp;</FONT></o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt 18pt; TEXT-INDENT: -18pt; mso-list: l0 level1 lfo1; tab-stops: list 18.0pt"><SPAN lang=EN-US style="mso-fareast-font-family: 'Times New Roman'"><SPAN style="mso-list: Ignore"><FONT face="Times New Roman">2、<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp; </SPAN></FONT></SPAN></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">做事要细心、用心</SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 18pt"><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">很多时候，我们做事的方式总是希望做得越快越好，做完了可以休息。但事实上恰恰相反，由于你急于完成，或者说急功近利，办起事来总难免会忘这丢那的。最后，由于粗心造成的问题更为严重，弄到最后还要重新审查整理一番。</SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 18pt"><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">虽然细心办事需要的时间更长，但长久锻炼下去做事的效果其实也不会低，而且保证了质量。</SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 18pt"><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">许多人工作的时候，总喜欢边做事边偷懒（身边的人或上网等），这样也许工作起来会轻松、舒服些，而且有时候觉得这样工作效率会更高。当然，这种做法是有其好处的，就如刚才所说的。但是，当你在解决某个问题时，最好能够达到一气呵成，用心去做。当你非常用心，非常专注去做时，你会发现做事的效率和正确性高了很多很多。能做到这一点是很难得的，但通常很少人能办到但通常很少人能办到。</SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 18pt"><SPAN lang=EN-US><o:p><FONT face="Times New Roman">&nbsp;</FONT></o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt 18pt; TEXT-INDENT: -18pt; mso-list: l0 level1 lfo1; tab-stops: list 18.0pt"><SPAN lang=EN-US style="mso-fareast-font-family: 'Times New Roman'"><SPAN style="mso-list: Ignore"><FONT face="Times New Roman">3、<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp; </SPAN></FONT></SPAN></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">做事要耐心</SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 18pt"><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">在我们的工作过程中，会经常遇到各种各样的不愉快的事情。比如，加班、事情搞砸了、问题解决不了等等一些令人烦恼的问题。</SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 18pt"><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">首先对于加班，通常大部分人都是不太愿意的，但很多时候由于工作的需要又是迫不得已的，特别是当你加了班而又得不到相应报酬时，心理会特别不舒服。对于偶尔出现的加班情况我想大家还是得忍。如果是经常加班而且又没报酬的话，那大可跟上级反应，如果再无效果，一段时间后可以考虑走人，除非工作能使你学到许多东西或者某方面令你不想走。</SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 18pt"><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">有时遇到难以解决的问题时，总会使人感到焦急，心烦意乱。此时，应该让自己放松放松，先完成其他简单的事情，或起来走走，看看外面的风景，使自己的心情彻底放松。或者如果问题不是很紧要，可以过一两天再来做，那时可能一下子就有了思路。</SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN lang=EN-US><o:p><FONT face="Times New Roman">&nbsp;</FONT></o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt 18pt; TEXT-INDENT: -18pt; mso-list: l0 level1 lfo1; tab-stops: list 18.0pt"><SPAN lang=EN-US style="mso-fareast-font-family: 'Times New Roman'"><SPAN style="mso-list: Ignore"><FONT face="Times New Roman">4、<SPAN style="FONT: 7pt 'Times New Roman'">&nbsp; </SPAN></FONT></SPAN></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">把工作当事业</SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 18pt"><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">工作永远是做不完的，许多人都是这种心理，当然没什么不对。正因为是做不完的，而且不是自己的事业，所以做起事来总不会特别认真。</SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 18pt"><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">如果你把工作当成自己的事业看待，那么无论什么事情你都会更细心、更用心的去做，考虑更周全，当然做起事来也不会有那么多的怨言。这一点很考验人，想完全做到没那么简单，还需经过不断的锻炼才能慢慢体会出来。</SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 18pt"><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'"></SPAN>&nbsp;</P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 18pt"><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'"></SPAN>最后，祝打工仔们都能够开开心心的工作，快快乐乐的过日子 ^_^&nbsp;</P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 18pt">&nbsp;</P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 18pt"><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'"></SPAN>&nbsp;</P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 18pt"><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'"></SPAN>&nbsp;</P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 18pt"><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">个人博客: http://zhan.zhmy.com</SPAN></P>]]></description>
<author>sam</author>
<pubDate>2008-7-3 21:47:00</pubDate>
</item>
<item>
<title><![CDATA[Ext+DWRTreeLoader