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執行個體,進行解析的。