spring、mybatis加载xml源码解析

来源:互联网 发布:台灯 淘宝网 编辑:程序博客网 时间:2024/06/03 09:30
最近在研究spring以及mybatis源码,这两款优秀框架的使用必然是从加载配置文件来加载配置的,闲话少说,先进入spring加载配置文件过程

    举例一个最简单的配置文件

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:cache="http://www.springframework.org/schema/cache" xmlns:context="http://www.springframework.org/schema/c"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd 
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/cache 
http://www.springframework.org/schema/cache/spring-cache.xsd">
    <bean id="hello" class="com.HelloEntity">
        <!-- 配置该Bean需要注入的属性(是通过属性set方法来注入的) -->
        <property name="info" value="Happy New Year!"/>
    </bean>
</beans>
  配置文件中并没有设置与其他框架整合,仅仅是注册一个HelloBeanEntity
  使用方式贴一个简单代码
  BeanFactory beanFactory= new ClassPathXmlApplicationContext("appclicationcontext.xml");
  HelloEntity helloEntity=(HelloEntity)beanFactory.getBean("hello");

  System.out.println(helloEntity.info);

    仅仅三行代码,但是五脏俱全,简单来说,
    第一行代码  spring加载appclicationcontext.xml数据,解析出bean标签,将HelloEntity注册并初始化后
    放入singleonObjects缓存中
    第二行代码  spring在singleonObjects根据id取到第一步加载的对象


    直接上spring加载xml中代码 spring将appclicationcontext.xml加载为inputstream之后
    public Document loadDocument(InputSource inputSource, EntityResolver entityResolver, ErrorHandler errorHandler,         int validationMode, boolean namespaceAware) throws Exception {
    DocumentBuilderFactory factory = this.createDocumentBuilderFactory(validationMode, namespaceAware);
    if(logger.isDebugEnabled()) {
      logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]");
    }
    DocumentBuilder builder = this.createDocumentBuilder(factory, entityResolver, errorHandler);
    return builder.parse(inputSource);
  }
  其实spring还是用的javax.xml.parsers.DocumentBuilderFactory,采用sax方式读取xml并封装返回值document(w3c),
  简单说一下xml解析基本方式,1 sax基于事件驱动机制,逐行读取,读到一个标签就处理,不占用内存,处理完就回不去了
                          2 dom将整个xml加载为一个dom树,类似前端html元素,可以修改dom树,操作期间数据都在内存
  介绍一下代码中的entityResolver以及errorHandler
  ErrorHandler

  用于处理XML解析阶段所发生的警告和错误.里面有三个方法,warning(), error()和fatalError(). waring和error用于处理XML的validation(DTD或XSD)错误.这种错误并不影响XML的解析,你可以把这种错误产生的exception压下来,而不向上抛.这样XML的解析不会被终断. fatalError是XML结构错误,这种错误无法被压制,即使我的handler不抛,Parser会向外抛exception.


  看一下spring实现的ErrorHandler
  public class SimpleSaxErrorHandler implements ErrorHandler {
  private final Log logger;
  public SimpleSaxErrorHandler(Log logger) {
    this.logger = logger;
  }
  public void warning(SAXParseException ex) throws SAXException {
    this.logger.warn("Ignored XML validation warning", ex);
  }
  public void error(SAXParseException ex) throws SAXException {
    throw ex;
  }
  public void fatalError(SAXParseException ex) throws SAXException {
    throw ex;
  }
}
  其实就是在解析过程中按照错误log级别处理,spring中是error以上会直接向上抛异常
  EntityResolver
  在XML的验证段落里面提到过DTD的定位. EntityResolver可以帮助我们做这件事情. EntityResolver里面只有一个方法,叫做ResolveEntity(publicId, systemId). 每当Parser需要使用external文件的时候,就会调用这个方法. 我们可以在这个方法里面做一些预处理. 代码如下:
  看一下spring实现的EntityResolver中解析会调用的方法
   public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
    if(systemId != null) {
      if(systemId.endsWith(".dtd")) {
        return this.dtdResolver.resolveEntity(publicId, systemId);
      }
      if(systemId.endsWith(".xsd")) {
        return this.schemaResolver.resolveEntity(publicId, systemId);
      }
    }
    return null;
  }

  是针对两种xml分别处理的

  回到spring流程,目前是已经加载获得了document,解析来就是去读取document(代码精简了一下)
  Element root = doc.getDocumentElement();//获取根节点
  private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
    if(delegate.nodeNameEquals(ele, "import")) {
      this.importBeanDefinitionResource(ele);
    } else if(delegate.nodeNameEquals(ele, "alias")) {
      this.processAliasRegistration(ele);
    } else if(delegate.nodeNameEquals(ele, "bean")) {
      this.processBeanDefinition(ele, delegate);
    } else if(delegate.nodeNameEquals(ele, "beans")) {
      this.doRegisterBeanDefinitions(ele);
    }
  }
  spring中主要的4个标签,这边不继续说了,下边会单独开一篇,下面进入mybatis的解析
  先来一个mybatis配置文件
  <?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>
    <properties resource="jdbc.properties"/>
    <typeAliases>H
        <typeAlias alias="hello" type="com.HelloEntity"/>
    </typeAliases>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="">
            ......
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="mappers/HelloMapper.xml"/>
    </mappers>
</configuration>
代码
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSession session = sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
HelloDao helloDao = session.getMapper(HelloDao.class);//可以直接调sql了
下边进入mybaits xml解析
mybaits有自己封装几个类帮助解析,但是最后还是用的跟spring一模一样的解析方式
直接贴
mybatis的entityResolver以及errorHandler
public InputSource resolveEntity(String publicId, String systemId) throws SAXException {
    try {
      if(systemId != null) {
        String e = systemId.toLowerCase(Locale.ENGLISH);
        if(e.contains("mybatis-3-config.dtd") || e.contains("ibatis-3-config.dtd")) {
          return this.getInputSource("org/apache/ibatis/builder/xml/mybatis-3-config.dtd", publicId, systemId);
        }
        if(e.contains("mybatis-3-mapper.dtd") || e.contains("ibatis-3-mapper.dtd")) {
          return this.getInputSource("org/apache/ibatis/builder/xml/mybatis-3-mapper.dtd", publicId, systemId);
        }
      }
      return null;
    } catch (Exception var4) {
      throw new SAXException(var4.toString());
    }
  }
  有木有感觉跟spring很像,都是针对配置文件的头部
  errorhandler更是基本一样
  new ErrorHandler() {
        public void error(SAXParseException exception) throws SAXException {
          throw exception;
        }
        public void fatalError(SAXParseException exception) throws SAXException {
          throw exception;
        }
        public void warning(SAXParseException exception) throws SAXException {
        }
      });


  个人对于mybaits的理解就是这个框架将你在配置文件里的sql以及返回type加载到map里边,当你调用你写的mapper接口时
  其实已经被动态代理,最后还是connection去执行sql,由于你配置了type,会直接反射返回的数据,完成orm映射
原创粉丝点击