前言
我们在上篇文章 spring boot 源码解析24-HealthEndpoint解析中 在解析DataSourceHealthIndicator的时候发现其使用到了DataSourcePoolMetadataProviders.我们就在本文中分析一下吧,还是比较简单的
解析
spring boot 中 关于jdbc metadata的实现都在org.springframework.boot.autoconfigure.jdbc.metadata包中,如图:
在这里涉及到了2个接口,DataSourcePoolMetadata,DataSourcePoolMetadataProvider.我们先看下DataSourcePoolMetadata的实现
DataSourcePoolMetadata
DataSourcePoolMetadata的类图如下:
DataSourcePoolMetadata 接口–> 提供大多数数据库都提供的元数据的接口.定义如下:
public interface DataSourcePoolMetadata { /** * 返回当前数据库连接池的使用量,返回值在0至1之间(或者是-1,如果当前数据库连接池没有限制的话) * <li>1 --> 该数据库连接池的链接数已达到最大数目</li> * <li>0 --> 该数据库连接池的链接数</li> * <li>-1 -->该数据库连接池的链接数没有限制 </li> * </li> * </ul> * 还有可能返回null,如果当前的数据库链接池不提供必要的信息进行计算的话 */ Float getUsage(); // 返回当前数据库连接池中已经在使用中的(激活)链接或者返回null,如果该信息不可用的话 Integer getActive(); // 返回当前数据库连接池可分配的最大链接数, 返回-1 意味着没有限制,返回null,意味着当前信息不可用 Integer getMax(); // 返回当前数据库连接池可分配的最小链接数, 返回null,意味着当前信息不可用 Integer getMin(); // 返回用来检查当前链接是否可以的sql,返回null-->当前信息不可用 String getValidationQuery(); }
AbstractDataSourcePoolMetadata –> DataSourcePoolMetadata 的抽象实现,实现了getUsage 方法,其它实现只需继承它实现其他的方法即可,代码如下:
public Float getUsage() { // 1. 获得当前数据库连接池可分配的最大链接数和当前数据库连接池中已经在使用中的链接,如果其中1个等于null,则返回null Integer maxSize = getMax(); Integer currentSize = getActive(); if (maxSize == null || currentSize == null) { return null; } // 2. 如果最大链接数小于0,则直接返回-1 if (maxSize < 0) { return -1F; } // 3. 如果使用中的(激活)链接等于0L if (currentSize == 0) { return 0F; } // 4. 通过currentSize/maxSize 进行计算 return (float) currentSize / (float) maxSize; }
获得当前数据库连接池可分配的最大链接数和当前数据库连接池中已经在使用中的链接,如果其中1个等于null,则返回null
如果最大链接数小于0,则直接返回-1
- 如果使用中的(激活)链接等于0L
- 通过currentSize/maxSize 进行计算
同时,AbstractDataSourcePoolMetadata 持有了DataSource.通过构造器传入,代码如下:
private final T dataSource; protected AbstractDataSourcePoolMetadata(T dataSource) { this.dataSource = dataSource; }
CommonsDbcp2DataSourcePoolMetadata–> 继承自AbstractDataSourcePoolMetadata,其持有的数据源类型为BasicDataSource.关于接口的实现只是简单的调用 dpcp中相关的api即可,这里就不进行解释了,代码如下:
public class CommonsDbcp2DataSourcePoolMetadata extends AbstractDataSourcePoolMetadata<BasicDataSource> { public CommonsDbcp2DataSourcePoolMetadata(BasicDataSource dataSource) { super(dataSource); } @Override public Integer getActive() { return getDataSource().getNumActive(); } @Override public Integer getMax() { return getDataSource().getMaxTotal(); } @Override public Integer getMin() { return getDataSource().getMinIdle(); } @Override public String getValidationQuery() { return getDataSource().getValidationQuery(); } }
其自动装配是在CommonsDbcp2PoolDataSourceMetadataProviderConfiguration中声明的,代码如下:
@Configuration @ConditionalOnClass(BasicDataSource.class) static class CommonsDbcp2PoolDataSourceMetadataProviderConfiguration { @Bean public DataSourcePoolMetadataProvider commonsDbcp2PoolDataSourceMetadataProvider() { return new DataSourcePoolMetadataProvider() { @Override public DataSourcePoolMetadata getDataSourcePoolMetadata( DataSource dataSource) { if (dataSource instanceof BasicDataSource) { return new CommonsDbcp2DataSourcePoolMetadata( (BasicDataSource) dataSource); } return null; } }; } }
- @Configuration –> 配置类
- @ConditionalOnClass(BasicDataSource.class) –> 在BeanFactory中如果存在BasicDataSource类型的bean时该配置生效
由于默认情况下,spring boot 引入的是org.apache.tomcat.jdbc.pool.DataSource,因此该配置默认不生效
如果生效的话,则注册id为commonsDbcp2PoolDataSourceMetadataProvider,类型为DataSourcePoolMetadataProvider的bean
CommonsDbcpDataSourcePoolMetadata–> 继承自AbstractDataSourcePoolMetadata,数据源类型为BasicDataSource.其实现如下:
public class CommonsDbcpDataSourcePoolMetadata extends AbstractDataSourcePoolMetadata<BasicDataSource> { public CommonsDbcpDataSourcePoolMetadata(BasicDataSource dataSource) { super(dataSource); } @Override public Integer getActive() { return getDataSource().getNumActive(); } @Override public Integer getMax() { return getDataSource().getMaxActive(); } @Override public Integer getMin() { return getDataSource().getMinIdle(); } @Override public String getValidationQuery() { return getDataSource().getValidationQuery(); } }
自动装配定义在CommonsDbcpPoolDataSourceMetadataProviderConfiguration中,代码如下:
@Configuration @ConditionalOnClass(org.apache.commons.dbcp.BasicDataSource.class) @Deprecated static class CommonsDbcpPoolDataSourceMetadataProviderConfiguration { @Bean public DataSourcePoolMetadataProvider commonsDbcpPoolDataSourceMetadataProvider() { return new DataSourcePoolMetadataProvider() { @Override public DataSourcePoolMetadata getDataSourcePoolMetadata( DataSource dataSource) { if (dataSource instanceof org.apache.commons.dbcp.BasicDataSource) { return new CommonsDbcpDataSourcePoolMetadata( (org.apache.commons.dbcp.BasicDataSource) dataSource); } return null; } }; } }
- @Configuration –> 配置类
- @ConditionalOnClass(org.apache.commons.dbcp.BasicDataSource.class) –> 在BeanFactory中如果存在BasicDataSource类型的bean时该配置生效
如果生效的话,则注册id为commonsDbcpPoolDataSourceMetadataProvider,类型为DataSourcePoolMetadataProvider的bean
HikariDataSourcePoolMetadata–>继承自AbstractDataSourcePoolMetadata,数据源类型为HikariDataSource.其实现如下:
public class HikariDataSourcePoolMetadata extends AbstractDataSourcePoolMetadata<HikariDataSource> { public HikariDataSourcePoolMetadata(HikariDataSource dataSource) { super(dataSource); } @Override public Integer getActive() { try { return getHikariPool().getActiveConnections(); } catch (Exception ex) { return null; } } // 通过实例化DirectFieldAccessor的方式直接方法HikariDataSource中的pool所对应的属性值,然后将其强转为HikariPool private HikariPool getHikariPool() { return (HikariPool) new DirectFieldAccessor(getDataSource()) .getPropertyValue("pool"); } @Override public Integer getMax() { return getDataSource().getMaximumPoolSize(); } @Override public Integer getMin() { return getDataSource().getMinimumIdle(); } @Override public String getValidationQuery() { return getDataSource().getConnectionTestQuery(); } }
其中定义了getHikariPool方法获得HikariPool,在该方法中通过实例化DirectFieldAccessor的方式直接方法HikariDataSource中的pool所对应的属性值,然后将其强转为HikariPool
自动装配是在HikariPoolDataSourceMetadataProviderConfiguration中定义的,代码如下:
@Configuration @ConditionalOnClass(HikariDataSource.class) static class HikariPoolDataSourceMetadataProviderConfiguration { @Bean public DataSourcePoolMetadataProvider hikariPoolDataSourceMetadataProvider() { return new DataSourcePoolMetadataProvider() { @Override public DataSourcePoolMetadata getDataSourcePoolMetadata( DataSource dataSource) { if (dataSource instanceof HikariDataSource) { return new HikariDataSourcePoolMetadata( (HikariDataSource) dataSource); } return null; } }; } }
- @Configuration –> 配置类
- @ConditionalOnClass(HikariDataSource.class) –> 在BeanFactory中如果存在HikariDataSource类型的bean时该配置生效
如果生效的话,则注册id为hikariPoolDataSourceMetadataProvider,类型为DataSourcePoolMetadataProvider的bean
TomcatDataSourcePoolMetadata–>继承自AbstractDataSourcePoolMetadata,数据源类型为DataSource.其实现如下:
public class TomcatDataSourcePoolMetadata extends AbstractDataSourcePoolMetadata<DataSource> { public TomcatDataSourcePoolMetadata(DataSource dataSource) { super(dataSource); } @Override public Integer getActive() { ConnectionPool pool = getDataSource().getPool(); // 如果没有配置ConnectionPool,则返回0,否则返回链接池中的激活链接数 return (pool == null ? 0 : pool.getActive()); } @Override public Integer getMax() { return getDataSource().getMaxActive(); } @Override public Integer getMin() { return getDataSource().getMinIdle(); } @Override public String getValidationQuery() { return getDataSource().getValidationQuery(); } }
其中getActive 是首先通过数据源获得配置的ConnectionPool,如果没有配置ConnectionPool,则返回0,否则返回链接池中的激活链接数
自动配置,代码如下:
“`
@Configuration
@ConditionalOnClass(org.apache.tomcat.jdbc.pool.DataSource.class)
static class TomcatDataSourcePoolMetadataProviderConfiguration {@Bean public DataSourcePoolMetadataProvider tomcatPoolDataSourceMetadataProvider() { return new DataSourcePoolMetadataProvider() { @Override public DataSourcePoolMetadata getDataSourcePoolMetadata( DataSource dataSource) { if (dataSource instanceof org.apache.tomcat.jdbc.pool.DataSource) { return new TomcatDataSourcePoolMetadata( (org.apache.tomcat.jdbc.pool.DataSource) dataSource); } return null; } }; }
}
“`
- @Configuration –> 配置类
- @ConditionalOnClass(org.apache.tomcat.jdbc.pool.DataSource.class) –> 在BeanFactory中如果存在org.apache.tomcat.jdbc.pool.DataSource类型的bean时该配置生效
如果生效的话,则注册id为tomcatPoolDataSourceMetadataProvider,类型为DataSourcePoolMetadataProvider的bean
由于spring boot 中默认引入的是org.apache.tomcat.jdbc.pool.DataSource,因此该配置默认生效
DataSourcePoolMetadataProvider
DataSourcePoolMetadataProvider的类图如下:
DataSourcePoolMetadataProvider接口–>根据DataSource 提供 DataSourcePoolMetadata.其只定义了1个方法,如下:
// 根据DataSource返回DataSourcePoolMetadata,或者返回null,如果给定的数据源没有对应的DataSourcePoolMetadata DataSourcePoolMetadata getDataSourcePoolMetadata(DataSource dataSource);
DataSourcePoolMetadataProviders 实现了DataSourcePoolMetadataProvider接口,其内部持有DataSourcePoolMetadataProvider类型的List,在构造器中会注入在BeanFactory所有DataSourcePoolMetadataProvider类型的bean.代码如下:
private final List<DataSourcePoolMetadataProvider> providers; // 会在DataSourcePoolMetadataProviders的构造器中注入在BeanFactory所有DataSourcePoolMetadataProvider类型的bean public DataSourcePoolMetadataProviders( Collection<? extends DataSourcePoolMetadataProvider> providers) { this.providers = (providers == null ? Collections.<DataSourcePoolMetadataProvider>emptyList() : new ArrayList<DataSourcePoolMetadataProvider>(providers)); }
getDataSourcePoolMetadata 会依次遍历持有的providers,如果能根据给定的DataSource获得DataSourcePoolMetadata,则直接返回,否则返回null,代码如下:
public DataSourcePoolMetadata getDataSourcePoolMetadata(DataSource dataSource) { for (DataSourcePoolMetadataProvider provider : this.providers) { DataSourcePoolMetadata metadata = provider .getDataSourcePoolMetadata(dataSource); if (metadata != null) { return metadata; } } return null; }
其实例化过程如下:
由于DataSourcesHealthIndicatorConfiguration 实现了InitializingBean接口,因此会调用其afterPropertiesSet,而在该方法中,实例化了DataSourcePoolMetadataProviders.代码如下:
public void afterPropertiesSet() throws Exception { this.poolMetadataProvider = new DataSourcePoolMetadataProviders( this.metadataProviders); }
关于DataSourcePoolMetadataProvider接口的其他实现是直接定义在DataSourcePoolMetadataProvidersConfiguration.分别如下:
tomcatPoolDataSourceMetadataProvider,代码如下:
@Configuration @ConditionalOnClass(org.apache.tomcat.jdbc.pool.DataSource.class) static class TomcatDataSourcePoolMetadataProviderConfiguration { @Bean public DataSourcePoolMetadataProvider tomcatPoolDataSourceMetadataProvider() { return new DataSourcePoolMetadataProvider() { @Override public DataSourcePoolMetadata getDataSourcePoolMetadata( DataSource dataSource) { if (dataSource instanceof org.apache.tomcat.jdbc.pool.DataSource) { return new TomcatDataSourcePoolMetadata( (org.apache.tomcat.jdbc.pool.DataSource) dataSource); } return null; } }; } }
- @Configuration –> 配置类
- @ConditionalOnClass(org.apache.tomcat.jdbc.pool.DataSource.class) –> 在BeanFactory中如果存在org.apache.tomcat.jdbc.pool.DataSource类型的bean时该配置生效
如果生效的话,则会注册1个id为tomcatPoolDataSourceMetadataProvider,类型为DataSourcePoolMetadataProvider的bean,其关于getDataSourcePoolMetadata方法的实现是首先判断是否是org.apache.tomcat.jdbc.pool.DataSource类型的数据源,如果是的话,则返回TomcatDataSourcePoolMetadata,否则返回null
由于spring boot 中默认引入的是org.apache.tomcat.jdbc.pool.DataSource,因此该配置默认生效
HikariPoolDataSourceMetadataProviderConfiguration,代码如下:
@Configuration @ConditionalOnClass(HikariDataSource.class) static class HikariPoolDataSourceMetadataProviderConfiguration { @Bean public DataSourcePoolMetadataProvider hikariPoolDataSourceMetadataProvider() { return new DataSourcePoolMetadataProvider() { @Override public DataSourcePoolMetadata getDataSourcePoolMetadata( DataSource dataSource) { if (dataSource instanceof HikariDataSource) { return new HikariDataSourcePoolMetadata( (HikariDataSource) dataSource); } return null; } }; } }
- @Configuration –> 配置类
- @ConditionalOnClass(HikariDataSource.class) –> 在BeanFactory中如果存在HikariDataSource类型的bean时该配置生效
如果生效的话,则会注册1个id为hikariPoolDataSourceMetadataProvider,类型为DataSourcePoolMetadataProvider的bean,其关于getDataSourcePoolMetadata方法的实现是首先判断是否是HikariDataSource类型的数据源,如果是的话,则返回HikariDataSourcePoolMetadata,否则返回null
该配置默认不生效
其他的CommonsDbcpPoolDataSourceMetadataProviderConfiguration,CommonsDbcp2PoolDataSourceMetadataProviderConfiguration实现和HikariPoolDataSourceMetadataProviderConfiguration一样,区别如下:
- 配置类上的@ConditionalOnClass 注解分别为org.apache.commons.dbcp.BasicDataSource.class,BasicDataSource.class
- DataSourcePoolMetadataProvider 分别判断是否是org.apache.commons.dbcp.BasicDataSource,BasicDataSource的实例.
这些配置都是默认不生效的.