﻿<?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/rendong/category/13644.html</link><description /><language>zh-cn</language><lastBuildDate>Fri, 02 Mar 2007 02:54:48 GMT</lastBuildDate><pubDate>Fri, 02 Mar 2007 02:54:48 GMT</pubDate><ttl>60</ttl><item><title>lucene实例</title><link>http://www.blogjava.net/rendong/archive/2006/12/25/89849.html</link><dc:creator>rendong</dc:creator><author>rendong</author><pubDate>Mon, 25 Dec 2006 03:26:00 GMT</pubDate><guid>http://www.blogjava.net/rendong/archive/2006/12/25/89849.html</guid><wfw:comment>http://www.blogjava.net/rendong/comments/89849.html</wfw:comment><comments>http://www.blogjava.net/rendong/archive/2006/12/25/89849.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/rendong/comments/commentRss/89849.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/rendong/services/trackbacks/89849.html</trackback:ping><description><![CDATA[
		<p>说明一下,这一篇文章的用到的lucene,是用2.0版本的,主要在查询的时候2.0版本的lucene与以前的版本有了一些区别. <br />其实这一些代码都是早几个月写的,自己很懒,所以到今天才写到自己的博客上,高深的文章自己写不了，只能记录下一些简单的记录与点滴，其中的代码算是自娱自乐的，希望高手不要把重构之类的砸下来...</p>
		<p>1、在windows系统下的的C盘，建一个名叫s的文件夹,在该文件夹里面随便建三个txt文件，随便起名啦，就叫"1.txt","2.txt"和"3.txt"啦 <br />其中1.txt的内容如下： <br /></p>
		<div class="code_title">代码</div>
		<div class="code_div">
				<div class="dp-highlighter">
						<div class="bar">
						</div>
						<ol class="dp-j">
								<li class="alt">
										<span>
												<span>中华人民共和国   </span>
										</span>
								</li>
								<li class="">
										<span>全国人民   </span>
								</li>
								<li class="alt">
										<span>
										</span>
										<span class="number">2006</span>
										<span>年  </span>
								</li>
						</ol>
				</div>
		</div>
		<script><![CDATA[ender_code();]]&gt;</script>
		<br />而"2.txt"和"3.txt"的内容也可以随便写几写，这里懒写，就复制一个和1.txt文件的内容一样吧 
<p></p><p>2、下载lucene包，放在classpath路径中 <br />建立索引: <br /></p><div class="code_title">代码</div><div class="code_div"><div class="dp-highlighter"><div class="bar"></div><ol class="dp-j"><li class="alt"><span><span class="keyword">package</span><span> lighter.javaeye.com;   </span></span></li><li class=""><span>  </span></li><li class="alt"><span></span><span class="keyword">import</span><span> java.io.BufferedReader;   </span></li><li class=""><span></span><span class="keyword">import</span><span> java.io.File;   </span></li><li class="alt"><span></span><span class="keyword">import</span><span> java.io.FileInputStream;   </span></li><li class=""><span></span><span class="keyword">import</span><span> java.io.IOException;   </span></li><li class="alt"><span></span><span class="keyword">import</span><span> java.io.InputStreamReader;   </span></li><li class=""><span></span><span class="keyword">import</span><span> java.util.Date;   </span></li><li class="alt"><span>  </span></li><li class=""><span></span><span class="keyword">import</span><span> org.apache.lucene.analysis.Analyzer;   </span></li><li class="alt"><span></span><span class="keyword">import</span><span> org.apache.lucene.analysis.standard.StandardAnalyzer;   </span></li><li class=""><span></span><span class="keyword">import</span><span> org.apache.lucene.document.Document;   </span></li><li class="alt"><span></span><span class="keyword">import</span><span> org.apache.lucene.document.Field;   </span></li><li class=""><span></span><span class="keyword">import</span><span> org.apache.lucene.index.IndexWriter;   </span></li><li class="alt"><span>  </span></li><li class=""><span></span><span class="comment">/** </span> </li><li class="alt"><span><span class="comment"> * author lighter date 2006-8-7 </span> </span></li><li class=""><span><span class="comment"> */</span><span>  </span></span></li><li class="alt"><span></span><span class="keyword">public</span><span> </span><span class="keyword">class</span><span> TextFileIndexer {   </span></li><li class=""><span>    </span><span class="keyword">public</span><span> </span><span class="keyword">static</span><span> </span><span class="keyword">void</span><span> main(String[] args) </span><span class="keyword">throws</span><span> Exception {   </span></li><li class="alt"><span>        </span><span class="comment">/* 指明要索引文件夹的位置,这里是C盘的S文件夹下 */</span><span>  </span></li><li class=""><span>        File fileDir = </span><span class="keyword">new</span><span> File(</span><span class="string">"c:\\s"</span><span>);   </span></li><li class="alt"><span>  </span></li><li class=""><span>        </span><span class="comment">/* 这里放索引文件的位置 */</span><span>  </span></li><li class="alt"><span>        File indexDir = </span><span class="keyword">new</span><span> File(</span><span class="string">"c:\\index"</span><span>);   </span></li><li class=""><span>        Analyzer luceneAnalyzer = </span><span class="keyword">new</span><span> StandardAnalyzer();   </span></li><li class="alt"><span>        IndexWriter indexWriter = </span><span class="keyword">new</span><span> IndexWriter(indexDir, luceneAnalyzer,   </span></li><li class=""><span>                </span><span class="keyword">true</span><span>);   </span></li><li class="alt"><span>        File[] textFiles = fileDir.listFiles();   </span></li><li class=""><span>        </span><span class="keyword">long</span><span> startTime = </span><span class="keyword">new</span><span> Date().getTime();   </span></li><li class="alt"><span>           </span></li><li class=""><span>        </span><span class="comment">//增加document到索引去 </span><span>  </span></li><li class="alt"><span>        </span><span class="keyword">for</span><span> (</span><span class="keyword">int</span><span> i = </span><span class="number">0</span><span>; i &lt; textFiles.length; i++) {   </span></li><li class=""><span>            </span><span class="keyword">if</span><span> (textFiles[i].isFile()   </span></li><li class="alt"><span>                    &amp;&amp; textFiles[i].getName().endsWith(</span><span class="string">".txt"</span><span>)) {   </span></li><li class=""><span>                System.out.println(</span><span class="string">"File "</span><span> + textFiles[i].getCanonicalPath()   </span></li><li class="alt"><span>                        + </span><span class="string">"正在被索引...."</span><span>);   </span></li><li class=""><span>                String temp = FileReaderAll(textFiles[i].getCanonicalPath(),   </span></li><li class="alt"><span>                        </span><span class="string">"GBK"</span><span>);   </span></li><li class=""><span>                System.out.println(temp);   </span></li><li class="alt"><span>                Document document = </span><span class="keyword">new</span><span> Document();   </span></li><li class=""><span>                Field FieldPath = </span><span class="keyword">new</span><span> Field(</span><span class="string">"path"</span><span>, textFiles[i].getPath(),   </span></li><li class="alt"><span>                        Field.Store.YES, Field.Index.NO);   </span></li><li class=""><span>                Field FieldBody = </span><span class="keyword">new</span><span> Field(</span><span class="string">"body"</span><span>, temp, Field.Store.YES,   </span></li><li class="alt"><span>                        Field.Index.TOKENIZED,   </span></li><li class=""><span>                        Field.TermVector.WITH_POSITIONS_OFFSETS);   </span></li><li class="alt"><span>                document.add(FieldPath);   </span></li><li class=""><span>                document.add(FieldBody);   </span></li><li class="alt"><span>                indexWriter.addDocument(document);   </span></li><li class=""><span>            }   </span></li><li class="alt"><span>        }   </span></li><li class=""><span>        </span><span class="comment">//optimize()方法是对索引进行优化 </span><span>  </span></li><li class="alt"><span>        indexWriter.optimize();   </span></li><li class=""><span>        indexWriter.close();   </span></li><li class="alt"><span>           </span></li><li class=""><span>        </span><span class="comment">//测试一下索引的时间 </span><span>  </span></li><li class="alt"><span>        </span><span class="keyword">long</span><span> endTime = </span><span class="keyword">new</span><span> Date().getTime();   </span></li><li class=""><span>        System.out   </span></li><li class="alt"><span>                .println(</span><span class="string">"这花费了"</span><span>  </span></li><li class=""><span>                        + (endTime - startTime)   </span></li><li class="alt"><span>                        + </span><span class="string">" 毫秒来把文档增加到索引里面去!"</span><span>  </span></li><li class=""><span>                        + fileDir.getPath());   </span></li><li class="alt"><span>    }   </span></li><li class=""><span>  </span></li><li class="alt"><span>    </span><span class="keyword">public</span><span> </span><span class="keyword">static</span><span> String FileReaderAll(String FileName, String charset)   </span></li><li class=""><span>            </span><span class="keyword">throws</span><span> IOException {   </span></li><li class="alt"><span>        BufferedReader reader = </span><span class="keyword">new</span><span> BufferedReader(</span><span class="keyword">new</span><span> InputStreamReader(   </span></li><li class=""><span>                </span><span class="keyword">new</span><span> FileInputStream(FileName), charset));   </span></li><li class="alt"><span>        String line = </span><span class="keyword">new</span><span> String();   </span></li><li class=""><span>        String temp = </span><span class="keyword">new</span><span> String();   </span></li><li class="alt"><span>           </span></li><li class=""><span>        </span><span class="keyword">while</span><span> ((line = reader.readLine()) != </span><span class="keyword">null</span><span>) {   </span></li><li class="alt"><span>            temp += line;   </span></li><li class=""><span>        }   </span></li><li class="alt"><span>        reader.close();   </span></li><li class=""><span>        </span><span class="keyword">return</span><span> temp;   </span></li><li class="alt"><span>    }   </span></li><li class=""><span>}  </span></li></ol></div></div><script><![CDATA[ender_code();]]&gt;</script><p></p><p>索引的结果： <br /></p><div class="code_title">代码</div><div class="code_div"><div class="dp-highlighter"><div class="bar"></div><ol class="dp-j"><li class="alt"><span><span>File C:\s\</span><span class="number">1</span><span>.txt正在被索引....   </span></span></li><li class=""><span>中华人民共和国全国人民</span><span class="number">2006</span><span>年   </span></li><li class="alt"><span>File C:\s\</span><span class="number">2</span><span>.txt正在被索引....   </span></li><li class=""><span>中华人民共和国全国人民</span><span class="number">2006</span><span>年   </span></li><li class="alt"><span>File C:\s\</span><span class="number">3</span><span>.txt正在被索引....   </span></li><li class=""><span>中华人民共和国全国人民</span><span class="number">2006</span><span>年   </span></li><li class="alt"><span>这花费了</span><span class="number">297</span><span> 毫秒来把文档增加到索引里面去!c:\s  </span></li></ol></div></div><script><![CDATA[ender_code();]]&gt;</script><p></p><p>3、建立了索引之后，查询啦.... <br /></p><div class="code_title">代码</div><div class="code_div"><div class="dp-highlighter"><div class="bar"></div><ol class="dp-j"><li class="alt"><span><span class="keyword">package</span><span> lighter.javaeye.com;   </span></span></li><li class=""><span>  </span></li><li class="alt"><span></span><span class="keyword">import</span><span> java.io.IOException;   </span></li><li class=""><span>  </span></li><li class="alt"><span></span><span class="keyword">import</span><span> org.apache.lucene.analysis.Analyzer;   </span></li><li class=""><span></span><span class="keyword">import</span><span> org.apache.lucene.analysis.standard.StandardAnalyzer;   </span></li><li class="alt"><span></span><span class="keyword">import</span><span> org.apache.lucene.queryParser.ParseException;   </span></li><li class=""><span></span><span class="keyword">import</span><span> org.apache.lucene.queryParser.QueryParser;   </span></li><li class="alt"><span></span><span class="keyword">import</span><span> org.apache.lucene.search.Hits;   </span></li><li class=""><span></span><span class="keyword">import</span><span> org.apache.lucene.search.IndexSearcher;   </span></li><li class="alt"><span></span><span class="keyword">import</span><span> org.apache.lucene.search.Query;   </span></li><li class=""><span>  </span></li><li class="alt"><span></span><span class="keyword">public</span><span> </span><span class="keyword">class</span><span> TestQuery {   </span></li><li class=""><span>    </span><span class="keyword">public</span><span> </span><span class="keyword">static</span><span> </span><span class="keyword">void</span><span> main(String[] args) </span><span class="keyword">throws</span><span> IOException, ParseException {   </span></li><li class="alt"><span>        Hits hits = </span><span class="keyword">null</span><span>;   </span></li><li class=""><span>        String queryString = </span><span class="string">"中华"</span><span>;   </span></li><li class="alt"><span>        Query query = </span><span class="keyword">null</span><span>;   </span></li><li class=""><span>        IndexSearcher searcher = </span><span class="keyword">new</span><span> IndexSearcher(</span><span class="string">"c:\\index"</span><span>);   </span></li><li class="alt"><span>  </span></li><li class=""><span>        Analyzer analyzer = </span><span class="keyword">new</span><span> StandardAnalyzer();   </span></li><li class="alt"><span>        </span><span class="keyword">try</span><span> {   </span></li><li class=""><span>            QueryParser qp = </span><span class="keyword">new</span><span> QueryParser(</span><span class="string">"body"</span><span>, analyzer);   </span></li><li class="alt"><span>            query = qp.parse(queryString);   </span></li><li class=""><span>        } </span><span class="keyword">catch</span><span> (ParseException e) {   </span></li><li class="alt"><span>        }   </span></li><li class=""><span>        </span><span class="keyword">if</span><span> (searcher != </span><span class="keyword">null</span><span>) {   </span></li><li class="alt"><span>            hits = searcher.search(query);   </span></li><li class=""><span>            </span><span class="keyword">if</span><span> (hits.length() &gt; </span><span class="number">0</span><span>) {   </span></li><li class="alt"><span>                System.out.println(</span><span class="string">"找到:"</span><span> + hits.length() + </span><span class="string">" 个结果!"</span><span>);   </span></li><li class=""><span>            }   </span></li><li class="alt"><span>        }   </span></li><li class=""><span>    }   </span></li><li class="alt"><span>  </span></li><li class=""><span>}  </span></li></ol></div></div><script><![CDATA[ender_code();]]&gt;</script><p></p><p>其运行结果： <br /></p><div class="quote_title">引用</div><div class="quote_div">找到:3 个结果!</div><br />具体的API的用法，这里就不说了，具体的做法参考lucene的官方文档吧... <br />下一篇文章: <br />搜索篇:lucene的简单实例&lt;二&gt; <a href="http://www.javaeye.com/post/190576" target="blank">http://www.javaeye.com/post/190576</a><br />打一个例子吧, <br />这是lucene2.0的API <br /><div class="code_title">代码</div><div class="code_div"><div class="dp-highlighter"><div class="bar"></div><ol class="dp-j"><li class="alt"><span><span>QueryParser qp = </span><span class="keyword">new</span><span> QueryParser(</span><span class="string">"body"</span><span>, analyzer);      </span></span></li><li class=""><span>query = qp.parse(queryString);      </span></li></ol></div></div><script><![CDATA[ender_code();]]&gt;</script><p></p><p>这是lucene1.4.3版的API <br /></p><div class="code_title">代码</div><div class="code_div"><div class="dp-highlighter"><div class="bar"></div><ol class="dp-j"><li class="alt"><span><span>query = QueryParser.parse(key,queryString,</span><span class="keyword">new</span><span> </span><span class="keyword">new</span><span> StandardAnalyzer());  </span></span></li></ol></div></div><script><![CDATA[ender_code();]]&gt;</script><br />详细的改动看一些官方的文档就清楚啦<br />文章的时候,感觉比较难写的就是标题,有时候不知道起什么名字好,反正这里写的都是关于lucene的一些简单的实例,就随便起啦.
<p>Lucene 其实很简单的,它最主要就是做两件事:建立索引和进行搜索 <br />来看一些在lucene中使用的术语,这里并不打算作详细的介绍,只是点一下而已----因为这一个世界有一种好东西，叫搜索。</p><p><span style="COLOR: blue">IndexWriter</span>:lucene中最重要的的类之一，它主要是用来将文档加入索引，同时控制索引过程中的一些参数使用。</p><p><span style="COLOR: blue">Analyzer</span>:分析器,主要用于分析搜索引擎遇到的各种文本。常用的有StandardAnalyzer分析器,StopAnalyzer分析器,WhitespaceAnalyzer分析器等。</p><p><span style="COLOR: blue">Directory</span>:索引存放的位置;lucene提供了两种索引存放的位置，一种是磁盘，一种是内存。一般情况将索引放在磁盘上；相应地lucene提供了FSDirectory和RAMDirectory两个类。</p><p><span style="COLOR: blue">Document</span>:文档;Document相当于一个要进行索引的单元，任何可以想要被索引的文件都必须转化为Document对象才能进行索引。</p><p><span style="COLOR: blue">Field</span>：字段。</p><p><span style="COLOR: blue">IndexSearcher</span>:是lucene中最基本的检索工具，所有的检索都会用到IndexSearcher工具;</p><p><span style="COLOR: blue">Query</span>:查询，lucene中支持模糊查询，语义查询，短语查询，组合查询等等,如有TermQuery,BooleanQuery,RangeQuery,WildcardQuery等一些类。</p><p><span style="COLOR: blue">QueryParser</span>: 是一个解析用户输入的工具，可以通过扫描用户输入的字符串，生成Query对象。</p><p><span style="COLOR: blue">Hits</span>:在搜索完成之后，需要把搜索结果返回并显示给用户，只有这样才算是完成搜索的目的。在lucene中，搜索的结果的集合是用Hits类的实例来表示的。</p><p>上面作了一大堆名词解释，下面就看几个简单的实例吧: <br />1、简单的的StandardAnalyzer测试例子 <br /></p><div class="code_title">代码</div><div class="code_div"><div class="dp-highlighter"><div class="bar"></div><ol class="dp-j"><li class="alt"><span><span class="keyword">package</span><span> lighter.javaeye.com;   </span></span></li><li class=""><span>  </span></li><li class="alt"><span></span><span class="keyword">import</span><span> java.io.IOException;   </span></li><li class=""><span></span><span class="keyword">import</span><span> java.io.StringReader;   </span></li><li class="alt"><span>  </span></li><li class=""><span></span><span class="keyword">import</span><span> org.apache.lucene.analysis.Analyzer;   </span></li><li class="alt"><span></span><span class="keyword">import</span><span> org.apache.lucene.analysis.Token;   </span></li><li class=""><span></span><span class="keyword">import</span><span> org.apache.lucene.analysis.TokenStream;   </span></li><li class="alt"><span></span><span class="keyword">import</span><span> org.apache.lucene.analysis.standard.StandardAnalyzer;   </span></li><li class=""><span>  </span></li><li class="alt"><span></span><span class="keyword">public</span><span> </span><span class="keyword">class</span><span> StandardAnalyzerTest    </span></li><li class=""><span>{   </span></li><li class="alt"><span>    </span><span class="comment">//构造函数， </span><span>  </span></li><li class=""><span>    </span><span class="keyword">public</span><span> StandardAnalyzerTest()   </span></li><li class="alt"><span>    {   </span></li><li class=""><span>    }   </span></li><li class="alt"><span>    </span><span class="keyword">public</span><span> </span><span class="keyword">static</span><span> </span><span class="keyword">void</span><span> main(String[] args)    </span></li><li class=""><span>    {   </span></li><li class="alt"><span>        </span><span class="comment">//生成一个StandardAnalyzer对象 </span><span>  </span></li><li class=""><span>        Analyzer aAnalyzer = </span><span class="keyword">new</span><span> StandardAnalyzer();   </span></li><li class="alt"><span>        </span><span class="comment">//测试字符串 </span><span>  </span></li><li class=""><span>        StringReader sr = </span><span class="keyword">new</span><span> StringReader(</span><span class="string">"lighter javaeye com is the are on"</span><span>);   </span></li><li class="alt"><span>        </span><span class="comment">//生成TokenStream对象 </span><span>  </span></li><li class=""><span>        TokenStream ts = aAnalyzer.tokenStream(</span><span class="string">"name"</span><span>, sr);    </span></li><li class="alt"><span>        </span><span class="keyword">try</span><span> {   </span></li><li class=""><span>            </span><span class="keyword">int</span><span> i=</span><span class="number">0</span><span>;   </span></li><li class="alt"><span>            Token t = ts.next();   </span></li><li class=""><span>            </span><span class="keyword">while</span><span>(t!=</span><span class="keyword">null</span><span>)   </span></li><li class="alt"><span>            {   </span></li><li class=""><span>                </span><span class="comment">//辅助输出时显示行号 </span><span>  </span></li><li class="alt"><span>                i++;   </span></li><li class=""><span>                </span><span class="comment">//输出处理后的字符 </span><span>  </span></li><li class="alt"><span>                System.out.println(</span><span class="string">"第"</span><span>+i+</span><span class="string">"行:"</span><span>+t.termText());   </span></li><li class=""><span>                </span><span class="comment">//取得下一个字符 </span><span>  </span></li><li class="alt"><span>                t=ts.next();   </span></li><li class=""><span>            }   </span></li><li class="alt"><span>        } </span><span class="keyword">catch</span><span> (IOException e) {   </span></li><li class=""><span>            e.printStackTrace();   </span></li><li class="alt"><span>        }   </span></li><li class=""><span>    }   </span></li><li class="alt"><span>}   </span></li></ol></div></div><script><![CDATA[ender_code();]]&gt;</script><br />显示结果： <br /><div class="quote_title">引用</div><div class="quote_div">第1行:lighter <br />第2行:javaeye <br />第3行:com</div><br />提示一下： <br />StandardAnalyzer是lucene中内置的"标准分析器",可以做如下功能: <br />1、对原有句子按照空格进行了分词 <br />2、所有的大写字母都可以能转换为小写的字母 <br />3、可以去掉一些没有用处的单词，例如"is","the","are"等单词，也删除了所有的标点 <br />查看一下结果与"new StringReader("lighter javaeye com is the are on")"作一个比较就清楚明了。 <br />这里不对其API进行解释了，具体见lucene的官方文档。需要注意一点，这里的代码使用的是lucene2的API，与1.43版有一些明显的差别。 
<p></p><p>2、看另一个实例,简单地建立索引，进行搜索 <br /></p><div class="code_title">代码</div><div class="code_div"><div class="dp-highlighter"><div class="bar"></div><ol class="dp-j"><li class="alt"><span><span class="keyword">package</span><span> lighter.javaeye.com;   </span></span></li><li class=""><span></span><span class="keyword">import</span><span> org.apache.lucene.analysis.standard.StandardAnalyzer;   </span></li><li class="alt"><span></span><span class="keyword">import</span><span> org.apache.lucene.document.Document;   </span></li><li class=""><span></span><span class="keyword">import</span><span> org.apache.lucene.document.Field;   </span></li><li class="alt"><span></span><span class="keyword">import</span><span> org.apache.lucene.index.IndexWriter;   </span></li><li class=""><span></span><span class="keyword">import</span><span> org.apache.lucene.queryParser.QueryParser;   </span></li><li class="alt"><span></span><span class="keyword">import</span><span> org.apache.lucene.search.Hits;   </span></li><li class=""><span></span><span class="keyword">import</span><span> org.apache.lucene.search.IndexSearcher;   </span></li><li class="alt"><span></span><span class="keyword">import</span><span> org.apache.lucene.search.Query;   </span></li><li class=""><span></span><span class="keyword">import</span><span> org.apache.lucene.store.FSDirectory;   </span></li><li class="alt"><span>  </span></li><li class=""><span></span><span class="keyword">public</span><span> </span><span class="keyword">class</span><span> FSDirectoryTest {   </span></li><li class="alt"><span>  </span></li><li class=""><span>    </span><span class="comment">//建立索引的路径 </span><span>  </span></li><li class="alt"><span>    </span><span class="keyword">public</span><span> </span><span class="keyword">static</span><span> </span><span class="keyword">final</span><span> String path = </span><span class="string">"c:\\index2"</span><span>;   </span></li><li class=""><span>  </span></li><li class="alt"><span>    </span><span class="keyword">public</span><span> </span><span class="keyword">static</span><span> </span><span class="keyword">void</span><span> main(String[] args) </span><span class="keyword">throws</span><span> Exception {   </span></li><li class=""><span>        Document doc1 = </span><span class="keyword">new</span><span> Document();   </span></li><li class="alt"><span>        doc1.add( </span><span class="keyword">new</span><span> Field(</span><span class="string">"name"</span><span>, </span><span class="string">"lighter javaeye com"</span><span>,Field.Store.YES,Field.Index.TOKENIZED));   </span></li><li class=""><span>  </span></li><li class="alt"><span>        Document doc2 = </span><span class="keyword">new</span><span> Document();   </span></li><li class=""><span>        doc2.add(</span><span class="keyword">new</span><span> Field(</span><span class="string">"name"</span><span>, </span><span class="string">"lighter blog"</span><span>,Field.Store.YES,Field.Index.TOKENIZED));   </span></li><li class="alt"><span>  </span></li><li class=""><span>        IndexWriter writer = </span><span class="keyword">new</span><span> IndexWriter(FSDirectory.getDirectory(path, </span><span class="keyword">true</span><span>), </span><span class="keyword">new</span><span> StandardAnalyzer(), </span><span class="keyword">true</span><span>);   </span></li><li class="alt"><span>        writer.setMaxFieldLength(</span><span class="number">3</span><span>);   </span></li><li class=""><span>        writer.addDocument(doc1);   </span></li><li class="alt"><span>        writer.setMaxFieldLength(</span><span class="number">3</span><span>);   </span></li><li class=""><span>        writer.addDocument(doc2);   </span></li><li class="alt"><span>        writer.close();   </span></li><li class=""><span>  </span></li><li class="alt"><span>        IndexSearcher searcher = </span><span class="keyword">new</span><span> IndexSearcher(path);   </span></li><li class=""><span>        Hits hits = </span><span class="keyword">null</span><span>;   </span></li><li class="alt"><span>        Query query = </span><span class="keyword">null</span><span>;   </span></li><li class=""><span>        QueryParser qp = </span><span class="keyword">new</span><span> QueryParser(</span><span class="string">"name"</span><span>,</span><span class="keyword">new</span><span> StandardAnalyzer());   </span></li><li class="alt"><span>           </span></li><li class=""><span>        query = qp.parse(</span><span class="string">"lighter"</span><span>);   </span></li><li class="alt"><span>        hits = searcher.search(query);   </span></li><li class=""><span>        System.out.println(</span><span class="string">"查找\"lighter\" 共"</span><span> + hits.length() + </span><span class="string">"个结果"</span><span>);   </span></li><li class="alt"><span>  </span></li><li class=""><span>        query = qp.parse(</span><span class="string">"javaeye"</span><span>);   </span></li><li class="alt"><span>        hits = searcher.search(query);   </span></li><li class=""><span>        System.out.println(</span><span class="string">"查找\"javaeye\" 共"</span><span> + hits.length() + </span><span class="string">"个结果"</span><span>);   </span></li><li class="alt"><span>  </span></li><li class=""><span>    }   </span></li><li class="alt"><span>  </span></li><li class=""><span>}  </span></li></ol></div></div><script><![CDATA[ender_code();]]&gt;</script><br />运行结果： <br /><div class="code_title">代码</div><div class="code_div"><div class="dp-highlighter"><div class="bar"></div><ol class="dp-j"><li class="alt"><span><span>查找</span><span class="string">"lighter"</span><span> 共</span><span class="number">2</span><span>个结果   </span></span></li><li class=""><span>查找</span><span class="string">"javaeye"</span><span> 共</span><span class="number">1</span><span>个结果  </span></li></ol></div></div><script><![CDATA[ender_code();]]&gt;</script><p></p><p>很久没有看lucene了，这两三天又复习了一下,上一些代码都是前几个月写的，只是改动了一些字符串和包名显示。转载时请说明，文章来自:http://lighter.javaeye.com。 <br />如有什么错误的地方，恳请指出，谢谢。<br /></p><div class="quote_title">wen19851025 写道</div><div class="quote_div">//测试字符串 <br />StringReader sr = new StringReader("lighter javaeye com"); <br />//生成TokenStream对象 <br />TokenStream ts = aAnalyzer.tokenStream("name", sr); <br />请问:以上的解析是按什么来解析,为什么他会自动的按空格或者","进行字符分割,再一个当SR里输入是中文字符时,他将会对每个字进行分割,请问这是为什么,同时这功能的实现又意为着什么呢.....????</div><p><br />StandardAnalyzer是lucene中内置的"标准分析器",可以做如下功能: <br />1、对原有句子按照空格进行了分词 <br />2、所有的大写字母都可以能转换为小写的字母 <br />3、可以去掉一些没有用处的单词，例如"is","the","are"等单词，也删除了所有的标点 <br />同时也可以对中文进行分词(效果不好),现在有很多的中文分词包可以采用 <br /><br /></p><img src ="http://www.blogjava.net/rendong/aggbug/89849.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/rendong/" target="_blank">rendong</a> 2006-12-25 11:26 <a href="http://www.blogjava.net/rendong/archive/2006/12/25/89849.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Lucene</title><link>http://www.blogjava.net/rendong/archive/2006/12/25/89846.html</link><dc:creator>rendong</dc:creator><author>rendong</author><pubDate>Mon, 25 Dec 2006 03:05:00 GMT</pubDate><guid>http://www.blogjava.net/rendong/archive/2006/12/25/89846.html</guid><wfw:comment>http://www.blogjava.net/rendong/comments/89846.html</wfw:comment><comments>http://www.blogjava.net/rendong/archive/2006/12/25/89846.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/rendong/comments/commentRss/89846.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/rendong/services/trackbacks/89846.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: Lucene是apache组织的一个用java实现全文搜索引擎的开源项目。其功能非常的强大，api也很简单。总得来说用Lucene来进行建立和搜索和操作数据库是差不多的(有点像)，Document可以看作是数据库的一行记录，Field可以看作是数据库的字段。用lucene实现搜索引擎就像用JDBC实现连接数据库一样简单。																									...&nbsp;&nbsp;<a href='http://www.blogjava.net/rendong/archive/2006/12/25/89846.html'>阅读全文</a><img src ="http://www.blogjava.net/rendong/aggbug/89846.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/rendong/" target="_blank">rendong</a> 2006-12-25 11:05 <a href="http://www.blogjava.net/rendong/archive/2006/12/25/89846.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>tag大全</title><link>http://www.blogjava.net/rendong/archive/2006/12/24/89775.html</link><dc:creator>rendong</dc:creator><author>rendong</author><pubDate>Sun, 24 Dec 2006 12:07:00 GMT</pubDate><guid>http://www.blogjava.net/rendong/archive/2006/12/24/89775.html</guid><wfw:comment>http://www.blogjava.net/rendong/comments/89775.html</wfw:comment><comments>http://www.blogjava.net/rendong/archive/2006/12/24/89775.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/rendong/comments/commentRss/89775.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/rendong/services/trackbacks/89775.html</trackback:ping><description><![CDATA[
		<p>昨天，由于程序中需要用到一个WEB TABS样式的页面，GOOGLE之，才发现这东西真不好找。 <br />displaytag 这个东西计划要支持TABS，为啥只是计划，虽然它的表格样式看起来不错。 <br />webtabs 这个嘛，样子太丑了，拿不出手。 <br />prizetags 这个可控制的属性太少了，不爽。 <br />Ditchnet JSP Tabs Taglib 就是它，为什么没有地方能下载到，哪位好心人能告诉我？</p>
		<p>
		</p>
		<p>下面的这个站收录的比较全，收藏之。 <br />东西还真不少，慢慢学啦，可惜没有我想要的 <br />【Java开源 Jsp标签库】 <br />Posted by E_wsq 2006-3-29 9:32:00 <br />displytag</p>
		<p>
		</p>
		<p>与Struts结合使用最出名的一个tag主要是显示表格数据很漂亮、完善。 <br />500)this.style.width=500;” border=0&gt; <a href="http://displaytag.sourceforge.net/"><font color="#003399">http://displaytag.sourceforge.net/</font></a><br />cewolf tag</p>
		<p>
		</p>
		<p>用来在web上显示复杂图形报表的一个jsp tag。 <br />500)this.style.width=500;” border=0&gt; <a href="http://cewolf.sourceforge.net/"><font color="#003399">http://cewolf.sourceforge.net/</font></a><br />Loading Tab</p>
		<p>
		</p>
		<p>当一个复杂的操作可以加载比较长的时间时，用这个tag。 <br />500)this.style.width=500;” border=0&gt; <a href="http://www.mycgiserver.com/~eboudrant/#taglibs"><font color="#003399">http://www.mycgiserver.com/~eboudrant/#taglibs</font></a><br />DbForms</p>
		<p>
		</p>
		<p>DbForms!它是一个基于 Java (Servlet,JSP/Taglib)的快速应用程序开发环境，可以帮助开发人员快速建造基于Web的数据库应用程序。 <br />500)this.style.width=500;” border=0&gt; <a href="http://jdbforms.sourceforge.net/"><font color="#003399">http://jdbforms.sourceforge.net/</font></a><br />Jakarta Taglibs</p>
		<p>
		</p>
		<p>Jakarta Taglibs 是为JSP定制标签库和相关的项目提供的一个开源仓库，如 TagLibraryValidator类，和对页面生成工具的扩展来支持标签库。Jakarta Taglibs 也包括了对JSP Standard Tag Library (JSTL)的参考实现。这个实现基于项目标准。目前，在Jakarta Taglibs 中没有其它标签库代表了Java Community Process (JCP) 标准。 <br />500)this.style.width=500;” border=0&gt; <a href="http://jakarta.apache.org/taglibs/index.html"><font color="#003399">http://jakarta.apache.org/taglibs/index.html</font></a><br />EasyLDAP</p>
		<div>LDAP标签库为JSP程序员和Web页面设计者提供了最容易的方法来执行任意的LDAP操作。 <br />500)this.style.width=500;” border=0&gt; <a href="http://easyldap.sourceforge.net/"><font color="#003399">http://easyldap.sourceforge.net/</font></a><br />WebJMX</div>
		<div>
				<div>WebJMX标签库项目可以控制你的JMX 接口。WebJMX 这个标签库项目的目的是生成一个JSP标签库，可以让有技巧的JSP开发人员为JMX生成一个可定制的、规范的、基于Web的界面。 <br />500)this.style.width=500;” border=0&gt; <a href="http://webjmx.sourceforge.net/"><font color="#003399">http://webjmx.sourceforge.net/</font></a><br />JPivot</div>
				<div>
						<div>JPivot - 是一个JSP 自定制的标签库，可以绘制一个OLAP表格和图表。用户可以执行典型的OLAP导航，如下钻，切片和方块。它使用Mondrian 作为其OLAP服务器。 <br />500)this.style.width=500;” border=0&gt; <a href="http://jpivot.sourceforge.net/"><font color="#003399">http://jpivot.sourceforge.net/</font></a><br />JSP Tree Tag</div>
						<div>
								<div>JSP Tree Tag是一个显示树型结构jsp标签，它只把需要显示的部分送到客户浏览器。 <br />500)this.style.width=500;” border=0&gt; <a href="http://www.jenkov.dk/projects/treetag/treetag.jsp"><font color="#003399">http://www.jenkov.dk/projects/treetag/treetag.jsp</font></a><br />Google Tag Library</div>
								<div>
										<div>该标记库和 Google 有关。使用该标记库，利用 Google 为你的网站提供网站查询，并且可以直接在你的网页里面显示搜查的结果。 <br />500)this.style.width=500;” border=0&gt; <a href="http://google-taglib.sourceforge.net/"><font color="#003399">http://google-taglib.sourceforge.net/</font></a><br />TableTag</div>
										<div>
												<div>TableTag是类似于DataGrid的Jsp标签库。通过java.util.List填充数据。 <br />500)this.style.width=500;” border=0&gt; <a href="http://tabletag.sourceforge.net/"><font color="#003399">http://tabletag.sourceforge.net/</font></a><br />uitags</div>
												<div>
														<div>uitags利用这个开源自定义标签可以很容易开发出一个友好的用户界面。 <br />500)this.style.width=500;” border=0&gt; <a href="http://uitags.sourceforge.net/"><font color="#003399">http://uitags.sourceforge.net/</font></a><br />ValueList</div>
														<div>
																<div>ValueList利用这个标签可以进行数据过滤，排序，分页。而且界面挺漂亮的。 <br />500)this.style.width=500;” border=0&gt; <a href="http://valuelist.sourceforge.net/"><font color="#003399">http://valuelist.sourceforge.net/</font></a><br />JCE taglib</div>
																<div>
																		<div>JCE taglib把JCE（Java Cryptographic Extensions）包装成TagLib并且包含了EL函数。使用这个标签能够为jsp应用程序加强安全性。 <br />500)this.style.width=500;” border=0&gt; <a href="http://jcetaglib.sourceforge.net/"><font color="#003399">http://jcetaglib.sourceforge.net/</font></a><br />Prize Tags</div>
																		<div>
																				<div>Prize Tags是一个集许多功能于一身的Jsp标签库。其中最受欢迎的Tree Tag，这个Tag可以为不同节点指定不同的图标，而且可以服务端可以监控客户端节点的展开，关闭，选中与未选中等事件。除了Tree Tag还有日历Tag,Icon Tag,Alternate Tag ,Template Tag 等其它的功能。 <br />500)this.style.width=500;” border=0&gt; <a href="http://www.jenkov.com/prizetags/introduction.tmpl"><font color="#003399">http://www.jenkov.com/prizetags/introduction.tmpl</font></a><br />Struts-Layout</div>
																				<div>
																						<div>Struts-Layout是一个用在Struts的标签库.这个强大的标签库可以用来显示面板(panels),输入框,表格,treeviews,sortable lists,datagrids,popups,日历等.使用这些标签可以不用写HTML代码,甚至可以不用懂得HTML.这个项目还提供一个Eclipse下的插件Kiwi帮助使用Struts和Struts-Layout来开发Jsp页面.以下是一张例图: <br />500)this.style.width=500;” border=0&gt;</div>
																						<div>
																								<div>500)this.style.width=500;” border=0&gt; <a href="http://struts.application-servers.com/"><font color="#003399">http://struts.application-servers.com/</font></a><br />JImageTaglib</div>
																								<div>
																										<div>JImageTaglib是一个用在J2EE Web应用程序的Java标签库.它用来在服务端生成与处理图片然后再反馈到JSP页面.可以过滤(filtering)图片,调整图片文件大小,生成条形码等. <br />500)this.style.width=500;” border=0&gt; <a href="http://jimagetaglib.sourceforge.net/"><font color="#003399">http://jimagetaglib.sourceforge.net/</font></a><br />uitags</div>
																										<div>
																												<div>uitags是一个开源的JSP custom-tag库.它让开发友好的用户界面变得简单. <br />500)this.style.width=500;” border=0&gt; <a href="http://uitags.sourceforge.net/"><font color="#003399">http://uitags.sourceforge.net/</font></a><br />AJAX Tags</div>
																												<div>
																														<div>AJAX Tag是一组Jsp标签,用来简化AJAX(Asynchronous JavaScript and XML )技术在JSP页面中的使用. <br />500)this.style.width=500;” border=0&gt; <a href="http://ajaxtags.sourceforge.net/"><font color="#003399">http://ajaxtags.sourceforge.net/</font></a><br />Struts AjaxTags</div>
																														<div>
																																<div>这个AjaxTags是在现有的Struts HTML标签库上添加对AJAX (Asynchronous Javascript+XML)技术的支持。这样就可以为现有的基于Struts HTML标签库的应用程序添加AJAX功能而不用破坏现存的代码并且开发者不需要了解AJAX是怎样工作的。 <br />500)this.style.width=500;” border=0&gt; <a href="http://struts.sourceforge.net/ajaxtags/"><font color="#003399">http://struts.sourceforge.net/ajaxtags/</font></a><br />AWTaglib</div>
																																<div>
																																		<div>AWTaglib是一个Jsp标签可用于创建网格(grid)控件.它还提供一些额外的功能可以把网格中的数据导出为XLS,PDF和CSV(利用JasperReports来实现)并能与Struts框架相结合. <br />500)this.style.width=500;” border=0&gt; <a href="http://awtaglib.sourceforge.net/"><font color="#003399">http://awtaglib.sourceforge.net/</font></a><br />eXtremeTable</div>
																																		<div>
																																				<div>eXtremeTable是一个可扩展的用于以表格的形式来显示数据的一组JSP标签库.</div>
																																		</div>
																																</div>
																														</div>
																												</div>
																										</div>
																								</div>
																						</div>
																				</div>
																		</div>
																</div>
														</div>
												</div>
										</div>
								</div>
						</div>
				</div>
		</div>
<img src ="http://www.blogjava.net/rendong/aggbug/89775.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/rendong/" target="_blank">rendong</a> 2006-12-24 20:07 <a href="http://www.blogjava.net/rendong/archive/2006/12/24/89775.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>apache负载</title><link>http://www.blogjava.net/rendong/archive/2006/12/02/85047.html</link><dc:creator>rendong</dc:creator><author>rendong</author><pubDate>Sat, 02 Dec 2006 08:58:00 GMT</pubDate><guid>http://www.blogjava.net/rendong/archive/2006/12/02/85047.html</guid><wfw:comment>http://www.blogjava.net/rendong/comments/85047.html</wfw:comment><comments>http://www.blogjava.net/rendong/archive/2006/12/02/85047.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/rendong/comments/commentRss/85047.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/rendong/services/trackbacks/85047.html</trackback:ping><description><![CDATA[
		<ol>
				<li class="">
						<span>LoadModule proxy_module modules/mod_proxy.so   </span>
				</li>
				<li class="alt">
						<span>LoadModule proxy_ajp_module modules/mod_proxy_ajp.so   </span>
				</li>
				<li class="">
						<span>LoadModule proxy_balancer_module modules/mod_proxy_balancer.so   </span>
				</li>
				<li class="alt">
						<span>  </span>
				</li>
				<li class="">
						<span>ProxyPass /admin balancer://tomcatcluster/admin </span>
						<span class="attribute">lbmethod</span>
						<span>=</span>
						<span class="attribute-value">byrequests</span>
						<span> </span>
						<span class="attribute">stickysession</span>
						<span>=</span>
						<span class="attribute-value">JSESSIONID</span>
						<span> </span>
						<span class="attribute">nofailover</span>
						<span>=</span>
						<span class="attribute-value">Off</span>
						<span> </span>
						<span class="attribute">timeout</span>
						<span>=</span>
						<span class="attribute-value">5</span>
						<span> </span>
						<span class="attribute">maxattempts</span>
						<span>=</span>
						<span class="attribute-value">3</span>
						<span>  </span>
				</li>
				<li class="alt">
						<span>ProxyPassReverse /admin balancer://tomcatcluster/admin   </span>
				</li>
				<li class="">
						<span>  </span>
				</li>
				<li class="alt">
						<span>
						</span>
						<span class="tag">&lt;</span>
						<span class="tag-name">Proxy</span>
						<span> balancer://tomcatcluster</span>
						<span class="tag">&gt;</span>
						<span>  </span>
				</li>
				<li class="">
						<span>BalancerMember ajp://localhost:8009 </span>
						<span class="attribute">route</span>
						<span>=</span>
						<span class="attribute-value">web1</span>
						<span>  </span>
				</li>
				<li class="alt">
						<span>BalancerMember ajp://localhost:10009 </span>
						<span class="attribute">smax</span>
						<span>=</span>
						<span class="attribute-value">10</span>
						<span> </span>
						<span class="attribute">route</span>
						<span>=</span>
						<span class="attribute-value">web2</span>
						<span>  </span>
				</li>
				<li class="">
						<span>BalancerMember ajp://localhost:11009 </span>
						<span class="attribute">route</span>
						<span>=</span>
						<span class="attribute-value">web3</span>
						<span>  </span>
				</li>
				<li class="alt">
						<span>BalancerMember ajp://localhost:12009 </span>
						<span class="attribute">smax</span>
						<span>=</span>
						<span class="attribute-value">10</span>
						<span> </span>
						<span class="attribute">route</span>
						<span>=</span>
						<span class="attribute-value">web4</span>
						<span>  </span>
				</li>
				<li class="">
						<span>
						</span>
						<span class="tag">&lt;/</span>
						<span class="tag-name">Proxy</span>
						<span class="tag">&gt;</span>
						<span>  </span>
				</li>
		</ol>
<img src ="http://www.blogjava.net/rendong/aggbug/85047.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/rendong/" target="_blank">rendong</a> 2006-12-02 16:58 <a href="http://www.blogjava.net/rendong/archive/2006/12/02/85047.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Taglib 原理和实现之支持El表达式</title><link>http://www.blogjava.net/rendong/archive/2006/09/15/69961.html</link><dc:creator>rendong</dc:creator><author>rendong</author><pubDate>Fri, 15 Sep 2006 10:14:00 GMT</pubDate><guid>http://www.blogjava.net/rendong/archive/2006/09/15/69961.html</guid><wfw:comment>http://www.blogjava.net/rendong/comments/69961.html</wfw:comment><comments>http://www.blogjava.net/rendong/archive/2006/09/15/69961.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/rendong/comments/commentRss/69961.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/rendong/services/trackbacks/69961.html</trackback:ping><description><![CDATA[
		<table class="center_tdbgall" style="WORD-BREAK: break-all" cellspacing="0" cellpadding="0" width="760" align="center" border="0">
				<tbody>
						<tr valign="center" align="middle">
								<td class="main_ArticleTitle" style="WORD-BREAK: break-all" colspan="2" height="50">Taglib 原理和实现之支持El表达式</td>
						</tr>
						<tr valign="center" align="middle">
								<td class="main_ArticleSubheading" style="WORD-BREAK: break-all" colspan="2" height="20">
								</td>
						</tr>
						<tr class="left_tdbgall" align="middle">
								<td colspan="2">作者：佚名    文章来源：未知    点击数：
<script language="javascript" src="/Article/GetHits.asp?ArticleID=3773"></script>
 107    更新时间：2006-2-17</td>
						</tr>
						<tr>
								<td class="main_tdbg_760" id="fontzoom" style="WORD-BREAK: break-all" valign="top" colspan="2" height="300">　　1.先看这么一个例子<br /><br /><table bordercolor="#ffcc66" width="90%" align="center" bgcolor="#c8c7b9" border="1"><tbody><tr><td>＜%@ page contentType="text/html; charset=gb2312" language="java"%＞<br />＜%@ taglib uri="/WEB-INF/tlds/c.tld" prefix="c"%＞<br />＜!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"＞<br />＜html＞<br />＜body＞<br />＜%<br />String tut = "tutorial";<br />request.setAttribute("tut",tut);<br />%＞<br />The String in request is :<br />＜c:out value="${tut}"/＞ <br />＜/body＞<br />＜/html＞</td></tr></tbody></table><br />　　2.如何支持el表达式<br /><br />　　在路径org.apache.taglibs.standard.lang.support下，有个叫 ExpressionEvaluatorManager.evaluate 的方法，当el表达式作为入参时，调用这个方法，在tag内即可自动把el表达式转化。例如，你想tag的value字段支持el表达式，那么只需在set方法里如下调用：<br /><br /><table bordercolor="#ffcc66" width="90%" align="center" bgcolor="#c8c7b9" border="1"><tbody><tr><td>public void setValue(Object value)throws JspException<br />{<br />this.value = ExpressionEvaluatorManager.evaluate(<br />"value", value.toString(), Object.class, this, pageContext); <br />}</td></tr></tbody></table><br />　　ExpressionEvaluatorManager.evaluate有四个参数。第一个表示tag的名字，在取el表达式出错时使用。一般和<a class="channel_keylink" href="http://www.bc-cn.net/Article/Search.asp?Field=Title&amp;ClassID=&amp;keyword=%CA%F4%D0%D4">属性</a>名字相同。第二个要求字符串，通常简单调用输入对象的toString方法。第三个是类，通常用Object.class。第四个用this即可，第五个是pageContext变量。<br /><br />　　通常不用对这个方法思考太多。只需改改<a class="channel_keylink" href="http://www.bc-cn.net/Article/Search.asp?Field=Title&amp;ClassID=&amp;keyword=%CA%F4%D0%D4">属性</a>名字，其他照搬即可。<br /><br />　　注意：当你的tag<a class="channel_keylink" href="http://www.bc-cn.net/Article/Search.asp?Field=Title&amp;ClassID=&amp;keyword=%CA%F4%D0%D4">属性</a>支持el表达式时，你必须把它声明为Object对象。如上述的value，应该声明为：<br /><br /><table bordercolor="#ffcc66" width="90%" align="center" bgcolor="#c8c7b9" border="1"><tbody><tr><td>private Object value = null;</td></tr></tbody></table><br />　　3.实例：让OutputTag支持El表达式<br /><br /><table bordercolor="#ffcc66" width="90%" align="center" bgcolor="#c8c7b9" border="1"><tbody><tr><td>package diegoyun;<br /><br />import javax.servlet.jsp.JspException;<br />import javax.servlet.jsp.JspWriter;<br />import javax.servlet.jsp.tagext.TagSupport;<br /><br />import org.apache.taglibs.standard.lang.support.ExpressionEvaluatorManager;<br /><br />public class NewOutputTag extends TagSupport<br />{<br />private Object name = null;<br /><br />public void setName(Object name) throws JspException<br />{<br />this.name = ExpressionEvaluatorManager.evaluate(<br />"name", name.toString(), Object.class, this, pageContext);<br />}<br />public int doStartTag() throws JspException{<br />try<br />{<br />JspWriter out = pageContext.getOut();<br />out.print("Hello! " + name);<br />}<br />catch (Exception e)<br />{ <br />throw new JspException(e);<br />}<br />return EVAL_PAGE;<br /><br /><br />}<br />}</td></tr></tbody></table><br />　　在diego.tld里添加声明<br /><br /><table bordercolor="#ffcc66" width="90%" align="center" bgcolor="#c8c7b9" border="1"><tbody><tr><td>＜!--NewOutputTag--＞<br />＜tag＞<br />＜name＞newout＜/name＞<br />＜tag-class＞diegoyun.NewOutputTag＜/tag-class＞<br />＜body-content＞empty＜/body-content＞<br />＜attribute＞<br />＜name＞name＜/name＞<br />＜required＞false＜/required＞<br />＜rtexprvalue＞true＜/rtexprvalue＞<br />＜/attribute＞<br />＜/tag＞</td></tr></tbody></table><br />　　编写jsp测试<br /><br /><table bordercolor="#ffcc66" width="90%" align="center" bgcolor="#c8c7b9" border="1"><tbody><tr><td>＜%@ page language="java" %＞<br />＜%@ taglib uri="/WEB-INF/tlds/diego.tld" prefix="diego"%＞<br />＜html＞<br />＜body bgcolor="#FFFFFF"＞<br />＜%<br />String s = "diego";<br />request.setAttribute("name",s);<br />%＞<br />Test El supported tag:<br />＜br＞<br />＜diego:newout name="${name}"/＞<br /><br />＜/body＞<br />＜/html＞</td></tr></tbody></table><br />　　可以看到页面输出为：<br /><br />　　Test El supported tag: <br />　　Hello! diego </td>
						</tr>
				</tbody>
		</table>
<img src ="http://www.blogjava.net/rendong/aggbug/69961.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/rendong/" target="_blank">rendong</a> 2006-09-15 18:14 <a href="http://www.blogjava.net/rendong/archive/2006/09/15/69961.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>解析 Java 类和对象的初始化过程(zhuang张 国建 (guojian.zhang@gmail.com), 软件工程师, 北京高伟达西南分软)</title><link>http://www.blogjava.net/rendong/archive/2006/09/09/68732.html</link><dc:creator>rendong</dc:creator><author>rendong</author><pubDate>Sat, 09 Sep 2006 09:36:00 GMT</pubDate><guid>http://www.blogjava.net/rendong/archive/2006/09/09/68732.html</guid><wfw:comment>http://www.blogjava.net/rendong/comments/68732.html</wfw:comment><comments>http://www.blogjava.net/rendong/archive/2006/09/09/68732.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/rendong/comments/commentRss/68732.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/rendong/services/trackbacks/68732.html</trackback:ping><description><![CDATA[
		<blockquote>
				<p>类的初始化和对象初始化是 JVM 管理的类型生命周期中非常重要的两个环节，Google 了一遍网络，有关类装载机制的文章倒是不少，然而类初始化和对象初始化的文章并不多，特别是从字节码和 JVM 层次来分析的文章更是鲜有所见。</p>
				<p>本文主要对类和对象初始化全过程进行分析，通过一个实际问题引入，将源代码转换成 JVM 字节码后，对 JVM 执行过程的关键点进行全面解析，并在文中穿插入了相关 JVM 规范和 JVM 的部分内部理论知识，以理论与实际结合的方式介绍对象初始化和类初始化之间的协作以及可能存在的冲突问题。</p>
		</blockquote>
		<!--START RESERVED FOR FUTURE USE INCLUDE FILES-->
		<!-- include java script once we verify teams wants to use this and it will work on dbcs and cyrillic characters -->
		<!--END RESERVED FOR FUTURE USE INCLUDE FILES-->
		<p>
				<a name="N10045">
						<span class="atitle">
								<font face="Arial" size="4">问题引入</font>
						</span>
				</a>
		</p>
		<p>近日我在调试一个枚举类型的解析器程序，该解析器是将数据库内一万多条枚举代码装载到缓存中，为了实现快速定位枚举代码和具体枚举类别的所有枚举元素，该类在装载枚举代码的同时对其采取两种策略建立内存索引。由于该类是一个公共服务类，在程序各个层面都会使用到它，因此我将它实现为一个单例类。这个类在我调整类实例化语句位置之前运行正常，但当我把该类实例化语句调整到静态初始化语句之前时，我的程序不再为我工作了。</p>
		<p>下面是经过我简化后的示例代码：</p>
		<br />
		<a name="N10051">
				<b>［清单一］</b>
		</a>
		<br />
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<code>
												<pre class="section">package com.ccb.framework.enums;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

public class CachingEnumResolver {
	//单态实例　一切问题皆由此行引起
	private static final CachingEnumResolver SINGLE_ENUM_RESOLVER = new
	CachingEnumResolver();
	/*MSGCODE-&gt;Category内存索引*/
	private static Map CODE_MAP_CACHE;
	static {
		CODE_MAP_CACHE = new HashMap();
		//为了说明问题,我在这里初始化一条数据
		CODE_MAP_CACHE.put("0","北京市");
	}
	
	//private, for single instance
	private CachingEnumResolver() {
		//初始化加载数据  引起问题，该方法也要负点责任
		initEnums();
	}
	
	/**
	 * 初始化所有的枚举类型
	 */
	public static void initEnums() {		
		// ~~~~~~~~~问题从这里开始暴露 ~~~~~~~~~~~//
		if (null == CODE_MAP_CACHE) {
			System.out.println("CODE_MAP_CACHE为空,问题在这里开始暴露.");
			CODE_MAP_CACHE = new HashMap();
		}
		CODE_MAP_CACHE.put("1", "北京市");
		CODE_MAP_CACHE.put("2", "云南省");
		
		//..... other code...
	}
	
	public Map getCache() {
		return Collections.unmodifiableMap(CODE_MAP_CACHE);
	}
	
	/**
	 * 获取单态实例
	 * 
	 * @return
	 */
	public static CachingEnumResolver getInstance() {
		return SINGLE_ENUM_RESOLVER;
	}
	
	public static void main(String[] args) {
		System.out.println(CachingEnumResolver.getInstance().getCache());
	}
}
</pre>
										</code>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>想必大家看了上面的代码后会感觉有些茫然，这个类看起来没有问题啊，这的确属于典型的饿汉式单态模式啊，怎么会有问题呢？</p>
		<p>是的，他看起来的确没有问题，可是如果将他 run 起来时，其结果是他不会为你正确 work。运行该类，它的执行结果是：</p>
		<br />
		<a name="N10061">
				<b>［清单二］</b>
		</a>
		<br />
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<code>
												<pre class="section">CODE_MAP_CACHE为空,问题在这里开始暴露.
{0=北京市}
</pre>
										</code>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>我的程序怎么会这样？为什么在 initEnum() 方法里 CODE_MAP_CACHE 为空？为什么我输出的 CODE_MAP_CACHE 内容只有一个元素，其它两个元素呢？？？？！！</p>
		<p>看到这里，如果是你在调试该程序，你此刻一定觉得很奇怪，难道是我的 Jvm 有问题吗？非也！如果不是，那我的程序是怎么了？这绝对不是我想要的结果。可事实上无论怎么修改 initEnum() 方法都无济于事，起码我最初是一定不会怀疑到问题可能出在创建 CachingEnumResolver 实例这一环节上。正是因为我太相信我创建 CachingEnumResolver 实例的方法，加之对 Java 类初始化与对象实例化底层原理理解有所偏差，使我为此付出了三、四个小时--约半个工作日的大好青春。</p>
		<p>那么问题究竟出在哪里呢？为什么会出现这样的怪事呢？在解决这个问题之前，先让我们来了解一下JVM的类和对象初始化的底层机制。</p>
		<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%" />
										<br />
										<img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" />
								</td>
						</tr>
				</tbody>
		</table>
		<table class="no-print" cellspacing="0" cellpadding="0" align="right">
				<tbody>
						<tr align="right">
								<td>
										<img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" />
										<br />
										<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-lo-clobj-init/index.html#main">
																				<b>
																						<font color="#996699">回页首</font>
																				</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<a name="N10074">
						<span class="atitle">
								<font face="Arial" size="4">类的生命周期</font>
						</span>
				</a>
		</p>
		<br />
		<font face="Arial" size="4">
				<img height="243" alt="" src="http://www-128.ibm.com/developerworks/cn/java/j-lo-clobj-init/images/image002.jpg" width="511" border="0" />
		</font>
		<br />
		<p>上图展示的是类生命周期流向；在本文里，我只打算谈谈类的"初始化"以及"对象实例化"两个阶段。</p>
		<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%" />
										<br />
										<img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" />
								</td>
						</tr>
				</tbody>
		</table>
		<table class="no-print" cellspacing="0" cellpadding="0" align="right">
				<tbody>
						<tr align="right">
								<td>
										<img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" />
										<br />
										<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-lo-clobj-init/index.html#main">
																				<b>
																						<font color="#996699">回页首</font>
																				</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<a name="N1008E">
						<span class="atitle">
								<font face="Arial" size="4">类初始化</font>
						</span>
				</a>
		</p>
		<p>类"初始化"阶段，它是一个类或接口被首次使用的前阶段中的最后一项工作，本阶段负责为类变量赋予正确的初始值。</p>
		<p>Java 编译器把所有的类变量初始化语句和类型的静态初始化器通通收集到 &lt;clinit&gt; 方法内，该方法只能被 Jvm 调用，专门承担初始化工作。</p>
		<p>除接口以外，初始化一个类之前必须保证其直接超类已被初始化，并且该初始化过程是由 Jvm 保证线程安全的。另外，并非所有的类都会拥有一个 &lt;clinit&gt;() 方法，在以下条件中该类不会拥有 &lt;clinit&gt;() 方法：</p>
		<ul>
				<li>该类既没有声明任何类变量，也没有静态初始化语句； 
</li>
				<li>该类声明了类变量，但没有明确使用类变量初始化语句或静态初始化语句初始化； 
</li>
				<li>该类仅包含静态 final 变量的类变量初始化语句，并且类变量初始化语句是编译时常量表达式。 </li>
		</ul>
		<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%" />
										<br />
										<img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" />
								</td>
						</tr>
				</tbody>
		</table>
		<table class="no-print" cellspacing="0" cellpadding="0" align="right">
				<tbody>
						<tr align="right">
								<td>
										<img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" />
										<br />
										<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-lo-clobj-init/index.html#main">
																				<b>
																						<font color="#996699">回页首</font>
																				</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<a name="N100A9">
						<span class="atitle">
								<font face="Arial" size="4">对象初始化</font>
						</span>
				</a>
		</p>
		<p>在类被装载、连接和初始化，这个类就随时都可能使用了。对象实例化和初始化是就是对象生命的起始阶段的活动，在这里我们主要讨论对象的初始化工作的相关特点。</p>
		<p>Java 编译器在编译每个类时都会为该类至少生成一个实例初始化方法--即 "&lt;init&gt;()" 方法。此方法与源代码中的每个构造方法相对应，如果类没有明确地声明任何构造方法，编译器则为该类生成一个默认的无参构造方法，这个默认的构造器仅仅调用父类的无参构造器，与此同时也会生成一个与默认构造方法对应的 "&lt;init&gt;()" 方法.</p>
		<p>通常来说，&lt;init&gt;() 方法内包括的代码内容大概为：调用另一个 &lt;init&gt;() 方法；对实例变量初始化；与其对应的构造方法内的代码。</p>
		<p>如果构造方法是明确地从调用同一个类中的另一个构造方法开始，那它对应的 &lt;init&gt;() 方法体内包括的内容为：一个对本类的 &lt;init&gt;() 方法的调用；对应用构造方法内的所有字节码。</p>
		<p>如果构造方法不是通过调用自身类的其它构造方法开始，并且该对象不是 Object 对象，那 &lt;init&gt;() 法内则包括的内容为：一个对父类 &lt;init&gt;() 方法的调用；对实例变量初始化方法的字节码；最后是对应构造子的方法体字节码。</p>
		<p>如果这个类是 Object，那么它的 &lt;init&gt;() 方法则不包括对父类 &lt;init&gt;() 方法的调用。</p>
		<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%" />
										<br />
										<img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" />
								</td>
						</tr>
				</tbody>
		</table>
		<table class="no-print" cellspacing="0" cellpadding="0" align="right">
				<tbody>
						<tr align="right">
								<td>
										<img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" />
										<br />
										<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-lo-clobj-init/index.html#main">
																				<b>
																						<font color="#996699">回页首</font>
																				</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<a name="N100C1">
						<span class="atitle">
								<font face="Arial" size="4">类的初始化时机</font>
						</span>
				</a>
		</p>
		<p>本文到目前为止，我们已经大概有了解到了类生命周期中都经历了哪些阶段，但这个类的生命周期的开始阶段--类装载又是在什么时候被触发呢？类又是何时被初始化的呢？让我们带着这三个疑问继续去寻找答案。</p>
		<p>Java 虚拟机规范为类的初始化时机做了严格定义："initialize on first active use"--" 在首次主动使用时初始化"。这个规则直接影响着类装载、连接和初始化类的机制--因为在类型被初始化之前它必须已经被连接，然而在连接之前又必须保证它已经被装载了。</p>
		<p>在与初始化时机相关的类装载时机问题上，Java 虚拟机规范并没有对其做严格的定义，这就使得 JVM 在实现上可以根据自己的特点提供采用不同的装载策略。我们可以思考一下 Jboss AOP 框架的实现原理，它就是在对你的 class 文件装载环节做了手脚--插入了 AOP 的相关拦截字节码，这使得它可以对程序员做到完全透明化，哪怕你用 new 操作符创建出的对象实例也一样能被 AOP 框架拦截--与之相对应的 Spring AOP，你必须通过他的 BeanFactory 获得被 AOP 代理过的受管对象，当然 Jboss AOP 的缺点也很明显--他是和 JBOSS 服务器绑定很紧密的，你不能很轻松的移植到其它服务器上。嗯~……，说到这里有些跑题了，要知道 AOP 实现策略足可以写一本厚厚的书了，嘿嘿，就此打住。</p>
		<p>说了这么多，类的初始化时机就是在"在首次主动使用时"，那么，哪些情形下才符合首次主动使用的要求呢？</p>
		<p>首次主动使用的情形：</p>
		<ul>
				<li>创建某个类的新实例时--new、反射、克隆或反序列化； 
</li>
				<li>调用某个类的静态方法时； 
</li>
				<li>使用某个类或接口的静态字段或对该字段赋值时（final字段除外）； 
</li>
				<li>调用Java的某些反射方法时 
</li>
				<li>初始化某个类的子类时 
</li>
				<li>在虚拟机启动时某个含有main()方法的那个启动类。 </li>
		</ul>
		<p>除了以上几种情形以外，所有其它使用JAVA类型的方式都是被动使用的，他们不会导致类的初始化。</p>
		<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%" />
										<br />
										<img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" />
								</td>
						</tr>
				</tbody>
		</table>
		<table class="no-print" cellspacing="0" cellpadding="0" align="right">
				<tbody>
						<tr align="right">
								<td>
										<img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" />
										<br />
										<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-lo-clobj-init/index.html#main">
																				<b>
																						<font color="#996699">回页首</font>
																				</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<a name="N100EE">
						<span class="atitle">
								<font face="Arial" size="4">我的问题究竟出在哪里</font>
						</span>
				</a>
		</p>
		<p>好了，了解了JVM的类初始化与对象初始化机制后，我们就有了理论基础，也就可以理性的去分析问题了。</p>
		<p>下面让我们来看看前面[清单一]的JAVA源代码反组译出的字节码：</p>
		<br />
		<a name="N100FA">
				<b>［清单三］</b>
		</a>
		<br />
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<code>
												<pre class="section">public class com.ccb.framework.enums.CachingEnumResolver extends
java.lang.Object{
static {};
  Code:
   0:	new	#2; //class CachingEnumResolver
   3:	dup
   4:	invokespecial	#14; //Method "&lt;init&gt;":()V  ①
   7:	putstatic	#16; //Field
   SINGLE_ENUM_RESOLVER:Lcom/ccb/framework/enums/CachingEnumResolver;
   10:	new	#18; //class HashMap              ②
   13:	dup
   14:	invokespecial	#19; //Method java/util/HashMap."&lt;init&gt;":()V
   17:	putstatic	#21; //Field CODE_MAP_CACHE:Ljava/util/Map;  
   20:	getstatic	#21; //Field CODE_MAP_CACHE:Ljava/util/Map;  
   23:	ldc	#23; //String 0
   25:	ldc	#25; //String 北京市
   27:	invokeinterface	#31,  3; //InterfaceMethod
   java/util/Map.put:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;   ③
   32:	pop
   33:	return

private com.ccb.framework.enums.CachingEnumResolver();
  Code:
   0:	aload_0
   1:	invokespecial	#34; //Method java/lang/Object."&lt;init&gt;":()V
   4:	invokestatic	#37; //Method initEnums:()V                  ④
   7:	return

public static void initEnums();
  Code:
   0:	getstatic	#21; //Field CODE_MAP_CACHE:Ljava/util/Map;    ⑤
   3:	ifnonnull	24
   6:	getstatic	#44; //Field java/lang/System.out:Ljava/io/PrintStream;
   9:	ldc	#46; //String CODE_MAP_CACHE为空,问题在这里开始暴露.
   11:	invokevirtual	#52; //Method
   java/io/PrintStream.println:(Ljava/lang/String;)V
   14:	new	#18; //class HashMap
   17:	dup
   18:	invokespecial	#19; //Method java/util/HashMap."&lt;init&gt;":()V      ⑥
   21:	putstatic	#21; //Field CODE_MAP_CACHE:Ljava/util/Map;
   24:	getstatic	#21; //Field CODE_MAP_CACHE:Ljava/util/Map;
   27:	ldc	#54; //String 1
   29:	ldc	#25; //String 北京市
   31:	invokeinterface	#31,  3; //InterfaceMethod
   java/util/Map.put:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;   ⑦
   36:	pop
   37:	getstatic	#21; //Field CODE_MAP_CACHE:Ljava/util/Map;
   40:	ldc	#56; //String 2
   42:	ldc	#58; //String 云南省
   44:	invokeinterface	#31,  3; //InterfaceMethod
   java/util/Map.put:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;    ⑧
   49:	pop
   50:	return

public java.util.Map getCache();
  Code:
   0:	getstatic	#21; //Field CODE_MAP_CACHE:Ljava/util/Map;
   3:	invokestatic	#66; //Method
   java/util/Collections.unmodifiableMap:(Ljava/util/Map;)Ljava/util/Map;
   6:	areturn

public static com.ccb.framework.enums.CachingEnumResolver getInstance();
  Code:
   0:	getstatic	#16; 
   //Field SINGLE_ENUM_RESOLVER:Lcom/ccb/framework/enums/CachingEnumResolver;   ⑨
   3:	areturn
}
</pre>
										</code>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>如果上面［清单一］显示，清单内容是在 JDK1.4 环境下的字节码内容，可能这份清单对于很大部分兄弟来说确实没有多少吸引力，因为这些 JVM 指令确实不像源代码那样漂亮易懂。但它的的确确是查找和定位问题最直接的办法，我们想要的答案就在这份 JVM 指令清单里。</p>
		<p>现在，让我们对该类从类初始化到对象实例初始化全过程分析[清单一]中的代码执行轨迹。</p>
		<p>如前面所述，类初始化是在类真正可用时的最后一项前阶工作，该阶段负责对所有类正确的初始化值，此项工作是线程安全的，JVM会保证多线程同步。</p>
		<p>第１步：调用类初始化方法 CachingEnumResolver.&lt;clinit&gt;()，该方法对外界是不可见的，换句话说是 JVM 内部专用方法，&lt;clinit&gt;() 内包括了 CachingEnumResolver 内所有的具有指定初始值的类变量的初始化语句。要注意的是并非每个类都具有该方法，具体的内容在前面已有叙述。</p>
		<p>第２步：进入 &lt;clinit&gt;() 方法内，让我们看字节码中的 "①" 行，该行与其上面两行组合起来代表 new 一个 CachingEnumResolver 对象实例，而该代码行本身是指调用 CachingEnumResolver 类的 &lt;init&gt;（）方法。每一个 Java 类都具有一个 &lt;init&gt;() 方法，该方法是 Java 编译器在编译时生成的，对外界不可见，&lt;init&gt;() 方法内包括了所有具有指定初始化值的实例变量初始化语句和java类的构造方法内的所有语句。对象在实例化时，均通过该方法进行初始化。然而到此步，一个潜在的问题已经在此埋伏好，就等着你来犯了。</p>
		<p>第３步：让我们顺着执行顺序向下看，"④" 行，该行所在方法就是该类的构造器，该方法先调用父类的构造器 &lt;init&gt;() 对父对象进行初始化，然后调用 CachingEnumResolver.initEnum() 方法加载数据。</p>
		<p>第４步："⑤" 行，该行获取 "CODE_MAP_CACHE" 字段值，其运行时该字段值为 null。注意，问题已经开始显现了。（作为程序员的你一定是希望该字段已经被初始化过了，而事实上它还没有被初始化）。通过判断，由于该字段为 NULL，因此程序将继续执行到 "⑥" 行，将该字段实例化为 HashMap()。</p>
		<p>第５步：在 "⑦"、"⑧" 行，其功能就是为 "CODE_MAP_CACHE" 字段填入两条数据。</p>
		<p>第６步：退出对象初始化方法 &lt;init&gt;()，将生成的对象实例初始化给类字段 "SINGLE_ENUM_RESOLVER"。（注意，此刻该对象实例内的类变量还未初始化完全，刚才由 &lt;init&gt;() 调用 initEnum() 方法赋值的类变量 "CODE_MAP_CACHE" 是 &lt;clinit&gt;() 方法还未初始化字段，它还将在后面的类初始化过程再次被覆盖）。</p>
		<p>第７步：继续执行 &lt;clinit&gt;（）方法内的后继代码，"②" 行，该行对 "CODE_MAP_CACHE" 字段实例化为 HashMap 实例（注意：在对象实例化时已经对该字段赋值过了，现在又重新赋值为另一个实例，此刻，"CODE_MAP_CACHE"变量所引用的实例的类变量值被覆盖，到此我们的疑问已经有了答案）。</p>
		<p>第8步：类初始化完毕，同时该单态类的实例化工作也完成。</p>
		<p>通过对上面的字节码执行过程分析，或许你已经清楚了解到导致错误的深层原因了，也或许你可能早已被上面的分析过程给弄得晕头转向了，不过也没折，虽然我也可以从源代码的角度来阐述问题，但这样不够深度，同时也会有仅为个人观点、不足可信之嫌。</p>
		<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%" />
										<br />
										<img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" />
								</td>
						</tr>
				</tbody>
		</table>
		<table class="no-print" cellspacing="0" cellpadding="0" align="right">
				<tbody>
						<tr align="right">
								<td>
										<img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" />
										<br />
										<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-lo-clobj-init/index.html#main">
																				<b>
																						<font color="#996699">回页首</font>
																				</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<a name="N10128">
						<span class="atitle">
								<font face="Arial" size="4">如何解决</font>
						</span>
				</a>
		</p>
		<p>要解决上面代码所存在的问题很简单，那就是将 "SINGLE_ENUM_RESOLVER" 变量的初始化赋值语句转移到 getInstance() 方法中去即可。换句话说就是要避免在类还未初始化完成时从内部实例化该类或在初始化过程中引用还未初始化的字段。</p>
		<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%" />
										<br />
										<img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" />
								</td>
						</tr>
				</tbody>
		</table>
		<table class="no-print" cellspacing="0" cellpadding="0" align="right">
				<tbody>
						<tr align="right">
								<td>
										<img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" />
										<br />
										<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-lo-clobj-init/index.html#main">
																				<b>
																						<font color="#996699">回页首</font>
																				</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<a name="N10131">
						<span class="atitle">
								<font face="Arial" size="4">写在最后</font>
						</span>
				</a>
		</p>
		<p>静下浮燥之心，仔细思量自己是否真的掌握了本文主题所引出的知识，如果您觉得您已经完全或基本掌握了，那么很好，在最后，我将前面的代码稍做下修改，请思考下面两组程序是否同样会存在问题呢？</p>
		<br />
		<a name="N1013A">
				<b>程序一</b>
		</a>
		<br />
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<code>
												<pre class="section">	public class CachingEnumResolver {
	public  static Map CODE_MAP_CACHE;
	static {
		CODE_MAP_CACHE = new HashMap();
		//为了说明问题,我在这里初始化一条数据
		CODE_MAP_CACHE.put("0","北京市");
		initEnums();
	}
	</pre>
										</code>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<a name="N10144">
				<b>程序二</b>
		</a>
		<br />
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<code>
												<pre class="section">	public class CachingEnumResolver {
		private static final CachingEnumResolver SINGLE_ENUM_RESOLVER;
		public  static Map CODE_MAP_CACHE;
		static {
			CODE_MAP_CACHE = new HashMap();
			//为了说明问题,我在这里初始化一条数据
			CODE_MAP_CACHE.put("0","北京市");
			SINGLE_ENUM_RESOLVER = new CachingEnumResolver();
			initEnums();
		}
	</pre>
										</code>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>最后，一点关于 JAVA 群体的感言：时下正是各种开源框架盛行时期，Spring 更是大行其道，吸引着一大批 JEE 开发者的眼球（我也是 fans 中的一员）。然而，让我们仔细观察一下--以 Spring 群体为例，在那么多的 Spring fans 当中，有多少人去研究过 Spring 源代码？又有多少人对 Spring 设计思想有真正深入了解呢？当然，我是没有资格以这样的口吻来说事的，我只是想表明一个观点--学东西一定要"正本清源"。</p>
		<p>献上此文，谨以共勉。</p>
<img src ="http://www.blogjava.net/rendong/aggbug/68732.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/rendong/" target="_blank">rendong</a> 2006-09-09 17:36 <a href="http://www.blogjava.net/rendong/archive/2006/09/09/68732.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>精确截取字符串（转载http://www.blogjava.net/rosen/archive/2005/08/12/9949.aspx） </title><link>http://www.blogjava.net/rendong/archive/2006/08/05/61888.html</link><dc:creator>rendong</dc:creator><author>rendong</author><pubDate>Fri, 04 Aug 2006 16:56:00 GMT</pubDate><guid>http://www.blogjava.net/rendong/archive/2006/08/05/61888.html</guid><wfw:comment>http://www.blogjava.net/rendong/comments/61888.html</wfw:comment><comments>http://www.blogjava.net/rendong/archive/2006/08/05/61888.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/rendong/comments/commentRss/61888.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/rendong/services/trackbacks/61888.html</trackback:ping><description><![CDATA[
		<div class="post">
				<div class="postTitle">
						<a id="viewpost1_TitleUrl" href="/rosen/archive/2005/08/12/9949.html">
								<font color="#000080">精确截取字符串（转载）</font>
						</a>
				</div>
				<div class="postText">开发中经常遇到，字符串过长，无法完全显示的问题<br /><br />这时候就需要截取我们所需要的长度，后面显示省略号或其他字符。<br /><br />由于中文字符占两个字节，而英文字符占用一个字节，所以，单纯地判断字符数，效果往往不尽如人意<br /><br />下面的方法通过判断字符的类型来进行截取，效果还算可以：）<br /><br /><br />如果大家有其他的解决方法欢迎贴出来，共同学习：）<br />**********************************************************************<br />private String str;<br />private int counterOfDoubleByte;<br />private byte b[];<br />/**<br />* 设置需要被限制长度的字符串<br />* @param str 需要被限制长度的字符串<br />*/<br />public void setLimitLengthString(String str){<br />  this.str = str;<br />}<br />/**<br />* @param len 需要显示的长度(&lt;font color="red"&gt;注意：长度是以byte为单位的，一个汉字是2个byte&lt;/font&gt;)<br />* @param symbol 用于表示省略的信息的字符，如“...”,“&gt;&gt;&gt;”等。<br />* @return 返回处理后的字符串<br />*/<br />public String getLimitLengthString(int len, String symbol) throws UnsupportedEncodingException {<br />  counterOfDoubleByte = 0;<br />  b = str.getBytes("GBK");<br />  if(b.length &lt;= len)<br />    return str;<br />  for(int i = 0; i &lt; len; i++){<br />    if(b[i] &lt; 0)<br />      counterOfDoubleByte++;<br />  }<br /><br />  if(counterOfDoubleByte % 2 == 0)<br />    return new String(b, 0, len, "GBK") + symbol;<br />  else<br />    return new String(b, 0, len - 1, "GBK") + symbol;<br />}<br /><br /><br /><font color="#ff0000" size="2"><strong>本文转贴自网友：focus2004 的文章</strong></font></div>
				<div class="postfoot">posted on 2005-08-12 15:39 <a href="/rosen/"><font color="#000080">Rosen</font></a> 阅读(963) <a href="/rosen/archive/2005/08/12/9949.aspx#Post"><font color="#000080">评论(6)</font></a>  <a href="/rosen/admin/EditPosts.aspx?postid=9949"><font color="#000080">编辑</font></a> <a href="/rosen/AddToFavorite.aspx?id=9949"><font color="#000080">收藏</font></a><a title="功能强大的网络收藏夹，一秒钟操作就可以轻松实现保存带来的价值、分享带来的快乐" href="javascript:d=document;t=d.selection?(d.selection.type!='None'?d.selection.createRange().text:''):(d.getSelection?d.getSelection():'');void(keyit=window.open('http://www.365key.com/storeit.aspx?t='+escape(d.title)+'&amp;u='+escape(d.location.href)+'&amp;c='+escape(t),'keyit','scrollbars=no,width=475,height=575,left=75,top=20,status=no,resizable=yes'));keyit.focus();"><font color="#000080">收藏至365Key</font></a> 所属分类: <a href="/rosen/category/2689.html"><font color="#000080">J2EE 技术</font></a></div>
		</div>
		<img height="1" src="/rosen/aggbug/9949.html?webview=1" width="1" />
		<!--
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/">
<rdf:Description
rdf:about="http://www.blogjava.net/rosen/archive/2005/08/12/9949.html"
dc:identifier="http://www.blogjava.net/rosen/archive/2005/08/12/9949.html"
dc:title="精确截取字符串（转载）"
trackback:ping="http://www.blogjava.net/rosen/services/trackbacks/9949.aspx" />
</rdf:RDF>
-->
		<a name="评论">
				<br />
				<div id="comments">
						<h3>评论</h3>
						<div class="post">
								<div class="postTitle">
										<a title="permalink: re: 精确截取字符串（转载）" href="/rosen/archive/2005/08/12/9949.aspx#10469">
												<font color="#000080">#</font>
										</a> <a name="10469"></a>re: 精确截取字符串（转载） <span>2005-08-19 08:27 </span><a id="Comments1_CommentList_ctl00_NameLink" href="/ivan" target="_blank"><font color="#000080">ivan</font></a></div>
								<div class="postText">if(b&lt;0) 编译会出错。  <a onclick="return SetReplyAuhor(&quot;ivan&quot;)" href="/rosen/archive/2005/08/12/9949.aspx#post"><font color="#000080">回复</font></a><br /><a id="Comments1_CommentList_ctl00_DeleteLink" href="javascript:__doPostBack('Comments1$CommentList$ctl00$DeleteLink','')"></a>  <a id="Comments1_CommentList_ctl00_EditLink"></a></div>
						</div>
						<br />
						<div class="post">
								<div class="postTitle">
										<a title="permalink: re: 精确截取字符串（转载）" href="/rosen/archive/2005/08/12/9949.aspx#10548">
												<font color="#000080">#</font>
										</a> <a name="10548"></a>re: 精确截取字符串（转载） <span>2005-08-19 20:39 </span><a id="Comments1_CommentList_ctl01_NameLink" href="/rosen" target="_blank"><font color="#000080">Rosen</font></a></div>
								<div class="postText">马上修改一下代码，去年转贴的时候一直忘记修改了。 <br />是 if(b[i] &lt; 0)，谢谢 ivan 指正。  <a onclick="return SetReplyAuhor(&quot;Rosen&quot;)" href="/rosen/archive/2005/08/12/9949.aspx#post"><font color="#000080">回复</font></a><br /><a id="Comments1_CommentList_ctl01_DeleteLink" href="javascript:__doPostBack('Comments1$CommentList$ctl01$DeleteLink','')"></a>  <a id="Comments1_CommentList_ctl01_EditLink"></a></div>
						</div>
						<br />
						<div class="post">
								<div class="postTitle">
										<a title="permalink: re: 精确截取字符串（转载）" href="/rosen/archive/2005/08/12/9949.aspx#29339">
												<font color="#000080">#</font>
										</a> <a name="29339"></a>re: 精确截取字符串（转载） <span>2006-01-28 18:24 </span><a id="Comments1_CommentList_ctl02_NameLink" href="http://fstdg2003@21cn.com/" target="_blank"><font color="#000080">tdg</font></a></div>
								<div class="postText">大作拜读，有一点愚见，特抛砖引玉： <br />1。字符串除了可以基于byte[]操作外，还可以基于char[]操作。看老大你的意图是想截取字符串的前几个字符然后加上省略符号最后输出而已，完全不必考虑用byte[]数组操作啊 。而且好像开发中更注重语义上的第几个字符而不是你说的这种情况哦。 <br />2。以下是拙作，请斧正： <br />/** <br />* 字符串截取函数 <br />* @param str String 要处理的字符串 <br />* @param length int 需要显示的长度 <br />* @param symbol String 用于表示省略的信息的字符，如“...”,“&gt;&gt;&gt;”等 <br />* @return String 返回处理后的字符串 <br />* @throws UnsupportedEncodingException <br />*/ <br />public String getLimitLengthString(String str, int length, String symbol) throws <br />UnsupportedEncodingException { <br />assert str != null; <br />assert length &gt; 0; <br />assert symbol != null; <br />//如果字符串的位数小于等于要截取的位数，附加上表示省略的信息的字符串后返回 <br />if (str.length() &lt;= length) { <br />return str + symbol; <br />//从零开始，截取length个字符，附加上表示省略的信息的字符串后返回 <br />} else { <br />str = new String(str.getBytes("GBK")); <br />char[] charArray = str.toCharArray(); <br />char[] charArrayDesc = new char[length]; <br />System.arraycopy(charArray, 0, charArrayDesc, 0, length); <br />return new String(charArrayDesc) + symbol; <br />} <br />}  <a onclick="return SetReplyAuhor(&quot;tdg&quot;)" href="/rosen/archive/2005/08/12/9949.aspx#post"><font color="#000080">回复</font></a><br /><a id="Comments1_CommentList_ctl02_DeleteLink" href="javascript:__doPostBack('Comments1$CommentList$ctl02$DeleteLink','')"></a>  <a id="Comments1_CommentList_ctl02_EditLink"></a></div>
						</div>
						<br />
						<div class="post">
								<div class="postTitle">
										<a title="permalink: re: 精确截取字符串（转载）" href="/rosen/archive/2005/08/12/9949.aspx#29343">
												<font color="#000080">#</font>
										</a> <a name="29343"></a>re: 精确截取字符串（转载） <span>2006-01-28 20:03 </span><a id="Comments1_CommentList_ctl03_NameLink" href="/rosen/" target="_blank"><font color="#000080">Rosen</font></a></div>
								<div class="postText">呵呵 tdg 兄很认真喔，谈不上斧正。主要是这个问题，用 char 处理，如果是字母或者数字，实际上截取出来的会比汉字少占用一半的空间，所以截取出来后，还是不能对齐。而实际上 char 数组中，不管是字母、数字还是汉字，它们都只代表一个单元。但是 byte 则不同，字母、数字只占用一个字节，而汉字占用两个字节（都是GBK编码）。  <a onclick="return SetReplyAuhor(&quot;Rosen&quot;)" href="/rosen/archive/2005/08/12/9949.aspx#post"><font color="#000080">回复</font></a><br /><a id="Comments1_CommentList_ctl03_DeleteLink" href="javascript:__doPostBack('Comments1$CommentList$ctl03$DeleteLink','')"></a>  <a id="Comments1_CommentList_ctl03_EditLink"></a></div>
						</div>
						<br />
						<div class="post">
								<div class="postTitle">
										<a title="permalink: re: 精确截取字符串（转载）" href="/rosen/archive/2005/08/12/9949.aspx#37159">
												<font color="#000080">#</font>
										</a> <a name="37159"></a>re: 精确截取字符串（转载） <span>2006-03-24 10:49 </span><a id="Comments1_CommentList_ctl04_NameLink" target="_blank">istarliu</a></div>
								<div class="postText">您好！ <br />你的文章让我受益不少，有个问题想向您确认一下： <br />在代码中 <br />b = str.getBytes("GBK"); <br />if(b.length &lt;= len) <br />return str; <br />for(int i = 0; i &lt; len; i++){ <br />if(b[i] &lt; 0) <br />counterOfDoubleByte++; <br />} <br />是不是如果只要是中文汉字，在b[i]对应的值都是小于0的， <br />也就是说，在汉字代表的两个字节中，这两个汉字分别转化为整数值时，是不是一定小于0，但值范围不能小于-127的。做过测试，不能肯定。：） <br /></div>
						</div>
				</div>
		</a>
<img src ="http://www.blogjava.net/rendong/aggbug/61888.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/rendong/" target="_blank">rendong</a> 2006-08-05 00:56 <a href="http://www.blogjava.net/rendong/archive/2006/08/05/61888.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>运用ajax制作提示页面 (原地址http://www.blogjava.net/rickhunter/articles/61648.html)</title><link>http://www.blogjava.net/rendong/archive/2006/08/05/61885.html</link><dc:creator>rendong</dc:creator><author>rendong</author><pubDate>Fri, 04 Aug 2006 16:48:00 GMT</pubDate><guid>http://www.blogjava.net/rendong/archive/2006/08/05/61885.html</guid><wfw:comment>http://www.blogjava.net/rendong/comments/61885.html</wfw:comment><comments>http://www.blogjava.net/rendong/archive/2006/08/05/61885.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/rendong/comments/commentRss/61885.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/rendong/services/trackbacks/61885.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: ajax确实是个很好的技术，在提高客户的体验度上面能做很多以前不能做或者不好做的事情。出现提示页面就是一个很好的示例。需要制作提示页面的地方其实很多，但以前大多是要求用户点击相关信息进入详细信息页面察看，然后返回，再点击其他的信息察看详细信息页面。这样就降低了客户的体验度，在没有ajax的时候，我们是劝导客户只能这么做。现在用ajax就可以很轻松的解决这个问题了。我的平台仍然是struts+spr...&nbsp;&nbsp;<a href='http://www.blogjava.net/rendong/archive/2006/08/05/61885.html'>阅读全文</a><img src ="http://www.blogjava.net/rendong/aggbug/61885.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/rendong/" target="_blank">rendong</a> 2006-08-05 00:48 <a href="http://www.blogjava.net/rendong/archive/2006/08/05/61885.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>动态组合公式的运算（转载）</title><link>http://www.blogjava.net/rendong/archive/2006/08/01/61191.html</link><dc:creator>rendong</dc:creator><author>rendong</author><pubDate>Tue, 01 Aug 2006 05:56:00 GMT</pubDate><guid>http://www.blogjava.net/rendong/archive/2006/08/01/61191.html</guid><wfw:comment>http://www.blogjava.net/rendong/comments/61191.html</wfw:comment><comments>http://www.blogjava.net/rendong/archive/2006/08/01/61191.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/rendong/comments/commentRss/61191.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/rendong/services/trackbacks/61191.html</trackback:ping><description><![CDATA[组合公式为 "((r1c1+r1c2)/r1c3)"<br />        1. 组合的公式是一个字符串.<br />        2. 字符串元素包括"rc0123456789+-*/()"<br />        3. r1c1=7,r1c2=2,r1c3=4<br />        4. 求组合成的公式的值是多少.
<p>解决思路:<br />        1.对公式进行字符合法性检查.<br />        2.对公式中出现的变量进行取值替换.<br />        3.检查语句的合法性.(组合规则)<br />        4.检查运算的合法性.(除数为0的情况)<br />        5.求值.</p><p><br />具体解决方法:<br />        str1 = "(r1c1+r1c2)/r1c3)"<br />        str1 = replace(str1," ","")  '去除公式中间出现的空格<br />        1.  对公式进行字符合法性检查.</p><p>              bool1 = getDataOperationCharCheck(str1)</p><p>              if bool1 = false then<br />                      Response.write "公式不合法.有异常字符出现.&lt;br&gt;"<br />              else<br />                      Response.write "公式检查通过!&lt;br&gt;"<br />              end if</p><p>        2.对公式中出现的变量进行取值替换</p><p>                RCstr = getdataoperation(str1)</p><p>        3.检查语句的合法性.(组合规则)</p><p>                bool2 = getDataOperationSyntaxCheck(RCstr)</p><p>                      if bool2 = false then<br />                                 Response.write "运算公式语法不合法,有组合异常字符出现.&lt;br&gt;"<br />                      else<br />                                 Response.Write "运算公式语法检查通过!&lt;br&gt;"<br />                      end if</p><p>          4.检查运算的合法性.(除数为0的情况)</p><p>                   bool3 = getDataOperationRunCheck(RCstr)</p><p>                        if bool3 = false then<br />                                Response.write "运算公式运算不合法,有除数为0出现.&lt;br&gt;"<br />                        else<br />                                Response.write "运算公式运算合法性检查通过!&lt;br&gt;"  <br />                        end if</p><p>           5.求值.<br />  <br />                        intValue = getRunSearch(RCstr)</p><p>           6.运算结果: </p><p>                        (((7*1.0)+(2*1.0))/(4*1.0)) = 2.25</p><p>'1.=============================================================</p><p>'对原始公式进行字符检查,是否有不合法的字符</p><p>function getDataOperationCharCheck(datastr)<br />       datastr = replace(datastr," ","")<br />        sumchar = "rc0123456789+-*/()"<br />        strlen = len(datastr)<br />        strreturn = true<br />        for i = 1 to strlen<br />                singlechar = mid(datastr,i,1)<br />                if instr(sumchar,singlechar) = 0 then<br />                        strreturn = false<br />                        exit for<br />                end if<br />        next<br />        getDataOperationCharCheck = strreturn<br />end function</p><p>'2.==============================================================</p><p>'对原始计划公式进行取值替换.<br />'实现方法:对原始字符串进行单个字符检查,<br />'在出现 "+-*/()" 中的任何一个字符时,对已经组合生成的变量进行取值.<br />'在取到值的同时对其进行 double 转换 ,实现方法是 (intvalue * 1.0)</p><p>function getdataoperation(datastr)<br />        strlen = len(datastr)  <br />        sunstr = ""<br />        strID = ""<br />        intvalue = 0<br />        singlechar = ""<br />        operationstr="()+-*/"<br />        for i=1 to strlen<br />                'Response.write mid(datastr,i,1) &amp; "&lt;br&gt;" <br />                singlechar = mid(datastr,i,1)<br />                if instr(operationstr,singlechar) &gt; 0 then<br />   <br />                        if strID &lt;&gt; "" then<br />                                intvalue = getValue(strID)<br />                                sunstr = sunstr &amp; "(" &amp; intvalue &amp; "*1.0)"   '(1)     <br />                                intvalue = 0<br />                                strID = ""          <br />                        end if<br />                        sunstr = sunstr &amp; singlechar<br />                        singlechar = ""<br />                else<br />                        strID = strID &amp; singlechar<br />                end if     <br />        next<br />        getdataoperation = sunstr<br />end function</p><p>'变量取值函数.<br />'下列数据是为测试使用.<br />'<br />function getValue(strRC)<br />        select case strRC<br />                case "r1c1"<br />                        getValue = 2<br />                case "r1c2"<br />                        getValue = 7<br />                case "r1c3"<br />                        getValue = 2<br />               end select<br />end function</p><p>'3.==============================================================</p><p>'对公式进行语法合法性检查.<br />'eg.检查 (),--,++,**,//,(/,*) 等 是否成对出现.<br />'检查是否有</p><p><br />function getDataOperationSyntaxCheck(datastr)<br />        strreturn = true<br />        datastr = replace(datastr," ","")  '去除所有的空格<br />        strlen = len(datastr)<br />        num1 = 0                           '记录 括号的 个数  采用 有 (  加1, 有 ) 减1<br />        upsinglechar = ""                  '相对本次的字符的上一个字符<br />        singlechar = ""<br />        operationstr1="()+-*/"<br />        operationstr2="(+-*/"              '相对 在 (  左边出现的运的符号是正确的.<br /> <br />        for i = 1 to strlen<br />                singlechar = mid(datastr,i,1)<br />                select case singlechar<br />                        case "("<br />                                num1 = num1 + 1<br />                                if upsinglechar &lt;&gt; "" then<br />                                        if instr(operationstr2,upsinglechar) = 0 then   '在左括号的左边若不为空,必需出现 "(+-*/" 中的一个.<br />                                                strreturn = false<br />                                                exit for<br />                                        end if<br />                                end if<br />   <br />                        case ")"<br />                                num1 = num1 - 1<br />                                if num1 &lt; 0 then<br />                                        strreturn = false<br />                                        exit for<br />                                end if<br />                                if instr(operationstr2,upsinglechar) &gt; 0 then       '在右括号的左边若不为空,必需不能出现 "(+-*/" 中的一个<br />                                        strreturn = false<br />                                        exit for<br />                                end if<br />   <br />                        case "+"<br />                                if instr(operationstr2,upsinglechar) &gt; 0 then       '在加号的左边若不空,必需不能出现 "(+-*/" 中的一个<br />                                        strreturn = false<br />                                        exit for<br />                                end if<br />   <br />                        case "-"<br />                                 if instr(operationstr2,upsinglechar) &gt; 0 then      '在减号的左边若不空,必需不能出现 "(+-*/" 中的一个<br />                                        strreturn = false<br />                                        exit for<br />                                end if<br />                        case "*"<br />                                 if instr(operationstr2,upsinglechar) &gt; 0 then      '在乘号的左边若不空,必需不能出现 "(+-*/" 中的一个<br />                                        strreturn = false<br />                                        exit for<br />                                end if<br />                        case "/"<br />                                 if instr(operationstr2,upsinglechar) &gt; 0 then      '在除号的左边若不空,必需不能出现 "(+-*/" 中的一个<br />                                        strreturn = false<br />                                        exit for<br />                                end if<br />                end select<br />                upsinglechar = singlechar  <br />                singlechar = ""<br />        next<br />        getDataOperationSyntaxCheck = strreturn<br />end function</p><p>'4.==============================================================<br /> <br />'对组合公式进行运算合法性的检查<br />'首选查找有没有 "/0"出现.<br />'其次查找类似 "/(****)" = /0 出现</p><p>function getDataOperationRunCheck(datastr)<br />        strreturn = true<br />        if instr(datastr,"/")&gt;0 then<br />                if instr(datastr,"/0") &gt; 0 then    <br />                        strreturn = false<br />                else</p><p>                        '对/ 后面出现的()内的数据进行运算,取值是否会有0出现.<br />                        '首先计算 "/" 出现的次数<br />                        '再次判断 "/(" 出现的次数<br />                        '若 "/(" 出现的次为0 则安全.</p><p>                        intnum1 = getInstrNum(datastr,"/")<br />                        intnum2 = getInstrNum(datastr,"/(")<br />                        if intnum2 &gt; 0 then<br />                                for j = intnum2 to 1 step -1<br />                                        intpoint1 = getInstrPoint(datastr,"/(",j)<br />                                        if intpoint1 &gt; 0 then<br />                                                sumpoint = getRunCheck(datastr,intpoint1)<br />                                                if  CDbl(sumpoint) = CDbl(0) then<br />                                                        strreturn = false<br />                                                        exit for<br />                                                end if<br />                                        end if<br />                                next     <br />                        end if<br />                end if  <br />        end if<br />        getDataOperationRunCheck= strreturn <br />end function</p><p><br />'检查字符运行的合法性.<br />'主要是对/()出现的字公式进行计算是否会等于0</p><p>function getRunCheck(datastr,intpoint1)<br />        strlen = len(datastr)<br />        intpoint = intpoint1 + 1<br />        intnum = 0<br />        singlechar = ""<br />        rcsearch = ""<br />        intreturn = 0<br />        for m = intpoint to strlen<br />                singlechar = mid(datastr,m,1)<br />                if singlechar = "(" then<br />                        intnum = intnum + 1<br />                end if<br />                if singlechar = ")" then<br />                        intnum = intnum - 1<br />                end if<br />                rcsearch = rcsearch &amp; singlechar<br />                if intnum = 0 then    <br />                        intreturn  = getRunSearch(rcsearch)<br />                        exit for<br />                end if   <br />        next<br />        getRunCheck = intreturn<br />end function</p><p>'5.==============================================================</p><p>'求值.<br />function getRunSearch(strrcsearch)<br />         sql = "select " &amp; strrcsearch &amp; " as rcvalue "<br />         Set rs = conn.execute(sql)<br />         'Response.write "&lt;br&gt;" &amp; strrcsearch &amp; "=" &amp; rs("rcvalue") &amp; "&lt;br&gt;"<br />         getRunSearch = rs("rcvalue")<br />end function</p><p>'公共函数==============================================================</p><p>'返回substr  在 str1 中出现的次数</p><p>function getInstrNum(str1,substr)<br />        strlen = len(str1)<br />        substrlen = len(substr)<br />        singlechar = ""<br />        intreturn = 0<br />        for i = 1 to strlen<br />                singlechar = mid(str1,i,substrlen)<br />                if singlechar = substr then<br />                        intreturn = intreturn + 1<br />                end if<br />        next<br />        getInstrNum = intreturn<br />end function</p><p>'返回substr 在 str1 中 第 intnum 次出现的位置<br />'intnum 必需是大于0的正整数</p><p>function getInstrPoint(str1,substr,intnum)<br />        intreturn = 0<br />        strlen = len(str1)<br />        substrlen = len(substr)<br />        singlechar = ""<br />        intcount = 0<br />        for i = 1 to strlen<br />                singlechar = mid(str1,i,substrlen)<br />                if singlechar = substr then<br />                        intcount = intcount + 1<br />                end if<br />                if intcount = intnum then<br />                        intreturn = i<br />                        exit for<br />                end if<br />        next<br />        getInstrPoint = intreturn<br />end function</p><img src ="http://www.blogjava.net/rendong/aggbug/61191.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/rendong/" target="_blank">rendong</a> 2006-08-01 13:56 <a href="http://www.blogjava.net/rendong/archive/2006/08/01/61191.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>不需要应用服务器的J2EE(摘自:http://www.matrix.org.cn/resource/article/44/44250_J2ee+Application+Server.html)</title><link>http://www.blogjava.net/rendong/archive/2006/08/01/61137.html</link><dc:creator>rendong</dc:creator><author>rendong</author><pubDate>Tue, 01 Aug 2006 02:11:00 GMT</pubDate><guid>http://www.blogjava.net/rendong/archive/2006/08/01/61137.html</guid><wfw:comment>http://www.blogjava.net/rendong/comments/61137.html</wfw:comment><comments>http://www.blogjava.net/rendong/archive/2006/08/01/61137.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/rendong/comments/commentRss/61137.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/rendong/services/trackbacks/61137.html</trackback:ping><description><![CDATA[
		<h4>摘要:</h4>这篇文章提供了一个对J2EE的简化，展示了如何消除应用服务器的消耗和限制。特别地，这篇文章提到了：许多应用程序实际上并不需要运行应用服务器。 <br />尽管J2EE平台(应用程序服务器)及其编程模型(企业JAVA组件,简称EJB)拥有的众所周知的复杂性,但是基于J2EE的应用程序仍然在企业领域里变得非常成功.我们要感谢应用于轻量级容器的控制反转(IoC)和面向方面编程(AOP),比如Spring框架. 我们能够更简单地设计更大型的编程模型。然而，即使有了这些工具，应用服务器仍然是复杂度和消耗的一个重要瓶颈。这篇文章提供了一个对J2EE的简化，展示了如何消除应用服务器的消耗和限制。特别地，这篇文章提到了：许多应用程序实际上并不需要运行应用服务器。这样，J2EE应用组件将会变得：<br />·        开发更容易：不再需要EJB运行代码；<br />·        更简单： 继承不需要EJB类或接口；<br />·        测试更容易：你的应用程序及测试能在你的开发环境（IDE）中直接运行；<br />·        更少的资源消耗：你只需要你的对象，不需要应用服务器，更不需要应用服务器的对象；<br />·        安装更容易：没有运行应用服务专门的安装软件， 没有加载额外的XML文件；<br />·        维护更容易：所有的过程都更简单，因此维护也更容易。<br /><br />J2EE不必要的复杂度已经成为一个阻碍。今天，这种复杂度能够通过在这篇文章中提到的方法来避免。另外，程序还能够保留事务和安全这些典型的服务。J2EE程序从来没有比这更有趣过。<br /><br /><span style="COLOR: red">版权声明：任何获得Matrix授权的网站，转载时请务必保留以下作者信息和链接</span><br />作者:Guy Pardon；<a href="http://www.matrix.org.cn/user.shtml?username=chmei83" target="_new"><font color="#002c99">chmei83</font></a>(作者的blog:<a href="http://blog.matrix.org.cn/page/chmei83" target="_new"><font color="#002c99">http://blog.matrix.org.cn/page/chmei83</font></a>)<br />原文:<a href="http://www.onjava.com/pub/a/onjava/2006/02/08/j2ee-without-application-server.html" target="_new"><font color="#002c99">http://www.onjava.com/pub/a/onjava/2006/02/08/j2ee-without-application-server.html</font></a><br />译文:<a href="http://www.matrix.org.cn/resource/article/44/44250_J2ee+Application+Server.html" target="_new"><font color="#002c99">http://www.matrix.org.cn/resource/article/44/44250_J2ee+Application+Server.html</font></a><br />关键字:J2ee;Application;Server<br /><br /><b><span style="FONT-SIZE: 16px">例子:消息驱动Bank</span></b><br /><br />为了阐述我们的观点,我们将开发和安装一个完整的样板应用程序:一个消息驱动的银行系统. 通过（幸亏有Spring）改进的基于POJOs的编程模型和保留相同的事务，我们可以不需要EJB或者一个应用服务器来实现这个系统。在下一个部分,我们将从消息驱动架构产生到另一个架构.就像基于WEB的架构一样.图1展示我们的样本应用程序的架构.  <br /><br /><img onmouseover="javascript:imgShowTip(this);" style="DISPLAY: inline" onclick="javascript:imgClick(this);" alt="image" src="http://www.matrix.org.cn/resource/upload/forum/2006_03_03_094057_kGThDZdfyi.gif" onload="javascript:imgLoad(this);" border="0" /><br />Figure 1. Architecture of the message-driven bank<br /><br />在我们的例子中,我们将处理来自Java消息服务队列的银行定单.一张定单的处理包括通过JDBC来更新当前帐户的数据库.为了避免信息的丢失和重复,我们将使用JTA和JTA/XA事务来配合更新:处理信息和更新数据库将发生在一个原子事务里.资源部分可得到JTA/XA的更多信息.<br /><br /><b><span style="FONT-SIZE: 16px">编写应用程序代码</span></b><br /><br />该应用程序将由两个JAVA类组成: Bank(一个DAO)和MessageDrivenBank.如图2.<br /><br /><img onmouseover="javascript:imgShowTip(this);" style="DISPLAY: inline" onclick="javascript:imgClick(this);" alt="image" src="http://www.matrix.org.cn/resource/upload/forum/2006_03_03_094218_GQAjBavArJ.gif" onload="javascript:imgLoad(this);" border="0" /><br />Figure 2. Classes for the message-driven bank<br /><br />Bank是一个数据访问对象，这个对象封装数据库访问。MessageDrivenBank是一个消息驱动fa&amp;ccedil;ade并且是DAO的委托.与典型的J2EE方法不同,这个应用程序不包括EJB类.<br /><br /><b>第一步:编写Bank DAO</b><br /><br />如下, Bank源代码是很直接和简单的JDBC操作.<br /><br /><pre class="overflow">package jdbc;<br />import javax.sql.*;<br />import java.sql.*;<br />public class Bank <br />{<br />  private DataSource dataSource;<br />  public Bank() {}<br />  public void setDataSource ( DataSource dataSource )<br />  {<br />    this.dataSource = dataSource;<br />  }<br /><br /> private DataSource getDataSource()<br />  {<br />    return this.dataSource;<br />  }<br /><br />  private Connection getConnection()<br />  throws SQLException<br />  {<br />    Connection ret = null;<br />    if ( getDataSource() != null ) {<br />        ret = getDataSource().<br />              getConnection();<br />    }<br />    return ret;<br />  }<br /><br />  private void closeConnection ( Connection c )<br />  throws SQLException<br />  {<br />    if ( c != null ) c.close();<br />  }<br />    <br />  public void checkTables()<br />  throws SQLException<br />  {<br />        <br />    Connection conn = null;<br />    try {<br />      conn = getConnection();<br />      Statement s = conn.createStatement();<br />      try {<br />        s.executeQuery ( <br />        "select * from Accounts" );<br />      }<br />      catch ( SQLException ex ) {<br />        //table not there =&gt; create it<br />        s.executeUpdate ( <br />        "create table Accounts ( " +<br />        "account VARCHAR ( 20 ), " + <br />        "owner VARCHAR(300), " + <br />        "balance DECIMAL (19,0) )" );<br />        for ( int i = 0; i &lt; 100 ; i++ ){<br />          s.executeUpdate ( <br />          "insert into Accounts values ( " +<br />          "'account"+i +"' , 'owner"+i +"', 10000 )"<br />          );<br />        }<br />      }<br />      s.close();<br />      }<br />      finally {<br />        closeConnection ( conn );<br /><br />      }<br /><br />      //That concludes setup<br />  }<br /><br />    <br />  //<br />  //Business methods are below<br />  //<br /><br />  public long getBalance ( int account )<br />  throws SQLException<br />  {<br />        <br />    long res = -1;<br />    Connection conn = null;<br /><br />    try {<br />      conn = getConnection();<br />      Statement s = conn.createStatement();<br />      <br />      String query = <br />      "select balance from Accounts where account='"+<br />      "account" + account +"'";<br />      <br />      ResultSet rs = s.executeQuery ( query );<br />      if ( rs == null || !rs.next() ) <br />        throw new SQLException ( <br />        "Account not found: " + account );<br />      res = rs.getLong ( 1 );<br />      s.close();<br />    }<br />    finally {<br />        closeConnection ( conn );<br />    }<br />    return res;<br />        <br />  }<br /><br />  public void withdraw ( int account , int amount )<br />  throws Exception<br />  {<br />    Connection conn = null;<br /><br />    try {<br />      conn = getConnection();<br />      Statement s = conn.createStatement();<br /><br />      String sql = <br />      "update Accounts set balance = balance - "+<br />      amount + " where account ='account"+<br />      account+"'";<br />      <br />      s.executeUpdate ( sql );<br />      s.close();<br />    <br />    }<br />    finally {<br />        closeConnection ( conn );<br /><br />    }<br />  }<br />}</pre><br /><br />注意:代码并没有依赖EJB或任何专门的应用程序服务器.实际上,这是一个纯JAVA代码,这个JAVA代码是能在任何J2SE环境下运行的.<br />你同时应注意:我们使用了来自JDBC的DataSource接口.这意味着我们的类是独立于目前JDBC供应商提供的类. 你可能会疑惑,这怎么能与特定的数据管理系统(DBMS)提供商的JDBC实现紧密结合呢？ 这里就是Spring框架帮你实现的. 这个技术被称为依赖注入:在我们的应用程序的启动期间，通过调用setDataSource方法,Spring为我们提供了相应的datasource对象.在后面几部分我们会更多地提到Spring.如果我们在以前使用应用程序服务器,我们将不得不借助于JAVA命名绑定接口(JNDI)查询.<br /><br />除了直接使用JDBC,我们也可以使用Hibernate或者一个JDO工具来实现我们的持久层.这同样不需要任何的EJB代码.<br /><br /><b>第二步:配置BankDAO</b><br /><br />我们会将便用Spring框架来配置我们的应用程序.Spring不是必需的,但是使用Spring的好处是我们将可以简单的添加服务,如:我们JAVA对象的事务和安全.这类似于应用服务器为EJB提供的东西,只是在我们的例子中Spring将变得更容易.<br />Spring也允许我们把我们的类从目前的JDBC驱动实现中分离出来:Spring能够配置Driver(基于我们的XML配置数据)并把它提供给BankDAO对象(依赖注入原理).这样可以保持我们的JAVA代码的清淅和集中.这步的Spring配置文件如下:<br /><br /><pre class="overflow">&lt;?xml version="1.0" encoding="UTF-8"?&gt;<br /><br /><br />&lt;beans&gt;<br /><br />&lt;bean id="datasource" <br />class="com.atomikos.jdbc.nonxa.NonXADataSourceBean"&gt;<br /><br />    &lt;property name="user"&gt;<br />        &lt;value&gt;sa&lt;/value&gt;<br />    &lt;/property&gt;<br />    &lt;property name="url"&gt;<br />        &lt;value&gt;jdbc:hsqldb:SpringNonXADB<br />        &lt;/value&gt;<br />    &lt;/property&gt;<br />    &lt;property name="driverClassName"&gt;<br />        &lt;value&gt;org.hsqldb.jdbcDriver&lt;/value&gt;<br />    &lt;/property&gt;<br />    &lt;property name="poolSize"&gt;<br />        &lt;value&gt;1&lt;/value&gt;<br />    &lt;/property&gt;<br />    &lt;property name="connectionTimeout"&gt;<br />        &lt;value&gt;60&lt;/value&gt;<br />    &lt;/property&gt;<br />&lt;/bean&gt;<br /><br />&lt;bean id="bank" class="jdbc.Bank"&gt;<br />        &lt;property name="dataSource"&gt;<br />            &lt;ref bean="datasource"/&gt;<br />        &lt;/property&gt;<br />&lt;/bean&gt;<br /><br />&lt;/beans&gt;</pre><br /><br />这个XML文件包括两个对象的配置:访问数据库的DataSource和使用这个DataSource的Bank对象.下面是由Spring维护的一些基本任务.<br />·        创建应用程序(例: Bank和DataSource)需要的对象(“beans”).在XML文件中给出了这些对象的类名,并且在我们的例子中,这些对象需要有一个公共的无参数constructor (Spring也允许参数,但是配置语法上有所不同).这些对象都被命名(XML中的id属性),所以我们后面能够引用这些对象. id也允许我们的应用程序找回它需要的已配置对象.<br />·        这些对象的初始化是通过在XML文件中的properties的值实现. 在XML文件中这些properties名 应与对应的类中的setXXX方法相对应.<br />·        将对象连接在一起 :一个property可能是另一个对象(例如:在我们例子中的数据源)的引用,引用可以通过id创建.<br /><br />注意:在我们下一步中, 我们将选择配置一个JTA-enabled的数据源(由Atomikos Transactions提供,可用于企业和J2SE的JTA产品,我们将应用于我们的应用程序). 简单起见,我们将使用HypersonicSQLDB,这个DBMS不需要专门的安装步骤—它是在.jar文件里,就像JTA和Spring.<br /><br />但是,考虑到渐增的可靠性需求,强列推荐你使用XA-capable的DBMS和JDBC驱动.没有XA的支持, 在crash或重启之后你的应用程序将不能恢复原有数据. 资源部分有链接到关于事务和XA的信息和一些例子.<br /><br />作为一个练习,你可以试试从HypersonicSQLDB转换到FirstSQL,一个易安装XA-compliant的DBMS.换句话说,任何其他为企业准备的和XA-capable的DBMS也会做得很好.<br /><br /><b>第三步:测试BankDAO</b><br /><br />让我们来测试我们的代码,(使用极限编程的程序员会首先写测试,但因开始不是很清淅,所以我们直到现在才开始写测试.)下面是一个简单的单元测试.这个测试可在你的的应用程序里运行:它通过Spring获得一个BANK对象来进行测试(这在setUp方法中实现).注意:这个测试使用清楚的事务划分:每一个测试开始之前开始一个事务,每个测试结束时强制进行事务回滚.这是通过手工的方式来减少测试对数据库数据的影响.<br /><br /><pre class="overflow">package jdbc;<br />import com.atomikos.icatch.jta.UserTransactionImp;<br />import junit.framework.TestCase;<br />import java.io.FileInputStream;<br />import java.io.InputStream;<br />import org.springframework.beans.factory.xml.XmlBeanFactory;<br /><br />public class BankTest extends TestCase<br />{<br /><br />    private UserTransactionImp utx;<br /><br />    private Bank bank;<br /><br />    public BankTest ( String name )<br />    {<br />        super ( name );<br />        utx = new UserTransactionImp();<br />        <br /><br />    }<br /><br />    protected void setUp()<br />        throws Exception<br />    {<br />        //start a new transaction<br />        //so we can rollback the<br />        //effects of each test<br />        //in teardown!<br />        utx.begin();<br />        <br />        //open bean XML file<br />        InputStream is =<br />            new FileInputStream("config.xml");<br /><br />        //the factory is Spring's entry point<br />        //for retrieving the configured<br />        //objects from the XML file<br /><br />        XmlBeanFactory factory =<br />            new XmlBeanFactory(is);<br /><br />        bank = ( Bank ) factory.getBean ( "bank" );<br />        bank.checkTables();<br />    }<br /><br />    protected void tearDown()<br />        throws Exception<br />    {<br />        //rollback all DBMS effects<br />        //of testing<br />        utx.rollback();<br />    }<br /><br />    public void testBank()<br />    throws Exception<br />    {<br />        int accNo = 10;<br />        long initialBalance = bank.getBalance ( accNo );<br />        bank.withdraw ( accNo , 100 );<br />        long newBalance = bank.getBalance ( accNo );<br />        if ( ( initialBalance - newBalance ) != 100 )<br />            fail ( "Wrong balance after withdraw: " +<br />                   newBalance );<br />    }<br />    <br />}</pre><br /><br />我们将需要JTA事务来确保JMS和JDBC都是原子操作.一般来说,当经常都是两个或多个连接的时候，你应考虑一下JTA/XA。例如,在我们例子中的JMS和JDBC. Spring本身不提供JTA事务;它需要一个JTA实现或者委派一个应用服务器来处理这个事务.在这里,我们使用了一个JTA实现,这个实现可以在任何J2SE平台上工作.<br />最终架构如下面图3.白色方框代表我们的应用程序代码.<br /><br /><img onmouseover="javascript:imgShowTip(this);" style="DISPLAY: inline" onclick="javascript:imgClick(this);" alt="image" src="http://www.matrix.org.cn/resource/upload/forum/2006_03_03_094626_rjBVuIkYlm.gif" onload="javascript:imgLoad(this);" border="0" /><br />Figure 3. Architecture for the test<br /><br />如你所看到的,当我们执行我们的测试，将会发生下面的情况:<br />1.        BankTest开始一个新事务.<br />2.        然后,这个test在Spring运行期间获得一个BANK对象.这步触发Sping的创建和初始化过程.<br />3.        这个test调用BANK的方法. <br />4.        BANK调用datasource对象,通过它自己的setDataSource 方法从Spring 获取这个对像.<br />5.        这个数据源是JTA-enabled,并且与JTA实现交互来注册当前事务.<br />6.        JDBC statements和帐户数据库交互.<br />7.        当方法返回时, test调用事务回滚.<br />8.        JTA记得住datasource对象，会命令它进行回滚. <br /><br /><b>第四步:添加声明式事务管理</b><br /><br />Spring允许添加声明式事务管理来管理java对象.假设我们想确认bank总是和一个有效的事务上下文一起被调用.我们通过在实际对象的上部配置一个proxy对象. Proxy和实际对象有相同接口,所以客户通过完全相同的方式使用它. 配置Proxy wrap每个BankDAO方法到事务中.结果配置文件如下. 不要被XML的庞大吓倒—大多数内容能通过复制和粘贴到你自己的工程中再使用.<br /><br /><pre class="overflow">&lt;?xml version="1.0" encoding="UTF-8"?&gt;<br /><br />&lt;beans&gt;<br />    &lt;!-- <br />        Use a JTA-aware DataSource <br />        to access the DB transactionally <br />    --&gt;<br />    &lt;bean id="datasource" <br />        class="com.atomikos.jdbc.nonxa.NonXADataSourceBean"&gt;<br />        &lt;property name="user"&gt;<br />            &lt;value&gt;sa&lt;/value&gt;<br />        &lt;/property&gt;<br />        &lt;property name="url"&gt;<br />            &lt;value&gt;jdbc:hsqldb:SpringNonXADB&lt;/value&gt;<br />        &lt;/property&gt;<br />        &lt;property name="driverClassName"&gt;<br />            &lt;value&gt;org.hsqldb.jdbcDriver&lt;/value&gt;<br />        &lt;/property&gt;<br />        &lt;property name="poolSize"&gt;<br />            &lt;value&gt;1&lt;/value&gt;<br />        &lt;/property&gt;<br />        &lt;property name="connectionTimeout"&gt;<br />            &lt;value&gt;60&lt;/value&gt;<br />        &lt;/property&gt;<br />    &lt;/bean&gt;<br />    &lt;!-- <br />    Construct a TransactionManager, <br />    needed to configure Spring <br />    --&gt;<br />    &lt;bean id="jtaTransactionManager" <br />        class="com.atomikos.icatch.jta.UserTransactionManager"/&gt;<br />    &lt;!-- <br />    Also configure a UserTransaction, <br />    needed to configure Spring  <br />    --&gt;<br />    <br />    &lt;bean id="jtaUserTransaction" <br />        class="com.atomikos.icatch.jta.UserTransactionImp"/&gt;<br />    &lt;!-- <br />    Configure the Spring framework to use <br />    JTA transactions from the JTA provider <br />    --&gt;<br />    &lt;bean id="springTransactionManager" <br />    class="org.springframework.transaction.jta.JtaTransactionManager"&gt;<br />        &lt;property name="transactionManager"&gt;<br />            &lt;ref bean="jtaTransactionManager"/&gt;<br />        &lt;/property&gt;<br />        &lt;property name="userTransaction"&gt;<br />            &lt;ref bean="jtaUserTransaction"/&gt;<br />        &lt;/property&gt;<br />    &lt;/bean&gt;<br />    &lt;!-- Configure the bank to use our datasource --&gt;<br />    &lt;bean id="bankTarget" class="jdbc.Bank"&gt;<br />        &lt;property name="dataSource"&gt;<br />            &lt;ref bean="datasource"/&gt;<br />        &lt;/property&gt;<br />    &lt;/bean&gt;<br />    &lt;!-- <br />    Configure Spring to insert <br />    JTA transaction logic for all methods <br />    --&gt;<br />    &lt;bean id="bank" <br />    class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"&gt;<br />        &lt;property name="transactionManager"&gt;<br />            &lt;ref bean="springTransactionManager"/&gt;<br />        &lt;/property&gt;<br />        &lt;property name="target"&gt;<br />            &lt;ref bean="bankTarget"/&gt;<br />        &lt;/property&gt;<br />        &lt;property name="transactionAttributes"&gt;<br />            &lt;props&gt;<br />                &lt;prop key="*"&gt;<br />                    PROPAGATION_REQUIRED, -Exception<br />                &lt;/prop&gt;<br />            &lt;/props&gt;<br />        &lt;/property&gt;<br />    &lt;/bean&gt;<br />&lt;/beans&gt;</pre><br /><br />这个XML文件告诉Spring去配置下面的对象:<br />1.        需要通过JDBC连接的datasource.<br />2.         添加jtaTransactionManager和jtaUserTransaction用于为JTA事务的Spring配置作准备.<br />3.        springTransactionManager用于告诉Spring需要使用JTA.<br />4.         BankDAO被重命名为bankTarget (因如下解释的原因). <br />5.        bank对象被添加用于包装事务和bankTarget的所有方法.我们通过配置bank对象来使用<br /><br />springTransactionManager,这意味着所有事务将是JTA事务. 每个事务都被设置为PROPAGATION_REQUIRED,这将在任何异常下出现强制回滚.<br /><br />对这些对象包含的内容,你都可以很容易的复制和粘贴jtaTransactionManager, jtaUserTransaction, springTransactionManager到其他工程.其他的是应用程序相关的对象：datasource, bankTarget, bank. Bank对象很有趣:事实上对于bankTarget它是一个proxy;他们拥有相同的接口. Trick如下:当我们的应用程序请求Spring去配置和返回bank对象,Spring实际上将返回proxy(看起来和我们的应用程序完全相同)，随后这个proxy将为我们开始/结束事务.这样,应用程序和Bank类本身都不需要知道JTA!图4阐述了在这步我们所得到的.  <br /><br /><img onmouseover="javascript:imgShowTip(this);" style="DISPLAY: inline" onclick="javascript:imgClick(this);" alt="image" src="http://www.matrix.org.cn/resource/upload/forum/2006_03_03_094715_uKBnaIaaOG.gif" onload="javascript:imgLoad(this);" border="0" /><br />Figure 4. Architecture with declarative JTA transactions in Spring<br /><br />现在的工作如下:<br />1.        应用程序调用bank对象.这将触发Spring的初始化处理和返回proxy对象. 对应用程序而言,这个proxy行为和我们的Bank是一样的.<br />2.        当bank的一个方法被调用, 这个调用将会通过proxy进行.<br />3.        proxy使用springTransactionManager创建一个新事务.<br />4.        springTransactionManager被配置为使用JTA,因些它委派到JTA.<br />5.        调用被forward到Bank的实际对象,bankTarget.<br />6.        bankTarget使用从Spring中得到的datasource.<br />7.        datasource对事务进行注册.<br />8.        通过规则的JDBC访问数据库.<br />9.        在返回时, proxy终止事务:如果在先前的序列中没有发生异常,那么将会提交终止指令.否则,它将会被回滚.<br />10.        transaction 管理器与数据库配合进行提交和回滚. <br /><br />在这步中的测试怎样进行?我们可重用BankTest 和它清晰的事务划分:因为PROPAGATION_REQUIRED, proxy将和在BankTest中创建的事务上下文一起执行.<br /><br /><b>第五步:编写PROPAGATION_REQUIRED</b><br /><br />在这步,我们将添加JMS处理逻辑.为了做到这样,我们主要需要实现JMS MessageListener接口.我们也会添加公共的setBank方法使Spring的依赖注入起作用.源代码如下:<br /><br /><pre class="overflow">package jms;<br />import jdbc.Bank;<br />import javax.jms.Message;<br />import javax.jms.MapMessage;<br />import javax.jms.MessageListener;<br /><br />public class MessageDrivenBank<br />implements MessageListener<br />{<br />    private Bank bank;<br /><br />    public void setBank ( Bank bank )<br />    {<br />        this.bank = bank;<br />    }<br /><br />    //this method can be private<br />    //since it is only needed within<br />    //this class <br />    private Bank getBank()<br />    {<br />        return this.bank;<br />    }<br /><br />    public void onMessage ( Message msg )<br />    {<br />        try {<br />          MapMessage m = ( MapMessage ) msg;<br />          int account = m.getIntProperty ( "account" );<br />          int amount = m.getIntProperty ( "amount" );<br />          bank.withdraw ( account , amount );<br />          System.out.println ( "Withdraw of " + <br />          amount + " from account " + account );<br />        }<br />        catch ( Exception e ) {<br />          e.printStackTrace();<br />            <br />          //force rollback<br />          throw new RuntimeException ( <br />          e.getMessage() );<br />        }<br />    }<br />    <br />}</pre><br /><br /><br /><b>第六步:配置MessageDrivenBank</b><br /><br />这里我们配置MessageDrivenBank去监听事务的QueueReceiverSessionPool.这样给我们可以实现和EJB(没有丢失信息和冗余信息)类似的消息机制,但在这里我们是用简单的POJO对象实现.当向pool中插入一个MessageListener,这个会话池将确保用JTA/XA事务接收到消息.结合JTA/XA-capable 的JDBC数据源,我们可以实现可靠的消息机制.  Spring的配置如下:<br /><br /><pre class="overflow">&lt;?xml version="1.0" encoding="UTF-8"?&gt;<br /><br /><br />&lt;!-- <br />        NOTE: no explicit transaction manager bean <br />        is necessary<br />        because the QueueReceiverSessionPool will<br />        start transactions by itself.<br />--&gt;<br />&lt;beans&gt;<br />    &lt;bean id="datasource" <br />        class="com.atomikos.jdbc.nonxa.NonXADataSourceBean"&gt;<br />        &lt;property name="user"&gt;<br />            &lt;value&gt;sa&lt;/value&gt;<br />        &lt;/property&gt;<br />        &lt;property name="url"&gt;<br />            &lt;value&gt;jdbc:hsqldb:SpringNonXADB&lt;/value&gt;<br />        &lt;/property&gt;<br />        &lt;property name="driverClassName"&gt;<br />            &lt;value&gt;org.hsqldb.jdbcDriver&lt;/value&gt;<br />        &lt;/property&gt;<br />        &lt;property name="poolSize"&gt;<br />            &lt;value&gt;1&lt;/value&gt;<br />        &lt;/property&gt;<br />        &lt;property name="connectionTimeout"&gt;<br />            &lt;value&gt;60&lt;/value&gt;<br />        &lt;/property&gt;<br />    &lt;/bean&gt;<br />    &lt;bean id="xaFactory" <br />        class="org.activemq.ActiveMQXAConnectionFactory"&gt;<br />        &lt;property name="brokerURL"&gt;<br />            &lt;value&gt;tcp://localhost:61616&lt;/value&gt;<br />        &lt;/property&gt;<br />    &lt;/bean&gt;<br />    &lt;bean id="queue" <br />        class="org.activemq.message.ActiveMQQueue"&gt;<br />        &lt;property name="physicalName"&gt;<br />            &lt;value&gt;BANK_QUEUE&lt;/value&gt;<br />        &lt;/property&gt;<br />    &lt;/bean&gt;<br />    &lt;bean id="bank" class="jdbc.Bank"&gt;<br />        &lt;property name="dataSource"&gt;<br />            &lt;ref bean="datasource"/&gt;<br />        &lt;/property&gt;<br />    &lt;/bean&gt;<br />    &lt;bean id="messageDrivenBank" <br />        class="jms.MessageDrivenBank"&gt;<br />        &lt;property name="bank"&gt;<br />            &lt;ref bean="bank"/&gt;<br />        &lt;/property&gt;<br />    &lt;/bean&gt;<br />    &lt;bean id="queueConnectionFactoryBean" <br />        class="com.atomikos.jms.QueueConnectionFactoryBean"&gt;<br />        &lt;property name="resourceName"&gt;<br />            &lt;value&gt;QUEUE_BROKER&lt;/value&gt;<br />        &lt;/property&gt;<br />        &lt;property name="xaQueueConnectionFactory"&gt;<br />            &lt;ref bean="xaFactory"/&gt;<br />        &lt;/property&gt;<br />    &lt;/bean&gt;<br />    &lt;bean id="queueReceiverSessionPool" <br />        class="com.atomikos.jms.QueueReceiverSessionPool" <br />        init-method="start"&gt;<br />        <br />        &lt;property name="queueConnectionFactoryBean"&gt;<br />            &lt;ref bean="queueConnectionFactoryBean"/&gt;<br />        &lt;/property&gt;<br />        &lt;property name="transactionTimeout"&gt;<br />            &lt;value&gt;120&lt;/value&gt;<br />        &lt;/property&gt;<br />        &lt;!-- <br />        default license allows only limited <br />        concurrency so keep pool small <br />        --&gt;<br />        &lt;property name="poolSize"&gt;<br />            &lt;value&gt;1&lt;/value&gt;<br />        &lt;/property&gt;<br />        &lt;property name="queue"&gt;<br />            &lt;ref bean="queue"/&gt;<br />        &lt;/property&gt;<br />        &lt;property name="messageListener"&gt;<br />            &lt;ref bean="messageDrivenBank"/&gt;<br />        &lt;/property&gt;<br />    &lt;/bean&gt;<br />&lt;/beans&gt;</pre><br /><br />因为这篇文章需要一个便于安装的JMS服务,所以这里我们使用ActiveMQ.如果你正在使用另一个JMS实现,那么你将仍然能使用这部分提出的技术.接下来除了datasource和bank对象,我们将增加下面的对象定义:<br /><br />·        xaFactory: 为建立JMS连接的connection工厂.<br />·        queue: queue代表我们将使用的JMS队列, 这个队列被配置成ActiveMQ要求的形式.<br />·        queueConnectionFactoryBean:一个JTA-aware的JMS连接器. <br />·        A queueReceiverSessionPool for JTA-enabled message consumption：注意:我们同时指定了用来调用的初始化方法(例:start);这是Spring的另一个特性. Start方法在session pool类里定义,它是在Spring配置文件中进行配置的.<br />·        messageDrivenBank：负责处理消息.<br /><br />你可以问问自己事务管理是在哪里进行的.事实上, 在先前部分被添加的对象已消失.为什么呢?因为我们现在使用QueueReceiverSessionPool来接收来自JMS的消息,并且这个类也为每次接收启动一个JTA事务.我们也可以保留JTA配置，另外添加JMS配置, 但是这样可能会使XML文件更长. 现在session pool类将担当事务管理角色.它和proxy方法的工作相似; 只是这个类需要JMS  MessageListener 为之添加事务. 通过这样配置,在每个消息收接之前程序将启动一个新事务 无论何时, 当我们的消息实例正常返回时, 这个事务将提交. 如果出现RuntimeException, 那么这个事务将回滚. 结构如下面图5(可以清淅地看到一些JMS对象).<br /><br /><img onmouseover="javascript:imgShowTip(this);" style="DISPLAY: inline" onclick="javascript:imgClick(this);" alt="image" src="http://www.matrix.org.cn/resource/upload/forum/2006_03_03_094815_uedFGVKnoV.gif" onload="javascript:imgLoad(this);" border="0" /><br />Figure 5. Architecture for message-driven applications in Spring<br /><br />现在该架构工作如下:<br />1.        应用程序调用bank对象和初始化数据库表.<br />2.        应用程序queueReceiverSessionPool, 因此触发一个start方法的调用去监听到达的消息.<br />3.        queueReceiverSessionPool在队列中侦察一个新消息.<br />4.        queueReceiverSessionPool开始一个新事务,并且注册这个事务.<br />5.        queueReceiverSessionPool调用已注册的MessageListener (messageDrivenBank).<br />6.        这将触发对bank 对象的调用.<br />7.        bank 对象通过datasource访问数据库.<br />8.        datasource注册事务.<br />9.        通过JDBC访问数据库.<br />10.        当处理完成时, queueReceiverSessionPool会终止这个事务。然后进行commint(除非发生RuntimeException).<br />11.        transaction manager开始消息队列的两阶段提交.<br />12.        transaction manager开始数据库的两阶段提交.<br /><br /><b>第七步:编写应用程序</b><br />因为我们没有使用容器,我们仅仅提供一个Java应用程序就可以启动整个银行系统.我们的Java应用程序是非常简单: 它有能力找回配置的对象(Spring通过XML文件将他们放到一起). 这个应用程序能在任何兼容的JDK(Java Development Kit)上运行,并且不需要应用服务器.<br /><br /><pre class="overflow">package jms;<br />import java.io.FileInputStream;<br />import java.io.InputStream;<br />import org.springframework.beans.factory.xml.XmlBeanFactory;<br />import com.atomikos.jms.QueueReceiverSessionPool;<br />import jdbc.Bank;<br /><br />public class StartBank<br />{<br />  public static void main ( String[] args )<br />  throws Exception<br />  {<br />    //open bean XML file<br />    InputStream is = <br />    new FileInputStream(args[0]);<br />    <br />    //the factory is Spring's entry point<br />    //for retrieving the configured <br />    //objects from the XML file<br />    XmlBeanFactory factory = <br />        new XmlBeanFactory(is);<br />    <br />    //retrieve the bank to initialize<br />    //alternatively, this could be done<br />    //in the XML configuration too<br />    Bank bank = <br />        ( Bank ) factory.getBean ( "bank" );<br />    <br />    //initialize the bank if needed<br />    bank.checkTables();<br /><br />    //retrieve the pool; <br />    //this will also start the pool <br />    //as specified in the beans XML file<br />    //by the init-method attribute!<br /><br />    QueueReceiverSessionPool pool  = <br />        ( QueueReceiverSessionPool ) <br />        factory.getBean ( <br />        "queueReceiverSessionPool" );<br /><br />    //Alternatively, start pool here <br />    //(if not done in XML)<br />    //pool.start();<br /><br />    System.out.println ( <br />        "Bank is listening for messages..." );<br />        <br />  }<br />}</pre><br /><br />这就是J2EE!是不是认为J2EE也很容易呢? <br /><br /><b><span style="FONT-SIZE: 16px">对通用性的考虑</span></b><br />这部分里我们看看更多的概念,这些概念在许多J2EE应用程序中是很重要的.我们同样将看到对这些概念来说，一个应用服务器并不是必须的.<br /><br /><b>集群和可扩展性</b><br />健壮的企业应用程序需要集群来分流负担. 在消息驱动应用程序的例子中,这很容易:我们自动地从JMS应用程序继承得来处理能力.如果我们需要更强大的处理能力,那么我们只需增加更多连接相同JMS服务器的进程.一个对服务性能有效的衡量标准是在队列中停留的消息的数量. 在其他情况下,如基于web的 架构(如下)我们能很容易地使用web环境下的集群能力.<br /><br /><b>方法级别的安全</b><br />一个典型的观点是认为EJB能增加方法级的安全性.虽然并没有在这篇文章中提到,但是在Sping中配置方法级别的安全是可能的.这种配置类似于我们增加方法级的事务划分的方式.<br /><br /><b>对非消息驱动的应用程序的通用性</b><br />在不改变源代码的情况下(除了主应用程序类),我们使用的平台能很容易地被整合到任何J2EE web 应用服务器. 换句话说 , 通过JMS进行后台处理;这使得web服务器在面对后台处理的延迟问题上更可靠和更独立.在任何情况下, 为了实现容器管理的事务或容器管理的安全性,我们都不再需要依靠EJB容器来实现.<br /><br /><b>关于容器管理持久化?</b><br />存在并被很多开发者检验过的技术例如:JDO或者Hibernate 都不一定需要一个应用服务器. 另外,这些工具已经占据了持久化市场.<br /><br /><b><span style="FONT-SIZE: 16px">结论</span></b><br />今天,不需要应用服务器的J2EE已经成为可能，也很容易. 有人或许会说,没有应用程序服务器,有一些应用程序仍不能实现:例如,如果你需要一般的JCA(Java Connectivity API) 功能性, 那么我们上面提供的平台是不够的. 但是,这可能会发生改变，因为不用使用一个应用程序服务器进行开发,测式和部署的好处实在是太大. 人们越来越相信: 将来得J2EE是一个模块化的”选择你所需要”架构. 这与我们之前的完全基于应用服务器的方法相反. 在这样的情况下,J2EE开发者将从应用服务器和EJB中解放出来.<br /><br /><b>资源</b>:<br />·        <a href="http://www.onjava.com/onjava/2006/02/08/examples/j2eeWithoutAppServerSources.zip" target="_new"><font color="#002c99">代码下载</font></a><br />·        Guy Pardon's presentation on Transactions in Spring published at TheServerSide <br />·        More information on Atomikos Transactions and message-driven functionality without EJB <br />·        The home page of Spring <br />·        More information on JUnit <br />·        FirstSQL is an easy-to-install, XA-compliant DBMS <br />·        More information on HSQLDB <br />·        More information on ActiveMQ<img src ="http://www.blogjava.net/rendong/aggbug/61137.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/rendong/" target="_blank">rendong</a> 2006-08-01 10:11 <a href="http://www.blogjava.net/rendong/archive/2006/08/01/61137.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>