??xml version="1.0" encoding="utf-8" standalone="yes"?>久久久久亚洲AV无码永不,亚洲国产成人无码AV在线影院,亚洲综合激情另类小说区http://www.tkk7.com/rendong/category/13644.htmlzh-cnFri, 02 Mar 2007 02:54:48 GMTFri, 02 Mar 2007 02:54:48 GMT60lucene实例http://www.tkk7.com/rendong/archive/2006/12/25/89849.htmlrendongrendongMon, 25 Dec 2006 03:26:00 GMThttp://www.tkk7.com/rendong/archive/2006/12/25/89849.htmlhttp://www.tkk7.com/rendong/comments/89849.htmlhttp://www.tkk7.com/rendong/archive/2006/12/25/89849.html#Feedback0http://www.tkk7.com/rendong/comments/commentRss/89849.htmlhttp://www.tkk7.com/rendong/services/trackbacks/89849.html说明一?q一文章的用到的lucene,是用2.0版本?主要在查询的时?.0版本的lucene与以前的版本有了(jin)一些区?
其实q一些代码都是早几个月写?自己很懒,所以到今天才写到自q博客?高深的文章自己写不了(jin)Q只能记录下一些简单的记录与点_(d)其中的代码算是自p乐的Q希望高手不要把重构之类的砸下来...

1、在windowspȝ下的的C盘,Z个名叫s的文件夹,在该文g多w面随便徏三个txt文gQ随便v名啦Q就?1.txt","2.txt"?3.txt"?
其中1.txt的内容如下:(x)

代码
  1. 中华人民共和?  
  2. 全国人民   
  3. 2006 qb?/span>

?2.txt"?3.txt"的内容也可以随便写几写,q里懒写Q就复制一个和1.txt文g的内容一样吧

2、下载lucene包,攑֜classpath路径?
建立索引:

代码
  1. package lighter.javaeye.com;   
  2.   
  3. import java.io.BufferedReader;   
  4. import java.io.File;   
  5. import java.io.FileInputStream;   
  6. import java.io.IOException;   
  7. import java.io.InputStreamReader;   
  8. import java.util.Date;   
  9.   
  10. import org.apache.lucene.analysis.Analyzer;   
  11. import org.apache.lucene.analysis.standard.StandardAnalyzer;   
  12. import org.apache.lucene.document.Document;   
  13. import org.apache.lucene.document.Field;   
  14. import org.apache.lucene.index.IndexWriter;   
  15.   
  16. /**  
  17.  * author lighter date 2006-8-7  
  18.  */  
  19. public class TextFileIndexer {   
  20.     public static void main(String[] args) throws Exception {   
  21.         /* 指明要烦(ch)引文件夹的位|?q里是C盘的S文g夹下 */  
  22.         File fileDir = new File("c:\\s");   
  23.   
  24.         /* q里攄(ch)引文件的位置 */  
  25.         File indexDir = new File("c:\\index");   
  26.         Analyzer luceneAnalyzer = new StandardAnalyzer();   
  27.         I(yng)ndexWriter indexWriter = new I(yng)ndexWriter(indexDir, luceneAnalyzer,   
  28.                 true);   
  29.         File[] textFiles = fileDir.listFiles();   
  30.         long startTime = new Date().getTime();   
  31.            
  32.         //增加document到烦(ch)引去   
  33.         for (int i = 0; i < textFiles.length; i++) {   
  34.             if (textFiles[i].isFile()   
  35.                     && textFiles[i].getName().endsWith(".txt")) {   
  36.                 System.out.println("File " + textFiles[i].getCanonicalPath()   
  37.                         + "正在被烦(ch)?...");   
  38.                 String temp = FileReaderAll(textFiles[i].getCanonicalPath(),   
  39.                         "GBK");   
  40.                 System.out.println(temp);   
  41.                 Document document = new Document();   
  42.                 Field FieldPath = new Field("path", textFiles[i].getPath(),   
  43.                         Field.Store.YES, Field.Index.NO);   
  44.                 Field FieldBody = new Field("body", temp, Field.Store.YES,   
  45.                         Field.Index.TOKENIZED,   
  46.                         Field.TermVector.WITH_POSITIONS_OFFSETS);   
  47.                 document.add(FieldPath);   
  48.                 document.add(FieldBody);   
  49.                 indexWriter.addDocument(document);   
  50.             }   
  51.         }   
  52.         //optimize()Ҏ(gu)是对索引q行优化   
  53.         indexWriter.optimize();   
  54.         indexWriter.close();   
  55.            
  56.         //试一下烦(ch)引的旉   
  57.         long endTime = new Date().getTime();   
  58.         System.out   
  59.                 .println("q花费了(jin)"  
  60.                         + (endTime - startTime)   
  61.                         + " 毫秒来把文增加到烦(ch)引里面去!"  
  62.                         + fileDir.getPath());   
  63.     }   
  64.   
  65.     public static String FileReaderAll(String FileName, String charset)   
  66.             throws I(yng)OException {   
  67.         BufferedReader reader = new BufferedReader(new I(yng)nputStreamReader(   
  68.                 new FileInputStream(FileName), charset));   
  69.         String line = new String();   
  70.         String temp = new String();   
  71.            
  72.         while ((line = reader.readLine()) != null) {   
  73.             temp += line;   
  74.         }   
  75.         reader.close();   
  76.         return temp;   
  77.     }   
  78. }  

索引的结果:(x)

代码
  1. File C:\s\1.txt正在被烦(ch)?...   
  2. 中华人民共和国全国h?/span>2006q?  
  3. File C:\s\2.txt正在被烦(ch)?...   
  4. 中华人民共和国全国h?/span>2006q?  
  5. File C:\s\3.txt正在被烦(ch)?...   
  6. 中华人民共和国全国h?/span>2006q?  
  7. q花费了(jin)297 毫秒来把文增加到烦(ch)引里面去!c:\s  

3、徏立了(jin)索引之后Q查询啦....

代码
  1. package lighter.javaeye.com;   
  2.   
  3. import java.io.IOException;   
  4.   
  5. import org.apache.lucene.analysis.Analyzer;   
  6. import org.apache.lucene.analysis.standard.StandardAnalyzer;   
  7. import org.apache.lucene.queryParser.ParseException;   
  8. import org.apache.lucene.queryParser.QueryParser;   
  9. import org.apache.lucene.search.Hits;   
  10. import org.apache.lucene.search.IndexSearcher;   
  11. import org.apache.lucene.search.Query;   
  12.   
  13. public class TestQuery {   
  14.     public static void main(String[] args) throws I(yng)OException, ParseException {   
  15.         Hits hits = null;   
  16.         String queryString = "中华";   
  17.         Query query = null;   
  18.         I(yng)ndexSearcher searcher = new I(yng)ndexSearcher("c:\\index");   
  19.   
  20.         Analyzer analyzer = new StandardAnalyzer();   
  21.         try {   
  22.             QueryParser qp = new QueryParser("body", analyzer);   
  23.             query = qp.parse(queryString);   
  24.         } catch (ParseException e) {   
  25.         }   
  26.         if (searcher != null) {   
  27.             hits = searcher.search(query);   
  28.             if (hits.length() > 0) {   
  29.                 System.out.println("扑ֈ:" + hits.length() + " 个结?");   
  30.             }   
  31.         }   
  32.     }   
  33.   
  34. }  

其运行结果:(x)

引用
扑ֈ:3 个结?

具体的API的用法,q里׃说了(jin)Q具体的做法参考lucene的官Ҏ(gu)档吧...
下一文?
搜烦(ch)?lucene的简单实?lt;?gt; http://www.javaeye.com/post/190576
打一个例子吧,
q是lucene2.0的API
代码
  1. QueryParser qp = new QueryParser("body", analyzer);      
  2. query = qp.parse(queryString);      

q是lucene1.4.3版的API

代码
  1. query = QueryParser.parse(key,queryString,new new StandardAnalyzer());  

详细的改动看一些官方的文清楚啦
文章的时?感觉比较隑ֆ的就是标?有时候不知道起什么名字好,反正q里写的都是关于lucene的一些简单的实例,随便v?

Lucene 其实很简单的,它最主要是做两件事:建立索引和进行搜?
来看一些在lucene中用的术语,q里q不打算作详l的介绍,只是点一下而已----因ؓ(f)q一个世界有一U好东西Q叫搜烦(ch)?/p>

IndexWriter:lucene中最重要的的cM一Q它主要是用来将文加入索引Q同时控制烦(ch)引过E中的一些参C用?/p>

Analyzer:分析?主要用于分析搜烦(ch)引擎遇到的各U文本。常用的有StandardAnalyzer分析?StopAnalyzer分析?WhitespaceAnalyzer分析器等?/p>

Directory:索引存放的位|?lucene提供?jin)两U烦(ch)引存攄位置Q一U是盘Q一U是内存。一般情况将索引攑֜盘上;相应地l(f)ucene提供?jin)FSDirectory和RAMDirectory两个cR?/p>

Document:文档;Document相当于一个要q行索引的单元,M可以惌被烦(ch)引的文g都必{化ؓ(f)Document对象才能q行索引?/p>

FieldQ字Dc(din)?/p>

IndexSearcher:是lucene中最基本的检索工P所有的(g)索都?x)用到IndexSearcher工具;

Query:查询Qlucene中支持模p查询,语义查询Q短语查询,l合查询{等,如有TermQuery,BooleanQuery,RangeQuery,WildcardQuery{一些类?/p>

QueryParser: 是一个解析用戯入的工具Q可以通过扫描用户输入的字W串Q生成Query对象?/p>

Hits:在搜索完成之后,需要把搜烦(ch)l果q回q显C给用户Q只有这h是完成搜烦(ch)的目的。在lucene中,搜烦(ch)的结果的集合是用Hitscȝ实例来表C的?/p>

上面作了(jin)一大堆名词解释Q下面就看几个简单的实例?
1、简单的的StandardAnalyzer试例子

代码
  1. package lighter.javaeye.com;   
  2.   
  3. import java.io.IOException;   
  4. import java.io.StringReader;   
  5.   
  6. import org.apache.lucene.analysis.Analyzer;   
  7. import org.apache.lucene.analysis.Token;   
  8. import org.apache.lucene.analysis.TokenStream;   
  9. import org.apache.lucene.analysis.standard.StandardAnalyzer;   
  10.   
  11. public class StandardAnalyzerTest    
  12. {   
  13.     //构造函敎ͼ   
  14.     public StandardAnalyzerTest()   
  15.     {   
  16.     }   
  17.     public static void main(String[] args)    
  18.     {   
  19.         //生成一个StandardAnalyzer对象   
  20.         Analyzer aAnalyzer = new StandardAnalyzer();   
  21.         //试字符?  
  22.         StringReader sr = new StringReader("lighter javaeye com is the are on");   
  23.         //生成TokenStream对象   
  24.         TokenStream ts = aAnalyzer.tokenStream("name", sr);    
  25.         try {   
  26.             int i=0;   
  27.             Token t = ts.next();   
  28.             while(t!=null)   
  29.             {   
  30.                 //辅助输出时显C?  
  31.                 i++;   
  32.                 //输出处理后的字符   
  33.                 System.out.println("W?+i+"?"+t.termText());   
  34.                 //取得下一个字W?  
  35.                 t=ts.next();   
  36.             }   
  37.         } catch (IOException e) {   
  38.             e.printStackTrace();   
  39.         }   
  40.     }   
  41. }   

昄l果Q?
引用
W??lighter
W??javaeye
W??com

提示一下:(x)
StandardAnalyzer是lucene中内|的"标准分析?,可以做如下功?
1、对原有句子按照I格q行?jin)分?
2、所有的大写字母都可以能转换为小写的字母
3、可以去掉一些没有用处的单词Q例?is","the","are"{单词,也删除了(jin)所有的标点
查看一下结果与"new StringReader("lighter javaeye com is the are on")"作一个比较就清楚明了(jin)?
q里不对其APIq行解释?jin),具体见lucene的官Ҏ(gu)档。需要注意一点,q里的代码用的是lucene2的APIQ与1.43版有一些明昄差别?

2、看另一个实?单地建立索引Q进行搜?

代码
  1. package lighter.javaeye.com;   
  2. import org.apache.lucene.analysis.standard.StandardAnalyzer;   
  3. import org.apache.lucene.document.Document;   
  4. import org.apache.lucene.document.Field;   
  5. import org.apache.lucene.index.IndexWriter;   
  6. import org.apache.lucene.queryParser.QueryParser;   
  7. import org.apache.lucene.search.Hits;   
  8. import org.apache.lucene.search.IndexSearcher;   
  9. import org.apache.lucene.search.Query;   
  10. import org.apache.lucene.store.FSDirectory;   
  11.   
  12. public class FSDirectoryTest {   
  13.   
  14.     //建立索引的\?  
  15.     public static final String path = "c:\\index2";   
  16.   
  17.     public static void main(String[] args) throws Exception {   
  18.         Document doc1 = new Document();   
  19.         doc1.add( new Field("name""lighter javaeye com",Field.Store.YES,Field.Index.TOKENIZED));   
  20.   
  21.         Document doc2 = new Document();   
  22.         doc2.add(new Field("name""lighter blog",Field.Store.YES,Field.Index.TOKENIZED));   
  23.   
  24.         I(yng)ndexWriter writer = new I(yng)ndexWriter(FSDirectory.getDirectory(path, true), new StandardAnalyzer(), true);   
  25.         writer.setMaxFieldLength(3);   
  26.         writer.addDocument(doc1);   
  27.         writer.setMaxFieldLength(3);   
  28.         writer.addDocument(doc2);   
  29.         writer.close();   
  30.   
  31.         I(yng)ndexSearcher searcher = new I(yng)ndexSearcher(path);   
  32.         Hits hits = null;   
  33.         Query query = null;   
  34.         QueryParser qp = new QueryParser("name",new StandardAnalyzer());   
  35.            
  36.         query = qp.parse("lighter");   
  37.         hits = searcher.search(query);   
  38.         System.out.println("查找\"lighter\" ? + hits.length() + "个结?);   
  39.   
  40.         query = qp.parse("javaeye");   
  41.         hits = searcher.search(query);   
  42.         System.out.println("查找\"javaeye\" ? + hits.length() + "个结?);   
  43.   
  44.     }   
  45.   
  46. }  

q行l果Q?
代码
  1. 查找"lighter" ?/span>2个结?  
  2. 查找"javaeye" ?/span>1个结果 ?/span>

很久没有看lucene?jin),q两三天又复?fn)?jin)一?上一些代码都是前几个月写的,只是改动?jin)一些字W串和包名显C。{载时误明,文章来自:http://lighter.javaeye.com?
如有什么错误的地方Q恳h出,谢谢?br />

wen19851025 写道
//试字符?
StringReader sr = new StringReader("lighter javaeye com");
//生成TokenStream对象
TokenStream ts = aAnalyzer.tokenStream("name", sr);
请问:以上的解析是按什么来解析,Z么他?x)自动的按空格或?,"q行字符分割,再一个当SR里输入是中文字符?他将?x)对每个字进行分?请问q是Z?同时q功能的实现又意为着什么呢.....????


StandardAnalyzer是lucene中内|的"标准分析?,可以做如下功?
1、对原有句子按照I格q行?jin)分?
2、所有的大写字母都可以能转换为小写的字母
3、可以去掉一些没有用处的单词Q例?is","the","are"{单词,也删除了(jin)所有的标点
同时也可以对中文q行分词(效果不好),现在有很多的中文分词包可以采?



rendong 2006-12-25 11:26 发表评论
]]>
Lucenehttp://www.tkk7.com/rendong/archive/2006/12/25/89846.htmlrendongrendongMon, 25 Dec 2006 03:05:00 GMThttp://www.tkk7.com/rendong/archive/2006/12/25/89846.htmlhttp://www.tkk7.com/rendong/comments/89846.htmlhttp://www.tkk7.com/rendong/archive/2006/12/25/89846.html#Feedback0http://www.tkk7.com/rendong/comments/commentRss/89846.htmlhttp://www.tkk7.com/rendong/services/trackbacks/89846.html阅读全文

rendong 2006-12-25 11:05 发表评论
]]>
tag大全http://www.tkk7.com/rendong/archive/2006/12/24/89775.htmlrendongrendongSun, 24 Dec 2006 12:07:00 GMThttp://www.tkk7.com/rendong/archive/2006/12/24/89775.htmlhttp://www.tkk7.com/rendong/comments/89775.htmlhttp://www.tkk7.com/rendong/archive/2006/12/24/89775.html#Feedback0http://www.tkk7.com/rendong/comments/commentRss/89775.htmlhttp://www.tkk7.com/rendong/services/trackbacks/89775.html昨天Q由于程序中需要用C个WEB TABS样式的页面,GOOGLE之,才发现这东西真不好找?
displaytag q个东西计划要支持TABSQؓ(f)啥只是计划,虽然它的表格样式看v来不错?
webtabs q个嘛,样子太丑?jin),拿不出手?
prizetags q个可控制的属性太了(jin)Q不爽?
Ditchnet JSP Tabs Taglib 是它,Z么没有地方能下蝲刎ͼ哪位好心(j)告诉我?

下面的这个站收录的比较全Q收藏之?
东西q真不少Q慢慢学啦,可惜没有我想要的
【Java开?Jsp标签库?
Posted by E_wsq 2006-3-29 9:32:00
displytag

与Strutsl合使用最出名的一个tag主要是显C格数据很漂亮、完善?
500)this.style.width=500;?border=0> http://displaytag.sourceforge.net/
cewolf tag

用来在web上显C复杂图形报表的一个jsp tag?
500)this.style.width=500;?border=0> http://cewolf.sourceforge.net/
Loading Tab

当一个复杂的操作可以加蝲比较长的旉Ӟ用这个tag?
500)this.style.width=500;?border=0> http://www.mycgiserver.com/~eboudrant/#taglibs
DbForms

DbForms!它是一个基?Java (Servlet,JSP/Taglib)的快速应用程序开发环境,可以帮助开发h员快速徏造基于Web的数据库应用E序?
500)this.style.width=500;?border=0> http://jdbforms.sourceforge.net/
Jakarta Taglibs

Jakarta Taglibs 是ؓ(f)JSP定制标签库和相关的项目提供的一个开源仓库,?TagLibraryValidatorc,和对面生成工具的扩展来支持标签库。Jakarta Taglibs 也包括了(jin)对JSP Standard Tag Library (JSTL)的参考实现。这个实现基于项目标准。目前,在Jakarta Taglibs 中没有其它标{ֺ代表?jin)Java Community Process (JCP) 标准?
500)this.style.width=500;?border=0> http://jakarta.apache.org/taglibs/index.html
EasyLDAP

LDAP标签库ؓ(f)JSPE序员和W(xu)eb面设计者提供了(jin)最Ҏ(gu)的方法来执行L的LDAP操作?
500)this.style.width=500;?border=0> http://easyldap.sourceforge.net/
WebJMX
WebJMX标签库项目可以控制你的JMX 接口。WebJMX q个标签库项目的目的是生成一个JSP标签库,可以让有技巧的JSP开发h员ؓ(f)JMX生成一个可定制的、规范的、基于Web的界面?
500)this.style.width=500;?border=0> http://webjmx.sourceforge.net/
JPivot
JPivot - 是一个JSP 自定制的标签库,可以l制一个OLAP表格和图表。用户可以执行典型的OLAPDQ如下钻Q切片和方块。它使用Mondrian 作ؓ(f)其OLAP服务器?
500)this.style.width=500;?border=0> http://jpivot.sourceforge.net/
JSP Tree Tag
JSP Tree Tag是一个显C树(wi)型结构jsp标签Q它只把需要显C的部分送到客户览器?
500)this.style.width=500;?border=0> http://www.jenkov.dk/projects/treetag/treetag.jsp
Google Tag Library
该标记库?Google 有关。用该标记库,利用 Google Z的网站提供网站查询,q且可以直接在你的网里面显C搜查的l果?
500)this.style.width=500;?border=0> http://google-taglib.sourceforge.net/
TableTag
TableTag是类gDataGrid的Jsp标签库。通过java.util.List填充数据?
500)this.style.width=500;?border=0> http://tabletag.sourceforge.net/
uitags
uitags利用q个开源自定义标签可以很容易开发出一个友好的用户界面?
500)this.style.width=500;?border=0> http://uitags.sourceforge.net/
ValueList
ValueList利用q个标签可以q行数据qo(h)Q排序,分页。而且界面挺漂亮的?
500)this.style.width=500;?border=0> http://valuelist.sourceforge.net/
JCE taglib
JCE taglib把JCEQJava Cryptographic ExtensionsQ包装成TagLibq且包含?jin)EL函数。用这个标{能够ؓ(f)jsp应用E序加强安全性?
500)this.style.width=500;?border=0> http://jcetaglib.sourceforge.net/
Prize Tags
Prize Tags是一个集许多功能于一w的Jsp标签库。其中最受欢q的Tree TagQ这个Tag可以Z同节Ҏ(gu)定不同的图标Q而且可以服务端可以监控客L(fng)节点的展开Q关闭,选中与未选中{事件。除?jin)Tree Tagq有日历Tag,Icon Tag,Alternate Tag ,Template Tag {其它的功能?
500)this.style.width=500;?border=0> http://www.jenkov.com/prizetags/introduction.tmpl
Struts-Layout
Struts-Layout是一个用在Struts的标{ֺ.q个强大的标{ֺ可以用来昄面板(panels),输入?表格,treeviews,sortable lists,datagrids,popups,日历{?使用q些标签可以不用写HTML代码,甚至可以不用懂得HTML.q个目q提供一个Eclipse下的插gKiwi帮助使用Struts和Struts-Layout来开发Jsp面.以下是一张例?
500)this.style.width=500;?border=0>
500)this.style.width=500;?border=0> http://struts.application-servers.com/
JImageTaglib
JImageTaglib是一个用在J2EE Web应用E序的Java标签?它用来在服务端生成与处理囄然后再反馈到JSP面.可以qo(h)(filtering)囄,调整囄文g大小,生成条Ş码等.
500)this.style.width=500;?border=0> http://jimagetaglib.sourceforge.net/
uitags
uitags是一个开源的JSP custom-tag?它让开发友好的用户界面变得?
500)this.style.width=500;?border=0> http://uitags.sourceforge.net/
AJAX Tags
AJAX Tag是一lJsp标签,用来化AJAX(Asynchronous JavaScript and XML )技术在JSP面中的使用.
500)this.style.width=500;?border=0> http://ajaxtags.sourceforge.net/
Struts AjaxTags
q个AjaxTags是在现有的Struts HTML标签库上d对AJAX (Asynchronous Javascript+XML)技术的支持。这样就可以为现有的ZStruts HTML标签库的应用E序dAJAX功能而不用破坏现存的代码q且开发者不需要了(jin)解AJAX是怎样工作的?
500)this.style.width=500;?border=0> http://struts.sourceforge.net/ajaxtags/
AWTaglib
AWTaglib是一个Jsp标签可用于创建网?grid)控g.它还提供一些额外的功能可以把网g的数据导Zؓ(f)XLS,PDF和CSV(利用JasperReports来实?q能与Struts框架相结?
500)this.style.width=500;?border=0> http://awtaglib.sourceforge.net/
eXtremeTable
eXtremeTable是一个可扩展的用于以表格的Ş式来昄数据的一lJSP标签?


rendong 2006-12-24 20:07 发表评论
]]>
apache负蝲http://www.tkk7.com/rendong/archive/2006/12/02/85047.htmlrendongrendongSat, 02 Dec 2006 08:58:00 GMThttp://www.tkk7.com/rendong/archive/2006/12/02/85047.htmlhttp://www.tkk7.com/rendong/comments/85047.htmlhttp://www.tkk7.com/rendong/archive/2006/12/02/85047.html#Feedback1http://www.tkk7.com/rendong/comments/commentRss/85047.htmlhttp://www.tkk7.com/rendong/services/trackbacks/85047.html
  • LoadModule proxy_module modules/mod_proxy.so   
  • LoadModule proxy_ajp_module modules/mod_proxy_ajp.so   
  • LoadModule proxy_balancer_module modules/mod_proxy_balancer.so   
  •   
  • ProxyPass /admin balancer://tomcatcluster/admin  lbmethod = byrequests   stickysession = JSESSIONID   nofailover = Off   timeout = 5   maxattempts = 3   
  • ProxyPassReverse /admin balancer://tomcatcluster/admin   
  •   
  • < Proxy  balancer://tomcatcluster >   
  • BalancerMember ajp://localhost:8009  route = web1   
  • BalancerMember ajp://localhost:10009  smax = 10   route = web2   
  • BalancerMember ajp://localhost:11009  route = web3   
  • BalancerMember ajp://localhost:12009  smax = 10   route = web4   
  • </ Proxy >   


  • rendong 2006-12-02 16:58 发表评论
    ]]>
    Taglib 原理和实C支持El表达?/title><link>http://www.tkk7.com/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.tkk7.com/rendong/archive/2006/09/15/69961.html</guid><wfw:comment>http://www.tkk7.com/rendong/comments/69961.html</wfw:comment><comments>http://www.tkk7.com/rendong/archive/2006/09/15/69961.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.tkk7.com/rendong/comments/commentRss/69961.html</wfw:commentRss><trackback:ping>http://www.tkk7.com/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 原理和实C支持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">作者:(x)佚名    文章来源Q未知    点?yn)LQ? <script language="javascript" src="/Article/GetHits.asp?ArticleID=3773"></script> 107    更新旉Q?006-2-17</td> </tr> <tr> <td class="main_tdbg_760" id="fontzoom" style="WORD-BREAK: break-all" valign="top" colspan="2" height="300">  1.先看q么一个例?br /><br /><table bordercolor="#ffcc66" width="90%" align="center" bgcolor="#c8c7b9" border="1"><tbody><tr><td>Q?@ page contentType="text/html; charset=gb2312" language="java"%Q?br />Q?@ taglib uri="/WEB-INF/tlds/c.tld" prefix="c"%Q?br />Q?DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"Q?br />QhtmlQ?br />QbodyQ?br />Q?<br />String tut = "tutorial";<br />request.setAttribute("tut",tut);<br />%Q?br />The String in request is :<br />Qc:out value="${tut}"/Q?<br />Q?bodyQ?br />Q?htmlQ?/td></tr></tbody></table><br />  2.如何支持el表达?br /><br />  在\径org.apache.taglibs.standard.lang.support下,有个?ExpressionEvaluatorManager.evaluate 的方法,当el表达式作为入参时Q调用这个方法,在tag内即可自动把el表达式{化。例如,你想tag的value字段支持el表达式,那么只需在setҎ(gu)里如下调用:(x)<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有四个参数。第一个表Ctag的名字,在取el表达式出错时使用。一般和<a class="channel_keylink" >属?/a>名字相同。第二个要求字符Ԍ通常单调用输入对象的toStringҎ(gu)。第三个是类Q通常用Object.class。第四个用this卛_Q第五个是pageContext变量?br /><br />  通常不用对这个方法思考太多。只需Ҏ(gu)<a class="channel_keylink" >属?/a>名字Q其他照搬即可?br /><br />  注意Q当你的tag<a class="channel_keylink" >属?/a>支持el表达式时Q你必须把它声明为Object对象。如上述的valueQ应该声明ؓ(f)Q?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.实例Q让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>Q?--NewOutputTag--Q?br />QtagQ?br />QnameQnewoutQ?nameQ?br />Qtag-classQdiegoyun.NewOutputTagQ?tag-classQ?br />Qbody-contentQemptyQ?body-contentQ?br />QattributeQ?br />QnameQnameQ?nameQ?br />QrequiredQfalseQ?requiredQ?br />QrtexprvalueQtrueQ?rtexprvalueQ?br />Q?attributeQ?br />Q?tagQ?/td></tr></tbody></table><br />  ~写jsp试<br /><br /><table bordercolor="#ffcc66" width="90%" align="center" bgcolor="#c8c7b9" border="1"><tbody><tr><td>Q?@ page language="java" %Q?br />Q?@ taglib uri="/WEB-INF/tlds/diego.tld" prefix="diego"%Q?br />QhtmlQ?br />Qbody bgcolor="#FFFFFF"Q?br />Q?<br />String s = "diego";<br />request.setAttribute("name",s);<br />%Q?br />Test El supported tag:<br />QbrQ?br />Qdiego:newout name="${name}"/Q?br /><br />Q?bodyQ?br />Q?htmlQ?/td></tr></tbody></table><br />  可以看到面输出为:(x)<br /><br />  Test El supported tag: <br />  Hello! diego </td> </tr> </tbody> </table> <img src ="http://www.tkk7.com/rendong/aggbug/69961.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.tkk7.com/rendong/" target="_blank">rendong</a> 2006-09-15 18:14 <a href="http://www.tkk7.com/rendong/archive/2006/09/15/69961.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>解析 Java cd对象的初始化q程(zhuang?国徏 (guojian.zhang@gmail.com), 软g工程? 北京高伟达西南分?http://www.tkk7.com/rendong/archive/2006/09/09/68732.htmlrendongrendongSat, 09 Sep 2006 09:36:00 GMThttp://www.tkk7.com/rendong/archive/2006/09/09/68732.htmlhttp://www.tkk7.com/rendong/comments/68732.htmlhttp://www.tkk7.com/rendong/archive/2006/09/09/68732.html#Feedback0http://www.tkk7.com/rendong/comments/commentRss/68732.htmlhttp://www.tkk7.com/rendong/services/trackbacks/68732.html

    cȝ初始化和对象初始化是 JVM 理的类型生命周期中非常重要的两个环节,Google ?jin)一遍网l,有关c装载机制的文章倒是不少Q然而类初始化和对象初始化的文章q不多,特别是从字节码和 JVM 层次来分析的文章更是鲜有所见?/p>

    本文主要对类和对象初始化全过E进行分析,通过一个实际问题引入,源代码转换?JVM 字节码后Q对 JVM 执行q程的关键点q行全面解析Qƈ在文中穿插入?jin)相?JVM 规范?JVM 的部分内部理论知识,以理Z实际l合的方式介l对象初始化和类初始化之间的协作以及(qing)可能存在的冲H问题?/p>

    问题引入

    q日我在调试一个枚丄型的解析器程序,该解析器是将数据库内一万多条枚举代码装载到~存中,Z(jin)实现快速定位枚举代码和具体枚Dcd的所有枚丑օ素,该类在装载枚举代码的同时对其采取两种{略建立内存索引。由于该cL一个公共服务类Q在E序各个层面都会(x)使用到它Q因此我它实现Z个单例类。这个类在我调整cd例化语句位置之前q行正常Q但当我把该cd例化语句调整到静(rn)态初始化语句之前Ӟ我的E序不再为我工作?jin)?/p>

    下面是经q我化后的示例代码:(x)


    QL单一Q?/b>
    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->Category内存索引*/
    	private static Map CODE_MAP_CACHE;
    	static {
    		CODE_MAP_CACHE = new HashMap();
    		//Z(jin)说明问题,我在q里初始化一条数?
    		CODE_MAP_CACHE.put("0","北京?);
    	}
    	
    	//private, for single instance
    	private CachingEnumResolver() {
    		//初始化加载数? 引v问题Q该Ҏ(gu)也要负点责Q
    		initEnums();
    	}
    	
    	/**
    	 * 初始化所有的枚Dcd
    	 */
    	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());
    	}
    }
    

    惛_大家看了(jin)上面的代码后?x)感觉有些茫(dng)q个cȝh没有问题啊,q的属于典型的饿汉式单态模式啊Q怎么?x)有问题呢?/p>

    是的Q他看v来的没有问题,可是如果他 run hӞ其结果是他不?x)?f)你正?work。运行该c,它的执行l果是:(x)


    QL单二Q?/b>
    CODE_MAP_CACHE为空,问题在这里开始暴?
    {0=北京?jng)}
    

    我的E序怎么?x)这PZ么在 initEnum() Ҏ(gu)?CODE_MAP_CACHE 为空Qؓ(f)什么我输出?CODE_MAP_CACHE 内容只有一个元素,其它两个元素呢?Q?Q!Q?/p>

    看到q里Q如果是你在调试该程序,你此M定觉得很奇怪,N是我?Jvm 有问题吗Q非也!如果不是Q那我的E序是怎么?jin)?q绝对不是我惌的结果。可事实上无论怎么修改 initEnum() Ҏ(gu)都无于事,L(fng)我最初是一定不?x)怀疑到问题可能出在创徏 CachingEnumResolver 实例q一环节上。正是因为我太相信我创徏 CachingEnumResolver 实例的方法,加之?Java cd始化与对象实例化底层原理理解有所偏差Q我ؓ(f)此付Z(jin)三、四个小?-U半个工作日的大好青春?/p>

    那么问题I竟出在哪里呢?Z么会(x)出现q样的怪事呢?在解册个问题之前,先让我们来了(jin)解一下JVM的类和对象初始化的底层机制?/p>



    回页?/font>


    cȝ生命周期



    上图展示的是cȝ命周期流向;在本文里Q我只打谈谈类?初始?以及(qing)"对象实例?两个阶段?/p>



    回页?/font>


    cd始化

    c?初始?阶段Q它是一个类或接口被首次使用的前阶段中的最后一工作,本阶D负责ؓ(f)cd量赋予正的初始倹{?/p>

    Java ~译器把所有的cd量初始化语句和类型的?rn)态初始化器通通收集到 <clinit> Ҏ(gu)内,该方法只能被 Jvm 调用Q专门承担初始化工作?/p>

    除接口以外,初始化一个类之前必须保证其直接超cd被初始化Qƈ且该初始化过E是?Jvm 保证U程安全的。另外,q所有的c都?x)拥有一?<clinit>() Ҏ(gu)Q在以下条g中该cM?x)拥?<clinit>() Ҏ(gu)Q?/p>

    • 该类既没有声明Q何类变量Q也没有?rn)态初始化语句Q?
    • 该类声明?jin)类变量Q但没有明确使用cd量初始化语句或静(rn)态初始化语句初始化;
    • 该类仅包含静(rn)?final 变量的类变量初始化语句,q且cd量初始化语句是编译时帔R表达式?




    回页?/font>


    对象初始?/font>

    在类被装载、连接和初始化,q个cd随时都可能用了(jin)。对象实例化和初始化是就是对象生命的起始阶段的活动,在这里我们主要讨论对象的初始化工作的相关特点?/p>

    Java ~译器在~译每个cL都会(x)c至生成一个实例初始化Ҏ(gu)--?"<init>()" Ҏ(gu)。此Ҏ(gu)与源代码中的每个构造方法相对应Q如果类没有明确地声明Q何构造方法,~译器则cȝ成一个默认的无参构造方法,q个默认的构造器仅仅调用父类的无参构造器Q与此同时也?x)生成一个与默认构造方法对应的 "<init>()" Ҏ(gu).

    通常来说Q?lt;init>() Ҏ(gu)内包括的代码内容大概为:(x)调用另一?<init>() Ҏ(gu)Q对实例变量初始化;与其对应的构造方法内的代码?/p>

    如果构造方法是明确C调用同一个类中的另一个构造方法开始,那它对应?<init>() Ҏ(gu)体内包括的内容ؓ(f)Q一个对本类?<init>() Ҏ(gu)的调用;对应用构造方法内的所有字节码?/p>

    如果构造方法不是通过调用自ncȝ其它构造方法开始,q且该对象不?Object 对象Q那 <init>() 法内则包括的内容为:(x)一个对父类 <init>() Ҏ(gu)的调用;对实例变量初始化Ҏ(gu)的字节码Q最后是对应构造子的方法体字节码?/p>

    如果q个cL ObjectQ那么它?<init>() Ҏ(gu)则不包括对父c?<init>() Ҏ(gu)的调用?/p>



    回页?/font>


    cȝ初始化时?/font>

    本文到目前ؓ(f)止,我们已经大概有了(jin)解到?jin)类生命周期中都l历?jin)哪些阶D,但这个类的生命周期的开始阶D?-c装载又是在什么时候被触发呢?cd是何时被初始化的呢?让我们带着q三个疑问l去L{案?/p>

    Java 虚拟范ؓ(f)cȝ初始化时机做?jin)严格定义?x)"initialize on first active use"--" 在首ơ主动用时初始?。这个规则直接媄(jing)响着c装载、连接和初始化类的机?-因ؓ(f)在类型被初始化之前它必须已经被连接,然而在q接之前又必M证它已经被装载了(jin)?/p>

    在与初始化时机相关的c装载时机问题上QJava 虚拟范ƈ没有对其做严格的定义Q这׃?JVM 在实C可以Ҏ(gu)自己的特Ҏ(gu)供采用不同的装蝲{略。我们可以思考一?Jboss AOP 框架的实现原理,它就是在对你?class 文g装蝲环节做了(jin)手脚--插入?AOP 的相x截字节码Q这使得它可以对E序员做到完全透明化,哪怕你?new 操作W创建出的对象实例也一栯?AOP 框架拦截--与之相对应的 Spring AOPQ你必须通过他的 BeanFactory 获得?AOP 代理q的受管对象Q当?Jboss AOP 的缺点也很明?-他是?JBOSS 服务器绑定很紧密的,你不能很L的移植到其它服务器上。嗯~……,说到q里有些跑题?jin),要知?AOP 实现{略_以写一本厚厚的书了(jin)Q嘿嘿,此打住?/p>

    说了(jin)q么多,cȝ初始化时机就是在"在首ơ主动用时"Q那么,哪些情Ş下才W合首次d使用的要求呢Q?/p>

    首次d使用的情形:(x)

    • 创徏某个cȝ新实例时--new、反、克隆或反序列化Q?
    • 调用某个cȝ?rn)态方法时Q?
    • 使用某个cL接口的静(rn)态字D|对该字段赋值时Qfinal字段除外Q;
    • 调用Java的某些反方法时
    • 初始化某个类的子cL
    • 在虚拟机启动时某个含有main()Ҏ(gu)的那个启动类?

    除了(jin)以上几种情Ş以外Q所有其它用JAVAcd的方式都是被动用的Q他们不?x)导致类的初始化?/p>



    回页?/font>


    我的问题I竟出在哪里

    好了(jin)Q了(jin)解了(jin)JVM的类初始化与对象初始化机制后Q我们就有了(jin)理论基础Q也可以理性的d析问题了(jin)?/p>

    下面让我们来看看前面[清单一]的JAVA源代码反l译出的字节码:(x)


    QL单三Q?/b>
    public class com.ccb.framework.enums.CachingEnumResolver extends
    java.lang.Object{
    static {};
      Code:
       0:	new	#2; //class CachingEnumResolver
       3:	dup
       4:	invokespecial	#14; //Method "<init>":()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."<init>":()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."<init>":()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."<init>":()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
    }
    

    如果上面QL单一Q显C,清单内容是在 JDK1.4 环境下的字节码内容,可能q䆾清单对于很大部分兄弟来说实没有多少吸引力,因ؓ(f)q些 JVM 指o(h)实不像源代码那h亮易懂。但它的的确是查找和定位问题最直接的办法,我们惌的答案就在这?JVM 指o(h)清单里?/p>

    现在Q让我们对该cMcd始化到对象实例初始化全过E分析[清单一]中的代码执行轨迹?/p>

    如前面所qͼcd始化是在cȝ正可用时的最后一前阶工作,该阶D负责对所有类正确的初始化|此项工作是线E安全的QJVM?x)保证多U程同步?/p>

    W1步:(x)调用cd始化Ҏ(gu) CachingEnumResolver.<clinit>()Q该Ҏ(gu)对外界是不可见的Q换句话说是 JVM 内部专用Ҏ(gu)Q?lt;clinit>() 内包括了(jin) CachingEnumResolver 内所有的h指定初始值的cd量的初始化语句。要注意的是q每个c都h该方法,具体的内容在前面已有叙述?/p>

    W2步:(x)q入 <clinit>() Ҏ(gu)内,让我们看字节码中?"? 行,该行与其上面两行l合h代表 new 一?CachingEnumResolver 对象实例Q而该代码行本w是指调?CachingEnumResolver cȝ <init>Q)(j)Ҏ(gu)。每一?Java c都h一?<init>() Ҏ(gu)Q该Ҏ(gu)?Java ~译器在~译时生成的Q对外界不可见,<init>() Ҏ(gu)内包括了(jin)所有具有指定初始化值的实例变量初始化语句和javacȝ构造方法内的所有语句。对象在实例化时Q均通过该方法进行初始化。然而到此步Q一个潜在的问题已经在此埋伏好,q着你来犯了(jin)?/p>

    W3步:(x)让我们顺着执行序向下看,"? 行,该行所在方法就是该cȝ构造器Q该Ҏ(gu)先调用父cȝ构造器 <init>() 对父对象q行初始化,然后调用 CachingEnumResolver.initEnum() Ҏ(gu)加蝲数据?/p>

    W4步:(x)"? 行,该行获取 "CODE_MAP_CACHE" 字段|其运行时该字Dgؓ(f) null。注意,问题已经开始显C(jin)。(作ؓ(f)E序员的你一定是希望该字D已l被初始化过?jin),而事实上它还没有被初始化Q。通过判断Q由于该字段?NULLQ因此程序将l箋执行?"? 行,该字段实例化ؓ(f) HashMap()?/p>

    W5步:(x)?"??? 行,其功能就是ؓ(f) "CODE_MAP_CACHE" 字段填入两条数据?/p>

    W6步:(x)退出对象初始化Ҏ(gu) <init>()Q将生成的对象实例初始化l类字段 "SINGLE_ENUM_RESOLVER"。(注意Q此刻该对象实例内的cd量还未初始化完全Q刚才由 <init>() 调用 initEnum() Ҏ(gu)赋值的cd?"CODE_MAP_CACHE" ?<clinit>() Ҏ(gu)q未初始化字D,它还在后面的类初始化过E再ơ被覆盖Q?/p>

    W7步:(x)l箋执行 <clinit>Q)(j)Ҏ(gu)内的后代码Q?? 行,该行?"CODE_MAP_CACHE" 字段实例化ؓ(f) HashMap 实例Q注意:(x)在对象实例化时已l对该字D赋D?jin),现在又重新赋gؓ(f)另一个实例,此刻Q?CODE_MAP_CACHE"变量所引用的实例的cd量D覆盖Q到此我们的疑问已经有了(jin){案Q?/p>

    W?步:(x)cd始化完毕Q同时该单态类的实例化工作也完成?/p>

    通过对上面的字节码执行过E分析,或许你已l清楚了(jin)解到D错误的深层原因了(jin)Q也或许你可能早已被上面的分析过E给弄得晕头转向?jin),不过也没折,虽然我也可以从源代码的角度来阐述问题Q但q样不够深度Q同时也?x)有仅?f)个h观点、不_信之嫌?/p>



    回页?/font>


    如何解决

    要解决上面代码所存在的问题很单,那就是将 "SINGLE_ENUM_RESOLVER" 变量的初始化赋D句{Ud getInstance() Ҏ(gu)中去卛_。换句话说就是要避免在类q未初始化完成时从内部实例化该类或在初始化过E中引用q未初始化的字段?/p>



    回页?/font>


    写在最?/font>

    ?rn)下燥之?j)Q仔l思量自己是否真的掌握?jin)本文主题所引出的知识,如果(zhn)觉得?zhn)已经完全或基本掌握?jin)Q那么很好,在最后,我将前面的代码稍做下修改Q请思考下面两l程序是否同样会(x)存在问题呢?


    E序一
    	public class CachingEnumResolver {
    	public  static Map CODE_MAP_CACHE;
    	static {
    		CODE_MAP_CACHE = new HashMap();
    		//Z(jin)说明问题,我在q里初始化一条数?
    		CODE_MAP_CACHE.put("0","北京?);
    		initEnums();
    	}
    	


    E序?/b>
    	public class CachingEnumResolver {
    		private static final CachingEnumResolver SINGLE_ENUM_RESOLVER;
    		public  static Map CODE_MAP_CACHE;
    		static {
    			CODE_MAP_CACHE = new HashMap();
    			//Z(jin)说明问题,我在q里初始化一条数?
    			CODE_MAP_CACHE.put("0","北京?);
    			SINGLE_ENUM_RESOLVER = new CachingEnumResolver();
    			initEnums();
    		}
    	

    最后,一点关?JAVA 体的感aQ时下正是各U开源框架盛行时期,Spring 更是大行光Q吸引着一大批 JEE 开发者的眼球Q我也是 fans 中的一员)(j)。然而,让我们仔l观察一?-?Spring 体ZQ在那么多的 Spring fans 当中Q有多少人去研究q?Spring 源代码?又有多少人对 Spring 设计思想有真正深入了(jin)解呢Q当?dng)我是没有资格以这L(fng)口吻来说事的Q我只是惌明一个观?-学东西一定要"正本清源"?/p>

    献上此文Q}以共勉?/p>

    rendong 2006-09-09 17:36 发表评论
    ]]>
    _截取字符Ԍ转蝲http://www.tkk7.com/rosen/archive/2005/08/12/9949.aspxQ?http://www.tkk7.com/rendong/archive/2006/08/05/61888.htmlrendongrendongFri, 04 Aug 2006 16:56:00 GMThttp://www.tkk7.com/rendong/archive/2006/08/05/61888.htmlhttp://www.tkk7.com/rendong/comments/61888.htmlhttp://www.tkk7.com/rendong/archive/2006/08/05/61888.html#Feedback0http://www.tkk7.com/rendong/comments/commentRss/61888.htmlhttp://www.tkk7.com/rendong/services/trackbacks/61888.html
    开发中l常遇到Q字W串q长Q无法完全显C的问题

    q时候就需要截取我们所需要的长度Q后面显C省略号或其他字W?br />
    ׃中文字符占两个字节,而英文字W占用一个字节,所以,单纯地判断字W数Q效果往往不尽如h?br />
    下面的方法通过判断字符的类型来q行截取Q效果还可以:(x)Q?br />

    如果大家有其他的解决Ҏ(gu)Ƣ迎贴出来,共同学习(fn)Q)(j)
    **********************************************************************
    private String str;
    private int counterOfDoubleByte;
    private byte b[];
    /**
    * 讄需要被限制长度的字W串
    * @param str 需要被限制长度的字W串
    */
    public void setLimitLengthString(String str){
      this.str = str;
    }
    /**
    * @param len 需要显C的长度(<font color="red">注意Q长度是以byte为单位的Q一个汉字是2个byte</font>)
    * @param symbol 用于表示省略的信息的字符Q如?..??gt;>>”等?br />* @return q回处理后的字符?br />*/
    public String getLimitLengthString(int len, String symbol) throws UnsupportedEncodingException {
      counterOfDoubleByte = 0;
      b = str.getBytes("GBK");
      if(b.length <= len)
        return str;
      for(int i = 0; i < len; i++){
        if(b[i] < 0)
          counterOfDoubleByte++;
      }

      if(counterOfDoubleByte % 2 == 0)
        return new String(b, 0, len, "GBK") + symbol;
      else
        return new String(b, 0, len - 1, "GBK") + symbol;
    }


    本文转脓(chung)自网友:(x)focus2004 的文?/strong>
    posted on 2005-08-12 15:39 Rosen 阅读(963) 评论(6)  ~辑 收藏收藏?65Key 所属分c? J2EE 技?/font>

    评论

    #  re: _截取字符Ԍ转蝲Q?2005-08-19 08:27 ivan
    if(b<0) ~译?x)出错。 ?a onclick="return SetReplyAuhor("ivan")" href="/rosen/archive/2005/08/12/9949.aspx#post">回复
      

    #  re: _截取字符Ԍ转蝲Q?2005-08-19 20:39 Rosen
    马上修改一下代码,d转脓(chung)的时候一直忘C改了(jin)?
    ?if(b[i] < 0)Q谢?ivan 指正。 ?a onclick="return SetReplyAuhor("Rosen")" href="/rosen/archive/2005/08/12/9949.aspx#post">回复
      

    #  re: _截取字符Ԍ转蝲Q?2006-01-28 18:24 tdg
    大作拜读Q有一Ҏ(gu)见,Ҏ(gu)砖引玉:(x)
    1。字W串除了(jin)可以Zbyte[]操作外,q可以基于char[]操作。看老大你的意图是想截取字符串的前几个字W然后加上省略符h后输?gu)已Q完全不必考虑用byte[]数组操作?。而且好像开发中更注重语义上的第几个字符而不是你说的q种情况哦?
    2。以下是拙作Q请斧正Q?
    /**
    * 字符串截取函?
    * @param str String 要处理的字符?
    * @param length int 需要显C的长度
    * @param symbol String 用于表示省略的信息的字符Q如?..??gt;>>”等
    * @return String q回处理后的字符?
    * @throws UnsupportedEncodingException
    */
    public String getLimitLengthString(String str, int length, String symbol) throws
    UnsupportedEncodingException {
    assert str != null;
    assert length > 0;
    assert symbol != null;
    //如果字符串的位数于{于要截取的位数Q附加上表示省略的信息的字符串后q回
    if (str.length() <= length) {
    return str + symbol;
    //从零开始,截取length个字W,附加上表C省略的信息的字W串后返?
    } else {
    str = new String(str.getBytes("GBK"));
    char[] charArray = str.toCharArray();
    char[] charArrayDesc = new char[length];
    System.arraycopy(charArray, 0, charArrayDesc, 0, length);
    return new String(charArrayDesc) + symbol;
    }
    }  回复
      

    #  re: _截取字符Ԍ转蝲Q?2006-01-28 20:03 Rosen
    呵呵 tdg 兄很认真喔,谈不上斧正。主要是q个问题Q用 char 处理Q如果是字母或者数字,实际上截取出来的?x)比汉字占用一半的I间Q所以截取出来后Q还是不能对齐。而实际上 char 数组中,不管是字母、数字还是汉字,它们都只代表一个单元。但?byte 则不同,字母、数字只占用一个字节,而汉字占用两个字节(都是GBK~码Q。 ?a onclick="return SetReplyAuhor("Rosen")" href="/rosen/archive/2005/08/12/9949.aspx#post">回复
      

    #  re: _截取字符Ԍ转蝲Q?2006-03-24 10:49 istarliu
    (zhn)好Q?
    你的文章让我受益不少Q有个问题想向?zhn)认一下:(x)
    在代码中
    b = str.getBytes("GBK");
    if(b.length <= len)
    return str;
    for(int i = 0; i < len; i++){
    if(b[i] < 0)
    counterOfDoubleByte++;
    }
    是不是如果只要是中文汉字Q在b[i]对应的值都是小?的,
    也就是说Q在汉字代表的两个字节中Q这两个汉字分别转化为整数值时Q是不是一定小?Q但D围不能小?127的。做q测试,不能肯定。:(x)Q?


    rendong 2006-08-05 00:56 发表评论
    ]]>
    q用ajax制作提示面 (原地址http://www.tkk7.com/rickhunter/articles/61648.html)http://www.tkk7.com/rendong/archive/2006/08/05/61885.htmlrendongrendongFri, 04 Aug 2006 16:48:00 GMThttp://www.tkk7.com/rendong/archive/2006/08/05/61885.htmlhttp://www.tkk7.com/rendong/comments/61885.htmlhttp://www.tkk7.com/rendong/archive/2006/08/05/61885.html#Feedback1http://www.tkk7.com/rendong/comments/commentRss/61885.htmlhttp://www.tkk7.com/rendong/services/trackbacks/61885.html阅读全文

    rendong 2006-08-05 00:48 发表评论
    ]]>
    动态组合公式的q算Q{载)(j)http://www.tkk7.com/rendong/archive/2006/08/01/61191.htmlrendongrendongTue, 01 Aug 2006 05:56:00 GMThttp://www.tkk7.com/rendong/archive/2006/08/01/61191.htmlhttp://www.tkk7.com/rendong/comments/61191.htmlhttp://www.tkk7.com/rendong/archive/2006/08/01/61191.html#Feedback0http://www.tkk7.com/rendong/comments/commentRss/61191.htmlhttp://www.tkk7.com/rendong/services/trackbacks/61191.html        1. l合的公式是一个字W串.
            2. 字符串元素包?rc0123456789+-*/()"
            3. r1c1=7,r1c2=2,r1c3=4
            4. 求组合成的公式的值是多少.

    解决思\:
            1.对公式进行字W合法性检?
            2.对公式中出现的变量进行取值替?
            3.(g)查语句的合法?(l合规则)
            4.(g)查运的合法?(除数?的情?
            5.求?


    具体解决Ҏ(gu):
            str1 = "(r1c1+r1c2)/r1c3)"
            str1 = replace(str1," ","")  '去除公式中间出现的空?br />        1.  对公式进行字W合法性检?

                  bool1 = getDataOperationCharCheck(str1)

                  if bool1 = false then
                          Response.write "公式不合?有异常字W出?<br>"
                  else
                          Response.write "公式(g)查通过!<br>"
                  end if

            2.对公式中出现的变量进行取值替?/p>

                    RCstr = getdataoperation(str1)

            3.(g)查语句的合法?(l合规则)

                    bool2 = getDataOperationSyntaxCheck(RCstr)

                          if bool2 = false then
                                     Response.write "q算公式语法不合?有组合异常字W出?<br>"
                          else
                                     Response.Write "q算公式语法(g)查通过!<br>"
                          end if

              4.(g)查运的合法?(除数?的情?

                       bool3 = getDataOperationRunCheck(RCstr)

                            if bool3 = false then
                                    Response.write "q算公式q算不合?有除Cؓ(f)0出现.<br>"
                            else
                                    Response.write "q算公式q算合法性检查通过!<br>"  
                            end if

               5.求?
     
                            intValue = getRunSearch(RCstr)

               6.q算l果:

                            (((7*1.0)+(2*1.0))/(4*1.0)) = 2.25

    '1.=============================================================

    '对原始公式进行字W检?是否有不合法的字W?/p>

    function getDataOperationCharCheck(datastr)
           datastr = replace(datastr," ","")
            sumchar = "rc0123456789+-*/()"
            strlen = len(datastr)
            strreturn = true
            for i = 1 to strlen
                    singlechar = mid(datastr,i,1)
                    if instr(sumchar,singlechar) = 0 then
                            strreturn = false
                            exit for
                    end if
            next
            getDataOperationCharCheck = strreturn
    end function

    '2.==============================================================

    '对原始计划公式进行取值替?
    '实现Ҏ(gu):对原始字W串q行单个字符(g)?
    '在出?"+-*/()" 中的M一个字W时,对已l组合生成的变量q行取?
    '在取到值的同时对其q行 double 转换 ,实现Ҏ(gu)?(intvalue * 1.0)

    function getdataoperation(datastr)
            strlen = len(datastr)  
            sunstr = ""
            strID = ""
            intvalue = 0
            singlechar = ""
            operationstr="()+-*/"
            for i=1 to strlen
                    'Response.write mid(datastr,i,1) & "<br>" 
                    singlechar = mid(datastr,i,1)
                    if instr(operationstr,singlechar) > 0 then
       
                            if strID <> "" then
                                    intvalue = getValue(strID)
                                    sunstr = sunstr & "(" & intvalue & "*1.0)"   '(1)     
                                    intvalue = 0
                                    strID = ""          
                            end if
                            sunstr = sunstr & singlechar
                            singlechar = ""
                    else
                            strID = strID & singlechar
                    end if     
            next
            getdataoperation = sunstr
    end function

    '变量取值函?
    '下列数据是ؓ(f)试使用.
    '
    function getValue(strRC)
            select case strRC
                    case "r1c1"
                            getValue = 2
                    case "r1c2"
                            getValue = 7
                    case "r1c3"
                            getValue = 2
                   end select
    end function

    '3.==============================================================

    '对公式进行语法合法性检?
    'eg.(g)?(),--,++,**,//,(/,*) {?是否成对出现.
    '(g)查是否有


    function getDataOperationSyntaxCheck(datastr)
            strreturn = true
            datastr = replace(datastr," ","")  '去除所有的I格
            strlen = len(datastr)
            num1 = 0                           '记录 括号?个数  采用 ?(  ?, ?) ?
            upsinglechar = ""                  '相对本次的字W的上一个字W?br />        singlechar = ""
            operationstr1="()+-*/"
            operationstr2="(+-*/"              '相对 ?(  左边出现的运的符h正确?
     
            for i = 1 to strlen
                    singlechar = mid(datastr,i,1)
                    select case singlechar
                            case "("
                                    num1 = num1 + 1
                                    if upsinglechar <> "" then
                                            if instr(operationstr2,upsinglechar) = 0 then   '在左括号的左边若不ؓ(f)I?必需出现 "(+-*/" 中的一?
                                                    strreturn = false
                                                    exit for
                                            end if
                                    end if
       
                            case ")"
                                    num1 = num1 - 1
                                    if num1 < 0 then
                                            strreturn = false
                                            exit for
                                    end if
                                    if instr(operationstr2,upsinglechar) > 0 then       '在右括号的左边若不ؓ(f)I?必需不能出现 "(+-*/" 中的一?br />                                        strreturn = false
                                            exit for
                                    end if
       
                            case "+"
                                    if instr(operationstr2,upsinglechar) > 0 then       '在加L(fng)左边若不I?必需不能出现 "(+-*/" 中的一?br />                                        strreturn = false
                                            exit for
                                    end if
       
                            case "-"
                                     if instr(operationstr2,upsinglechar) > 0 then      '在减L(fng)左边若不I?必需不能出现 "(+-*/" 中的一?br />                                        strreturn = false
                                            exit for
                                    end if
                            case "*"
                                     if instr(operationstr2,upsinglechar) > 0 then      '在乘L(fng)左边若不I?必需不能出现 "(+-*/" 中的一?br />                                        strreturn = false
                                            exit for
                                    end if
                            case "/"
                                     if instr(operationstr2,upsinglechar) > 0 then      '在除L(fng)左边若不I?必需不能出现 "(+-*/" 中的一?br />                                        strreturn = false
                                            exit for
                                    end if
                    end select
                    upsinglechar = singlechar  
                    singlechar = ""
            next
            getDataOperationSyntaxCheck = strreturn
    end function

    '4.==============================================================
     
    '对组合公式进行运合法性的(g)?br />'首选查找有没有 "/0"出现.
    '其次查找cM "/(****)" = /0 出现

    function getDataOperationRunCheck(datastr)
            strreturn = true
            if instr(datastr,"/")>0 then
                    if instr(datastr,"/0") > 0 then    
                            strreturn = false
                    else

                            '? 后面出现?)内的数据q行q算,取值是否会(x)?出现.
                            '首先计算 "/" 出现的次?br />                        '再次判断 "/(" 出现的次?br />                        '?"/(" 出现的次? 则安?

                            intnum1 = getInstrNum(datastr,"/")
                            intnum2 = getInstrNum(datastr,"/(")
                            if intnum2 > 0 then
                                    for j = intnum2 to 1 step -1
                                            intpoint1 = getInstrPoint(datastr,"/(",j)
                                            if intpoint1 > 0 then
                                                    sumpoint = getRunCheck(datastr,intpoint1)
                                                    if  CDbl(sumpoint) = CDbl(0) then
                                                            strreturn = false
                                                            exit for
                                                    end if
                                            end if
                                    next     
                            end if
                    end if  
            end if
            getDataOperationRunCheck= strreturn
    end function


    '(g)查字W运行的合法?
    '主要是对/()出现的字公式q行计算是否?x)等?

    function getRunCheck(datastr,intpoint1)
            strlen = len(datastr)
            intpoint = intpoint1 + 1
            intnum = 0
            singlechar = ""
            rcsearch = ""
            intreturn = 0
            for m = intpoint to strlen
                    singlechar = mid(datastr,m,1)
                    if singlechar = "(" then
                            intnum = intnum + 1
                    end if
                    if singlechar = ")" then
                            intnum = intnum - 1
                    end if
                    rcsearch = rcsearch & singlechar
                    if intnum = 0 then    
                            intreturn  = getRunSearch(rcsearch)
                            exit for
                    end if   
            next
            getRunCheck = intreturn
    end function

    '5.==============================================================

    '求?
    function getRunSearch(strrcsearch)
             sql = "select " & strrcsearch & " as rcvalue "
             Set rs = conn.execute(sql)
             'Response.write "<br>" & strrcsearch & "=" & rs("rcvalue") & "<br>"
             getRunSearch = rs("rcvalue")
    end function

    '公共函数==============================================================

    'q回substr  ?str1 中出现的ơ数

    function getInstrNum(str1,substr)
            strlen = len(str1)
            substrlen = len(substr)
            singlechar = ""
            intreturn = 0
            for i = 1 to strlen
                    singlechar = mid(str1,i,substrlen)
                    if singlechar = substr then
                            intreturn = intreturn + 1
                    end if
            next
            getInstrNum = intreturn
    end function

    'q回substr ?str1 ?W?intnum ơ出现的位置
    'intnum 必需是大?的正整数

    function getInstrPoint(str1,substr,intnum)
            intreturn = 0
            strlen = len(str1)
            substrlen = len(substr)
            singlechar = ""
            intcount = 0
            for i = 1 to strlen
                    singlechar = mid(str1,i,substrlen)
                    if singlechar = substr then
                            intcount = intcount + 1
                    end if
                    if intcount = intnum then
                            intreturn = i
                            exit for
                    end if
            next
            getInstrPoint = intreturn
    end function



    rendong 2006-08-01 13:56 发表评论
    ]]>
    不需要应用服务器的J2EE(摘自:http://www.matrix.org.cn/resource/article/44/44250_J2ee+Application+Server.html)http://www.tkk7.com/rendong/archive/2006/08/01/61137.htmlrendongrendongTue, 01 Aug 2006 02:11:00 GMThttp://www.tkk7.com/rendong/archive/2006/08/01/61137.htmlhttp://www.tkk7.com/rendong/comments/61137.htmlhttp://www.tkk7.com/rendong/archive/2006/08/01/61137.html#Feedback0http://www.tkk7.com/rendong/comments/commentRss/61137.htmlhttp://www.tkk7.com/rendong/services/trackbacks/61137.html摘要:q篇文章提供?jin)一个对J2EE的简化,展示?jin)如何消除应用服务器的消耗和限制。特别地Q这文章提C(jin)Q许多应用程序实际上q不需要运行应用服务器?
    管J2EEq_(应用E序服务??qing)其~程模型(企业JAVAlg,UEJB)拥有的众所周知的复杂?但是ZJ2EE的应用程序仍然在企业领域里变得非常成?我们要感谢应用于轻量U容器的控制反{(IoC)和面向方面编E?AOP),比如Spring框架. 我们能够更简单地设计更大型的~程模型。然而,即有了(jin)q些工具Q应用服务器仍然是复杂度和消耗的一个重要瓶颈。这文章提供了(jin)一个对J2EE的简化,展示?jin)如何消除应用服务器的消耗和限制。特别地Q这文章提C(jin)Q许多应用程序实际上q不需要运行应用服务器。这PJ2EE应用lg会(x)变得Q?br />·        开发更Ҏ(gu)Q不再需要EJBq行代码Q?br />·        更简单:(x) l承不需要EJBcL接口Q?br />·        试更容易:(x)你的应用E序?qing)测试能在你的开发环境(IDEQ中直接q行Q?br />·        更少的资源消耗:(x)你只需要你的对象,不需要应用服务器Q更不需要应用服务器的对象;
    ·        安装更容易:(x)没有q行应用服务专门的安装YӞ 没有加蝲额外的XML文gQ?br />·        l护更容易:(x)所有的q程都更单,因此l护也更Ҏ(gu)?br />
    J2EE不必要的复杂度已l成Z个阻。今天,q种复杂度能够通过在这文章中提到的方法来避免。另外,E序q能够保留事务和安全q些典型的服务。J2EEE序从来没有比这更有过?br />
    版权声明QQ何获得Matrix授权的网站,转蝲时请务必保留以下作者信息和链接
    作?Guy PardonQ?a target="_new">chmei83(作者的blog:http://blog.matrix.org.cn/page/chmei83)
    原文:http://www.onjava.com/pub/a/onjava/2006/02/08/j2ee-without-application-server.html
    译文:http://www.matrix.org.cn/resource/article/44/44250_J2ee+Application+Server.html
    关键?J2ee;Application;Server

    例子:消息驱动Bank

    Z(jin)阐述我们的观?我们开发和安装一个完整的h应用E序:一个消息驱动的银行pȝ. 通过Q幸亏有SpringQ改q的ZPOJOs的编E模型和保留相同的事务,我们可以不需要EJB或者一个应用服务器来实现这个系l。在下一个部?我们从消息驱动架构产生到另一个架?像ZWEB的架构一??展示我们的样本应用程序的架构.  

    image
    Figure 1. Architecture of the message-driven bank

    在我们的例子?我们处理来自Java消息服务队列的银行定?一张定单的处理包括通过JDBC来更新当前帐L(fng)数据?Z(jin)避免信息的丢失和重复,我们用JTA和JTA/XA事务来配合更?处理信息和更新数据库发生在一个原子事务里.资源部分可得到JTA/XA的更多信?

    ~写应用E序代码

    该应用程序将׃个JAVAcȝ? Bank(一个DAO)和MessageDrivenBank.如图2.

    image
    Figure 2. Classes for the message-driven bank

    Bank是一个数据访问对象,q个对象装数据库访问。MessageDrivenBank是一个消息驱动fa&ccedil;adeq且是DAO的委?与典型的J2EEҎ(gu)不同,q个应用E序不包括EJBc?

    W一?~写Bank DAO

    如下, Bank源代码是很直接和单的JDBC操作.

    package jdbc;
    import javax.sql.*;
    import java.sql.*;
    public class Bank
    {
      private DataSource dataSource;
      public Bank() {}
      public void setDataSource ( DataSource dataSource )
      {
        this.dataSource = dataSource;
      }

    private DataSource getDataSource()
      {
        return this.dataSource;
      }

      private Connection getConnection()
      throws SQLException
      {
        Connection ret = null;
        if ( getDataSource() != null ) {
            ret = getDataSource().
                  getConnection();
        }
        return ret;
      }

      private void closeConnection ( Connection c )
      throws SQLException
      {
        if ( c != null ) c.close();
      }
        
      public void checkTables()
      throws SQLException
      {
            
        Connection conn = null;
        try {
          conn = getConnection();
          Statement s = conn.createStatement();
          try {
            s.executeQuery (
            "select * from Accounts" );
          }
          catch ( SQLException ex ) {
            //table not there => create it
            s.executeUpdate (
            "create table Accounts ( " +
            "account VARCHAR ( 20 ), " +
            "owner VARCHAR(300), " +
            "balance DECIMAL (19,0) )" );
            for ( int i = 0; i < 100 ; i++ ){
              s.executeUpdate (
              "insert into Accounts values ( " +
              "'account"+i +"' , 'owner"+i +"', 10000 )"
              );
            }
          }
          s.close();
          }
          finally {
            closeConnection ( conn );

          }

          //That concludes setup
      }

        
      //
      //Business methods are below
      //

      public long getBalance ( int account )
      throws SQLException
      {
            
        long res = -1;
        Connection conn = null;

        try {
          conn = getConnection();
          Statement s = conn.createStatement();
          
          String query =
          "select balance from Accounts where account='"+
          "account" + account +"'";
          
          ResultSet rs = s.executeQuery ( query );
          if ( rs == null || !rs.next() )
            throw new SQLException (
            "Account not found: " + account );
          res = rs.getLong ( 1 );
          s.close();
        }
        finally {
            closeConnection ( conn );
        }
        return res;
            
      }

      public void withdraw ( int account , int amount )
      throws Exception
      {
        Connection conn = null;

        try {
          conn = getConnection();
          Statement s = conn.createStatement();

          String sql =
          "update Accounts set balance = balance - "+
          amount + " where account ='account"+
          account+"'";
          
          s.executeUpdate ( sql );
          s.close();
        
        }
        finally {
            closeConnection ( conn );

        }
      }
    }


    注意:代码q没有依赖EJB或Q何专门的应用E序服务?实际?q是一个纯JAVA代码,q个JAVA代码是能在Q何J2SE环境下运行的.
    你同时应注意:我们使用?jin)来自JDBC的DataSource接口.q意味着我们的类是独立于目前JDBC供应商提供的c? 你可能会(x)疑惑,q怎么能与特定的数据管理系l?DBMS)提供商的JDBC实现紧密l合呢? q里是Spring框架帮你实现? q个技术被UCؓ(f)依赖注入:在我们的应用E序的启动期_(d)通过调用setDataSourceҎ(gu),Spring为我们提供了(jin)相应的datasource对象.在后面几部分我们?x)更多地提到Spring.如果我们在以前用应用程序服务器,我们不得不借助于JAVA命名l定接口(JNDI)查询.

    除了(jin)直接使用JDBC,我们也可以用Hibernate或者一个JDO工具来实现我们的持久?q同样不需要Q何的EJB代码.

    W二?配置BankDAO

    我们?x)将便用Spring框架来配|我们的应用E序.Spring不是必需?但是使用Spring的好处是我们可以简单的d服务,?我们JAVA对象的事务和安全.q类g应用服务器ؓ(f)EJB提供的东?只是在我们的例子中Spring变得更Ҏ(gu).
    Spring也允许我们把我们的类从目前的JDBC驱动实现中分d?Spring能够配置Driver(Z我们的XML配置数据)q把它提供给BankDAO对象(依赖注入原理).q样可以保持我们的JAVA代码的清淅和集中.q步的Spring配置文g如下:

    <?xml version="1.0" encoding="UTF-8"?>


    <beans>

    <bean id="datasource"
    class="com.atomikos.jdbc.nonxa.NonXADataSourceBean">

        <property name="user">
            <value>sa</value>
        </property>
        <property name="url">
            <value>jdbc:hsqldb:SpringNonXADB
            </value>
        </property>
        <property name="driverClassName">
            <value>org.hsqldb.jdbcDriver</value>
        </property>
        <property name="poolSize">
            <value>1</value>
        </property>
        <property name="connectionTimeout">
            <value>60</value>
        </property>
    </bean>

    <bean id="bank" class="jdbc.Bank">
            <property name="dataSource">
                <ref bean="datasource"/>
            </property>
    </bean>

    </beans>


    q个XML文g包括两个对象的配|?讉K数据库的DataSource和用这个DataSource的Bank对象.下面是由Springl护的一些基本Q?
    ·        创徏应用E序(? Bank和DataSource)需要的对象(“beans?.在XML文g中给Z(jin)q些对象的类?q且在我们的例子?q些对象需要有一个公q无参数constructor (Spring也允许参?但是配置语法上有所不同).q些对象都被命名(XML中的id属?,所以我们后面能够引用这些对? id也允许我们的应用E序扑֛它需要的已配|对?
    ·        q些对象的初始化是通过在XML文g中的properties的值实? 在XML文g中这些properties?应与对应的类中的setXXXҎ(gu)相对?
    ·        对象连接在一?:一个property可能是另一个对?例如:在我们例子中的数据源)的引?引用可以通过id创徏.

    注意:在我们下一步中, 我们选择配置一个JTA-enabled的数据源(由Atomikos Transactions提供,可用于企业和J2SE的JTA产品,我们应用于我们的应用程?. 单v?我们用HypersonicSQLDB,q个DBMS不需要专门的安装步骤—它是在.jar文g?像JTA和Spring.

    但是,考虑到渐增的可靠性需?强列推荐你用XA-capable的DBMS和JDBC驱动.没有XA的支? 在crash或重启之后你的应用程序将不能恢复原有数据. 资源部分有链接到关于事务和XA的信息和一些例?

    作ؓ(f)一个练?你可以试试从HypersonicSQLDB转换到FirstSQL,一个易安装XA-compliant的DBMS.换句话说,M其他Z业准备的和XA-capable的DBMS也会(x)做得很好.

    W三?试BankDAO

    让我们来试我们的代?(使用极限~程的程序员?x)首先写?但因开始不是很清淅,所以我们直到现在才开始写试.)下面是一个简单的单元试.q个试可在你的的应用程序里q行:它通过Spring获得一个BANK对象来进行测?q在setUpҎ(gu)中实?.注意:q个试使用清楚的事务划?每一个测试开始之前开始一个事?每个试l束时强制进行事务回?q是通过手工的方式来减少试Ҏ(gu)据库数据的媄(jing)?

    package jdbc;
    import com.atomikos.icatch.jta.UserTransactionImp;
    import junit.framework.TestCase;
    import java.io.FileInputStream;
    import java.io.InputStream;
    import org.springframework.beans.factory.xml.XmlBeanFactory;

    public class BankTest extends TestCase
    {

        private UserTransactionImp utx;

        private Bank bank;

        public BankTest ( String name )
        {
            super ( name );
            utx = new UserTransactionImp();
            

        }

        protected void setUp()
            throws Exception
        {
            //start a new transaction
            //so we can rollback the
            //effects of each test
            //in teardown!
            utx.begin();
            
            //open bean XML file
            I(yng)nputStream is =
                new FileInputStream("config.xml");

            //the factory is Spring's entry point
            //for retrieving the configured
            //objects from the XML file

            XmlBeanFactory factory =
                new XmlBeanFactory(is);

            bank = ( Bank ) factory.getBean ( "bank" );
            bank.checkTables();
        }

        protected void tearDown()
            throws Exception
        {
            //rollback all DBMS effects
            //of testing
            utx.rollback();
        }

        public void testBank()
        throws Exception
        {
            int accNo = 10;
            long initialBalance = bank.getBalance ( accNo );
            bank.withdraw ( accNo , 100 );
            long newBalance = bank.getBalance ( accNo );
            if ( ( initialBalance - newBalance ) != 100 )
                fail ( "Wrong balance after withdraw: " +
                       newBalance );
        }
        
    }


    我们需要JTA事务来确保JMS和JDBC都是原子操作.一般来?当经帔R是两个或多个q接的时候,你应考虑一下JTA/XA。例?在我们例子中的JMS和JDBC. Spring本n不提供JTA事务;它需要一个JTA实现或者委z一个应用服务器来处理这个事?在这?我们使用?jin)一个JTA实现,q个实现可以在Q何J2SEq_上工?
    最l架构如下面?.白色Ҏ(gu)代表我们的应用程序代?

    image
    Figure 3. Architecture for the test

    如你所看到?当我们执行我们的试Q将?x)发生下面的情?
    1.        BankTest开始一个新事务.
    2.        然后,q个test在Springq行期间获得一个BANK对象.q步触发Sping的创建和初始化过E?
    3.        q个test调用BANK的方?
    4.        BANK调用datasource对象,通过它自qsetDataSource Ҏ(gu)从Spring 获取q个对像.
    5.        q个数据源是JTA-enabled,q且与JTA实现交互来注册当前事?
    6.        JDBC statements和帐h据库交互.
    7.        当方法返回时, test调用事务回滚.
    8.        JTA记得住datasource对象Q会(x)命o(h)它进行回?

    W四?d声明式事务管?/b>

    Spring允许d声明式事务管理来理java对象.假设我们想确认bankL和一个有效的事务上下文一赯调用.我们通过在实际对象的上部配置一个proxy对象. Proxy和实际对象有相同接口,所以客户通过完全相同的方式用它. 配置Proxy wrap每个BankDAOҎ(gu)C务中.l果配置文g如下. 不要被XML的庞大吓倒—大多数内容能通过复制和粘贴到你自q工程中再使用.

    <?xml version="1.0" encoding="UTF-8"?>

    <beans>
        <!--
            Use a JTA-aware DataSource
            to access the DB transactionally
        -->
        <bean id="datasource"
            class="com.atomikos.jdbc.nonxa.NonXADataSourceBean">
            <property name="user">
                <value>sa</value>
            </property>
            <property name="url">
                <value>jdbc:hsqldb:SpringNonXADB</value>
            </property>
            <property name="driverClassName">
                <value>org.hsqldb.jdbcDriver</value>
            </property>
            <property name="poolSize">
                <value>1</value>
            </property>
            <property name="connectionTimeout">
                <value>60</value>
            </property>
        </bean>
        <!--
        Construct a TransactionManager,
        needed to configure Spring
        -->
        <bean id="jtaTransactionManager"
            class="com.atomikos.icatch.jta.UserTransactionManager"/>
        <!--
        Also configure a UserTransaction,
        needed to configure Spring  
        -->
        
        <bean id="jtaUserTransaction"
            class="com.atomikos.icatch.jta.UserTransactionImp"/>
        <!--
        Configure the Spring framework to use
        JTA transactions from the JTA provider
        -->
        <bean id="springTransactionManager"
        class="org.springframework.transaction.jta.JtaTransactionManager">
            <property name="transactionManager">
                <ref bean="jtaTransactionManager"/>
            </property>
            <property name="userTransaction">
                <ref bean="jtaUserTransaction"/>
            </property>
        </bean>
        <!-- Configure the bank to use our datasource -->
        <bean id="bankTarget" class="jdbc.Bank">
            <property name="dataSource">
                <ref bean="datasource"/>
            </property>
        </bean>
        <!--
        Configure Spring to insert
        JTA transaction logic for all methods
        -->
        <bean id="bank"
        class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
            <property name="transactionManager">
                <ref bean="springTransactionManager"/>
            </property>
            <property name="target">
                <ref bean="bankTarget"/>
            </property>
            <property name="transactionAttributes">
                <props>
                    <prop key="*">
                        PROPAGATION_REQUIRED, -Exception
                    </prop>
                </props>
            </property>
        </bean>
    </beans>


    q个XML文g告诉Spring去配|下面的对象:
    1.        需要通过JDBCq接的datasource.
    2.         djtaTransactionManager和jtaUserTransaction用于为JTA事务的Spring配置作准?
    3.        springTransactionManager用于告诉Spring需要用JTA.
    4.         BankDAO被重命名为bankTarget (因如下解释的原因).
    5.        bank对象被添加用于包装事务和bankTarget的所有方?我们通过配置bank对象来?br />
    springTransactionManager,q意味着所有事务将是JTA事务. 每个事务都被讄为PROPAGATION_REQUIRED,q将在Q何异怸出现强制回滚.

    对这些对象包含的内容,你都可以很容易的复制和粘贴jtaTransactionManager, jtaUserTransaction, springTransactionManager到其他工E?其他的是应用E序相关的对象:(x)datasource, bankTarget, bank. Bank对象很有?事实上对于bankTarget它是一个proxy;他们拥有相同的接? Trick如下:当我们的应用E序hSpring去配|和q回bank对象,Spring实际上将q回proxy(看v来和我们的应用程序完全相?Q随后这个proxyؓ(f)我们开?l束事务.q样,应用E序和BankcLw都不需要知道JTA!?阐述?jin)在q步我们所得到?  

    image
    Figure 4. Architecture with declarative JTA transactions in Spring

    现在的工作如?
    1.        应用E序调用bank对象.q将触发Spring的初始化处理和返回proxy对象. 对应用程序而言,q个proxy行ؓ(f)和我们的Bank是一L(fng).
    2.        当bank的一个方法被调用, q个调用会(x)通过proxyq行.
    3.        proxy使用springTransactionManager创徏一个新事务.
    4.        springTransactionManager被配|ؓ(f)使用JTA,因些它委zֈJTA.
    5.        调用被forward到Bank的实际对?bankTarget.
    6.        bankTarget使用从Spring中得到的datasource.
    7.        datasource对事务进行注?
    8.        通过规则的JDBC讉K数据?
    9.        在返回时, proxyl止事务:如果在先前的序列中没有发生异?那么会(x)提交l止指o(h).否则,它将?x)被回?
    10.        transaction 理器与数据库配合进行提交和回滚.

    在这步中的测试怎样q行?我们可重用BankTest 和它清晰的事务划?因ؓ(f)PROPAGATION_REQUIRED, proxy和在BankTest中创建的事务上下文一h?

    W五?~写PROPAGATION_REQUIRED

    在这?我们添加JMS处理逻辑.Z(jin)做到q样,我们主要需要实现JMS MessageListener接口.我们也会(x)d公共的setBankҎ(gu)使Spring的依赖注入v作用.源代码如?

    package jms;
    import jdbc.Bank;
    import javax.jms.Message;
    import javax.jms.MapMessage;
    import javax.jms.MessageListener;

    public class MessageDrivenBank
    implements MessageListener
    {
        private Bank bank;

        public void setBank ( Bank bank )
        {
            this.bank = bank;
        }

        //this method can be private
        //since it is only needed within
        //this class
        private Bank getBank()
        {
            return this.bank;
        }

        public void onMessage ( Message msg )
        {
            try {
              MapMessage m = ( MapMessage ) msg;
              int account = m.getIntProperty ( "account" );
              int amount = m.getIntProperty ( "amount" );
              bank.withdraw ( account , amount );
              System.out.println ( "Withdraw of " +
              amount + " from account " + account );
            }
            catch ( Exception e ) {
              e.printStackTrace();
                
              //force rollback
              throw new RuntimeException (
              e.getMessage() );
            }
        }
        
    }



    W六?配置MessageDrivenBank

    q里我们配置MessageDrivenBankȝ听事务的QueueReceiverSessionPool.q样l我们可以实现和EJB(没有丢失信息和冗余信?cM的消息机?但在q里我们是用单的POJO对象实现.当向pool中插入一个MessageListener,q个?x)话池将保用JTA/XA事务接收到消?l合JTA/XA-capable 的JDBC数据?我们可以实现可靠的消息机?  Spring的配|如?

    <?xml version="1.0" encoding="UTF-8"?>


    <!--
            NOTE: no explicit transaction manager bean
            is necessary
            because the QueueReceiverSessionPool will
            start transactions by itself.
    -->
    <beans>
        <bean id="datasource"
            class="com.atomikos.jdbc.nonxa.NonXADataSourceBean">
            <property name="user">
                <value>sa</value>
            </property>
            <property name="url">
                <value>jdbc:hsqldb:SpringNonXADB</value>
            </property>
            <property name="driverClassName">
                <value>org.hsqldb.jdbcDriver</value>
            </property>
            <property name="poolSize">
                <value>1</value>
            </property>
            <property name="connectionTimeout">
                <value>60</value>
            </property>
        </bean>
        <bean id="xaFactory"
            class="org.activemq.ActiveMQXAConnectionFactory">
            <property name="brokerURL">
                <value>tcp://localhost:61616</value>
            </property>
        </bean>
        <bean id="queue"
            class="org.activemq.message.ActiveMQQueue">
            <property name="physicalName">
                <value>BANK_QUEUE</value>
            </property>
        </bean>
        <bean id="bank" class="jdbc.Bank">
            <property name="dataSource">
                <ref bean="datasource"/>
            </property>
        </bean>
        <bean id="messageDrivenBank"
            class="jms.MessageDrivenBank">
            <property name="bank">
                <ref bean="bank"/>
            </property>
        </bean>
        <bean id="queueConnectionFactoryBean"
            class="com.atomikos.jms.QueueConnectionFactoryBean">
            <property name="resourceName">
                <value>QUEUE_BROKER</value>
            </property>
            <property name="xaQueueConnectionFactory">
                <ref bean="xaFactory"/>
            </property>
        </bean>
        <bean id="queueReceiverSessionPool"
            class="com.atomikos.jms.QueueReceiverSessionPool"
            init-method="start">
            
            <property name="queueConnectionFactoryBean">
                <ref bean="queueConnectionFactoryBean"/>
            </property>
            <property name="transactionTimeout">
                <value>120</value>
            </property>
            <!--
            default license allows only limited
            concurrency so keep pool small
            -->
            <property name="poolSize">
                <value>1</value>
            </property>
            <property name="queue">
                <ref bean="queue"/>
            </property>
            <property name="messageListener">
                <ref bean="messageDrivenBank"/>
            </property>
        </bean>
    </beans>


    因ؓ(f)q篇文章需要一个便于安装的JMS服务,所以这里我们用ActiveMQ.如果你正在用另一个JMS实现,那么你将仍然能用这部分提出的技?接下来除?jin)datasource和bank对象,我们增加下面的对象定义:

    ·        xaFactory: 为徏立JMSq接的connection工厂.
    ·        queue: queue代表我们用的JMS队列, q个队列被配|成ActiveMQ要求的Ş?
    ·        queueConnectionFactoryBean:一个JTA-aware的JMSq接?
    ·        A queueReceiverSessionPool for JTA-enabled message consumptionQ注?我们同时指定?jin)用来调用的初始化方??start);q是Spring的另一个特? StartҎ(gu)在session poolc里定义,它是在Spring配置文g中进行配|的.
    ·        messageDrivenBankQ负责处理消?

    你可以问问自׃务管理是在哪里进行的.事实? 在先前部分被d的对象已消失.Z么呢?因ؓ(f)我们现在使用QueueReceiverSessionPool来接收来自JMS的消?q且q个cM为每ơ接收启动一个JTA事务.我们也可以保留JTA配置Q另外添加JMS配置, 但是q样可能?x)XML文g更长. 现在session poolcd担当事务理角色.它和proxyҎ(gu)的工作相? 只是q个c需要JMS  MessageListener Zd事务. 通过q样配置,在每个消息收接之前程序将启动一个新事务 无论何时, 当我们的消息实例正常q回? q个事务提? 如果出现RuntimeException, 那么q个事务回? l构如下面图5(可以清淅地看C些JMS对象).

    image
    Figure 5. Architecture for message-driven applications in Spring

    现在该架构工作如?
    1.        应用E序调用bank对象和初始化数据库表.
    2.        应用E序queueReceiverSessionPool, 因此触发一个startҎ(gu)的调用去监听到达的消?
    3.        queueReceiverSessionPool在队列中侦察一个新消息.
    4.        queueReceiverSessionPool开始一个新事务,q且注册q个事务.
    5.        queueReceiverSessionPool调用已注册的MessageListener (messageDrivenBank).
    6.        q将触发对bank 对象的调?
    7.        bank 对象通过datasource讉K数据?
    8.        datasource注册事务.
    9.        通过JDBC讉K数据?
    10.        当处理完成时, queueReceiverSessionPool?x)终止这个事务。然后进行commint(除非发生RuntimeException).
    11.        transaction manager开始消息队列的两阶D|?
    12.        transaction manager开始数据库的两阶段提交.

    W七?~写应用E序
    因ؓ(f)我们没有使用容器,我们仅仅提供一个Java应用E序可以启动整个银行系l?我们的Java应用E序是非常简? 它有能力扑֛配置的对?Spring通过XML文g他们放C?. q个应用E序能在M兼容的JDK(Java Development Kit)上运?q且不需要应用服务器.

    package jms;
    import java.io.FileInputStream;
    import java.io.InputStream;
    import org.springframework.beans.factory.xml.XmlBeanFactory;
    import com.atomikos.jms.QueueReceiverSessionPool;
    import jdbc.Bank;

    public class StartBank
    {
      public static void main ( String[] args )
      throws Exception
      {
        //open bean XML file
        I(yng)nputStream is =
        new FileInputStream(args[0]);
        
        //the factory is Spring's entry point
        //for retrieving the configured
        //objects from the XML file
        XmlBeanFactory factory =
            new XmlBeanFactory(is);
        
        //retrieve the bank to initialize
        //alternatively, this could be done
        //in the XML configuration too
        Bank bank =
            ( Bank ) factory.getBean ( "bank" );
        
        //initialize the bank if needed
        bank.checkTables();

        //retrieve the pool;
        //this will also start the pool
        //as specified in the beans XML file
        //by the init-method attribute!

        QueueReceiverSessionPool pool  =
            ( QueueReceiverSessionPool )
            factory.getBean (
            "queueReceiverSessionPool" );

        //Alternatively, start pool here
        //(if not done in XML)
        //pool.start();

        System.out.println (
            "Bank is listening for messages..." );
            
      }
    }


    q就是J2EE!是不是认为J2EE也很Ҏ(gu)?

    寚w用性的考虑
    q部分里我们看看更多的概?q些概念在许多J2EE应用E序中是很重要的.我们同样看到对q些概念来说Q一个应用服务器q不是必ȝ.

    集群和可扩展?/b>
    健壮的企业应用程序需要集来分流负担. 在消息驱动应用程序的例子?q很Ҏ(gu):我们自动CJMS应用E序l承得来处理能力.如果我们需要更强大的处理能?那么我们只需增加更多q接相同JMS服务器的q程.一个对服务性能有效的衡量标准是在队列中停留的消息的数量. 在其他情况下,如基于web?架构(如下)我们能很Ҏ(gu)C用web环境下的集群能力.

    Ҏ(gu)U别的安?/b>
    一个典型的观点是认为EJB能增加方法的安全?虽然q没有在q篇文章中提?但是在Sping中配|方法别的安全是可能的.q种配置cM于我们增加方法的事务划分的方式.

    寚w消息驱动的应用程序的通用?/b>
    在不改变源代码的情况?除了(jin)d用程序类),我们使用的^台能很容易地被整合到MJ2EE web 应用服务? 换句话说 , 通过JMSq行后台处理;q得web服务器在面对后台处理的gq问题上更可靠和更独?在Q何情况下, Z(jin)实现容器理的事务或容器理的安全?我们都不再需要依靠EJB容器来实?

    关于容器理持久?
    存在q被很多开发者检验过的技术例?JDO或者Hibernate 都不一定需要一个应用服务器. 另外,q些工具已经占据?jin)持久化市(jng)?

    l论
    今天,不需要应用服务器的J2EE已经成ؓ(f)可能Q也很容? 有h或许?x)?没有应用E序服务?有一些应用程序仍不能实现:例如,如果你需要一般的JCA(Java Connectivity API) 功能? 那么我们上面提供的^台是不够? 但是,q可能会(x)发生改变Q因Z用用一个应用程序服务器q行开?式和部|的好处实在是太? Z来相? 来得J2EE是一个模块化的”选择你所需要”架? q与我们之前的完全基于应用服务器的方法相? 在这L(fng)情况?J2EE开发者将从应用服务器和EJB中解攑և?

    资源:
    ·        代码下蝲
    ·        Guy Pardon's presentation on Transactions in Spring published at TheServerSide
    ·        More information on Atomikos Transactions and message-driven functionality without EJB
    ·        The home page of Spring
    ·        More information on JUnit
    ·        FirstSQL is an easy-to-install, XA-compliant DBMS
    ·        More information on HSQLDB
    ·        More information on ActiveMQ

    rendong 2006-08-01 10:11 发表评论
    ]]>
    վ֩ģ壺 ޳AVӰƬ߹ۿ| ѿؼëƬ| 99ee6ȾþѾƷ6| ѿƬ| ޾Ʒþ| Ѿþþþþþ| ŷ޺ڴ| ޾Ʒһþ| һþAþѾƷ| ߲ŸԲ| 337pձŷ޴󵨾Ʒ555588| һëƬѹۿ| ߹ѹۿ| ޳77777| ˶wwwҹվ| ɫþþþۺ| AVר߹ۿ| 1000ֹ߽ѹۿƵ | Ҹ߳Ƶ| H߲߹ۿH| 99ѹۿƵ| þù޾Ʒ鶹| һëƬѹۿ| Ѹ߹ۿ| Ʒ456߲| ɫƬѹۿ| ֻĻ| 99þþƷƵ| þ޾Ʒۺ| ԺȫƵƵ| պ߹ۿ| ۺϾƷһ| ˻ҳվѹۿȫ| ĻۺСۺ| ձxxxxɫƵ| ޻ɫѵӰ| 6080ҹһëƬѿ| ƵƷ| 91Ƶ| þAV| þùɫAVѿ|