天天看點

SqlSession的建立過程一-MyBatis如何解析配置檔案

作者:小雨在進步

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

繼續閱讀