﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-黑灵客栈-随笔分类-J2EE</title><link>http://www.blogjava.net/mstar/category/1423.html</link><description>搞软件开发就像被强奸,如果不能反抗,就享受它吧！</description><language>zh-cn</language><lastBuildDate>Tue, 27 Feb 2007 11:38:50 GMT</lastBuildDate><pubDate>Tue, 27 Feb 2007 11:38:50 GMT</pubDate><ttl>60</ttl><item><title>Request Parameter中的空格与加号</title><link>http://www.blogjava.net/mstar/archive/2005/11/07/18594.html</link><dc:creator>黑灵</dc:creator><author>黑灵</author><pubDate>Mon, 07 Nov 2005 08:09:00 GMT</pubDate><guid>http://www.blogjava.net/mstar/archive/2005/11/07/18594.html</guid><wfw:comment>http://www.blogjava.net/mstar/comments/18594.html</wfw:comment><comments>http://www.blogjava.net/mstar/archive/2005/11/07/18594.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/mstar/comments/commentRss/18594.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/mstar/services/trackbacks/18594.html</trackback:ping><description><![CDATA[<P>今天才知道原来HttpServletRequest.getParameters()对于空格的处理是这样的！<BR><BR>url: xxx?p=a+b<BR>request.getParameters("p"): a b(a与b中间是空格)<BR><BR>url: xxx?p=a b(a与b中间是空格)<BR>request.getParameters("p"): a b(a与b中间是空格)<BR><BR>url: xxx?p=a%20b<BR>request.getParameters("p"): a b(a与b中间是空格)<BR><BR>url: xxx?p=a%2Bb<BR>request.getParameters("p"): a+b<BR><BR>直接用Get请求的时候，如果参数中有+号或者空格一定要看看是不是应该先encode一下。如果是空格用javascript的escape()就能encode，如果是+号就得自己转了。</P><img src ="http://www.blogjava.net/mstar/aggbug/18594.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/mstar/" target="_blank">黑灵</a> 2005-11-07 16:09 <a href="http://www.blogjava.net/mstar/archive/2005/11/07/18594.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>一个文件下载的Servlet</title><link>http://www.blogjava.net/mstar/archive/2005/10/27/17113.html</link><dc:creator>黑灵</dc:creator><author>黑灵</author><pubDate>Thu, 27 Oct 2005 14:52:00 GMT</pubDate><guid>http://www.blogjava.net/mstar/archive/2005/10/27/17113.html</guid><wfw:comment>http://www.blogjava.net/mstar/comments/17113.html</wfw:comment><comments>http://www.blogjava.net/mstar/archive/2005/10/27/17113.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/mstar/comments/commentRss/17113.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/mstar/services/trackbacks/17113.html</trackback:ping><description><![CDATA[<P><FONT size=4>把文件目录直接暴露给用户是很不安全的。所以要用Servlet来做，而且这样做，文件的存储方式就更丰富了，可以是从文件系统上取来的，也可以是数据库中经过计算生成的，或者从其它什么稀奇古怪的地方取来的。<BR></FONT></P>
<DIV style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><SPAN style="COLOR: #0000ff">public</SPAN><SPAN style="COLOR: #000000"> </SPAN><SPAN style="COLOR: #0000ff">class</SPAN><SPAN style="COLOR: #000000"> DownloadServlet </SPAN><SPAN style="COLOR: #0000ff">extends</SPAN><SPAN style="COLOR: #000000"> HttpServlet {<BR>    </SPAN><SPAN style="COLOR: #0000ff">private</SPAN><SPAN style="COLOR: #000000"> String contentType </SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000"> </SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">application/x-msdownload</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">;<BR>    </SPAN><SPAN style="COLOR: #0000ff">private</SPAN><SPAN style="COLOR: #000000"> String enc </SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000"> </SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">utf-8</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">;<BR>    </SPAN><SPAN style="COLOR: #0000ff">private</SPAN><SPAN style="COLOR: #000000"> String fileRoot </SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000"> </SPAN><SPAN style="COLOR: #000000">""</SPAN><SPAN style="COLOR: #000000">;<BR><BR><BR>    </SPAN><SPAN style="COLOR: #008000">/**</SPAN><SPAN style="COLOR: #008000"><BR>     * 初始化contentType，enc，fileRoot<BR>     </SPAN><SPAN style="COLOR: #008000">*/</SPAN><SPAN style="COLOR: #000000"><BR>    </SPAN><SPAN style="COLOR: #0000ff">public</SPAN><SPAN style="COLOR: #000000"> </SPAN><SPAN style="COLOR: #0000ff">void</SPAN><SPAN style="COLOR: #000000"> init(ServletConfig config) </SPAN><SPAN style="COLOR: #0000ff">throws</SPAN><SPAN style="COLOR: #000000"> ServletException {<BR>        String tempStr </SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000"> config.getInitParameter(</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">contentType</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">);<BR>        </SPAN><SPAN style="COLOR: #0000ff">if</SPAN><SPAN style="COLOR: #000000"> (tempStr </SPAN><SPAN style="COLOR: #000000">!=</SPAN><SPAN style="COLOR: #000000"> </SPAN><SPAN style="COLOR: #0000ff">null</SPAN><SPAN style="COLOR: #000000"> </SPAN><SPAN style="COLOR: #000000">&&</SPAN><SPAN style="COLOR: #000000"> </SPAN><SPAN style="COLOR: #000000">!</SPAN><SPAN style="COLOR: #000000">tempStr.equals(</SPAN><SPAN style="COLOR: #000000">""</SPAN><SPAN style="COLOR: #000000">)) {<BR>            contentType </SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000"> tempStr;<BR>        }<BR>        tempStr </SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000"> config.getInitParameter(</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">enc</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">);<BR>        </SPAN><SPAN style="COLOR: #0000ff">if</SPAN><SPAN style="COLOR: #000000"> (tempStr </SPAN><SPAN style="COLOR: #000000">!=</SPAN><SPAN style="COLOR: #000000"> </SPAN><SPAN style="COLOR: #0000ff">null</SPAN><SPAN style="COLOR: #000000"> </SPAN><SPAN style="COLOR: #000000">&&</SPAN><SPAN style="COLOR: #000000"> </SPAN><SPAN style="COLOR: #000000">!</SPAN><SPAN style="COLOR: #000000">tempStr.equals(</SPAN><SPAN style="COLOR: #000000">""</SPAN><SPAN style="COLOR: #000000">)) {<BR>            enc </SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000"> tempStr;<BR>        }<BR>        tempStr </SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000"> config.getInitParameter(</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">fileRoot</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">);<BR>        </SPAN><SPAN style="COLOR: #0000ff">if</SPAN><SPAN style="COLOR: #000000"> (tempStr </SPAN><SPAN style="COLOR: #000000">!=</SPAN><SPAN style="COLOR: #000000"> </SPAN><SPAN style="COLOR: #0000ff">null</SPAN><SPAN style="COLOR: #000000"> </SPAN><SPAN style="COLOR: #000000">&&</SPAN><SPAN style="COLOR: #000000"> </SPAN><SPAN style="COLOR: #000000">!</SPAN><SPAN style="COLOR: #000000">tempStr.equals(</SPAN><SPAN style="COLOR: #000000">""</SPAN><SPAN style="COLOR: #000000">)) {<BR>            fileRoot </SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000"> tempStr;<BR>        }<BR>    }<BR><BR>    </SPAN><SPAN style="COLOR: #0000ff">protected</SPAN><SPAN style="COLOR: #000000"> </SPAN><SPAN style="COLOR: #0000ff">void</SPAN><SPAN style="COLOR: #000000"> doGet(HttpServletRequest request, HttpServletResponse response) </SPAN><SPAN style="COLOR: #0000ff">throws</SPAN><SPAN style="COLOR: #000000"> ServletException, IOException {<BR>        String filepath </SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000"> request.getParameter(</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">filepath</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">);<BR>        String fullFilePath </SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000"> fileRoot </SPAN><SPAN style="COLOR: #000000">+</SPAN><SPAN style="COLOR: #000000"> filepath;<BR>        </SPAN><SPAN style="COLOR: #008000">/*</SPAN><SPAN style="COLOR: #008000">读取文件</SPAN><SPAN style="COLOR: #008000">*/</SPAN><SPAN style="COLOR: #000000"><BR>        File file </SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000"> </SPAN><SPAN style="COLOR: #0000ff">new</SPAN><SPAN style="COLOR: #000000"> File(fullFilePath);<BR>        </SPAN><SPAN style="COLOR: #008000">/*</SPAN><SPAN style="COLOR: #008000">如果文件存在</SPAN><SPAN style="COLOR: #008000">*/</SPAN><SPAN style="COLOR: #000000"><BR>        </SPAN><SPAN style="COLOR: #0000ff">if</SPAN><SPAN style="COLOR: #000000"> (file.exists()) {<BR>            String filename </SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000"> URLEncoder.encode(file.getName(), enc);<BR>            response.reset();<BR>            response.setContentType(contentType);<BR>            response.addHeader(</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">Content-Disposition</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">, </SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">attachment; filename=\</SPAN><SPAN style="COLOR: #000000">""</SPAN><SPAN style="COLOR: #000000"> + filename + </SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">\</SPAN><SPAN style="COLOR: #000000">""</SPAN><SPAN style="COLOR: #000000">);<BR>            </SPAN><SPAN style="COLOR: #0000ff">int</SPAN><SPAN style="COLOR: #000000"> fileLength </SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000"> (</SPAN><SPAN style="COLOR: #0000ff">int</SPAN><SPAN style="COLOR: #000000">) file.length();<BR>            response.setContentLength(fileLength);<BR>            </SPAN><SPAN style="COLOR: #008000">/*</SPAN><SPAN style="COLOR: #008000">如果文件长度大于0</SPAN><SPAN style="COLOR: #008000">*/</SPAN><SPAN style="COLOR: #000000"><BR>            </SPAN><SPAN style="COLOR: #0000ff">if</SPAN><SPAN style="COLOR: #000000"> (fileLength </SPAN><SPAN style="COLOR: #000000">!=</SPAN><SPAN style="COLOR: #000000"> </SPAN><SPAN style="COLOR: #000000">0</SPAN><SPAN style="COLOR: #000000">) {<BR>                </SPAN><SPAN style="COLOR: #008000">/*</SPAN><SPAN style="COLOR: #008000">创建输入流</SPAN><SPAN style="COLOR: #008000">*/</SPAN><SPAN style="COLOR: #000000"><BR>                InputStream inStream </SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000"> </SPAN><SPAN style="COLOR: #0000ff">new</SPAN><SPAN style="COLOR: #000000"> FileInputStream(file);<BR>                </SPAN><SPAN style="COLOR: #0000ff">byte</SPAN><SPAN style="COLOR: #000000">[] buf </SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000"> </SPAN><SPAN style="COLOR: #0000ff">new</SPAN><SPAN style="COLOR: #000000"> </SPAN><SPAN style="COLOR: #0000ff">byte</SPAN><SPAN style="COLOR: #000000">[</SPAN><SPAN style="COLOR: #000000">4096</SPAN><SPAN style="COLOR: #000000">];<BR>                </SPAN><SPAN style="COLOR: #008000">/*</SPAN><SPAN style="COLOR: #008000">创建输出流</SPAN><SPAN style="COLOR: #008000">*/</SPAN><SPAN style="COLOR: #000000"><BR>                ServletOutputStream servletOS </SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000"> response.getOutputStream();<BR>                </SPAN><SPAN style="COLOR: #0000ff">int</SPAN><SPAN style="COLOR: #000000"> readLength;<BR>                </SPAN><SPAN style="COLOR: #0000ff">while</SPAN><SPAN style="COLOR: #000000"> (((readLength </SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000"> inStream.read(buf)) </SPAN><SPAN style="COLOR: #000000">!=</SPAN><SPAN style="COLOR: #000000"> </SPAN><SPAN style="COLOR: #000000">-</SPAN><SPAN style="COLOR: #000000">1</SPAN><SPAN style="COLOR: #000000">)) {<BR>                    servletOS.write(buf, </SPAN><SPAN style="COLOR: #000000">0</SPAN><SPAN style="COLOR: #000000">, readLength);<BR>                }<BR>                inStream.close();<BR>                servletOS.flush();<BR>                servletOS.close();<BR>            }<BR>        }<BR>    }</SPAN></DIV><BR>web.xml<BR>
<DIV style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><SPAN style="COLOR: #000000">    </SPAN><SPAN style="COLOR: #0000ff"><</SPAN><SPAN style="COLOR: #800000">servlet</SPAN><SPAN style="COLOR: #0000ff">></SPAN><SPAN style="COLOR: #000000"><BR>        </SPAN><SPAN style="COLOR: #0000ff"><</SPAN><SPAN style="COLOR: #800000">servlet-name</SPAN><SPAN style="COLOR: #0000ff">></SPAN><SPAN style="COLOR: #000000">download</SPAN><SPAN style="COLOR: #0000ff"></</SPAN><SPAN style="COLOR: #800000">servlet-name</SPAN><SPAN style="COLOR: #0000ff">></SPAN><SPAN style="COLOR: #000000"><BR>        </SPAN><SPAN style="COLOR: #0000ff"><</SPAN><SPAN style="COLOR: #800000">servlet-class</SPAN><SPAN style="COLOR: #0000ff">></SPAN><SPAN style="COLOR: #000000">org.mstar.servlet.DownloadServlet</SPAN><SPAN style="COLOR: #0000ff"></</SPAN><SPAN style="COLOR: #800000">servlet-class</SPAN><SPAN style="COLOR: #0000ff">></SPAN><SPAN style="COLOR: #000000"><BR>        </SPAN><SPAN style="COLOR: #0000ff"><</SPAN><SPAN style="COLOR: #800000">init-param</SPAN><SPAN style="COLOR: #0000ff">></SPAN><SPAN style="COLOR: #000000"><BR>            </SPAN><SPAN style="COLOR: #0000ff"><</SPAN><SPAN style="COLOR: #800000">param-name</SPAN><SPAN style="COLOR: #0000ff">></SPAN><SPAN style="COLOR: #000000">fileRoot</SPAN><SPAN style="COLOR: #0000ff"></</SPAN><SPAN style="COLOR: #800000">param-name</SPAN><SPAN style="COLOR: #0000ff">></SPAN><SPAN style="COLOR: #000000"><BR>            </SPAN><SPAN style="COLOR: #0000ff"><</SPAN><SPAN style="COLOR: #800000">param-value</SPAN><SPAN style="COLOR: #0000ff">></SPAN><SPAN style="COLOR: #000000">d:/temp</SPAN><SPAN style="COLOR: #0000ff"></</SPAN><SPAN style="COLOR: #800000">param-value</SPAN><SPAN style="COLOR: #0000ff">></SPAN><SPAN style="COLOR: #000000"><BR>        </SPAN><SPAN style="COLOR: #0000ff"></</SPAN><SPAN style="COLOR: #800000">init-param</SPAN><SPAN style="COLOR: #0000ff">></SPAN><SPAN style="COLOR: #000000"><BR>        </SPAN><SPAN style="COLOR: #0000ff"><</SPAN><SPAN style="COLOR: #800000">init-param</SPAN><SPAN style="COLOR: #0000ff">></SPAN><SPAN style="COLOR: #000000"><BR>            </SPAN><SPAN style="COLOR: #0000ff"><</SPAN><SPAN style="COLOR: #800000">param-name</SPAN><SPAN style="COLOR: #0000ff">></SPAN><SPAN style="COLOR: #000000">contentType</SPAN><SPAN style="COLOR: #0000ff"></</SPAN><SPAN style="COLOR: #800000">param-name</SPAN><SPAN style="COLOR: #0000ff">></SPAN><SPAN style="COLOR: #000000"><BR>            </SPAN><SPAN style="COLOR: #0000ff"><</SPAN><SPAN style="COLOR: #800000">param-value</SPAN><SPAN style="COLOR: #0000ff">></SPAN><SPAN style="COLOR: #000000">application/x-msdownload</SPAN><SPAN style="COLOR: #0000ff"></</SPAN><SPAN style="COLOR: #800000">param-value</SPAN><SPAN style="COLOR: #0000ff">></SPAN><SPAN style="COLOR: #000000"><BR>        </SPAN><SPAN style="COLOR: #0000ff"></</SPAN><SPAN style="COLOR: #800000">init-param</SPAN><SPAN style="COLOR: #0000ff">></SPAN><SPAN style="COLOR: #000000"><BR>        </SPAN><SPAN style="COLOR: #0000ff"><</SPAN><SPAN style="COLOR: #800000">init-param</SPAN><SPAN style="COLOR: #0000ff">></SPAN><SPAN style="COLOR: #000000"><BR>            </SPAN><SPAN style="COLOR: #0000ff"><</SPAN><SPAN style="COLOR: #800000">param-name</SPAN><SPAN style="COLOR: #0000ff">></SPAN><SPAN style="COLOR: #000000">enc</SPAN><SPAN style="COLOR: #0000ff"></</SPAN><SPAN style="COLOR: #800000">param-name</SPAN><SPAN style="COLOR: #0000ff">></SPAN><SPAN style="COLOR: #000000"><BR>            </SPAN><SPAN style="COLOR: #0000ff"><</SPAN><SPAN style="COLOR: #800000">param-value</SPAN><SPAN style="COLOR: #0000ff">></SPAN><SPAN style="COLOR: #000000">utf-8</SPAN><SPAN style="COLOR: #0000ff"></</SPAN><SPAN style="COLOR: #800000">param-value</SPAN><SPAN style="COLOR: #0000ff">></SPAN><SPAN style="COLOR: #000000"><BR>        </SPAN><SPAN style="COLOR: #0000ff"></</SPAN><SPAN style="COLOR: #800000">init-param</SPAN><SPAN style="COLOR: #0000ff">></SPAN><SPAN style="COLOR: #000000"><BR>    </SPAN><SPAN style="COLOR: #0000ff"></</SPAN><SPAN style="COLOR: #800000">servlet</SPAN><SPAN style="COLOR: #0000ff">></SPAN><SPAN style="COLOR: #000000"><BR>    </SPAN><SPAN style="COLOR: #0000ff"><</SPAN><SPAN style="COLOR: #800000">servlet-mapping</SPAN><SPAN style="COLOR: #0000ff">></SPAN><SPAN style="COLOR: #000000"><BR>        </SPAN><SPAN style="COLOR: #0000ff"><</SPAN><SPAN style="COLOR: #800000">servlet-name</SPAN><SPAN style="COLOR: #0000ff">></SPAN><SPAN style="COLOR: #000000">download</SPAN><SPAN style="COLOR: #0000ff"></</SPAN><SPAN style="COLOR: #800000">servlet-name</SPAN><SPAN style="COLOR: #0000ff">></SPAN><SPAN style="COLOR: #000000"><BR>        </SPAN><SPAN style="COLOR: #0000ff"><</SPAN><SPAN style="COLOR: #800000">url-pattern</SPAN><SPAN style="COLOR: #0000ff">></SPAN><SPAN style="COLOR: #000000">/down</SPAN><SPAN style="COLOR: #0000ff"></</SPAN><SPAN style="COLOR: #800000">url-pattern</SPAN><SPAN style="COLOR: #0000ff">></SPAN><SPAN style="COLOR: #000000"><BR>    </SPAN><SPAN style="COLOR: #0000ff"></</SPAN><SPAN style="COLOR: #800000">servlet-mapping</SPAN><SPAN style="COLOR: #0000ff">></SPAN></DIV><img src ="http://www.blogjava.net/mstar/aggbug/17113.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/mstar/" target="_blank">黑灵</a> 2005-10-27 22:52 <a href="http://www.blogjava.net/mstar/archive/2005/10/27/17113.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Axis初学手册</title><link>http://www.blogjava.net/mstar/archive/2005/10/06/14870.html</link><dc:creator>黑灵</dc:creator><author>黑灵</author><pubDate>Thu, 06 Oct 2005 05:49:00 GMT</pubDate><guid>http://www.blogjava.net/mstar/archive/2005/10/06/14870.html</guid><wfw:comment>http://www.blogjava.net/mstar/comments/14870.html</wfw:comment><comments>http://www.blogjava.net/mstar/archive/2005/10/06/14870.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/mstar/comments/commentRss/14870.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/mstar/services/trackbacks/14870.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 这些天稍微玩了一下Axis，以前做WebServices都使用JBuilder，感觉做WebServices如此简单，现在自己手动做，原来也是如此简单。高兴之余写一个简单的初学手册，就算是学习成果吧。当然对Axis理解的还不很深，所以错误之处望指点。Axis是一个实现WebService的Framework，Apache Web Services Project（http://ws.apach...&nbsp;&nbsp;<a href='http://www.blogjava.net/mstar/archive/2005/10/06/14870.html'>阅读全文</a><img src ="http://www.blogjava.net/mstar/aggbug/14870.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/mstar/" target="_blank">黑灵</a> 2005-10-06 13:49 <a href="http://www.blogjava.net/mstar/archive/2005/10/06/14870.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>对于业务逻辑中发生的异常应该怎样显示到Web层？</title><link>http://www.blogjava.net/mstar/archive/2005/09/27/14240.html</link><dc:creator>黑灵</dc:creator><author>黑灵</author><pubDate>Tue, 27 Sep 2005 13:55:00 GMT</pubDate><guid>http://www.blogjava.net/mstar/archive/2005/09/27/14240.html</guid><wfw:comment>http://www.blogjava.net/mstar/comments/14240.html</wfw:comment><comments>http://www.blogjava.net/mstar/archive/2005/09/27/14240.html#Feedback</comments><slash:comments>10</slash:comments><wfw:commentRss>http://www.blogjava.net/mstar/comments/commentRss/14240.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/mstar/services/trackbacks/14240.html</trackback:ping><description><![CDATA[<SPAN class=postbody>刚进公司，参加了两个项目一个是日本外包，一个是自己的产品。这个两个系统在设计上都有一个要求，就是业务逻辑要放到Service层来完成，Action中尽量少写东西。这一点可以理解，但是存在这样一个问题，在service中进行业务逻辑处理时免不了要发生问题，要把这些问题显示在页面上，但是service层不能直接接触到action，所以着这两个项目中的设计方法上使吧actionmessages对象给了service。我认为这样做很不好，这样service对表现曾的依赖就太大了，因为ActionMessages是Struts的东西，也就是说这个serivce只能用来对Struts提供服务了。我认为应该在service曾定义好异常情况，遇到问题时抛出异常（RuntimeException），然后再表现层根据异常种类做出相应的动作。这样就是view层依赖于serivce层，而service层可以为更多种view层提供服务了。但是这样也有一些问题，就是如果serivce逻辑复杂，可能异常种类就非常多，这样项目做起来也很麻烦。 但是刚进公司也没有什么发言权，估计前辈们也知道这样做不好，但是估计是为了项目做起来不会太麻烦吧。<BR>大家有没有更好的办法？</SPAN><img src ="http://www.blogjava.net/mstar/aggbug/14240.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/mstar/" target="_blank">黑灵</a> 2005-09-27 21:55 <a href="http://www.blogjava.net/mstar/archive/2005/09/27/14240.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>如何实现由状态WebService，以及身份认证如何实现？ </title><link>http://www.blogjava.net/mstar/archive/2005/09/23/13870.html</link><dc:creator>黑灵</dc:creator><author>黑灵</author><pubDate>Fri, 23 Sep 2005 14:24:00 GMT</pubDate><guid>http://www.blogjava.net/mstar/archive/2005/09/23/13870.html</guid><wfw:comment>http://www.blogjava.net/mstar/comments/13870.html</wfw:comment><comments>http://www.blogjava.net/mstar/archive/2005/09/23/13870.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/mstar/comments/commentRss/13870.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/mstar/services/trackbacks/13870.html</trackback:ping><description><![CDATA[以前做过一点WebService但是都是一些简单的操作，都是没有状态的，有点像stateless session bean但是。现在想要Webservice是有状态的，如记录访问者信息，进行连续的业务方法调用等等。并且如果暴露的Webservice有重要操作，只有经过验证的用户才能调用，如何实现？<BR>大家有什么看法，谈一谈。<BR><img src ="http://www.blogjava.net/mstar/aggbug/13870.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/mstar/" target="_blank">黑灵</a> 2005-09-23 22:24 <a href="http://www.blogjava.net/mstar/archive/2005/09/23/13870.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用applet + xml-rpc + hsqldb + xml实现一个聊天室</title><link>http://www.blogjava.net/mstar/archive/2005/09/23/13795.html</link><dc:creator>黑灵</dc:creator><author>黑灵</author><pubDate>Thu, 22 Sep 2005 16:22:00 GMT</pubDate><guid>http://www.blogjava.net/mstar/archive/2005/09/23/13795.html</guid><wfw:comment>http://www.blogjava.net/mstar/comments/13795.html</wfw:comment><comments>http://www.blogjava.net/mstar/archive/2005/09/23/13795.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.blogjava.net/mstar/comments/commentRss/13795.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/mstar/services/trackbacks/13795.html</trackback:ping><description><![CDATA[<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'"></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><FONT size=4>用</FONT></SPAN><FONT size=4><SPAN lang=EN-US>applet + xml-rpc + hsqldb + xml</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">实现一个聊天室</SPAN></FONT></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><FONT size=4><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">昨天公司前辈看我没什么事就托我给他做个小聊天室，估计是别人托他做的</SPAN><SPAN lang=EN-US style="FONT-FAMILY: Wingdings; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-char-type: symbol; mso-symbol-font-family: Wingdings"><SPAN style="mso-char-type: symbol; mso-symbol-font-family: Wingdings">L</SPAN></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">，某个在校学生的作业（其实我也刚从学校里出来，呵呵）。没办法，做吧，其实前辈给了我一个网上下的源程序让我改一下。我看了看，其中有些错误，改了以后，前辈又说要加些功能，可是原来的那个源码使用</SPAN><SPAN lang=EN-US>socket</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">做的，要实现这样的功能，有些麻烦，功能其实很简单，不如自己做个玩玩。于是想到了，以前看到的一些技术和类库，就当练练手吧。</SPAN></FONT></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><FONT size=4><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">首先要解决的问题是</SPAN><SPAN lang=EN-US>applet</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">如何与</SPAN><SPAN lang=EN-US>server</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">进行通信。发放有很多，但是对于这样的小东西，最好用一个轻量级的实现起来简单的技术，于是我想到了</SPAN><SPAN lang=EN-US>xml-rpc</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">，以前做个用</SPAN><SPAN lang=EN-US>javascript</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">通过</SPAN><SPAN lang=EN-US>xml-rpc</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">与服务端通信（我以前的文章中写过），现在客户端也是</SPAN><SPAN lang=EN-US>Java</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">实现起来就容易多了。</SPAN><SPAN lang=EN-US>Xml-rpc</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">是个技术规范，有很多实现，现在客户端和服务器都用</SPAN><SPAN lang=EN-US>Java</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">，当然要用</SPAN><SPAN lang=EN-US>apache</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的实现。</SPAN></FONT></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><FONT size=4><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">接下来要解决的问题是用户数据库如何实现，这样一个小程序就不用精通</SPAN><SPAN lang=EN-US>mysql</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">或</SPAN><SPAN lang=EN-US>sqlserver</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的“大驾”了。本想用</SPAN><SPAN lang=EN-US>hsqldb</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">一起实现的了，但是想了想，不易管理，用户要想更改用户，</SPAN><SPAN lang=EN-US>hsqldb</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">没有一个很好的工具，而且，用户数据库，其实就是一张用户表，有没有大的访问量，完全可以用</SPAN><SPAN lang=EN-US>xml</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">来储存。于是我还是决定用</SPAN><SPAN lang=EN-US>dom4j</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">来操作揖个</SPAN><SPAN lang=EN-US>user.xml</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">来实现。</SPAN></FONT></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><FONT size=4><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">下面是聊天信息的中转，和在线状态的维护。其实这个自己写一个类来实现也可以，无非就是维护一个</SPAN><SPAN lang=EN-US>map</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">，但是想到自己还是没有那么高的水平一定能把这个做好，莫不如用内存数据库来实现，效率也不错，操作也方便。这时</SPAN><SPAN lang=EN-US>hsqldb</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">就派上用场了。其实</SPAN><SPAN lang=EN-US>hsqldb</SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的用途还是很大的。</SPAN></FONT></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'"><FONT size=4>好了下面是总体框架<BR></FONT><IMG height=625 alt=design.jpg src="http://www.blogjava.net/images/blogjava_net/mstar/pics/design.jpg" width=568 border=0><BR></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><FONT size=4><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">对了，还有一个</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">monitor</FONT></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">，是用来维护用户在线状态的，因为用户很有可能不是正常退出，所以这个</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">monitor</FONT></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">作为一个单独的线程对超时用户进行处理。</SPAN></FONT></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><FONT size=4><SPAN lang=EN-US><FONT face="Times New Roman">Service</FONT></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">（</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">ChatService.java</FONT></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">）是主业务类，其实它也只是一个代理类，实际业务是由</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">ChatEngine.java</FONT></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">（</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">hsql</FONT></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">）和</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">XmlHelper.java</FONT></SPAN><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">（</SPAN><SPAN lang=EN-US><FONT face="Times New Roman">xml</FONT></SPAN></FONT><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'"><FONT size=4>）完成的。<BR></FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'"><FONT size=4>先看一下截图吧<BR></FONT><IMG height=439 alt=appletrpc.jpg src="http://www.blogjava.net/images/blogjava_net/mstar/pics/appletrpc.jpg" width=495 border=0><BR><FONT size=4>下面详细的介绍一下，每部分的实现</FONT></SPAN></P>
<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><FONT size=4>首先是业务逻辑类ChatService.java<BR></FONT></P>
<DIV style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><SPAN style="COLOR: #0000ff">package</SPAN><SPAN style="COLOR: #000000">&nbsp;org.mstar.appletrpc.rpcserver;<BR><BR></SPAN><SPAN style="COLOR: #0000ff">import</SPAN><SPAN style="COLOR: #000000">&nbsp;java.util.</SPAN><SPAN style="COLOR: #000000">*</SPAN><SPAN style="COLOR: #000000">;<BR><BR></SPAN><SPAN style="COLOR: #0000ff">public</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">class</SPAN><SPAN style="COLOR: #000000">&nbsp;ChatService&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">private</SPAN><SPAN style="COLOR: #000000">&nbsp;XmlHelper&nbsp;helper;<BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">private</SPAN><SPAN style="COLOR: #000000">&nbsp;ChatEngine&nbsp;engine;<BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">public</SPAN><SPAN style="COLOR: #000000">&nbsp;ChatService(XmlHelper&nbsp;helper,ChatEngine&nbsp;engine){<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">this</SPAN><SPAN style="COLOR: #000000">.helper&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;helper;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">this</SPAN><SPAN style="COLOR: #000000">.engine&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;engine;<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">public</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">boolean</SPAN><SPAN style="COLOR: #000000">&nbsp;validateUser(String&nbsp;username,String&nbsp;password){<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;User&nbsp;user&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;helper.getUser(username);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">if</SPAN><SPAN style="COLOR: #000000">(user</SPAN><SPAN style="COLOR: #000000">!=</SPAN><SPAN style="COLOR: #0000ff">null</SPAN><SPAN style="COLOR: #000000">&amp;&amp;</SPAN><SPAN style="COLOR: #000000">user.getPassword()</SPAN><SPAN style="COLOR: #000000">!=</SPAN><SPAN style="COLOR: #0000ff">null</SPAN><SPAN style="COLOR: #000000">&amp;&amp;</SPAN><SPAN style="COLOR: #000000">user.getPassword().equals(password)){<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">return</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">true</SPAN><SPAN style="COLOR: #000000">;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</SPAN><SPAN style="COLOR: #0000ff">else</SPAN><SPAN style="COLOR: #000000">&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">return</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">false</SPAN><SPAN style="COLOR: #000000">;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">public</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">boolean</SPAN><SPAN style="COLOR: #000000">&nbsp;existUser(String&nbsp;username){<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">return</SPAN><SPAN style="COLOR: #000000">&nbsp;helper.existUser(username);<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">public</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">boolean</SPAN><SPAN style="COLOR: #000000">&nbsp;addUser(String&nbsp;username,String&nbsp;password,String&nbsp;nickname){<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;User&nbsp;user&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">new</SPAN><SPAN style="COLOR: #000000">&nbsp;User();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;user.setUsername(username);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;user.setPassword(password);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;user.setNickname(nickname);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">return</SPAN><SPAN style="COLOR: #000000">&nbsp;helper.addUser(user);<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">public</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">boolean</SPAN><SPAN style="COLOR: #000000">&nbsp;addMessage(String&nbsp;sender,&nbsp;String&nbsp;receiver,&nbsp;String&nbsp;content)&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">return</SPAN><SPAN style="COLOR: #000000">&nbsp;engine.addMessage(sender,receiver,content);<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">public</SPAN><SPAN style="COLOR: #000000">&nbsp;Map&nbsp;getMessage(String&nbsp;receiver)&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">return</SPAN><SPAN style="COLOR: #000000">&nbsp;engine.getMessage(receiver);<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">public</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">boolean</SPAN><SPAN style="COLOR: #000000">&nbsp;logon(String&nbsp;user){<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">return</SPAN><SPAN style="COLOR: #000000">&nbsp;engine.logon(user);<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">public</SPAN><SPAN style="COLOR: #000000">&nbsp;Vector&nbsp;getOnline(){<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">return</SPAN><SPAN style="COLOR: #000000">&nbsp;engine.getOnline();<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">public</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">boolean</SPAN><SPAN style="COLOR: #000000">&nbsp;logoff(String&nbsp;user){<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">return</SPAN><SPAN style="COLOR: #000000">&nbsp;engine.logoff(user);<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #008000">/**</SPAN><SPAN style="COLOR: #008000"><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;checkOnline<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #008000">*/</SPAN><SPAN style="COLOR: #000000"><BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">public</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">void</SPAN><SPAN style="COLOR: #000000">&nbsp;checkOnline()&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;engine.checkOnline();<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR>}<BR></SPAN></DIV><FONT size=4>这个类是个很普通的类，为底层的业务逻辑实现者做个代理。这里要注意的是由于xml-rpc支持的Type很有限，所以，很多是由返回值得类型要注意一下，数组要用Vector，对象要用HashTable或Map，不能直接返回一个复杂对象类型，如User之类的。<BR>具体实现类就不说了，不复杂，看源码应该看得懂。<BR>写完Service，要做一个RpcServer,其实是个Servlet<BR>RpcServer.java</FONT> 
<DIV style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><SPAN style="COLOR: #0000ff">package</SPAN><SPAN style="COLOR: #000000">&nbsp;org.mstar.appletrpc.rpcserver;<BR><BR></SPAN><SPAN style="COLOR: #0000ff">import</SPAN><SPAN style="COLOR: #000000">&nbsp;javax.servlet.</SPAN><SPAN style="COLOR: #000000">*</SPAN><SPAN style="COLOR: #000000">;<BR></SPAN><SPAN style="COLOR: #0000ff">import</SPAN><SPAN style="COLOR: #000000">&nbsp;javax.servlet.http.</SPAN><SPAN style="COLOR: #000000">*</SPAN><SPAN style="COLOR: #000000">;<BR></SPAN><SPAN style="COLOR: #0000ff">import</SPAN><SPAN style="COLOR: #000000">&nbsp;java.io.</SPAN><SPAN style="COLOR: #000000">*</SPAN><SPAN style="COLOR: #000000">;<BR></SPAN><SPAN style="COLOR: #0000ff">import</SPAN><SPAN style="COLOR: #000000">&nbsp;org.apache.xmlrpc.XmlRpcServer;<BR></SPAN><SPAN style="COLOR: #0000ff">import</SPAN><SPAN style="COLOR: #000000">&nbsp;org.dom4j.</SPAN><SPAN style="COLOR: #000000">*</SPAN><SPAN style="COLOR: #000000">;<BR></SPAN><SPAN style="COLOR: #0000ff">import</SPAN><SPAN style="COLOR: #000000">&nbsp;java.sql.SQLException;<BR><BR></SPAN><SPAN style="COLOR: #0000ff">public</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">class</SPAN><SPAN style="COLOR: #000000">&nbsp;RpcServer&nbsp;</SPAN><SPAN style="COLOR: #0000ff">extends</SPAN><SPAN style="COLOR: #000000">&nbsp;HttpServlet&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">private</SPAN><SPAN style="COLOR: #000000">&nbsp;ServletConfig&nbsp;servletConfig;<BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">private</SPAN><SPAN style="COLOR: #000000">&nbsp;XmlHelper&nbsp;xmlHelper;<BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">private</SPAN><SPAN style="COLOR: #000000">&nbsp;ChatEngine&nbsp;engine;<BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">private</SPAN><SPAN style="COLOR: #000000">&nbsp;XmlRpcServer&nbsp;xmlrpc;<BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">Initialize&nbsp;global&nbsp;variables</SPAN><SPAN style="COLOR: #008000"><BR></SPAN><SPAN style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">public</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">void</SPAN><SPAN style="COLOR: #000000">&nbsp;init(ServletConfig&nbsp;servletConfig)&nbsp;</SPAN><SPAN style="COLOR: #0000ff">throws</SPAN><SPAN style="COLOR: #000000">&nbsp;ServletException&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">this</SPAN><SPAN style="COLOR: #000000">.servletConfig&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;servletConfig;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;userdata&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;servletConfig.getInitParameter(</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">user-data</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;File&nbsp;file&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">new</SPAN><SPAN style="COLOR: #000000">&nbsp;File(servletConfig.getServletContext().getRealPath(userdata));<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">try</SPAN><SPAN style="COLOR: #000000">&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;xmlHelper&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">new</SPAN><SPAN style="COLOR: #000000">&nbsp;XmlHelper(file);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;engine&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">new</SPAN><SPAN style="COLOR: #000000">&nbsp;ChatEngine();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ChatService&nbsp;service&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">new</SPAN><SPAN style="COLOR: #000000">&nbsp;ChatService(xmlHelper,engine);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;xmlrpc&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">new</SPAN><SPAN style="COLOR: #000000">&nbsp;XmlRpcServer();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;xmlrpc.addHandler(</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">ChatService</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">,service);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">new</SPAN><SPAN style="COLOR: #000000">&nbsp;OnlineMonitor(service).start();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</SPAN><SPAN style="COLOR: #0000ff">catch</SPAN><SPAN style="COLOR: #000000">&nbsp;(DocumentException&nbsp;ex)&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ex.printStackTrace();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</SPAN><SPAN style="COLOR: #0000ff">catch</SPAN><SPAN style="COLOR: #000000">&nbsp;(SQLException&nbsp;ex)&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ex.printStackTrace();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</SPAN><SPAN style="COLOR: #0000ff">catch</SPAN><SPAN style="COLOR: #000000">&nbsp;(ClassNotFoundException&nbsp;ex)&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ex.printStackTrace();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">Process&nbsp;the&nbsp;HTTP&nbsp;Post&nbsp;request</SPAN><SPAN style="COLOR: #008000"><BR></SPAN><SPAN style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">public</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">void</SPAN><SPAN style="COLOR: #000000">&nbsp;doPost(HttpServletRequest&nbsp;request,&nbsp;HttpServletResponse&nbsp;response)&nbsp;</SPAN><SPAN style="COLOR: #0000ff">throws</SPAN><SPAN style="COLOR: #000000"><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ServletException,&nbsp;IOException&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">byte</SPAN><SPAN style="COLOR: #000000">[]&nbsp;result&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;xmlrpc.execute(request.getInputStream());<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;response.setContentType(</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">text/xml</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;response.setContentLength(result.length);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;OutputStream&nbsp;out&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;response.getOutputStream();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;out.write(result);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;out.flush();<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #008000">//</SPAN><SPAN style="COLOR: #008000">Clean&nbsp;up&nbsp;resources</SPAN><SPAN style="COLOR: #008000"><BR></SPAN><SPAN style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">public</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">void</SPAN><SPAN style="COLOR: #000000">&nbsp;destroy()&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR>}<BR></SPAN></DIV></SPAN><FONT size=4>这个类主要做两件事，一是初始化一些业务对象如</FONT><FONT face=宋体><FONT size=4>ChatService和XmlHelper，ChatEngine，启动monitor。<BR>二是将Service放到XmlRpcServer中。使客户端通过xml客户远程调用。<BR>然后就是把这个Servlet注册到web.xml中<BR></FONT>
<DIV style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><SPAN style="COLOR: #0000ff"><?</SPAN><SPAN style="COLOR: #ff00ff">xml&nbsp;version="1.0"&nbsp;encoding="UTF-8"</SPAN><SPAN style="COLOR: #0000ff">?&gt;</SPAN><SPAN style="COLOR: #000000"><BR></SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">web-app&nbsp;</SPAN><SPAN style="COLOR: #ff0000">xmlns</SPAN><SPAN style="COLOR: #0000ff">="http://java.sun.com/xml/ns/j2ee"</SPAN><SPAN style="COLOR: #ff0000">&nbsp;xmlns:xsi</SPAN><SPAN style="COLOR: #0000ff">="http://www.w3.org/2001/XMLSchema-instance"</SPAN><SPAN style="COLOR: #ff0000">&nbsp;xsi:schemaLocation</SPAN><SPAN style="COLOR: #0000ff">="http://java.sun.com/xml/ns/j2ee&nbsp;http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"</SPAN><SPAN style="COLOR: #ff0000">&nbsp;version</SPAN><SPAN style="COLOR: #0000ff">="2.4"</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR>&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">display-name</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000">AppRpc</SPAN><SPAN style="COLOR: #0000ff"></< SPAN><SPAN style="COLOR: #800000">display-name</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR>&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">welcome-file-list</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">welcome-file</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000">index.jsp</SPAN><SPAN style="COLOR: #0000ff"></< SPAN><SPAN style="COLOR: #800000">welcome-file</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR>&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff"></< SPAN><SPAN style="COLOR: #800000">welcome-file-list</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR>&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">servlet</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">servlet-name</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000">RpcServer</SPAN><SPAN style="COLOR: #0000ff"></< SPAN><SPAN style="COLOR: #800000">servlet-name</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">servlet-class</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000">org.mstar.appletrpc.rpcserver.RpcServer</SPAN><SPAN style="COLOR: #0000ff"></< SPAN><SPAN style="COLOR: #800000">servlet-class</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">init-param</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">param-name</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000">user-data</SPAN><SPAN style="COLOR: #0000ff"></< SPAN><SPAN style="COLOR: #800000">param-name</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">param-value</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000">/WEB-INF/user.xml</SPAN><SPAN style="COLOR: #0000ff"></< SPAN><SPAN style="COLOR: #800000">param-value</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff"></< SPAN><SPAN style="COLOR: #800000">init-param</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">load-on-startup</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000">1</SPAN><SPAN style="COLOR: #0000ff"></< SPAN><SPAN style="COLOR: #800000">load-on-startup</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR>&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff"></< SPAN><SPAN style="COLOR: #800000">servlet</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR>&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">servlet-mapping</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">servlet-name</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000">RpcServer</SPAN><SPAN style="COLOR: #0000ff"></< SPAN><SPAN style="COLOR: #800000">servlet-name</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">url-pattern</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000">/xmlrpc</SPAN><SPAN style="COLOR: #0000ff"></< SPAN><SPAN style="COLOR: #800000">url-pattern</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR>&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff"></< SPAN><SPAN style="COLOR: #800000">servlet-mapping</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR></SPAN><SPAN style="COLOR: #0000ff"></< SPAN><SPAN style="COLOR: #800000">web-app</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN></DIV><BR><FONT size=4>还要有个用户数据库user.xml<BR></FONT>
<DIV style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><SPAN style="COLOR: #0000ff"><?</SPAN><SPAN style="COLOR: #ff00ff">xml&nbsp;version="1.0"&nbsp;encoding="GBK"</SPAN><SPAN style="COLOR: #0000ff">?&gt;</SPAN><SPAN style="COLOR: #000000"><BR></SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">user-database</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">user&nbsp;</SPAN><SPAN style="COLOR: #ff0000">id</SPAN><SPAN style="COLOR: #0000ff">="mty1"</SPAN><SPAN style="COLOR: #ff0000">&nbsp;password</SPAN><SPAN style="COLOR: #0000ff">="123"</SPAN><SPAN style="COLOR: #ff0000">&nbsp;nickname</SPAN><SPAN style="COLOR: #0000ff">="马天一1"</SPAN><SPAN style="COLOR: #0000ff">/&gt;</SPAN><SPAN style="COLOR: #000000"><BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">user&nbsp;</SPAN><SPAN style="COLOR: #ff0000">id</SPAN><SPAN style="COLOR: #0000ff">="mty2"</SPAN><SPAN style="COLOR: #ff0000">&nbsp;password</SPAN><SPAN style="COLOR: #0000ff">="123"</SPAN><SPAN style="COLOR: #ff0000">&nbsp;nickname</SPAN><SPAN style="COLOR: #0000ff">="马天一2"</SPAN><SPAN style="COLOR: #0000ff">/&gt;</SPAN><SPAN style="COLOR: #000000"><BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">user&nbsp;</SPAN><SPAN style="COLOR: #ff0000">id</SPAN><SPAN style="COLOR: #0000ff">="mty3"</SPAN><SPAN style="COLOR: #ff0000">&nbsp;password</SPAN><SPAN style="COLOR: #0000ff">="123"</SPAN><SPAN style="COLOR: #ff0000">&nbsp;nickname</SPAN><SPAN style="COLOR: #0000ff">="马天一3"</SPAN><SPAN style="COLOR: #0000ff">/&gt;</SPAN><SPAN style="COLOR: #000000"><BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">user&nbsp;</SPAN><SPAN style="COLOR: #ff0000">id</SPAN><SPAN style="COLOR: #0000ff">="mty4"</SPAN><SPAN style="COLOR: #ff0000">&nbsp;password</SPAN><SPAN style="COLOR: #0000ff">="123"</SPAN><SPAN style="COLOR: #ff0000">&nbsp;nickname</SPAN><SPAN style="COLOR: #0000ff">="马天一4"</SPAN><SPAN style="COLOR: #0000ff">/&gt;</SPAN><SPAN style="COLOR: #000000"><BR></SPAN><SPAN style="COLOR: #0000ff"></< SPAN><SPAN style="COLOR: #800000">user-database</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000"><BR></SPAN></DIV><FONT size=4>注意user.xml的路径是在web.xml中设定的。<BR>&nbsp;&nbsp;&nbsp;&nbsp;<SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">init-param</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN></FONT><SPAN style="COLOR: #000000"><BR><FONT size=4>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</FONT></SPAN><FONT size=4><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">param-name</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000">user-data</SPAN></FONT><SPAN style="COLOR: #0000ff"></< SPAN><FONT size=4><SPAN style="COLOR: #800000">param-name</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN></FONT><SPAN style="COLOR: #000000"><BR><FONT size=4>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</FONT></SPAN><FONT size=4><SPAN style="COLOR: #0000ff">&lt;</SPAN><SPAN style="COLOR: #800000">param-value</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN><SPAN style="COLOR: #000000">/WEB-INF/user.xml</SPAN></FONT><SPAN style="COLOR: #0000ff"></< SPAN><FONT size=4><SPAN style="COLOR: #800000">param-value</SPAN><SPAN style="COLOR: #0000ff">&gt;</SPAN></FONT><SPAN style="COLOR: #000000"><BR><FONT size=4>&nbsp;&nbsp;&nbsp;&nbsp;</FONT></SPAN><SPAN style="COLOR: #0000ff"></< SPAN><SPAN style="COLOR: #800000"><FONT size=4>init-param</FONT></SPAN><SPAN style="COLOR: #0000ff"><FONT size=4>&gt;<BR>接下来就是用Applet调用这个Servlet了。<BR>太长了，就不写在页面上了，源码中有，这里写一个例子，登陆的actionPerformed方法</FONT><BR><SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">
<DIV style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><SPAN style="COLOR: #0000ff">public</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">void</SPAN><SPAN style="COLOR: #000000">&nbsp;actionPerformed(ActionEvent&nbsp;e)&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Vector&nbsp;params&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">new</SPAN><SPAN style="COLOR: #000000">&nbsp;Vector();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;params.addElement(useridTextField.getText());<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;params.addElement(</SPAN><SPAN style="COLOR: #0000ff">new</SPAN><SPAN style="COLOR: #000000">&nbsp;String(pwdField.getPassword()));<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Boolean&nbsp;result&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">new</SPAN><SPAN style="COLOR: #000000">&nbsp;Boolean(</SPAN><SPAN style="COLOR: #0000ff">false</SPAN><SPAN style="COLOR: #000000">);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">try</SPAN><SPAN style="COLOR: #000000">&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;XmlRpcClient&nbsp;xmlrpc&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">new</SPAN><SPAN style="COLOR: #000000">&nbsp;XmlRpcClient(getCodeBase().toString()&nbsp;</SPAN><SPAN style="COLOR: #000000">+</SPAN><SPAN style="COLOR: #000000"><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">xmlrpc</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;result&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;(Boolean)&nbsp;xmlrpc.execute(</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">ChatService.validateUser</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">,<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&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;params);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">if</SPAN><SPAN style="COLOR: #000000">&nbsp;(result.booleanValue())&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;params&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #0000ff">new</SPAN><SPAN style="COLOR: #000000">&nbsp;Vector();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;params.addElement(useridTextField.getText());<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;result&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;(Boolean)&nbsp;xmlrpc.execute(</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">ChatService.logon</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">,<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;params);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN style="COLOR: #0000ff">if</SPAN><SPAN style="COLOR: #000000">&nbsp;(result.booleanValue())&nbsp;{<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;userid&nbsp;</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">&nbsp;useridTextField.getText();<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;appendMessage(userid&nbsp;</SPAN><SPAN style="COLOR: #000000">+</SPAN><SPAN style="COLOR: #000000">&nbsp;</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">：&nbsp;您已经登陆成功</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">);<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;resetUserList();<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;</SPAN><SPAN style="COLOR: #0000ff">new</SPAN><SPAN style="COLOR: #000000">&nbsp;Timer(</SPAN><SPAN style="COLOR: #000000">2000</SPAN><SPAN style="COLOR: #000000">,&nbsp;</SPAN><SPAN style="COLOR: #0000ff">new</SPAN><SPAN style="COLOR: #000000">&nbsp;RefreshMessageActionListener()).<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;&nbsp;&nbsp;&nbsp;&nbsp;start();<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;</SPAN><SPAN style="COLOR: #0000ff">new</SPAN><SPAN style="COLOR: #000000">&nbsp;Timer(</SPAN><SPAN style="COLOR: #000000">5000</SPAN><SPAN style="COLOR: #000000">,&nbsp;</SPAN><SPAN style="COLOR: #0000ff">new</SPAN><SPAN style="COLOR: #000000">&nbsp;RefreshListActionListener()).<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;&nbsp;&nbsp;&nbsp;&nbsp;start();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</SPAN><SPAN style="COLOR: #0000ff">else</SPAN><SPAN style="COLOR: #000000">&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;appendMessage(</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">用户名密码错误，登陆失败</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</SPAN><SPAN style="COLOR: #0000ff">catch</SPAN><SPAN style="COLOR: #000000">&nbsp;(IOException&nbsp;ex)&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ex.printStackTrace();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</SPAN><SPAN style="COLOR: #0000ff">catch</SPAN><SPAN style="COLOR: #000000">&nbsp;(XmlRpcException&nbsp;ex)&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ex.printStackTrace();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</SPAN></DIV>
<P><FONT color=#000000><FONT size=4>xmlrpc.execute(<SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">ChatService.validateUser</SPAN><SPAN style="COLOR: #000000">"</SPAN></FONT></FONT><SPAN style="COLOR: #000000"><FONT size=4>,params);<BR>execute方法是xml-rpc client执行远程调用的方法。<BR>第一个参数是方法名，第二个是这个远程方法的参数，用Vector传递。<BR>基本上就是这样一个流程。一点都不复杂，有什么不明白的就给我发信，欢迎讨论。<BR>源码下载：</FONT><A href="http://www.blogjava.net/mstar/mstar/Files/mstar/AppletRPC.rar"><FONT size=4>http://www.blogjava.net/Files/mstar/AppletRPC.rar</FONT></A><BR><FONT size=4>项目是用JBuilder2006做的，不知道以前版本的JBuilder能不能打开啊。</FONT></SPAN></SPAN></SPAN></FONT></P></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN><img src ="http://www.blogjava.net/mstar/aggbug/13795.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/mstar/" target="_blank">黑灵</a> 2005-09-23 00:22 <a href="http://www.blogjava.net/mstar/archive/2005/09/23/13795.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[ZZ]Java 理论与实践: 用动态代理进行修饰</title><link>http://www.blogjava.net/mstar/archive/2005/09/22/13790.html</link><dc:creator>黑灵</dc:creator><author>黑灵</author><pubDate>Thu, 22 Sep 2005 14:25:00 GMT</pubDate><guid>http://www.blogjava.net/mstar/archive/2005/09/22/13790.html</guid><wfw:comment>http://www.blogjava.net/mstar/comments/13790.html</wfw:comment><comments>http://www.blogjava.net/mstar/archive/2005/09/22/13790.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/mstar/comments/commentRss/13790.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/mstar/services/trackbacks/13790.html</trackback:ping><description><![CDATA[<P id=subtitle><SPAN style="COLOR: #999999"><A href="http://www-128.ibm.com/developerworks/cn/java/j-jtp08305.html">http://www-128.ibm.com/developerworks/cn/java/j-jtp08305.html</A><BR>Java 理论与实践: </SPAN>用动态代理进行修饰<BR>动态代理是构建 Decorator 和 Adapter 的方便工具<BR></P>
<BLOCKQUOTE><I>动态代理工具</I> 是 <CODE>java.lang.reflect</CODE> 包的一部分，在 JDK 1.3 版本中添加到 JDK，它允许程序创建 <I>代理对象</I>，代理对象能实现一个或多个已知接口，并用反射代替内置的虚方法分派，编程地分派对接口方法的调用。这个过程允许实现“截取”方法调用，重新路由它们或者动态地添加功能。本期文章中，Brian Goetz 介绍了几个用于动态代理的应用程序。请在本文伴随的 <A href="javascript:void forumWindow()"><FONT color=#5c81a7>讨论论坛</FONT></A> 上与作者和其他读者分享您对这篇文章的想法。（也可以单击文章顶部或底部的 <B>讨论</B> 访问讨论论坛。）</BLOCKQUOTE>
<P>动态代理为实现许多常见设计模式（包括 Facade、Bridge、Interceptor、Decorator、Proxy（包括远程和虚拟代理）和 Adapter 模式）提供了替代的动态机制。虽然这些模式不使用动态代理，只用普通的类就能够实现，但是在许多情况下，动态代理方式更方便、更紧凑，可以清除许多手写或生成的类。</P>
<P><A name=1.0><SPAN class=atitle><FONT face=Arial size=4>Proxy 模式</FONT></SPAN></A></P>
<P>Proxy 模式中要创建“stub”或“surrogate”对象，它们的目的是接受请求并把请求转发到实际执行工作的其他对象。远程方法调用（RMI）利用 Proxy 模式，使得在其他 JVM 中执行的对象就像本地对象一样；企业 JavaBeans （EJB）利用 Proxy 模式添加远程调用、安全性和事务分界；而 JAX-RPC Web 服务则用 Proxy 模式让远程服务表现得像本地对象一样。在每一种情况中，潜在的远程对象的行为是由接口定义的，而接口本质上接受多种实现。调用者（在大多数情况下）不能区分出它们只是持有一个对 stub 而不是实际对象的引用，因为二者实现了相同的接口；stub 的工作是查找实际的对象、封送参数、把参数发送给实际对象、解除封送返回值、把返回值返回给调用者。代理可以用来提供远程控制（就像在 RMI、EJB 和 JAX-RPC 中那样），用安全性策略包装对象（EJB）、为昂贵的对象（EJB 实体 Bean）提供惰性装入，或者添加检测工具（例如日志记录）。</P>
<P>在 5.0 以前的 JDK 中，RMI stub（以及它对等的 skeleton）是在编译时由 RMI 编译器（rmic）生成的类，RMI 编译器是 JDK 工具集的一部分。对于每个远程接口，都会生成一个 stub（代理）类，它代表远程对象，还生成一个 skeleton 对象，它在远程 JVM 中做与 stub 相反的工作 —— 解除封送参数并调用实际的对象。类似地，用于 Web 服务的 JAX-RPC 工具也为远程 Web 服务生成代理类，从而使远程 Web 服务看起来就像本地对象一样。</P>
<P>不管 stub 类是以源代码还是以字节码生成的，代码生成仍然会向编译过程添加一些额外步骤，而且因为命名相似的类的泛滥，会带来意义模糊的可能性。另一方面，动态代理机制支持在编译时没有生成 stub 类的情况下，在运行时创建代理对象。在 JDK 5.0 及以后版本中，RMI 工具使用动态代理代替了生成的 stub，结果 RMI 变得更容易使用。许多 J2EE 容器也使用动态代理来实现 EJB。EJB 技术严重地依靠使用拦截（interception）来实现安全性和事务分界；动态代理为接口上调用的所有方法提供了集中的控制流程路径。</P>
<P id=subtitle><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/j-jtp08305.html#main"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR></P>
<P><A name=2.0><SPAN class=atitle><FONT face=Arial size=4>动态代理机制</FONT></SPAN></A></P>
<P>动态代理机制的核心是 <CODE>InvocationHandler</CODE> 接口，如清单 1 所示。调用句柄的工作是代表动态代理实际执行所请求的方法调用。传递给调用句柄一个 <CODE>Method</CODE> 对象（从 <CODE>java.lang.reflect</CODE> 包），参数列表则传递给方法；在最简单的情况下，可能仅仅是调用反射性的方法 <CODE>Method.invoke()</CODE> 并返回结果。</P>
<P id=subtitle><BR><A name=listing1><B>清单 1. InvocationHandler 接口</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">public interface InvocationHandler {
    Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
}
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR></P>
<P>每个代理都有一个与之关联的调用句柄，只要代理的方法被调用时就会调用该句柄。根据通用的设计原则：接口定义类型、类定义实现，代理对象可以实现一个或多个接口，但是不能实现类。因为代理类没有可以访问的名称，它们不能有构造函数，所以它们必须由工厂创建。清单 2 显示了动态代理的最简单的可能实现，它实现 <CODE>Set</CODE> 接口并把所有 <CODE>Set</CODE> 方法（以及所有 Object 方法）分派给封装的 <CODE>Set</CODE> 实例。</P>
<P id=subtitle><BR><A name=listing2><B>清单 2. 包装 Set 的简单的动态代理</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">public class SetProxyFactory {

    public static Set getSetProxy(final Set s) {
        return (Set) Proxy.newProxyInstance
          (s.getClass().getClassLoader(),
                new Class[] { Set.class },
                new InvocationHandler() {
                    public Object invoke(Object proxy, Method method, 
                      Object[] args) throws Throwable {
                        return method.invoke(s, args);
                    }
                });
    }
}
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR></P>
<P><CODE>SetProxyFactory</CODE> 类包含一个静态工厂方法 <CODE>getSetProxy()</CODE>，它返回一个实现了 <CODE>Set</CODE> 的动态代理。代理对象实际实现 <CODE>Set</CODE> —— 调用者无法区分（除非通过反射）返回的对象是动态代理。<CODE>SetProxyFactory</CODE> 返回的代理只做一件事，把方法分派给传递给工厂方法的 <CODE>Set</CODE> 实例。虽然反射代码通常比较难读，但是这里的内容很少，跟上控制流程并不难 —— 只要某个方法在 <CODE>Set</CODE> 代理上被调用，它就被分派给调用句柄，调用句柄只是反射地调用底层包装的对象上的目标方法。当然，绝对什么都不做的代理可能有点傻，是不是呢？</P>
<P><A name=2.1><SPAN class=smalltitle><STRONG><FONT face=Arial>什么都不做的适配器</FONT></STRONG></SPAN></A></P>
<P>对于像 <CODE>SetProxyFactory</CODE> 这样什么都不做的包装器来说，实际有个很好的应用 —— 可以用它安全地把对象引用的范围缩小到特定接口（或接口集）上，方式是，调用者不能提升引用的类型，使得可以更安全地把对象引用传递给不受信任的代码（例如插件或回调）。清单 3 包含一组类定义，实现了典型的回调场景。从中会看到动态代理可以更方便地替代通常用手工（或用 IDE 提供的代码生成向导）实现的 Adapter 模式。</P>
<P id=subtitle><BR><A name=listing3><B>清单 3. 典型的回调场景</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">public interface ServiceCallback {
    public void doCallback();
}

public interface Service {
    public void serviceMethod(ServiceCallback callback);
}

public class ServiceConsumer implements ServiceCallback {
    private Service service;

    ...
    public void someMethod() {
        ...
        service.serviceMethod(this);
    }
}
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR></P>
<P><CODE>ServiceConsumer</CODE> 类实现了 <CODE>ServiceCallback</CODE>（这通常是支持回调的一个方便途径）并把 <CODE>this</CODE> 引用传递给 <CODE>serviceMethod()</CODE> 作为回调引用。这种方法的问题是没有机制可以阻止 <CODE>Service</CODE> 实现把 <CODE>ServiceCallback</CODE> 提升为 <CODE>ServiceConsumer</CODE>，并调用 <CODE>ServiceConsumer</CODE> 不希望 <CODE>Service</CODE> 调用的方法。有时对这个风险并不关心 —— 但有时却关心。如果关心，那么可以把回调对象作为内部类，或者编写一个什么都不做的适配器类（请参阅清单 4 中的 <CODE>ServiceCallbackAdapter</CODE>）并用 <CODE>ServiceCallbackAdapter</CODE> 包装 <CODE>ServiceConsumer</CODE>。<CODE>ServiceCallbackAdapter</CODE> 防止 <CODE>Service</CODE> 把 <CODE>ServiceCallback</CODE> 提升为 <CODE>ServiceConsumer</CODE>。</P>
<P id=subtitle><BR><A name=listing4><B>清单 4. 用于安全地把对象限制在一个接口上以便不被恶意代码不能的适配器类</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">public class ServiceCallbackAdapter implements ServiceCallback {
    private final ServiceCallback cb;

    public ServiceCallbackAdapter(ServiceCallback cb) {
        this.cb = cb;
    }

    public void doCallback() {
        cb.doCallback();
    }
}
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR></P>
<P>编写 <CODE>ServiceCallbackAdapter</CODE> 这样的适配器类简单却乏味。必须为包装的接口中的每个方法编写重定向类。在 <CODE>ServiceCallback</CODE> 的示例中，只有一个需要实现的方法，但是某些接口，例如 Collections 或 JDBC 接口，则包含许多方法。现代的 IDE 提供了“Delegate Methods”向导，降低了编写适配器类的工作量，但是仍然必须为每个想要包装的接口编写一个适配器类，而且对于只包含生成的代码的类，也有一些让人不满意的地方。看起来应当有一种方式可以更紧凑地表示“什么也不做的限制适配器模式”。</P>
<P><A name=2.2><SPAN class=smalltitle><STRONG><FONT face=Arial>通用适配器类</FONT></STRONG></SPAN></A></P>
<P><A href="http://www-128.ibm.com/developerworks/cn/java/j-jtp08305.html#listing2"><FONT color=#996699>清单 2</FONT></A> 中的 <CODE>SetProxyFactory</CODE> 类当然比用于 <CODE>Set</CODE> 的等价的适配器类更紧凑，但是它仍然只适用于一个接口：<CODE>Set</CODE>。但是通过使用泛型，可以容易地创建通用的代理工厂，由它为任何接口做同样的工作，如清单 5 所示。它几乎与 <CODE>SetProxyFactory</CODE> 相同，但是可以适用于任何接口。现在再也不用编写限制适配器类了！如果想创建代理对象安全地把对象限制在接口 <CODE>T</CODE>，只要调用 <CODE>getProxy(T.class,object)</CODE> 就可以了，不需要一堆适配器类的额外累赘。</P>
<P id=subtitle><BR><A name=listing5><B>清单 5. 通用的限制适配器工厂类</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">public class GenericProxyFactory {

    public static&lt;T&gt; T getProxy(Class&lt;T&gt; intf, 
      final T obj) {
        return (T) 
          Proxy.newProxyInstance(obj.getClass().getClassLoader(),
                new Class[] { intf },
                new InvocationHandler() {
                    public Object invoke(Object proxy, Method method, 
                      Object[] args) throws Throwable {
                        return method.invoke(obj, args);
                    }
                });
    }
}
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><FONT face="Lucida Console"><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></FONT></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><FONT face="Lucida Console"><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></FONT></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/j-jtp08305.html#main"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR></P>
<P><A name=3.0><SPAN class=atitle><FONT face=Arial size=4>动态代理作为 Decorator</FONT></SPAN></A></P>
<P>当然，动态代理工具能做的，远不仅仅是把对象类型限制在特定接口上。从 <A href="http://www-128.ibm.com/developerworks/cn/java/j-jtp08305.html#listing2"><FONT color=#996699>清单 2</FONT></A> 和 <A href="http://www-128.ibm.com/developerworks/cn/java/j-jtp08305.html#listing5"><FONT color=#996699>清单 5</FONT></A> 中简单的限制适配器到 Decorator 模式，是一个小的飞跃，在 Decorator 模式中，代理用额外的功能（例如安全检测或日志记录）包装调用。清单 6 显示了一个日志 <CODE>InvocationHandler</CODE>，它在调用目标对象上的方法之外，还写入一条日志信息，显示被调用的方法、传递的参数，以及返回值。除了反射性的 <CODE>invoke()</CODE> 调用之外，这里的全部代码只是生成调试信息的一部分 —— 还不是太多。代理工厂方法的代码几乎与 <CODE>GenericProxyFactory</CODE> 相同，区别在于它使用的是 <CODE>LoggingInvocationHandler</CODE> 而不是匿名的调用句柄。</P>
<P id=subtitle><BR><A name=listing6><B>清单 6. 基于代理的 Decorator，为每个方法调用生成调试日志</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">    private static class LoggingInvocationHandler&lt;T&gt; 
      implements InvocationHandler {
        final T underlying;

        public LoggingHandler(T underlying) {
            this.underlying = underlying;
        }

        public Object invoke(Object proxy, Method method, 
          Object[] args) throws Throwable {
            StringBuffer sb = new StringBuffer();
            sb.append(method.getName()); sb.append("(");
            for (int i=0; args != null &amp;&amp; i&lt;args.length; i++) {
                if (i != 0)
                    sb.append(", ");
                sb.append(args[i]);
            }
            sb.append(")");
            Object ret = <SPAN class=boldcode><STRONG>method.invoke</STRONG></SPAN>(underlying, args);
            if (ret != null) {
                sb.append(" -&gt; "); sb.append(ret);
            }
            System.out.println(sb);
            return ret;
        }
    }
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR></P>
<P>如果用日志代理包装 <CODE>HashSet</CODE>，并执行下面这个简单的测试程序：</P>
<P id=subtitle>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">    Set s = newLoggingProxy(Set.class, new HashSet());
    s.add("three");
    if (!s.contains("four"))
        s.add("four");
    System.out.println(s);
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR></P>
<P>会得到以下输出：</P>
<P id=subtitle>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">  add(three) -&gt; true
  contains(four) -&gt; false
  add(four) -&gt; true
  toString() -&gt; [four, three]
  [four, three]
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR></P>
<P>这种方式是给对象添加调试包装器的一种好的而且容易的方式。它当然比生成代理类并手工创建大量 <CODE>println()</CODE> 语句容易得多（也更通用）。我进一步改进了这一方法；不必无条件地生成调试输出，相反，代理可以查询动态配置存储（从配置文件初始化，可以由 JMX <CODE>MBean</CODE> 动态修改），确定是否需要生成调试语句，甚至可能在逐个类或逐个实例的基础上进行。</P>
<P>在这一点上，我认为读者中的 AOP 爱好者们几乎要跳出来说“这正是 AOP 擅长的啊！”是的，但是解决问题的方法不止一种 —— 仅仅因为某项技术能解决某个问题，并不意味着它就是最好的解决方案。在任何情况下，动态代理方式都有完全在“纯 Java”范围内工作的优势，不是每个公司都用（或应当用） AOP 的。</P>
<P><A name=3.1><SPAN class=smalltitle><STRONG><FONT face=Arial>动态代理作为适配器</FONT></STRONG></SPAN></A></P>
<P>代理也可以用作真正的适配器，提供了对象的一个视图，导出与底层对象实现的接口不同的接口。调用句柄不需要把每个方法调用都分派给相同的底层对象；它可以检查名称，并把不同的方法分派给不同的对象。例如，假设有一组表示持久实体（<CODE>Person</CODE>、<CODE>Company</CODE> 和 <CODE>PurchaseOrder</CODE>） 的 JavaBean 接口，指定了属性的 getter 和 setter，而且正在编写一个持久层，把数据库记录映射到实现这些接口的对象上。现在不用为每个接口编写或生成类，可以只用一个 JavaBean 风格的通用代理类，把属性保存在 Map 中。</P>
<P>清单 7 显示的动态代理检查被调用方法的名称，并通过查询或修改属性图直接实现 getter 和 setter 方法。现在，这一个代理类就能实现多个 JavaBean 风格接口的对象。</P>
<P id=subtitle><BR><A name=listing7><B>清单 7. 用于把 getter 和 setter 分派给 Map 的动态代理类</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">public class JavaBeanProxyFactory {
    private static class JavaBeanProxy implements InvocationHandler {
        Map&lt;String, Object&gt; properties = new HashMap&lt;String, 
          Object&gt;();

        public JavaBeanProxy(Map&lt;String, Object&gt; properties) {
            this.properties.putAll(properties);
        }

        public Object invoke(Object proxy, Method method, 
          Object[] args) 
          throws Throwable {
            String meth = method.getName();
            if (meth.startsWith("get")) {
                String prop = meth.substring(3);
                Object o = properties.get(prop);
                if (o != null &amp;&amp; !method.getReturnType().isInstance(o))
                    throw new ClassCastException(o.getClass().getName() + 
                      " is not a " + method.getReturnType().getName());
                return o;
            }
            else if (meth.startsWith("set")) {
                // Dispatch setters similarly
            }
            else if (meth.startsWith("is")) {
                // Alternate version of get for boolean properties
            }
            else {
                // Can dispatch non get/set/is methods as desired
            }
        }
    }

    public static&lt;T&gt; T getProxy(Class&lt;T&gt; intf,
      Map&lt;String, Object&gt; values) {
        return (T) Proxy.newProxyInstance
          (JavaBeanProxyFactory.class.getClassLoader(),
                new Class[] { intf }, new JavaBeanProxy(values));
    }
}
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR></P>
<P>虽然因为反射在 <CODE>Object</CODE> 上工作会有潜在的类型安全性上的损失，但是，JavaBeanProxyFactory 中的 getter 处理会进行一些必要的额外的类型检测，就像我在这里用 <CODE>isInstance()</CODE> 对 getter 进行的检测一样。</P>
<P id=subtitle><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/j-jtp08305.html#main"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR></P>
<P><A name=4.0><SPAN class=atitle><FONT face=Arial size=4>性能成本</FONT></SPAN></A></P>
<P>正如已经看到的，动态代理拥有简化大量代码的潜力 —— 不仅能替代许多生成的代码，而且一个代理类还能代替多个手写的类或生成的代码。什么是成本呢？ 因为反射地分派方法而不是采用内置的虚方法分派，可能有一些性能上的成本。在早期的 JDK 中，反射的性能很差（就像早期 JDK 中几乎其他每件事的性能一样），但是在近 10 年，反射已经变得快多了。</P>
<P>不必进入基准测试构造的主题，我编写了一个简单的、不太科学的测试程序，它循环地把数据填充到 <CODE>Set</CODE>，随机地对 <CODE>Set</CODE>进行插入、查询和删除元素。我用三个 <CODE>Set</CODE> 实现运行它：一个未经修饰的 <CODE>HashSet</CODE>，一个手写的、只是把所有方法转发到底层的 <CODE>HashSet</CODE> 的 <CODE>Set</CODE> 适配器，还有一个基于代理的、也只是把所有方法转发到底层 <CODE>HashSet</CODE> 的 <CODE>Set</CODE> 适配器。每次循环迭代都生成若干随机数，并执行一个或多个 <CODE>Set</CODE> 操作。手写的适配器比起原始的 <CODE>HashSet</CODE> 只产生很少百分比的性能负荷（大概是因为 JVM 级有效的内联缓冲和硬件级的分支预测）；代理适配器则明显比原始 <CODE>HashSet</CODE> 慢，但是开销要少于两个量级。</P>
<P>我从这个试验得出的结论是：对于大多数情况，代理方式即使对轻量级方法也执行得足够好，而随着被代理的操作变得越来越重量级（例如远程方法调用，或者使用序列化、执行 IO 或者从数据库检索数据的方法），代理开销就会有效地接近于 0。当然也存在一些代理方式的性能开销无法接受的情况，但是这些通常只是少数情况。</P><img src ="http://www.blogjava.net/mstar/aggbug/13790.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/mstar/" target="_blank">黑灵</a> 2005-09-22 22:25 <a href="http://www.blogjava.net/mstar/archive/2005/09/22/13790.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[ZZ]Classworking 工具箱: 在旧版 JVM 上使用 J2SE 5.0 特性</title><link>http://www.blogjava.net/mstar/archive/2005/07/28/8590.html</link><dc:creator>黑灵</dc:creator><author>黑灵</author><pubDate>Thu, 28 Jul 2005 00:53:00 GMT</pubDate><guid>http://www.blogjava.net/mstar/archive/2005/07/28/8590.html</guid><wfw:comment>http://www.blogjava.net/mstar/comments/8590.html</wfw:comment><comments>http://www.blogjava.net/mstar/archive/2005/07/28/8590.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/mstar/comments/commentRss/8590.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/mstar/services/trackbacks/8590.html</trackback:ping><description><![CDATA[<SPAN class=atitle2><A href="http://www-128.ibm.com/developerworks/cn/java/j-cwt07065/index.html?ca=dwcn-newsletter-java">http://www-128.ibm.com/developerworks/cn/java/j-cwt07065/index.html?ca=dwcn-newsletter-java</A><BR>无法转向 JDK 5.0？学习一款开放源代码工具如何帮助在旧版 JVM 上使用这些特性</SPAN><BR>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR vAlign=top align=left>
<TD>
<P>级别: 中级</P></TD></TR></TBODY></TABLE>
<P><A href="http://www-128.ibm.com/developerworks/cn/java/j-cwt07065/index.html?ca=dwcn-newsletter-java#author1"><NAME>Dennis Sosnoski</NAME></A><BR>主席, Sosnoski Software Solutions, Inc.<BR>2005 年 7 月 25 日</P>
<BLOCKQUOTE>许多 J2SE 5.0 语言特性应该对旧版 JVM 也有用，但是实现这些特性的编译器会生成需要 JDK 5.0 或更高版本的代码。幸运的是，有一个开放源代码项目 Retroweaver 在 J2SE 5.0 与旧版 JVM 之间架起了一座桥梁。Retroweaver 转换您的类文件以消除 JDK 5.0 依赖性，同时添加其自己的支持函数库以使得大多数 5.0 特性在旧版 JVM 上完全有用。如果您喜欢 J2SE 5.0 语言特性，却无法在运行时使用 JDK 5.0，那么 Retroweaver 就是您所需要的。</BLOCKQUOTE>
<P>J2SE 5.0 为 Java 语言带来了巨大的改变，因此即使是经验丰富的 Java 开发人员也需要深入的培训才能利用 5.0 特性。不幸的是，实现这些语言特性的 JDK 5.0 编译器在生成特定于 JDK 5.0 或更高版本的代码时不支持这些特性。如果试图在早期的 JVM 上运行生成的代码，将会得到 <CODE>java.lang.UnsupportedClassVersionError</CODE> 错误。</P>
<P>即使生成的类指定 JDK 5.0 和更高的 JVM，但是故事并没有结束。开发人员发现，一些新特性实际上生成与旧版 JVM 完全兼容的代码，而其他特性可以与标准库的少量扩展兼容。有一个名叫 Toby Reyelts 的开发人员决定消除 JDK 5.0 编译器限制。结果就是开放源代码的 Retroweaver 项目（参见 <A href="http://www-128.ibm.com/developerworks/cn/java/j-cwt07065/index.html?ca=dwcn-newsletter-java#resources" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">参考资料</A>）。Retroweaver 使用 classworking 技术来修改由 JDK 5.0 编译器生成的二进制类表示，以便这些类可以与早期的 JVM 一起使用。</P>
<P>对于本文来说，我将展示 Retroweaver 的基本使用。Retroweaver 实际上非常容易使用，所以不用花太大的篇幅去介绍它，所以我还将修改 <A href="http://www-128.ibm.com/developerworks/cn/java/j-cwt06075/index.html" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">上个月</A> 介绍的 annotations+ASM 运行时代码生成方法以使用 5.0 之前的 JDK，期间使用了 Retroweaver 来回避 JDK 5.0 编译器限制。</P>
<P><A name=h1><SPAN class=atitle2>向后兼容 J2SE 5.0</SPAN></A><BR>Retroweaver 包含两个逻辑组件：一个字节码增强器和一个运行时库。字节码增强器使用 classworking 技术来修改由 JDK 5.0 编译器生成的类文件，使得这些类可以用于旧版 JVM。作为类文件修改的一部分，Retroweaver 可能需要替换对添加到 J2SE 5.0 中的标准类的引用。实际的替换类包含在运行时库中，以便在您执行修改过的代码时它们是可用的。</P>
<P>按照标准开发周期来说，字节码增强器需要在 Java 代码编译之后、类文件为部署而打包之前运行。在您使用一个 IDE 时，该更改是一个问题 ——“集成”一个类转换工具到“开发环境”是很痛苦的事情，因为 IDE 一般假设它们拥有类文件。限制这一痛苦的一种方式是，只对 IDE 中的大多数测试使用 JDK 5.0。这样，您只需要在想要为部署打包文件或者想要测试实际的部署 JVM 时转换类文件。如果使用 Ant 风格的构建过程，就没有问题；只添加 Retroweaver 字节码增强器作为编译之后的一个步骤。</P>
<P>Retroweaver 具有一个小小的限制：尽管 Retroweaver 允许您在运行在旧版 JVM 上的代码中使用 J2SE 5.0 语言特性，但是它并不支持也包含在 J2SE 5.0 中的所有添加到标准 Java 类的特性。如果您的代码使用任何添加到 J2SE 5.0 中的类或方法，那么就将在试图加载旧版 JVM 中的代码时得到错误，哪怕是在 Retroweaver 处理完成之后也如此。避免对标准库的 J2SE 5.0 添加不应该是一个主要问题，但是如果使用 IDE 中的感应弹出特性并偶然挑选了一个仅添加到 J2SE 5.0 中的方法或类，它就有可能让您得到错误。</P>
<P><A name=h1.1><SPAN class=atitle3>它做什么</SPAN></A><BR>J2SE 5.0 的更改既发生在 JVM 中，也发生在实际的 Java 语言，但是 JVM 更改相当小。有一个新的字符可以用于字节码中的标识符中 ("+")，一些处理类引用的指令发生了修改，还有一个不同的方法用于处理合成组件。 Retroweaver 在字节码增强步骤中处理这些 JVM 更改，方法是把这些更改返回原样，即替换成用于 J2SE 5.0 之前相同目的的方法（比如标识符中的 + 字符，就是用 $ 取代它）。</P>
<P>包含在 J2SE 5.0 中的语言更改要稍微复杂一点。一些最有趣的更改，比如增强的 <CODE>for</CODE> 循环，基本上只是语法更改，即为表示编程操作提供快捷方式。比如泛型更改 —— 泛型类型信息 —— 由编译器用于实施编译时安全，但是生成的字节码仍然到处使用强制转换。但是大多数更改使用了添加到核心 Java API 中的类或方法，所以您不能直接使用为 JDK 5.0 生成的字节码并将它直接运行在早期的 JVM 上。Retroweaver 为支持 J2SE 5.0 语言更改所需的新类提供其自己的等价物，并且用对其自己的类的引用替换对标准类的引用，这是字节码增强步骤的一部分。</P>
<P>Retroweaver 字节码增强不能对所有的 J2SE 5.0 语言更改提供完全支持。例如，没有对处理注释的运行时支持，因为运行时支持涉及到对基本 JVM 类加载器实现的更改。但是一般来说，只是不支持那些不会影响普通用户的小特性。</P>
<P><A name=h1.2><SPAN class=atitle3>Retroweaver 发挥作用</SPAN></A><BR>使用 Retroweaver 简直是太容易了。可以使用一个简单的 GUI 界面或者控制台应用程序来在应用程序类文件上运行字节码增强。两种方式都只要在将要转换的类文件树的根目录指出 Retroweaver 即可。在运行时，如果使用任何需要运行时支持的特性（比如 enums），那么就需要在类路径中包含 Retroweaver 运行时 jar。</P>
<P>清单 1 给出了一个简单的示例程序，其中使用了一些 J2SE 5.0 特性。<CODE>com.sosnoski.dwct.Primitive</CODE> 是一个针对 Java 语言原语类型的 <CODE>enum</CODE> 类。<CODE>main()</CODE> 方法使用增强的 <CODE>for</CODE> 循环来迭代通过不同的原语，并在当前实例上使用一个简单的 <CODE>switch</CODE> 语句来设置每个原语的大小值。</P><A name=code1><B>清单 1. 简单的 J2SE 5.0 enum 示例</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
package com.sosnoski.dwct;

public enum Primitive
{
    BOOLEAN, BYTE, CHARACTER, DOUBLE, FLOAT, INT, LONG, SHORT;
    
    public static void main(String[] args) {
        for (Primitive p : Primitive.values()) {
            int size = -1;
            switch (p) {
                case BOOLEAN:
                case BYTE:
                    size = 1;
                    break;
                case CHARACTER:
                case SHORT:
                    size = 2;
                    break;
                case FLOAT:
                case INT:
                    size = 4;
                    break;
                case DOUBLE:
                case LONG:
                    size = 8;
                    break;
            }
            System.out.println(p + " is size " + size);
        }
    }
}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>使用 JDK 5.0 编译并运行清单 1 代码会给出清单 2 中的输出。但是不能在早期的 JDK 下编译或运行清单 1 代码；由于特定于 J2SE 5.0 的特性会导致编译失败，而运行失败会抛出 <CODE>java.lang.UnsupportedClassVersionError</CODE> 异常。</P><A name=code2><B>清单 2. enum 示例输出</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
[dennis@notebook code]$ java -cp classes com.sosnoski.dwct.Primitive
BOOLEAN is size 1
BYTE is size 1
CHARACTER is size 2
DOUBLE is size 8
FLOAT is size 4
INT is size 4
LONG is size 8
SHORT is size 2
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>清单 3 展示了在 <CODE>Primitive</CODE> 类上运行 Retroweaver。这个类实际上编译为两个类文件，一个用于 enum 类，另一个支持在 <CODE>switch</CODE> 语句中使用 enum。（注意，清单代码换行是为了适应页面宽度。）</P><A name=code3><B>清单 3. enum 示例输出</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
[dennis@notebook code]$ java -cp retro/release/retroweaver.jar:retro/lib/bcel-5.1.jar:retro/lib/
  jace.jar:retro/lib/Regex.jar com.rc.retroweaver.Weaver -source classes
[RetroWeaver] Weaving /home/dennis/writing/articles/devworks/series/may05/code/
  classes/com/sosnoski/dwct/Primitive$1.class
[RetroWeaver] Weaving /home/dennis/writing/articles/devworks/series/may05/code/
  classes/com/sosnoski/dwct/Primitive.class
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>在运行 Retroweaver 之后，这些类就可以用于 JDK 5.0 和 JDK 1.4 JVM 上了。当使用 1.4 JVM 运行修改后的类时，输出与 <A href="http://www-128.ibm.com/developerworks/cn/java/j-cwt07065/index.html?ca=dwcn-newsletter-java#code2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">清单 2</A> 中的相同。Retroweaver 提供命令行选项来指定旧的 1.3 和 1.2 JVM 以取代默认的 1.4 目标，但是我下载的运行时 jar 版本需要 1.4，我不想重新构建它以检查对早期 JVM 的支持。</P>
<P><A name=h2><SPAN class=atitle2>JDK 1.4 上的注释</SPAN></A><BR>既然已经看到了 Retroweaver 如何让您运行在早期 JVM 上的同时在源代码中使用 J2SE 5.0 特性，我将返回到 <A href="http://www-128.ibm.com/developerworks/cn/java/j-cwt06075/index.html" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">上个月</A> 的代码。以防您没有阅读上一期，我在此做一个总结：我展示了如何使用 ASM 2.0 基于注释实现运行时类转换，并给出一个注释的特定例子，该注释用于指定 <CODE>toString()</CODE> 方法中应该包括哪些字段。</P>
<P>上个月的代码只适用于 JDK 5.0 或更高版本。在本文中，我将修改代码以适用于早期 JVM。与 Retroweaver 一起使用，自动化 <CODE>toString()</CODE> 生成的好处将会扩展到许多还停留在 J2SE 5.0 之前运行时的 Java 开发人员。</P>
<P><A name=h2.1><SPAN class=atitle3>回忆 ToStringAgent</SPAN></A><BR>我用于对 JDK 5.0 实现 <CODE>toString()</CODE> 方法生成的 <CODE>com.sosnoski.asm.ToStringAgent</CODE> 类对于旧版 JVM 有一个小小的问题：它使用 J2SE 5.0 中新增的 instrumentation API 来在运行时截取类加载和修改类。在早期 JVM 中截取类加载不太灵活，但是并不是不可能 —— 只需要用您自己的版本来取代用于应用程序的类加载器就可以了。由于所有的应用程序类都是通过您的自定义类加载器加载的，所以在它们被实际提供给 JVM 之前，您可以自由地修改类表示。</P><I xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">在上一篇文章中，我使用这种代入自定义类加载器的技术来在运行时修改类（参阅 <A href="http://www-128.ibm.com/developerworks/cn/java/j-cwt07065/index.html?ca=dwcn-newsletter-java#resources">参考资料</A>）。这里我不想重复背景材料，但是如果您感兴趣的话，可参阅上一篇文章。</I> 
<P>更新 <A href="http://www-128.ibm.com/developerworks/cn/java/j-cwt06075/index.html" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">上个月</A> 的代码以使用自定义类加载器方法是很容易的。清单 4 展示了带有所有修改的类。该类取代了上一期文章中使用的 <CODE>com.sosnoski.asm.ToStringAgent</CODE> 类。上一期中使用到的其他类保持不变。</P><A name=code4><B>清单 4. ToStringLoader 代码</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
package com.sosnoski.asm;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;

import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;

public class ToStringLoader extends URLClassLoader
{
    private ToStringLoader(URL[] urls, ClassLoader parent) {
        super(urls, parent);
    }

    // override of ClassLoader method
    protected Class findClass(String name) throws ClassNotFoundException {
        String resname = name.replace('.', '/') + ".class";
        InputStream is = getResourceAsStream(resname);
        if (is == null) {
            System.err.println("Unable to load class " + name +
                " for annotation checking");
            return super.findClass(name);
        } else {
            System.out.println("Processing class " + name);
            try {
                
                // read the entire content into byte array
                ByteArrayOutputStream bos = new ByteArrayOutputStream();
                byte[] buff = new byte[1024];
                int length;
                while ((length = is.read(buff)) &gt;= 0) {
                    bos.write(buff, 0, length);
                }
                byte[] bytes = bos.toByteArray();
                
                // scan class binary format to find fields for toString() method
                ClassReader creader = new ClassReader(bytes);
                FieldCollector visitor = new FieldCollector();
                creader.accept(visitor, true);
                FieldInfo[] fields = visitor.getFields();
                if (fields.length &gt; 0) {
                    
                    // annotated fields present, generate the toString() method
                    System.out.println("Modifying " + name);
                    ClassWriter writer = new ClassWriter(false);
                    ToStringGenerator gen = new ToStringGenerator(writer,
                            name.replace('.', '/'), fields);
                    creader.accept(gen, false);
                    bytes = writer.toByteArray();
                }
                
                // return the (possibly modified) class
                return defineClass(bytes, 0, bytes.length);
                
            } catch (IOException e) {
                throw new ClassNotFoundException("Error reading class " + name);
            }
        }
    }

    public static void main(String[] args) {
        if (args.length &gt;= 1) {
            try {
                
                // get paths to be used for loading
                ClassLoader base = ClassLoader.getSystemClassLoader();
                URL[] urls;
                if (base instanceof URLClassLoader) {
                    urls = ((URLClassLoader)base).getURLs();
                } else {
                    urls = new URL[] { new File(".").toURI().toURL() };
                }
                
                // load the target class using custom class loader
                ToStringLoader loader =
                    new ToStringLoader(urls, base.getParent());
                Class clas = loader.loadClass(args[0]);
                    
                // invoke the "main" method of the application class
                Class[] ptypes = new Class[] { args.getClass() };
                Method main = clas.getDeclaredMethod("main", ptypes);
                String[] pargs = new String[args.length-1];
                System.arraycopy(args, 1, pargs, 0, pargs.length);
                Thread.currentThread().setContextClassLoader(loader);
                main.invoke(null, new Object[] { pargs });
                
            } catch (Exception e) {
                e.printStackTrace();
            }
            
        } else {
            System.out.println("Usage: com.sosnoski.asm.ToStringLoader " +
                "report-class main-class args...");
        }
    }
}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>为了使用清单 4 代码，我仍然需要使用 JDK 5.0 编译与注释相关的代码，然后在产生的类集合上运行 Retroweaver。我也需要在类路径中包含 retroweaver.jar 运行时代码（因为 Retroweaver 对已转换的注释使用它自己的类）。清单 5 展示了运行与 <A href="http://www-128.ibm.com/developerworks/cn/java/j-cwt06075/index.html" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">上个月</A> 相同的测试代码所产生的输出，但是这一次使用了 Retroweaver 和清单 4 中的 <CODE>ToStringLoader</CODE> 类，其中命令行换行是为了适应页面宽度）。</P><A name=code5><B>清单 5. JDK 1.4 上的 ToString 注释</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
[dennis@notebook code]$ java -cp classes:retro/release/retroweaver-rt.jar:lib/
  asm-2.0.RC1.jar:lib/asm-commons-2.0.RC1.jar
  com.sosnoski.asm.ToStringLoader com.sosnoski.dwct.Run
Processing class com.sosnoski.dwct.Run
Processing class com.sosnoski.dwct.Name
Modifying com.sosnoski.dwct.Name
Processing class com.sosnoski.dwct.Address
Modifying com.sosnoski.dwct.Address
Processing class com.sosnoski.dwct.Customer
Modifying com.sosnoski.dwct.Customer
Customer: #=12345
 Name: Dennis Michael Sosnoski
 Address: street=1234 5th St. city=Redmond state=WA zip=98052
 homePhone=425 555-1212 dayPhone=425 555-1213
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>清单 5 显示了生成的 <CODE>toString()</CODE> 方法的输出，其末尾部分与 <A href="http://www-128.ibm.com/developerworks/cn/java/j-cwt06075/index.html" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">上个月</A> 代码的 JDK 5.0 版本的结果相同。被处理的类列表几乎是相同的，只是用于截取类加载的技术不同。用于 JDK 1.4 的自定义类加载器方法不提供 JDK 5.0 instrumentation API 的完全灵活性，但是它适用于所有最近的 JVM，并允许您修改任何应用程序类。</P>
<P><A name=h3><SPAN class=atitle2>结束语</SPAN></A><BR>在本期文章中，我展示了如何使用 Retroweaver 来使 J2SE 5.0 Java 代码可运行在旧版 JVM 上。如果您喜欢新的 J2SE 5.0 语言特性，并迫不及待想在自己的应用程序中使用这些特性，那么 Retroweaver 提供了完美的解决方案：您可以马上在开发中开始使用这些语言特性，根本不会影响生产平台。作为 Retroweaver 发挥作用的一个例子，我也 backport 了 <A href="http://www-128.ibm.com/developerworks/cn/java/j-cwt06075/index.html" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">上个月</A> 的基于注释的 <CODE>ToString</CODE> 生成器，以在早期 JVM 上运行。</P>
<P>对于下个月的文章，我将回到在上一期文章中简要提到的一个问题，即注释与外部配置文件之间的权衡。在配置文件疯狂了很多年之后，整个的 Java 扩展集合似乎都一股脑儿转向使用注释了。但是难道注释总是提供配置类型信息的最佳方式吗？我对此表示怀疑，下个月我将提供一些例子，以及一些我个人的最佳实践指导方针。</P>
<P><A name=resources><SPAN class=atitle2>参考资料 </SPAN></A>
<UL>
<LI>您可以参阅本文在 developerWorks 全球站点上的 <A href="http://www.ibm.com/developerworks/java/library/j-cwt07065/" target=_blank xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">英文原文</A>。<BR><BR>
<LI>单击本文顶部或底部的 <B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">代码</B> 图标，下载文中讨论的源代码。<BR><BR>
<LI>想要开始在旧版 JVM 上使用 J2SE 5.0 语言特性？请直接查看 <A href="http://retroweaver.sf.net/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Retroweaver 项目</A> 的开放源代码。<BR><BR>
<LI>获得 <A href="http://asm.objectweb.org/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">ASM</A> 这个快速而灵活的 Java 字节码操作框架的所有详细资料。<BR><BR>
<LI>对 J2SE 5.0 与旧版 Java 平台的区别感兴趣？请查看 John Zukowski 撰写的 <A href="http://www-128.ibm.com/developerworks/cn/views/java/articles.jsp?view_by=search&amp;search_by=%E9%A9%AF%E6%9C%8D+Tiger%3A" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><I>驯服 Tiger</I></A> 系列，了解所有的更改。<BR><BR>
<LI>在 <A href="http://www.jcp.org/en/jsr/detail?id=175" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">JSR-175: A Metadata Facility for the Java Programming Language</A> 中找到关于 J2SE 注释的所有信息。<BR><BR>
<LI>关于使用自定义类加载器在运行时进行类转换的深入讨论，请参阅作者的文章“<A href="http://www-128.ibm.com/developerworks/cn/java/j-dyn0203/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Java 编程的动态性，第 5 部分: 动态转换类</A>”（developerWorks, 2004 年 2 月）。<BR><BR>
<LI>不要错过 Dennis Sosnoski 撰写的 <A href="http://www-128.ibm.com/developerworks/cn/views/java/articles.jsp?view_by=search&amp;search_by=Classworking+%E5%B7%A5%E5%85%B7%E7%AE%B1%3A+" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><I>Classworking 工具箱</I></A> 系列中的其他文章。<BR><BR>
<LI>请参阅 Peter Haggar 撰写的“<A href="http://www.ibm.com/developerworks/ibm/library/it-haggar_bytecode/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Java bytecode: Understanding bytecode makes you a better programmer</A>”（developerWorks, 2001 年 7 月），了解关于 Java 字节码设计的更多信息。<BR><BR>
<LI>同样由 Dennis Sosnoski 撰写的 <A href="http://www-128.ibm.com/developerworks/cn/java/j-dyn0429/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><I>Java 编程的动态性</I></A> 系列，将带您漫游 Java 类结构、发射和 classworking。<BR><BR>
<LI><A href="http://jikes.sourceforge.net/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Jikes</A> 开放源代码项目提供了 Java 编程语言的非常快速和高兼容性的编译器。可以使用它来老式地生成字节码 —— 从 Java 源代码生成。<BR><BR>
<LI>要了解更多关于 Java 技术的信息，请访问 <A href="http://www.ibm.com/developerworks/cn/java/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">developerWorks Java 专区</A>。您将找到技术文档、how-to 文章、教程、下载、产品信息，以及更多内容。 <BR><BR>
<LI>请访问 <A href="http://www.ibm.com/developerworks/cn/java/newto/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">New to Java technology</A> 站点，找到帮助您开始 Java 编程的最新资源。<BR><BR>
<LI>通过参与 <A href="http://www.ibm.com/developerworks/blogs/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">developerWorks blogs</A> 加入 developerWorks 社区。<BR></LI></UL><img src ="http://www.blogjava.net/mstar/aggbug/8590.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/mstar/" target="_blank">黑灵</a> 2005-07-28 08:53 <a href="http://www.blogjava.net/mstar/archive/2005/07/28/8590.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[ZZ]Prepare for New EJB 3.0 Persistence</title><link>http://www.blogjava.net/mstar/archive/2005/07/28/8589.html</link><dc:creator>黑灵</dc:creator><author>黑灵</author><pubDate>Thu, 28 Jul 2005 00:46:00 GMT</pubDate><guid>http://www.blogjava.net/mstar/archive/2005/07/28/8589.html</guid><wfw:comment>http://www.blogjava.net/mstar/comments/8589.html</wfw:comment><comments>http://www.blogjava.net/mstar/archive/2005/07/28/8589.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/mstar/comments/commentRss/8589.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/mstar/services/trackbacks/8589.html</trackback:ping><description><![CDATA[<P class=FeatureHead><A href="http://www.ftponline.com/channels/java/2005_06/mschincariol_06_22_05/">http://www.ftponline.com/channels/java/2005_06/mschincariol_06_22_05/</A><BR>Prepare for New EJB 3.0 Persistence<BR><SPAN class=FeatureDek>See how to address the migration concerns of enterprise application developers and architects who are currently using EJB 2.1 CMP</SPAN><BR><SPAN class=AboutAuthor><EM>by Merrick Schincariol and Doug Clarke</EM></SPAN> </P><!-- posted date in this format: Posted September 17, 2003 -->
<P><EM>June 22, 2005</EM></P><!-- end posted date -->
<P><SPAN class=DropCap>M</SPAN>uch has already been much written and discussed concerning the upcoming changes to Enterprise JavaBeans (EJB) with the proposed 3.0 specification. The simplifications of the component model and metadata configuration will definitely make this update an important evolution of J2EE. One of the most significant changes within EJB 3.0 is the new persistence model. This new persistence API and approach to modeling is based on the success of leading object-relational mapping products that use a Plain Old Java Object (POJO) approach to persistence.</P><SPAN class=bigbox_left id=bigbox></SPAN><!-- end 336x280 ad -->
<P>We'll look into EJB 3.0 persistence and address the concerns of enterprise application developers and architects who are currently using EJB 2.1 CMP. The process of moving from EJB 2.1 persistence to EJB 3.0 can range from coexistence to a full migration and adoption of the new model. Although much has already been written about the new persistence model, specific strategies and development and design patterns have not been detailed. We'll attempt to fill these gaps with concrete solution options while visiting the key benefits that application developers will gain through this migration. We assume that you are familiar with the basic elements of the new persistence API. (Note: Look for an upcoming article on migrating J2EE applications to EJB 3.0 in <EM>Java Pro</EM> magazine by Debu Panda, a member of the Oracle Application Server Development team, that will post on FTPOnline in July, 2005.) </P>
<P><SPAN class=subhead>Why Migrate?</SPAN><BR>One question participants in J2EE projects should discuss when planning an upgrade to an EJB 3.0-enabled J2EE container is: Why should we migrate our application? These new EJB 3.0 containers will be backward compatible and thus support their existing application without modification. This backward compatibility means that upgrading a container to EJB 3.0 does not mean in itself that an application needs to be rewritten. However, what about this new persistence API? </P><SPAN id=lblShortArticlePage>
<P>The real benefits in migrating from an EJB 2 persistence solution using container-managed persistence (CMP) include reduction in the number of artifacts and complexity surrounding beans, interfaces, and home interfaces that are no longer required in the 3.0 approach; the ability to test entities and the application code that uses them outside of the container—the EJB 2.1 persistence solutions have led most applications to avoid strong unit testing of these components; and reduction in unnecessary code that works around the limitations of EJB 2.1 entity beans. These patterns include data-transfer objects (DTOs) and service locator implementations. </P>
<P>These benefits are significant to the developer productivity and overall application quality that comes with improved testing. The challenge is that if you are to see these benefits, the application’s persistence model and the client code that uses the model must be migrated. Unlike the session- and message-driven bean components, the entity bean migration is potentially much more intrusive and should be undertaken with a better understanding of the associated costs. </P>
<P>Migrating from EJB 2.1 to EJB 3.0 CMP involves migrating the model and its mappings, the queries, and the applications’ client code to the persistence layer. The migration can be divided into three basic approaches: 1) migrate the entity DTOs to EJB 3.0 entities; migrate the EJB 2.1 entity beans to EJB 3.0 entities; and 3) create a new EJB 3.0 entity model. <BR></P>
<P>The first two approaches are intended to minimize code changes in your application. The third involves creating a new model, possibly using forward-generation tools to reduce coding and configuration work. Although this may be the most efficient way to create the EJB 3.0 persistence model, it will most likely incur the most migration costs within the application code. </P>
<P><SPAN class=bigbox_left id=bigbox></SPAN><!-- end 336x280 ad --></P>
<P>Before we discuss which approach is best for a given application, the issue of DTOs and their role in EJB 3.0 persistence should be clarified. In EJB 2.1, DTOs were required for getting persistent data outside of the container for use in another tier of the application. In EJB 3.0, entities can be serialized and the need to use DTOs is removed. This factor doesn't mean that DTOs cannot be used if they provide necessary decoupling, but it does address the common practice of having DTOs that mirror entity beans and replicate business logic. These will be referred to as "entity DTOs," and they are no longer required. </P>
<P>Another flavor of DTO is the "view DTO," which is an object that does not mirror the CMP entities but instead provides a more coarse-grained view of data that may come from one or more entity beans. View DTOs are used to optimize interaction between tiers. EJB 3.0 does not remove the need for this style of DTO but instead provides facilities to simplify their use. Whereas in traditional applications, the underlying entities are retrieved and populated programmatically into view DTOs, EJB 3.0 query results may now be directly projected onto view DTOs without any additional binding code. </P>
<P><SPAN class=subhead>Nonpersistent Classes</SPAN><BR>In EJB 3.0 an EJB QL query can return results by using nonpersistent classes. In this case, the query statement defines the attributes needed and the selection criteria relative to the persistence model, but instead of returning one of these types, a nonpersistent class is specified. The NEW operator is used for this specification and defines the class and constructor to be used when building these result objects.</P><PRE class=codesnippet>SELECT NEW example.EmpView(
  e.id, e.firstName, e.lastName, 
  d.name)
FROM Employee e JOIN 
  e.department d 
WHERE e.salary &gt; 50000</PRE>
<P>Deciding to migrate your entity DTOs to become your EJB 3.0 entities is a good choice for minimizing changes on the client tier that use these serialized objects. It is also a good choice if your application logic in the EJB tier is minimal or also focused on the entity DTOs. </P>
<P>This approach requires that the entity DTO is a valid JavaBean and maintains a one-to-one correspondence with the data elements in the associated entity bean. It is also highly recommended that a DAO layer be in place, so that the persistent logic can be updated in isolation once the DTOs have been converted. If your application logic makes direct use of EJB 2.1 entities, the techniques described for migrating entity beans may be more suitable. </P>
<P>The first step in the migration process is to mark the entity DTO as a persistent object by adding the @Entity annotation to the class definition. Without any further changes, the entity DTO is now a persistent object that maps to the database, using the EJB 3.0 defaults. The table name is assumed to be the class name, and each public JavaBean getter method is assumed to correspond to a column in the table with the same name as the property. You can customize these default mappings by using @Table, @Column, and other EJB 3.0 annotations. </P>
<P>After each entity DTO is marked as a persistent object and the database mappings are set up correctly, the next step is to define the relationships between the new entities. EJB 3.0 includes a full set of object-relational annotations to identify entity relationships and control their behavior during persistent operations. The &lt;ejb-relation&gt; descriptor in the EJB 2.1 ejb-jar.xml file can be directly mapped onto each pair of entities involved in the relationship. Consider the relationship descriptor shown in <A href="javascript:openWindow1();"><FONT color=#002c99>Listing 1</FONT></A>. This fragment from an Emp