??????Struts+Spring+Hibernate是目前J2EE企業級開發絕對Killer級的框架組合,相當輕量,優雅,但毫不影響它的強大。雖然因為現在各種表示層框架發展異常活躍,Struts受到了前所未有的挑戰和沖擊,但是就目前使用面而言這三種框架的組合是JAVA程序員不得不深入的經典。他們相關的資料也比較多,實際運用中也最成熟。但是諸如JSF,iBATIS等后起之秀(說他們后起并不代表他們的歷史,只是流行程度)也漸漸的占據自己的一席之地成為某些場合下開發的不二選擇。在web服務器方面也是一樣,除了Tomcat我們還有了更多好的選擇(Resin ,Jetty等)。雖然各種框架都聲稱自己可以無縫集成各種其他框架,不需要任何修改運行在各種服務器上面,但是我在我最近的一些經驗發現把一些平時比較少組合在一起的東西放在一起后確實存在很多想不到的意外,排除起來也頗費一些周折。這可能也是開源和商業的差距,不完善的文檔,沒有足夠覆蓋面和嚴格的測試等,不過我們都要以一個寬容的態度對待開源,因為開源需要我們每個人的參與和貢獻,從而真正達到Richard. M. Stallman心中的軟件自由。
??????我會不斷的把在各種非常用的組合使用中遇到的的問題貼出來與大家分享,總有人會需要,呵呵。
??????言歸正傳,今天的主題是在Resin3下使用Spring和iBATIS的組合開發時碰到的SqlMap文件解析問題,和開頭提到的我的另一篇文章一樣,也是一個xml配置文件的解析問題。使用的仍然是SpringSide的HelloWorld示例,這里順便說個題外話SpringSide是一個我比較欣賞和推薦的國人自己打造的企業級快速開發框架,特別是他的wiki真是一個非常好的各種相關技術的學習資料,但是他發布的示例中含有不少的錯誤,特別是“非主流的”(非Tomcat下,iBATIS等)模式下有很多問題,希望SpringSide的大俠們能夠更加完善。我是你們忠實的支持者。
??????上篇文章? 中提到了Resin3下使用Spring必須要更換默認的xml解析器,換成xerces后就可以正常啟動了,然后把持久層框架由Hibernate換為iBATIS后又出現了新的問題,啟動時一切正常,Spring和Struts的初始化都沒有問題,但是當第一次需要連接數據庫時就出現了問題,因為這個時候需要解析SqlMap文件來提供對應的Sql語句進行查詢RDB,從異常信息來判斷問題出在文件解析的時候,錯誤信息如下(因為很長,所以只貼了最底層的異常):
?1
Caused?by:?
?2
org.xml.sax.SAXParseException:?Content?is?not?allowed?in?prolog.
?3
????at?org.apache.xerces.util.ErrorHandlerWrapper.createSAXParseException(Unknown?Source)
?4
????at?org.apache.xerces.util.ErrorHandlerWrapper.fatalError(Unknown?Source)
?5
????at?org.apache.xerces.impl.XMLErrorReporter.reportError(Unknown?Source)
?6
????at?org.apache.xerces.impl.XMLErrorReporter.reportError(Unknown?Source)
?7
????at?org.apache.xerces.impl.XMLScanner.reportFatalError(Unknown?Source)
?8
????at?org.apache.xerces.impl.XMLDocumentScannerImpl$PrologDispatcher.dispatch(Unknown?Source)
?9
????at?org.apache.xerces.impl.XMLDocumentFragmentScannerImpl.scanDocument(Unknown?Source)
10
????at?org.apache.xerces.parsers.XML11Configuration.parse(Unknown?Source)
11
????at?org.apache.xerces.parsers.XML11Configuration.parse(Unknown?Source)
12
????at?org.apache.xerces.parsers.XMLParser.parse(Unknown?Source)
13
????at?org.apache.xerces.parsers.DOMParser.parse(Unknown?Source)
14
????at?org.apache.xerces.jaxp.DocumentBuilderImpl.parse(Unknown?Source)
15
????at?com.ibatis.common.xml.NodeletParser.createDocument(NodeletParser.java:150)
16
????at?com.ibatis.common.xml.NodeletParser.parse(NodeletParser.java:50)
17
????at?com.ibatis.sqlmap.engine.builder.xml.SqlMapParser.parse(SqlMapParser.java:45)
18
????at?com.ibatis.sqlmap.engine.builder.xml.SqlMapConfigParser$11.process(SqlMapConfigParser.java:347)
19
????at?com.ibatis.common.xml.NodeletParser.processNodelet(NodeletParser.java:112)
20
????at?com.ibatis.common.xml.NodeletParser.process(NodeletParser.java:75)
21
????at?com.ibatis.common.xml.NodeletParser.process(NodeletParser.java:93)
22
????at?com.ibatis.common.xml.NodeletParser.parse(NodeletParser.java:63)
23
????at?com.ibatis.common.xml.NodeletParser.parse(NodeletParser.java:51)
24
????at?com.ibatis.sqlmap.engine.builder.xml.SqlMapConfigParser.parse(SqlMapConfigParser.java:78)
25
????at?com.ibatis.sqlmap.client.SqlMapClientBuilder.buildSqlMapClient(SqlMapClientBuilder.java:62)
26
????at?org.springframework.orm.ibatis.SqlMapClientFactoryBean.afterPropertiesSet(SqlMapClientFactoryBean.java:255)
27
????at?org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1062)
28
????at?org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1029)
29
????at?org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:420)
30
????at?org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:245)
31
????at?org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:141)
32
????at?org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:242)
33
????at?org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:156)
34
????at?org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireByName(AbstractAutowireCapableBeanFactory.java:749)
35
????at?org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:704)
36
????at?org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:416)
37
????at?org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:245)
38
????at?org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:141)
39
????at?org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:242)
40
????at?org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:223)
41
????at?org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:156)
42
????at?org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireByName(AbstractAutowireCapableBeanFactory.java:749)
43
????at?org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:704)
44
????at?org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:416)
45
????at?org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:245)
46
????at?org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:141)
47
????at?org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:242)
48
????at?org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:160)
49
????at?org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:646)
50
????at?org.springframework.web.struts.DelegatingRequestProcessor.getDelegateAction(DelegatingRequestProcessor.java:168)
51
????at?org.springframework.web.struts.DelegatingRequestProcessor.processActionCreate(DelegatingRequestProcessor.java:146)
52
????at?org.apache.struts.action.RequestProcessor.process(RequestProcessor.java:230)
53
????at?org.apache.struts.action.ActionServlet.process(ActionServlet.java:1196)
54
????at?org.apache.struts.action.ActionServlet.doGet(ActionServlet.java:414)
55
????at?javax.servlet.http.HttpServlet.service(HttpServlet.java:115)
56
????at?javax.servlet.http.HttpServlet.service(HttpServlet.java:92)
57
????at?com.caucho.server.dispatch.ServletFilterChain.doFilter(ServletFilterChain.java:106)
58
????at?org.extremecomponents.table.filter.AbstractExportFilter.doFilter(AbstractExportFilter.java:49)
59
????at?com.caucho.server.dispatch.FilterFilterChain.doFilter(FilterFilterChain.java:70)
60
????at?org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:78)
61
????at?org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:77)
62
????at?com.caucho.server.dispatch.FilterFilterChain.doFilter(FilterFilterChain.java:70)
63
????at?com.caucho.server.webapp.WebAppFilterChain.doFilter(WebAppFilterChain.java:178)
64
????at?com.caucho.server.dispatch.ServletInvocation.service(ServletInvocation.java:229)
65
????at?com.caucho.server.http.HttpRequest.handleRequest(HttpRequest.java:268)
66
????at?com.caucho.server.port.TcpConnection.run(TcpConnection.java:389)
67
????at?com.caucho.util.ThreadPool.runTasks(ThreadPool.java:507)
68
????at?com.caucho.util.ThreadPool.run(ThreadPool.java:433)
69
????at?java.lang.Thread.run(Thread.java:595)

?2

?3

?4

?5

?6

?7

?8

?9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

??????異常提示是Content is not allowed in prolog.意思就是說文件開頭的時候有問題。。。我E文不好不能解釋的“信,雅,達”,見諒,呵呵。還是跟我為了Spring而換上的xerces有關。。。狂暈,google了一番發現是一個叫BOM的東西搞的鬼,而xerces確實有BOM的問題,在Apache的論壇里看到一個帖子也提到xerces的BOM問題(可是借助靈格斯 一個單詞一個單詞翻譯的看的。。。,順便吐血推薦“靈格斯”,一款比金山詞霸好用X倍速度快Y倍資源占用少Z倍的免費軟件,注意是無插件,無廣告,完全免費!!!金山現在為了反D版,所有裝了D版金山的XP老是無緣無故死機,大家應該都很郁悶吧。被我推薦用靈格斯的人都說好,群眾的眼睛是雪亮的啊。支持面向個人用戶的軟件免費!靠好質量好服務賺錢才是王道!!!),回帖是說換掉xerces用其他解析器,可是我不能換,換了Spring還指望它呢,我也不知道換成什么比它更好。。。,還有的說給xerces的如果是stream就可以接受BOM,如果是reader就不行(我默認大家都對JAVA的IO有一定了解能夠理解stream和reader啊),難道我跑去把iBATIS里的源代碼改了,然后重新編譯???這樣也不好吧,總有簡單點的方式解決吧。
??????于是繼續google,隨著對BOM進一步的了解發現把SqlMap文件里的BOM給刪除調應該是最快最方便的方式,因為BOM在UTF-8里面并不起什么作用,本來是可有可無的。下面先讓大家對BOM有個大概的認識(細節可以參考官方網站?),然后告訴大家怎么樣快捷的刪掉文件里的BOM。
??????我們這里提到的BOM并不是制造業管理中料表(Bill of Material)的概念,而是UCS(Unicode的學名是"Universal Multiple-Octet Coded Character Set",簡稱為UCS)編碼中有一個叫做"ZERO WIDTH NO-BREAK SPACE"的字符,它的編碼是FEFF。而FFFE在UCS中是不存在的字符,所以不應該出現在實際傳輸中。UCS規范建議我們在傳輸字節流前,先傳輸字符"ZERO WIDTH NO-BREAK SPACE"。這樣如果接收者收到FEFF,就表明這個字節流是Big-Endian(big endian和little endian是CPU處理多字節數的不同方式。例如“漢”字的Unicode編碼是6C49。那么寫到文件里時,究竟是將6C寫在前面,還是將49寫在前面?如果將6C寫在前面,就是big endian。還是將49寫在前面,就是little endian。我們一般將endian翻譯成“字節序”,將big endian和little endian稱作“大尾”和“小尾”。)的;如果收到FFFE,就表明這個字節流是Little-Endian的。因此字符"ZERO WIDTH NO-BREAK SPACE"又被稱作BOM。
??????UTF-8是以8位為單位來表示UCS,其實不存在什么尾序的問題,其實BOM是沒有什么作用的,不過可以用BOM來表示編碼方式,字符"ZERO WIDTH NO-BREAK SPACE"的UTF-8編碼是EF BB BF,所以如果接受到以EF BB BF開頭的字節流就可以判斷是UTF-8編碼了。
??????如果上面提到的一些名詞你不是很清楚可以google一下,找到相關的解釋。追根究底是個好習慣?。如果對BOM的概念還不清楚或者想了解更多也可以搜索相關資料來研究。
??????下面重點來說一下怎么去掉BOM:
??????首先windows自帶的記事本會在保存UTF-8編碼的文件時不管是否含有BOM一律加上BOM,所以千萬別用記事本。我的程序跑不起來也是因為用記事本打開過。。。有段時間非常流行的“聯通”問題就是跟windows記事本處理BOM的機制有關,就是把“聯通”兩字寫進記事本保存后再打開時就會是黑點。。。如果你有興趣可以到網上找到相關文章詳細解釋給你聽,呵呵。
??????比較新版本的UltraEdit(以下簡稱UE,大家都是這么叫的)都對BOM支持比較好,所以推薦使用新版本的UE來解決這個問題。我用的版本是UltraEdit-32 10.20d+中文版(網上可以下的到,如果你比較懶可以聯系我,留下郵箱,呵呵)。首先打開UE,選擇菜單欄“高級”-“配置”,出現一個選項卡窗口,在“常規”選項卡中找到“保存時對所有UTF-8文件頭標記(BOM)”和“對在UltraEdit里創建的新文件寫入UTF-8文件頭標記(如上面)”,把它們的復選框都取消不選,然后確定。這個時候UE默認情況下就不會對沒有BOM的文件加入BOM,注意如果不執行上述操作UE默認也是會加的!那已經含有BOM的文件如何清除BOM呢?下面演示給大家。首先打開文件,選擇菜單欄“文件”-“格式轉換”-“UTF-8 轉 ASCII”,這樣文件會被轉為ASCII編碼,BOM就不會存在了,因為上面說過BOM是在UCS編碼的頭部的,然后再選擇菜單欄“文件”-“格式轉換”-“ASCII 轉 UTF-8(Unicode編輯)”,這個時候文件會被轉回為UTF-8編碼,因為我們上面選了不自動加BOM,所以這個時候的文件是不帶BOM的,然后保存文件。整個操作過程就完畢了。(如果仍然有什么疑問給我留言吧)
??????我去掉BOM后重新啟動服務器,訪問數據庫一切正常,問題果然解決了。當然在我碰到的這個問題中干脆把文件轉成ASCII直接用也可以,汗!不知道為什么,請大家指教!