近幾天一個bug困擾著我, 今天決定解決掉它.
沒有聯網的情況下, 啟動項目就會報錯.
異常信息
org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: Failed to import bean definitions from relative location [config/*.xml]
Offending resource: URL [file:/D:/Work/JAVA/workspace/xbbsport/babasport-parent/babasport-service-product/target/babasport-service-product-1.0-SNAPSHOT/WEB-INF/classes/applicationContext-service.xml]; nested exception is org.springframework.beans.factory.BeanDefinitionStoreException: IOException parsing XML document from file [D:\Work\JAVA\workspace\xbbsport\babasport-parent\babasport-service-product\target\babasport-service-product-1.0-SNAPSHOT\WEB-INF\classes\config\mybatis-config.xml]; nested exception is java.net.UnknownHostException: mybatis.org
at org.springframework.beans.factory.parsing.FailFastProblemReporter.error(FailFastProblemReporter.java:70)
at org.springframework.beans.factory.parsing.ReaderContext.error(ReaderContext.java:85)
at org.springframework.beans.factory.parsing.ReaderContext.error(ReaderContext.java:76)
at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.importBeanDefinitionResource(DefaultBeanDefinitionDocumentReader.java:259)
at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.parseDefaultElement(DefaultBeanDefinitionDocumentReader.java:184)
at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.parseBeanDefinitions(DefaultBeanDefinitionDocumentReader.java:169)
at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.doRegisterBeanDefinitions(DefaultBeanDefinitionDocumentReader.java:142)
at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.registerBeanDefinitions(DefaultBeanDefinitionDocumentReader.java:94)
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.registerBeanDefinitions(XmlBeanDefinitionReader.java:508)
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadBeanDefinitions(XmlBeanDefinitionReader.java:392)
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:336)
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:304)
at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:181)
at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:217)
at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:188)
at org.springframework.web.context.support.XmlWebApplicationContext.loadBeanDefinitions(XmlWebApplicationContext.java:125)
at org.springframework.web.context.support.XmlWebApplicationContext.loadBeanDefinitions(XmlWebApplicationContext.java:94)
at org.springframework.context.support.AbstractRefreshableApplicationContext.refreshBeanFactory(AbstractRefreshableApplicationContext.java:129)
at org.springframework.context.support.AbstractApplicationContext.obtainFreshBeanFactory(AbstractApplicationContext.java:612)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:513)
at org.springframework.web.servlet.FrameworkServlet.configureAndRefreshWebApplicationContext(FrameworkServlet.java:668)
at org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:634)
at org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:682)
at org.springframework.web.servlet.FrameworkServlet.initWebApplicationContext(FrameworkServlet.java:553)
at org.springframework.web.servlet.FrameworkServlet.initServletBean(FrameworkServlet.java:494)
at org.springframework.web.servlet.HttpServletBean.init(HttpServletBean.java:136)
at javax.servlet.GenericServlet.init(GenericServlet.java:158)
at org.apache.catalina.core.StandardWrapper.initServlet(StandardWrapper.java:1269)
at org.apache.catalina.core.StandardWrapper.loadServlet(StandardWrapper.java:1182)
at org.apache.catalina.core.StandardWrapper.load(StandardWrapper.java:1072)
at org.apache.catalina.core.StandardContext.loadOnStartup(StandardContext.java:5362)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5660)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:145)
at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:1015)
at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:991)
at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:652)
at org.apache.catalina.startup.HostConfig.manageApp(HostConfig.java:1899)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.apache.tomcat.util.modeler.BaseModelMBean.invoke(BaseModelMBean.java:301)
at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(DefaultMBeanServerInterceptor.java:819)
at com.sun.jmx.mbeanserver.JmxMBeanServer.invoke(JmxMBeanServer.java:801)
at org.apache.catalina.mbeans.MBeanFactory.createStandardContext(MBeanFactory.java:618)
at org.apache.catalina.mbeans.MBeanFactory.createStandardContext(MBeanFactory.java:565)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.apache.tomcat.util.modeler.BaseModelMBean.invoke(BaseModelMBean.java:301)
at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(DefaultMBeanServerInterceptor.java:819)
at com.sun.jmx.mbeanserver.JmxMBeanServer.invoke(JmxMBeanServer.java:801)
at javax.management.remote.rmi.RMIConnectionImpl.doOperation(RMIConnectionImpl.java:1487)
at javax.management.remote.rmi.RMIConnectionImpl.access$300(RMIConnectionImpl.java:97)
at javax.management.remote.rmi.RMIConnectionImpl$PrivilegedOperation.run(RMIConnectionImpl.java:1328)
at javax.management.remote.rmi.RMIConnectionImpl.doPrivilegedOperation(RMIConnectionImpl.java:1420)
at javax.management.remote.rmi.RMIConnectionImpl.invoke(RMIConnectionImpl.java:848)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:322)
at sun.rmi.transport.Transport$1.run(Transport.java:177)
at sun.rmi.transport.Transport$1.run(Transport.java:174)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.Transport.serviceCall(Transport.java:173)
at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:556)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:811)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:670)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:745)
Caused by: org.springframework.beans.factory.BeanDefinitionStoreException: IOException parsing XML document from file [D:\Work\JAVA\workspace\xbbsport\babasport-parent\babasport-service-product\target\babasport-service-product-1.0-SNAPSHOT\WEB-INF\classes\config\mybatis-config.xml]; nested exception is java.net.UnknownHostException: mybatis.org
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadBeanDefinitions(XmlBeanDefinitionReader.java:410)
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:336)
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:304)
at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:181)
at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:217)
at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.importBeanDefinitionResource(DefaultBeanDefinitionDocumentReader.java:248)
... 69 more
Caused by: java.net.UnknownHostException: mybatis.org
分析
通過查找資料, 發現這應該是因為, 加載xml時, 加載約束進行校驗時, 需要聯網, 于是, 在沒有聯網的情況下, 就報異常, 一連上網, 立馬正常.
這個約束就是: mybatis-3-config.dtd
. 對應引用:
"http://mybatis.org/dtd/mybatis-3-config.dtd"
該約束在mybatis-config.xml
的頭中.
解決思路
根據網上的思路, 主要有兩種辦法:
- 將對應的約束放至本地, 然后引用本地約束.
- 避免tomcat掃描xml, 進行校驗.
首先, 采用了第一種思路, 于是將約束文件放至resources/下.
- mybatis-config.xml位于 /resources/config/ , 被spring其他配置文件導入.
- mybatis-3-config.dtd位于/resources/dtd/
- mybatis-config.xml頭:
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "/dtd/mybatis-3-config.dtd">
然而....
結果:
nested exception is java.io.FileNotFoundException:
\dtd\mybatis-3-config.dtd (系統找不到指定的路徑。)
原因猜測:
(1)mybatis-config.xml被web.xml模糊掃描到, "web.xml 掃描到會對xml 內容進行標準校驗,此時需要網絡獲取dtd; 如果這個文件只是配置到mybatis.xml里面的話。這個是不會去網絡獲取dtd 會根據本地jar 里面的dtd 進行校驗。"[1]
本項目中,tomcat加載解析web.xml后, 會創建Spring的DispatcherServlet, 從而加載解析applicationContext-service.xml(因為web.xml中配有<param-value>classpath:applicationContext-service.xml</param-value>
), 而applicationContext-service.xml中配有<import resource="config/*.xml"/>
, 而這個會掃描到resources/config/下的mybatis-config.xml. 從而會加載頭文件中的約束: /dtd/mybatis-3-config.dtd. 到這里, tomcat加載不到這個約束.
(2)約束需要放置resources/下, 才能被識別?
(3)只會在配置文件當前路徑及其子路徑下搜索?
以上3中可能性, (2)(3)的可能性應該都不大.
于是, 將mybatis-conf.xml移出/config/, 讓tomcat無法直接加載解析.
說明:
- 直接加載 tomcat加載并解析xml, 本文稱為
直接加載
. - 由Spring或Mybatis自身加載xml稱為
非直接加載
.
效果:
果然, 猜想(1)比較合理, 避開tomcat直接掃描mybatis-conf.xml后, 正常啟動了!
并且, 換成初始的網址形式, 依然成功. 說明Spring和Mybatis可以在jar包中搜尋約束, 而tomcat不能.
這樣, 就可以不用修改頭信息, 不用準備本地dtd約束, 放于項目目錄下...(太麻煩!!)
結論
再次重申那條評論(給力!):
"web.xml 掃描到會對xml 內容進行標準校驗,此時需要網絡獲取dtd; 如果這個文件只是配置到mybatis.xml里面的話。這個是不會去網絡獲取dtd 會根據本地jar 里面的dtd 進行校驗。"[1]