天天看点

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实例,进行解析的。

继续阅读