spring+mybatis无网启动报错
提示加载不到xsd或dtd文件
项目改造时,遇到了项目在无外网环境下无法启动的问题。启动时抛出java.net.UnknownHostException: mybatis.org 异常。
原因是spring在加载mybatis-config.xml 文件时,无法找到
mybatis-3-config.dtd 这个文件。一般遇到这种项目加载不到本地dtd或xsd文件时,只要检查引入的jar包版本是否与xml文件中声明的dtd或xsd文件是否相匹配即可。
于是,我检查了一下引入的mybatis版本3.2.3,xml文件中声明的是mybatis-3-config.dtd,jar包中是存在的。这就有点困惑了,那就看一下spring是怎么加载dtd文件的吧。
这个文件在
mybatis配置文件.png
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"
>
<configuration>
<!-- 全局参数 -->
<settings>
······
</settings>
<typeAliases>
·······
</typeAliases>
</configuration>
- spring加载dtd文件
spring 使用 org.springframework.beans.factory.xml.BeansDtdResolver这个类来加载dtd文件,这个文件的注释是这么写的:
/**
* EntityResolver implementation for the Spring beans DTD,
* to load the DTD from the Spring class path (or JAR file).
*
* <p>Fetches "spring-beans-2.0.dtd" from the class path resource
* "/org/springframework/beans/factory/xml/spring-beans-2.0.dtd",
* no matter whether specified as some local URL that includes "spring-beans"
* in the DTD name or as "http://www.springframework.org/dtd/spring-beans-2.0.dtd".
*
* @author Juergen Hoeller
* @author Colin Sampaleanu
* @since 04.06.2003
* @see ResourceEntityResolver
*/
public class BeansDtdResolver implements EntityResolver {
private static final String DTD_EXTENSION = ".dtd";
private static final String DTD_FILENAME = "spring-beans-2.0";
private static final String DTD_NAME = "spring-beans";
private static final Log logger = LogFactory.getLog(BeansDtdResolver.class);
@Override
public InputSource resolveEntity(String publicId, String systemId) throws IOException {
if (logger.isTraceEnabled()) {
logger.trace("Trying to resolve XML entity with public ID [" + publicId +
"] and system ID [" + systemId + "]");
}
if (systemId != null && systemId.endsWith(DTD_EXTENSION)) {
int lastPathSeparator = systemId.lastIndexOf("/");
//看这里,该类只加载以spring-beans开头的dtd文件
int dtdNameStart = systemId.indexOf(DTD_NAME, lastPathSeparator);
if (dtdNameStart != -1) {
String dtdFile = DTD_FILENAME + DTD_EXTENSION;
if (logger.isTraceEnabled()) {
logger.trace("Trying to locate [" + dtdFile + "] in Spring jar on classpath");
}
try {
Resource resource = new ClassPathResource(dtdFile, getClass());
InputSource source = new InputSource(resource.getInputStream());
source.setPublicId(publicId);
source.setSystemId(systemId);
if (logger.isDebugEnabled()) {
logger.debug("Found beans DTD [" + systemId + "] in classpath: " + dtdFile);
}
return source;
}
catch (IOException ex) {
if (logger.isDebugEnabled()) {
logger.debug("Could not resolve beans DTD [" + systemId + "]: not found in classpath", ex);
}
}
}
}
// Use the default behavior -> download from website or wherever.
return null;
}
@Override
public String toString() {
return "EntityResolver for spring-beans DTD";
}
}
这个类很简单,只有一个resolveEntity方法。我们看注释和源码知道这个方法只加载spring-beans开头的dtd文件,所以mybatis-3-config.dtd文件当然不会从本地加载。
给mybatis的SqlSessionFactoryBean配置新的mybatis-config.xml文件地址。
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="mapperLocations" value="classpath:/**/mapper/*Mapper.xml" />
<property name="configLocation" value="classpath:/mybaits/mybatis-config.xml"></property>
</bean>
mybatis有自己的xml解析器
mybatis-xml解析器.png
这个类也非常简单,也是只实现了EntityResolver接口。有兴趣可以看一下。
改完,断网启动,没有问题。齐活,溜。
/*
* Offline entity resolver for the iBATIS DTDs
*/
public class XMLMapperEntityResolver implements EntityResolver {
private static final Map<String, String> doctypeMap = new HashMap<String, String>();
private static final String IBATIS_CONFIG_DOCTYPE = "-//ibatis.apache.org//DTD Config 3.0//EN".toUpperCase(Locale.ENGLISH);
private static final String IBATIS_CONFIG_URL = "http://ibatis.apache.org/dtd/ibatis-3-config.dtd".toUpperCase(Locale.ENGLISH);
private static final String IBATIS_MAPPER_DOCTYPE = "-//ibatis.apache.org//DTD Mapper 3.0//EN".toUpperCase(Locale.ENGLISH);
private static final String IBATIS_MAPPER_URL = "http://ibatis.apache.org/dtd/ibatis-3-mapper.dtd".toUpperCase(Locale.ENGLISH);
private static final String MYBATIS_CONFIG_DOCTYPE = "-//mybatis.org//DTD Config 3.0//EN".toUpperCase(Locale.ENGLISH);
private static final String MYBATIS_CONFIG_URL = "http://mybatis.org/dtd/mybatis-3-config.dtd".toUpperCase(Locale.ENGLISH);
private static final String MYBATIS_MAPPER_DOCTYPE = "-//mybatis.org//DTD Mapper 3.0//EN".toUpperCase(Locale.ENGLISH);
private static final String MYBATIS_MAPPER_URL = "http://mybatis.org/dtd/mybatis-3-mapper.dtd".toUpperCase(Locale.ENGLISH);
private static final String IBATIS_CONFIG_DTD = "org/apache/ibatis/builder/xml/mybatis-3-config.dtd";
private static final String IBATIS_MAPPER_DTD = "org/apache/ibatis/builder/xml/mybatis-3-mapper.dtd";
static {
doctypeMap.put(IBATIS_CONFIG_URL, IBATIS_CONFIG_DTD);
doctypeMap.put(IBATIS_CONFIG_DOCTYPE, IBATIS_CONFIG_DTD);
doctypeMap.put(IBATIS_MAPPER_URL, IBATIS_MAPPER_DTD);
doctypeMap.put(IBATIS_MAPPER_DOCTYPE, IBATIS_MAPPER_DTD);
doctypeMap.put(MYBATIS_CONFIG_URL, IBATIS_CONFIG_DTD);
doctypeMap.put(MYBATIS_CONFIG_DOCTYPE, IBATIS_CONFIG_DTD);
doctypeMap.put(MYBATIS_MAPPER_URL, IBATIS_MAPPER_DTD);
doctypeMap.put(MYBATIS_MAPPER_DOCTYPE, IBATIS_MAPPER_DTD);
}
/*
* Converts a public DTD into a local one
*
* @param publicId Unused but required by EntityResolver interface
* @param systemId The DTD that is being requested
* @return The InputSource for the DTD
* @throws org.xml.sax.SAXException If anything goes wrong
*/
public InputSource resolveEntity(String publicId, String systemId)
throws SAXException {
if (publicId != null) publicId = publicId.toUpperCase(Locale.ENGLISH);
if (systemId != null) systemId = systemId.toUpperCase(Locale.ENGLISH);
InputSource source = null;
try {
String path = doctypeMap.get(publicId);
source = getInputSource(path, source);
if (source == null) {
path = doctypeMap.get(systemId);
source = getInputSource(path, source);
}
} catch (Exception e) {
throw new SAXException(e.toString());
}
return source;
}
private InputSource getInputSource(String path, InputSource source) {
if (path != null) {
InputStream in;
try {
in = Resources.getResourceAsStream(path);
source = new InputSource(in);
} catch (IOException e) {
// ignore, null is ok
}
}
return source;
}
}
网友评论