mybatis 源码分析, 初始化

分析版本

我们先分析xml配置文件形式的 mybatis, 等我们分析完毕了, 可以去分析 mybatis 和 spring 的对接

pom.xml

<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.4.2</version>
</dependency>

案例代码

public static void main(String[] args) throws IOException {
    String resource = "mybatis-config.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    SqlSession sqlSession = sqlSessionFactory.openSession();
    try {
        AreaMapper mapper = sqlSession.getMapper(AreaMapper.class);
        List<Area> all = mapper.getAll();
        for (Area item : all)
            System.out.println(item.getAreaName());
    } finally {
        sqlSession.close();
    }
}

mybatis-config.xml

<?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="db.properties"></properties>

    <settings>
        <setting name="defaultExecutorType" value="SIMPLE"/>
    </settings>

    <environments default="default">
        <environment id="default">
            <transactionManager type="JDBC"></transactionManager>
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <mapper class="com.test.first.batis.mapper.AreaMapper"></mapper>
    </mappers>
</configuration>

mapper

public interface AreaMapper {
    List<Area> getAll();
}
<mapper namespace="com.test.first.batis.mapper.AreaMapper">
    <resultMap id="baseMap" type="com.test.first.batis.pojo.Area">
        <result property="id" column="id" jdbcType="INTEGER"></result>
        <result property="areaNo" column="area_no" jdbcType="VARCHAR"></result>
        <result property="areaName" column="area_name" jdbcType="VARCHAR"></result>
    </resultMap>
    <select id="getAll" resultMap="baseMap">
        select * from bd_area LIMIT 10
    </select>
</mapper>

bd.propoerties

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/zhzg?useUnicode=true&characterEncoding=utf8
jdbc.username=root
jdbc.password=123456

我们从 build 开始分析源码

SqlSessionFactoryBuilder().build(inputStream)
public class SqlSessionFactoryBuilder {
    
    // 默认构造器里面没有东西, 所以我们直接跳过吧
    public SqlSessionFactoryBuilder() {
    }
    
    // 1 
    public SqlSessionFactory build(InputStream inputStream) {
    	return build(inputStream, null, null);
  	}
    
    // 2, 最终的build
    public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
        try {
          // 创建一个 xml 配置加载器, 解析mybatis-config.xml呗
          XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
          return build(parser.parse());
        } catch (Exception e) {
          throw ExceptionFactory.wrapException("Error building SqlSession.", e);
        } finally {
          ErrorContext.instance().reset();
          try {
            inputStream.close();
          } catch (IOException e) {
            // Intentionally ignore. Prefer previous error.
          }
        }
  }
}

class XMLConfigBuilder {
  public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
    // 创建一个XPath 解析器
    this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
  }

  private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
   	// 我们关注一下这个 new Configuration()
    // Configuration 存储对 mybatis-config.xml 的对象解析, 把对象从xml 里面创建出来
    super(new Configuration());
    ErrorContext.instance().resource("SQL Mapper Configuration");
    this.configuration.setVariables(props);
    this.parsed = false;
    this.environment = environment;
    this.parser = parser;
  }
}
parser.parse()

上面的代码我们已经看过 XMLConfigBuilder 是怎么构造的了,

build(parser.parse());

分为两步, 先解析, 再构建, 我们先分析解析

public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
        XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
        // 我们现在分析这个
        return build(parser.parse());
    } catch (Exception e) {
    } finally {
    }
}

class XMLConfigBuilder {
    public Configuration parse() {
        // 防止重复解析
        if (parsed) {
            throw new BuilderException("Each XMLConfigBuilder can only be used once.");
        }
        parsed = true;
        // 获取 xml 树, configuration 也就是 mybatis-config.xml 最外层结构
        parseConfiguration(parser.evalNode("/configuration"));
        return configuration;
    }
    
    private void parseConfiguration(XNode root) {
        // 我们不需要关注 root.evalNode("properties")
        // 而是关注 解析节点之后做了什么
        // 只带各位分析一点点
        try {
          // 1, 获取参数配置
          propertiesElement(root.evalNode("properties"));
          Properties settings = settingsAsProperties(root.evalNode("settings"));
          loadCustomVfs(settings);
          typeAliasesElement(root.evalNode("typeAliases"));
          pluginElement(root.evalNode("plugins"));
          objectFactoryElement(root.evalNode("objectFactory"));
          objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
          reflectorFactoryElement(root.evalNode("reflectorFactory"));
          settingsElement(settings);
          // 2, 创建事务工厂和数据源
          environmentsElement(root.evalNode("environments"));
          databaseIdProviderElement(root.evalNode("databaseIdProvider"));
          typeHandlerElement(root.evalNode("typeHandlers"));
          // 3, 解析创建 mapper 代理对象
          mapperElement(root.evalNode("mappers"));
        } catch (Exception e) {
          throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
        }
  	}
}
propertiesElement(root.evalNode(“properties”));

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/zhzg?useUnicode=true&characterEncoding=utf8
jdbc.username=root
jdbc.password=123456  
class XMLConfigBuilder {
	private void propertiesElement(XNode context) throws Exception {
        if (context != null) {
          Properties defaults = context.getChildrenAsProperties();
          String resource = context.getStringAttribute("resource");
          String url = context.getStringAttribute("url");
          if (resource != null && url != null) {
              // ... resource 和 url 只能存在一个, 否则抛异常
          }
          if (resource != null) {
            // 我们进这个分支, 然后会把 db.properties 解析出来成 Properties
            defaults.putAll(Resources.getResourceAsProperties(resource));
          } else if (url != null) {
            defaults.putAll(Resources.getUrlAsProperties(url));
          }
          // 默认这里是个null
          Properties vars = configuration.getVariables();
          if (vars != null) {
            defaults.putAll(vars);
          }
          parser.setVariables(defaults);
          // 此时我们的 configuration 里面存入了连接信息
          configuration.setVariables(defaults);
	}    
}
environmentsElement(root.evalNode(“environments”));

这一步的步骤是创建了 事务工厂数据源

class XMLConfigBuilder {
    
    private void environmentsElement(XNode context) throws Exception {
        if (context != null) {
            if (environment == null) {
                environment = context.getStringAttribute("default");
            }
            for (XNode child : context.getChildren()) {
                String id = child.getStringAttribute("id");
                if (isSpecifiedEnvironment(id)) {
                    // 解析 environments > id = environment  > transactionManager 
                    TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
                    DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
                    DataSource dataSource = dsFactory.getDataSource();
                    
                    // 简单的设置一些属性
                    Environment.Builder environmentBuilder = new Environment.Builder(id)
                        .transactionFactory(txFactory)
                        .dataSource(dataSource);
                    // 将事务工厂和数据源封装成 Environment
                    // 这里使用建造者模式
                    configuration.setEnvironment(environmentBuilder.build());
                }
            }
        }
    } 
    
    // 解析transactionManager
    private TransactionFactory transactionManagerElement(XNode context) throws Exception {
        if (context != null) {
            String type = context.getStringAttribute("type");
            Properties props = context.getChildrenAsProperties();
            // 怎么知道这个实例是谁呢, 其实在 configuration 被初始化的时候就已经默认配置了
            // type = JDBC
            // 所以这个实例为 JdbcTransactionFactory.class
            TransactionFactory factory = (TransactionFactory) resolveClass(type).newInstance();
            factory.setProperties(props);
            return factory;
        }
        throw new BuilderException("Environment declaration requires a TransactionFactory.");
    }
    
    protected Class<?> resolveClass(String alias) {
        if (alias == null) {
            return null;
        }
        try {
            return resolveAlias(alias);
        } catch (Exception e) {
            throw new BuilderException("Error resolving class. Cause: " + e, e);
        }
    }
    
    protected Class<?> resolveAlias(String alias) {
        return typeAliasRegistry.resolveAlias(alias);
    }
}

Configuration 在构建的时候就已经加入了一批别名

public Configuration() {
  typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
  typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);

  typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
  typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
  typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);

  typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);
  typeAliasRegistry.registerAlias("FIFO", FifoCache.class);
  typeAliasRegistry.registerAlias("LRU", LruCache.class);
  typeAliasRegistry.registerAlias("SOFT", SoftCache.class);
  typeAliasRegistry.registerAlias("WEAK", WeakCache.class);

  typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class);

  typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class);
  typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class);

  typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);
  typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class);
  typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);
  typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);
  typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);
  typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);
  typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class);

  typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class);
  typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class);

  languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);
  languageRegistry.register(RawLanguageDriver.class);
}
mapperElement(root.evalNode(“mappers”));

看方法名都知道肯定是跟mapper有关, 这个步骤就是将配置的mapper 全部创建一个代理对象

<mappers>
    <mapper class="com.test.first.batis.mapper.AreaMapper"></mapper>
</mappers>
class XMLConfigBuilder {
    private void mapperElement(XNode parent) throws Exception {
        if (parent != null) {
            for (XNode child : parent.getChildren()) {
                if ("package".equals(child.getName())) {
                    // 如果配置的是包名就会扫码包路径下所有的mapper
                    String mapperPackage = child.getStringAttribute("name");
                    configuration.addMappers(mapperPackage);
                } else {
                    String resource = child.getStringAttribute("resource");
                    String url = child.getStringAttribute("url");
                    // 我们案例只配置了class
                    String mapperClass = child.getStringAttribute("class");
                    if (resource != null && url == null && mapperClass == null) {
                        // 本地路径
                        ErrorContext.instance().resource(resource);
                        InputStream inputStream = Resources.getResourceAsStream(resource);
                        XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
                        mapperParser.parse();
                    } else if (resource == null && url != null && mapperClass == null) {
                        // 网络路径
                        ErrorContext.instance().resource(url);
                        InputStream inputStream = Resources.getUrlAsStream(url);
                        XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
                        mapperParser.parse();
                    } else if (resource == null && url == null && mapperClass != null) {
                        // 进这个分支
                        Class<?> mapperInterface = Resources.classForName(mapperClass);
                        // 我们看 configuration.addMapper 都做了什么
                        configuration.addMapper(mapperInterface);
                    } else {
                        throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
                    }
                }
            }
        }
    }
}

class Configuration {
    
    protected final MapperRegistry mapperRegistry = new MapperRegistry(this);

    public <T> void addMapper(Class<T> type) {
        mapperRegistry.addMapper(type);
    } 

    public <T> void addMapper(Class<T> type) {
        if (type.isInterface()) {
            if (hasMapper(type)) {
                // 防止重复配置 throw new BindingException....
            }
            boolean loadCompleted = false;
            try {
                // 创建了一个代理对象
                knownMappers.put(type, new MapperProxyFactory<T>(type));
                
                MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
                // 解析mapper, Select, Insert, Update, Delete, 
                // 这个方法里面的 loadXmlResource() 就会去解析 xxxMapper.xml 文件
                parser.parse();
                loadCompleted = true;
            } finally {
                if (!loadCompleted) {
                    knownMappers.remove(type);
                }
            }
        }
    }
}
build(parser.parse())

OK, 我们回过去看build(parser.parse()); 中的build

build 就是使用 parse 以后的 Configuration 对象 ,创建一个DefaultSqlSessionFactory

class SqlSessionFactoryBuilder {
    public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
        try {
            XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
            return build(parser.parse());
        } catch (Exception e) {
            throw ExceptionFactory.wrapException("Error building SqlSession.", e);
        } finally {
            ErrorContext.instance().reset();
            try {
                inputStream.close();
            } catch (IOException e) {
                // Intentionally ignore. Prefer previous error.
            }
        }
    }

    public SqlSessionFactory build(Configuration config) {
        return new DefaultSqlSessionFactory(config);
    }  
}

class DefaultSqlSessionFactory implements SqlSessionFactory {

  private final Configuration configuration;

  public DefaultSqlSessionFactory(Configuration configuration) {
    this.configuration = configuration;
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值