MyBatis的主配置文件和Mapper配置都使用的是XML格式。MyBatis中的Configuration组件用于描述主配置文件信息,框架在启动时会解析XML配置,将配置信息转换为Configuration对象。
JDK API中提供了3种方式解析XML,分别为DOM、SAX和XPath。这3种方式都有各自的特点,具体优缺点读者可参考相关资料。在这3种方式中,API最易于使用的就是XPath方式,MyBatis框架中也采用XPath方式解析XML文件中的配置信息。
<?xml version="1.0" encoding="UTF-8" ?>
<users>
<user id = "1">
<name>张三</name>
<createTime>2018-06-06 00:00:00</createTime>
<passward>admin</passward>
<phone>180000000</phone>
<nickName>阿毛</nickName>
</user>
<user id = "2">
<name>李四</name>
<createTime>2018-06-06 00:00:00</createTime>
<passward>admin</passward>
<phone>180000001</phone>
<nickName>明明</nickName>
</user>
</users>
XML文件中的配置信息可以通过一个Java类来描述,代码如下:
@Data
public class UserEntity {
private Long id;
private String name;
private Date createTime;
private String password;
private String phone;
private String nickName;
}
我们需要将XML内容转换为UserEntity实体对象,存放在List对象中,解析代码如下:
@Test
public void testXPathParser() {
try {
// 创建DocumentBuilderFactory实例
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
// 创建DocumentBuilder实例
DocumentBuilder builder = factory.newDocumentBuilder();
InputStream inputSource = Resources.getResourceAsStream("users.xml");
Document doc = builder.parse(inputSource);
// 获取XPath实例
XPath xpath = XPathFactory.newInstance().newXPath();
// 执行XPath表达式,获取节点信息
NodeList nodeList = (NodeList)xpath.evaluate("/users/*", doc, XPathConstants.NODESET);
List<UserEntity> userList = new ArrayList<>();
for(int i=1; i < nodeList.getLength() + 1; i++) {
String path = "/users/user["+i+"]";
String id = (String)xpath.evaluate(path + "/@id", doc, XPathConstants.STRING);
String name = (String)xpath.evaluate(path + "/name", doc, XPathConstants.STRING);
String createTime = (String)xpath.evaluate(path + "/createTime", doc, XPathConstants.STRING);
String passward = (String)xpath.evaluate(path + "/passward", doc, XPathConstants.STRING);
String phone = (String)xpath.evaluate(path + "/phone", doc, XPathConstants.STRING);
String nickName = (String)xpath.evaluate(path + "/nickName", doc, XPathConstants.STRING);
// 调用buildUserEntity()方法,构建UserEntity对象
UserEntity userEntity = buildUserEntity(id,name, createTime, passward, phone, nickName);
userList.add(userEntity);
}
System.out.println(JSON.toJSONString(userList));
} catch (Exception e) {
throw new BuilderException("Error creating document instance. Cause: " + e, e);
}
}
private UserEntity buildUserEntity(String id,String name,
String createTime, String passward,
String phone, String nickName)
throws IllegalAccessException, InvocationTargetException {
UserEntity userEntity = new UserEntity();
DateConverter dateConverter = new DateConverter(null);
dateConverter.setPattern("yyyy-MM-dd HH:mm:ss");
ConvertUtils.register(dateConverter,Date.class);
BeanUtils.setProperty(userEntity,"id",id);
BeanUtils.setProperty(userEntity,"name",name);
BeanUtils.setProperty(userEntity,"createTime",createTime);
BeanUtils.setProperty(userEntity,"passward",passward);
BeanUtils.setProperty(userEntity,"phone",phone);
BeanUtils.setProperty(userEntity,"nickName",nickName);
return userEntity;
}
如上面的代码所示,使用JDK提供的XPath相关API解析XML需要以下几步:
(1)创建表示XML文档的Document对象无论通过哪种方式解析XML,都需要先创建表示XML文档的Document对象。Document对象的创建依赖于DocumentBuilder对象,DocumentBuilder采用工厂模式创建,所以我们首先需要调用DocumentBuilderFactory类的newInstance()方法创建DocumentBuilderFactory对象,然后调用BuilderFactory对象的newDocumentBuilder()方法创建DocumentBuilder对象,最后调用DocumentBuilder对象的parse()方法创建Document对象。
(2)创建用于执行XPath表达式的XPath对象XPath对象也是采用工厂模式创建的,我们首先需要调用XPathFactory工厂类的newInstance()方法获取XPathFactory工厂实例,然后调用XPathFactory对象的newXPath()方法获取XPath实例。
(3)使用XPath对象执行表达式,获取XML内容有了XPath实例后,就可以执行XPath表达式了。XPath表达式的执行结果为XML节点对象(例如Node、Element、NodeList等)或者字符串、数值类型等。
为了简化XPath解析操作,MyBatis通过XPathParser工具类封装了对XML的解析操作,同时使用XNode类增强了对XML节点的操作。使用XNode对象,我们可以很方便地获取节点的属性、子节点等信息。依然是前面的案例,我们看一下如何使用XPathParser工具类将users.xml文件中的配置信息转换为UserEntity实体对象,具体代码如下:
@Test
public void testXPathParser() throws Exception {
Reader resource = Resources.getResourceAsReader("users.xml");
XPathParser parser = new XPathParser(resource);
// 注册日期转换器
DateConverter dateConverter = new DateConverter(null);
dateConverter.setPattern("yyyy-MM-dd HH:mm:ss");
ConvertUtils.register(dateConverter, Date.class);
List<UserEntity> userList = new ArrayList<>();
// 调用evalNodes()方法获取XNode列表
List<XNode> nodes = parser.evalNodes("/users/*");
// 对XNode对象进行遍历,获取user相关信息
for (XNode node : nodes) {
UserEntity userEntity = new UserEntity();
Long id = node.getLongAttribute("id");
BeanUtils.setProperty(userEntity, "id", id);
List<XNode> childNods = node.getChildren();
for (XNode childNode : childNods) {
BeanUtils.setProperty(userEntity, childNode.getName(),
childNode.getStringBody());
}
userList.add(userEntity);
}
System.out.println(JSON.toJSONString(userList));
}
如上面的代码所示,使用MyBatis封装的XPathParser对XML进行解析,省去了Document对象和XPath对象的创建过程,XPathParser工具类封装了执行XPath表达式的方法,很大程度上简化了XML解析过程。
最后看MyBatis框架是怎么使用的
Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
// 创建XMLConfigBuilder实例
XMLConfigBuilder builder = new XMLConfigBuilder(reader);
// 调用XMLConfigBuilder.parse()方法,解析XML创建Configuration对象
Configuration conf = builder.parse();
主要的解析就是在创建XMLConfigBuilder实例,进行解析的。