mybatis-源码解析-DataSource(如何获取数据库源)

1、Mybatis是如何获取数据源的?

https://mybatis.org/mybatis-3/zh/configuration.html#environments mybatis官方 文档里面,根据xml配置可以查看到

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

数据源配置主要包含在environments,dataSource即为数据源,通过代码查看谁解析了dataSource,那么数据源就被谁拥有;

SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream)

XMLConfigBuilder.parse

private boolean parsed;为XMLConfigBuilder成员变量

上面方法使用的parser在XMLConfigBuilder构造函数的时候进行初始化


XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties)

//
public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
    this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
}

private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
    super(new Configuration());
    ErrorContext.instance().resource("SQL Mapper Configuration");
    this.configuration.setVariables(props);
    this.parsed = false;
    this.environment = environment;
    this.parser = parser;//
}

XMLConfigBuilder类属性
private boolean parsed;//实现为XPathParser
private final XPathParser parser;
private String environment;

parser.evalNode("/configuration");

String value = parser.evalNode("/configuration");

<configuration>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>            
            <dataSource type="POOLED">
                <property name="driver" value="${driver}"/>                
                <property name="url" value="${url}"/>                
                <property name="username" value="${username}"/>                
                <property name="password" value="${password}"/>                
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="com/wolf/dao/SysParamMapper.xml"/>        
    </mappers>
</configuration>

通过此其实就看到把整个mybatis-config.xml给解析出来了。

由于我们这里只研究dataSource到底被谁解析和持有,通过上述xml配置可以发现dataSource在environments里面。所以看看具体解析代码parseConfiguration方法

environmentsElement方法解析

  • 看看context内容是什么?

<environments default="development">
    <environment id="development">
        <transactionManager type="JDBC"/>        
        <dataSource type="POOLED">
            <property name="driver" value="${driver}"/>            
            <property name="url" value="${url}"/>            
            <property name="username" value="${username}"/>            
            <property name="password" value="${password}"/>            
        </dataSource>
    </environment>
</environments>
其实就是我们environmentsElement需要解析的environments配置信息

  • child内容

<environment id="development">
    <transactionManager type="JDBC"/>    
    <dataSource type="POOLED">
        <property name="driver" value="${driver}"/>        
        <property name="url" value="${url}"/>        
        <property name="username" value="${username}"/>        
        <property name="password" value="${password}"/>        
    </dataSource>
</environment>
这里就是就是对environments下面的子标签进行遍历循环处理

  • isSpecifiedEnvironment(id)这个方法在干什么
这个方法就是判断environments的default配置和要解析的environment的id是否相等,如果相等了,就需要去解析默认的环境(environment )
private boolean isSpecifiedEnvironment(String id) {
  if (environment == null) {
    throw new BuilderException("No environment specified.");
  } else if (id == null) {
    throw new BuilderException("Environment requires an id attribute.");
  } else if (environment.equals(id)) {
    return true;
  }
  return false;
}
  • TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));解析transactionManager然后生成txFactory
private TransactionFactory transactionManagerElement(XNode context) throws Exception {
  if (context != null) {
    String type = context.getStringAttribute("type");//去拿到 <transactionManager type="JDBC"/>标签的type属性
    Properties props = context.getChildrenAsProperties();//这里把xml标签转化为Properties
    //通过配置,我只设置了JDBC,通过这块代码
    //resolveClass(type)这个应该会返回一个Class,然后getDeclaredConstructor调用无参构造函数newInstance()然后实例化他
    //那么JDBC对应的Class是什么呢。【JDBC-org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory】下面源码
    TransactionFactory factory = (TransactionFactory) resolveClass(type).getDeclaredConstructor().newInstance();
    factory.setProperties(props);//将transactionManager 的子标签信息设置到TransactionFactory中,后面需要时,在使用
    return factory;
  }
  throw new BuilderException("Environment declaration requires a TransactionFactory.");
}
  • resolveClass(type)这个方法搞了些什么呢?
protected <T> Class<? extends T> resolveClass(String alias) {
  if (alias == null) {
    return null;
  }
  try {
    //resolveAlias去拿Class
    return resolveAlias(alias);
  } catch (Exception e) {
    throw new BuilderException("Error resolving class. Cause: " + e, e);
  }
}
protected <T> Class<? extends T> resolveAlias(String alias) {
//由于这个方法在XmlConfigBuilder中,那么typeAliasRegistry为一个属性看看这个属性结构及初始化内容  
return typeAliasRegistry.resolveAlias(alias);
}
  • XmlConfigBuilder.typeAliasRegistry属性
BaseBuilder为抽象类为XmlConfigBuilder的父类

BaseBuilder.typeAliasRegistry

通过初始化方法可以看见哦,typeAliasRegistry=this.configuration.getTypeAliasRegistry()中获取的,那么想,在XmlConfigBuilder类实例化的时候,typeAliasRegistry的内容其实已经就完成了初始化

1、configuration及typeAliasRegistry及typeHandlerRegistry都完成了初始化

2、configuration

在其子类通过new Configuration进行实例化并调用了父类的构造函数;【打住,学习源码的时候不要走远了,上面我们要分析typeAliasRegistry是怎么来的】

OK!!!回归正题,我们看到typeAliasRegistry=this.configuration.getTypeAliasRegistry()中获取的

那么我们这里看到了configuration中typeAliasRegistry的初始化,于是我们就找到了JDBC的class,里面还有很多其他参数,这里仅针对JDBC解析(org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory)

通过调试即可以看到TransactionFactory的最终实例就是JdbcTransactionFactory。【有时候通过调试其实最终知道他是什么,但是他如何来,这个就必须通过上述源码分析得到】

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);
}

==========【思路在回到:environmentsElement方法解析】

上面中间插了很多步骤,其实就是为了分析上述代码中TransactionFactory是如何来的。

  • 然后在看看dataSource是如何解析的?

分析dataSource跟分析TransactionFactory一样的道理;通过调试最终得到了PooledDataSourceFactory实例;(因为xml中dataSource配置的是<dataSource type="POOLED">)

实例就是这么来的?

  • 【在思路在回到:environmentsElement方法解析】

    上述中找到了TransactionFactory和DataSourceFactory如何来的。DataSource是通过DataSourceFactory中获取的来的
  • 抽丝剥茧,我要看看dataSource是如何通过DataSourceFactory拿来的。

通过调试发现DataSourceFactory的实例为PooledDataSourceFactory

先看看PooledDataSourceFactory类的构成

继承UnpooledDataSourceFactory,

PooledDataSourceFactory类属性

UnpooledDataSourceFactory类属性

  • 【在思路在回到:environmentsElement方法解析--environmentBuilder如何构造】

学习到此我们在看看下面代码

Environment.Builder environmentBuilder = new Environment.Builder(id)
    .transactionFactory(txFactory)
    .dataSource(dataSource);

1、可以看到这个是一个建造者模式,那他建造了什么。

看看Evironment源码呢?

public final class Environment {
  private final String id;
  private final TransactionFactory transactionFactory;
  private final DataSource dataSource;

  public Environment(String id, TransactionFactory transactionFactory, DataSource dataSource) {
    if (id == null) {
      throw new IllegalArgumentException("Parameter 'id' must not be null");
    }
    if (transactionFactory == null) {
      throw new IllegalArgumentException("Parameter 'transactionFactory' must not be null");
    }
    this.id = id;
    if (dataSource == null) {
      throw new IllegalArgumentException("Parameter 'dataSource' must not be null");
    }
    this.transactionFactory = transactionFactory;
    this.dataSource = dataSource;
  }

  public static class Builder {
    private final String id;
    private TransactionFactory transactionFactory;
    private DataSource dataSource;


    public Builder transactionFactory(TransactionFactory transactionFactory) {
      this.transactionFactory = transactionFactory;
      return this;
    }

    public Builder dataSource(DataSource dataSource) {
      this.dataSource = dataSource;
      return this;
    }
    public Environment build() {
      return new Environment(this.id, this.transactionFactory, this.dataSource);
    }

  }

}

1、Builder为Environment的内部类
2、Builder与Environment拥有相同的实例
3、Builder每次追加以后都会返回自己的实例,有点像StringBuilder或者StringBuffer
4、在最终Builder.build的时候,才会去构造Environment

这里通过内部类Builder建造了Envrionment对象;

最后将构造的Environment设置到configuration对象的属性中

configuration.setEnvironment(environmentBuilder.build());

到此为止DataSource解析完毕;

最终

org.apache.ibatis.session.SqlSessionFactoryBuilder.build(InputStream inputStream)
  》org.apache.ibatis.builder.xml.XMLConfigBuilder.parse()
    》org.apache.ibatis.builder.xml.XMLConfigBuilder.parseConfiguration(parser.evalNode("/configuration"))
	  》org.apache.ibatis.builder.xml.XMLConfigBuilder.environmentsElement(root.evalNode("environments"))
	    》org.apache.ibatis.builder.xml.XMLConfigBuilder.dataSourceElement(child.evalNode("dataSource"))
	      》org.apache.ibatis.builder.xml.XMLConfigBuilder.configuration.setEnvironment(environmentBuilder.build())
		####new XMLConfigBuilder().configuration.setEnvironment(Environment)####

看到了设置的执行步骤!

总结:

1、class XMLConfigBuilder extends BaseBuilder

1、configuration对象被BaseBuilder持有

2、Configuration持有Environment

3、Environment类属性

(1)持有mybatis-config.xml中environment的标签配置信息

(2)持有TransactionFactory实例(<transactionManager type="JDBC"/>-->PooledDataSourceFactory)

(3)持有dataSource实例(<dataSource type="POOLED">--JdbcTransactionFactory)

到此为止!!!整个解析过去就是对mybatis-config.xml配置的标签进行解析,解析后通过一系列对象进行封装和处理;

 

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
mybatis-plus-dynamic-datasource获取多数据实例的方法如下: 1. 导入相关依赖: ```xml <dependency> <groupId>com.baomidou</groupId> <artifactId>dynamic-datasource-spring-boot-starter</artifactId> <version>2.5.3</version> </dependency> ``` 2. 在 application.yml 或 application.properties 中配置数据信息,例如: ```yaml spring: datasource: dynamic: primary: ds1 # 设置默认数据 datasource: ds1: url: jdbc:mysql://localhost:3306/db1 username: root password: 123456 ds2: url: jdbc:mysql://localhost:3306/db2 username: root password: 123456 ``` 3. 创建一个继承自 AbstractRoutingDataSource 的类,用于动态切换数据: ```java public class DynamicRoutingDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return DynamicDataSourceContextHolder.getDataSourceKey(); } } ``` 4. 创建一个数据上下文持有者类,用于设置和获取当前线程使用的数据: ```java public class DynamicDataSourceContextHolder { private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>(); public static void setDataSourceKey(String dataSourceKey) { CONTEXT_HOLDER.set(dataSourceKey); } public static String getDataSourceKey() { return CONTEXT_HOLDER.get(); } public static void clearDataSourceKey() { CONTEXT_HOLDER.remove(); } } ``` 5. 配置数据切换策略和数据 bean: ```java @Configuration public class DataSourceConfig { @Bean @ConfigurationProperties(prefix = "spring.datasource.dynamic") public DataSourceProperties dataSourceProperties() { return new DataSourceProperties(); } @Bean public DataSource dataSource(DataSourceProperties properties) { Map<String, DataSource> dataSourceMap = new HashMap<>(); dataSourceMap.put("ds1", properties.getDataSource("ds1")); dataSourceMap.put("ds2", properties.getDataSource("ds2")); // 设置默认数据 DynamicRoutingDataSource dataSource = new DynamicRoutingDataSource(); dataSource.setDefaultTargetDataSource(dataSourceMap.get(properties.getPrimary())); dataSource.setTargetDataSources(dataSourceMap); return dataSource; } @Bean public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { MybatisSqlSessionFactoryBean sessionFactoryBean = new MybatisSqlSessionFactoryBean(); sessionFactoryBean.setDataSource(dataSource); return sessionFactoryBean.getObject(); } @Bean public PlatformTransactionManager transactionManager(DataSource dataSource) { return new DataSourceTransactionManager(dataSource); } } ``` 现在,你可以通过注入 DataSource 类型的 bean 来获取多数据实例,例如: ```java @Autowired private DataSource dataSource; ``` 然后,你就可以使用这个数据实例来执行数据库操作了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值