博主介绍: ✌博主从事应用安全和大数据领域,有8年研发经验,5年面试官经验,Java技术专家✌
Java知识图谱点击链接:体系化学习Java(Java面试专题)
💕💕 感兴趣的同学可以收藏关注下 ,不然下次找不到哟💕💕
文章目录
- 1、自动配置注解解析
- 2、DataSourcePoolMetadataProvidersConfiguration
- 3、DataSourcePoolMetadataProvider
- 4、DataSourceUnwrapper
- 5、TomcatDataSourcePoolMetadata
- 6、AbstractDataSourcePoolMetadata
1、自动配置注解解析
下面我们通过分析 DataSourceAutoConfiguration类的源代码来学习数据库自动配置的机制。先看注解部分。
@Configuration
@ConditionalOnClass({DataSource.class, EmbeddedDatabaseType.class})
@EnableConfigurationProperties({DataSourceProperties.class})
@Import({DataSourcePoolMetadataProvidersConfiguration.class, DataSourceInitializationConfiguration.class})
public class DataSourceAutoConfiguration {
这段源代码是Spring Boot中的DataSourceAutoConfiguration类的简化版本。它的作用是自动配置数据源。
具体解释如下:
- @Configuration 注解表示这是一个配置类,会被Spring容器扫描和加载。
- @ConditionalOnClass({DataSource.class, EmbeddedDatabaseType.class}) 注解表示只有当类路径中存在DataSource和EmbeddedDatabaseType类时,才会执行自动配置。
- @EnableConfigurationProperties({DataSourceProperties.class}) 注解表示启用对DataSourceProperties类的配置属性的支持。
- @Import({DataSourcePoolMetadataProvidersConfiguration.class, DataSourceInitializationConfiguration.class}) 注解表示导入了DataSourcePoolMetadataProvidersConfiguration和DataSourceInitializationConfiguration这两个配置类。
- DataSourcePoolMetadataProvidersConfiguration 类用于配置数据源的连接池元数据提供者。
- DataSourceInitializationConfiguration 类用于配置数据源的初始化操作,比如执行SQL脚本等。
这段源代码是用于自动配置数据源的核心类。它通过条件判断和属性配置,自动创建并配置DataSource对象,并导入了其他相关的配置类来完成数据源的初始化和连接池元数据的配置。这样,我们就可以在应用程序中直接使用DataSource类型的Bean,而无需手动配置和管理数据源。
@ConditionalOnClass({DataSource.class, EmbeddedDatabaseType.class})
注解@ConditionalOnClass 要求类路径下必须有DataSource和EmbeddedDatabaseType类的存在。@EnableConfigurationProperties 属性会装配 DataSourceProperties类,该配置类与applicationproperties中的配置相对应。
比如,对于数据库我们经常在application.properties中做如下的配置
spring.datasource.url=
sprinqdatasource.username=
spring.datasource.password=
在DataSourceProperties类中都有对应的属性存在
@ConfiqurationProperties(prefix="spring.datasource")
public class DataSourceProperties implements BeanClassloaderAware,InitializingBear {
...
private Stringurl;
privateStringusername
private String password;
...
}
2、DataSourcePoolMetadataProvidersConfiguration
@Import注解引人了两个自动配置类 DataSourcePoolMetadataProvidersConfiguration和DataSourcelnitializationConfiguration。
源码如下:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package org.springframework.boot.autoconfigure.jdbc.metadata;
import com.zaxxer.hikari.HikariDataSource;
import org.apache.commons.dbcp2.BasicDataSource;
import org.apache.tomcat.jdbc.pool.DataSource;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.jdbc.DataSourceUnwrapper;
import org.springframework.boot.jdbc.metadata.CommonsDbcp2DataSourcePoolMetadata;
import org.springframework.boot.jdbc.metadata.DataSourcePoolMetadataProvider;
import org.springframework.boot.jdbc.metadata.HikariDataSourcePoolMetadata;
import org.springframework.boot.jdbc.metadata.TomcatDataSourcePoolMetadata;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class DataSourcePoolMetadataProvidersConfiguration {
public DataSourcePoolMetadataProvidersConfiguration() {
}
@Configuration
@ConditionalOnClass({BasicDataSource.class})
static class CommonsDbcp2PoolDataSourceMetadataProviderConfiguration {
CommonsDbcp2PoolDataSourceMetadataProviderConfiguration() {
}
@Bean
public DataSourcePoolMetadataProvider commonsDbcp2PoolDataSourceMetadataProvider() {
return (dataSource) -> {
BasicDataSource dbcpDataSource = (BasicDataSource)DataSourceUnwrapper.unwrap(dataSource, BasicDataSource.class);
return dbcpDataSource != null ? new CommonsDbcp2DataSourcePoolMetadata(dbcpDataSource) : null;
};
}
}
@Configuration
@ConditionalOnClass({HikariDataSource.class})
static class HikariPoolDataSourceMetadataProviderConfiguration {
HikariPoolDataSourceMetadataProviderConfiguration() {
}
@Bean
public DataSourcePoolMetadataProvider hikariPoolDataSourceMetadataProvider() {
return (dataSource) -> {
HikariDataSource hikariDataSource = (HikariDataSource)DataSourceUnwrapper.unwrap(dataSource, HikariDataSource.class);
return hikariDataSource != null ? new HikariDataSourcePoolMetadata(hikariDataSource) : null;
};
}
}
@Configuration
@ConditionalOnClass({DataSource.class})
static class TomcatDataSourcePoolMetadataProviderConfiguration {
TomcatDataSourcePoolMetadataProviderConfiguration() {
}
@Bean
public DataSourcePoolMetadataProvider tomcatPoolDataSourceMetadataProvider() {
return (dataSource) -> {
DataSource tomcatDataSource = (DataSource)DataSourceUnwrapper.unwrap(dataSource, DataSource.class);
return tomcatDataSource != null ? new TomcatDataSourcePoolMetadata(tomcatDataSource) : null;
};
}
}
}
这段代码是一个Spring Boot自动配置的类,用于提供数据源池的元数据提供者。
代码首先定义了一个 DataSourcePoolMetadataProvidersConfiguration 类,它被标记为@Configuration,表示它是一个配置类。
在该类中,定义了三个内部静态类: CommonsDbcp2PoolDataSourceMetadataProviderConfiguration 、 HikariPoolDataSourceMetadataProviderConfiguration 和 TomcatDataSourcePoolMetadataProviderConfiguration ,它们分别用于提供Apache Commons DBCP2、HikariCP和Tomcat JDBC数据源池的元数据。
每个内部类都被标记为@Configuration和@ConditionalOnClass,表示它们是配置类,并且只有在对应的类存在时才会生效。
在每个内部类中,定义了一个@Bean方法,用于创建对应的数据源池元数据提供者。
这些元数据提供者的实现都是通过调用 DataSourceUnwrapper.unwrap 方法来获取对应的数据源对象,然后根据数据源对象创建相应的数据源池元数据对象。
最后,这些数据源池元数据提供者可以被其他组件或者配置类使用,以获取数据源池的元数据信息。
3、DataSourcePoolMetadataProvider
@Configuration
@ConditionalOnClass({DataSource.class})
static class TomcatDataSourcePoolMetadataProviderConfiguration {
TomcatDataSourcePoolMetadataProviderConfiguration() {
}
@Bean
public DataSourcePoolMetadataProvider tomcatPoolDataSourceMetadataProvider() {
return (dataSource) -> {
DataSource tomcatDataSource = (DataSource)DataSourceUnwrapper.unwrap(dataSource, DataSource.class);
return tomcatDataSource != null ? new TomcatDataSourcePoolMetadata(tomcatDataSource) : null;
};
}
}
上面代码中返回一个 Bean 对象 DataSourcePoolMetadataProvider。他是一个函数式接口。
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package org.springframework.boot.jdbc.metadata;
import javax.sql.DataSource;
@FunctionalInterface
public interface DataSourcePoolMetadataProvider {
DataSourcePoolMetadata getDataSourcePoolMetadata(DataSource dataSource);
}
该接口包含一个方法 getDataSourcePoolMetadata ,用于获取数据源的池元数据。
接口中的方法有一个参数 DataSource dataSource ,表示要获取池元数据的数据源。方法返回一个 DataSourcePoolMetadata 对象,表示数据源的池元数据。
该接口使用了 @FunctionalInterface 注解,表示它是一个函数式接口,可以用作lambda表达式或方法引用的目标。
整个代码位于 org.springframework.boot.jdbc.metadata 包中。
4、DataSourceUnwrapper
DataSourceUnwrapper是一个用于解包数据源的工具类。在Java中,数据源通常是由连接池库提供的实现类,例如HikariCP、Tomcat JDBC等。这些实现类通常会对原生的JDBC数据源进行包装,以提供更多功能或性能优化。
DataSourceUnwrapper的作用就是解包这些被包装的数据源,获取到原生的JDBC数据源对象。它提供了一个静态方法 unwrapDataSource ,接受一个数据源对象作为参数,并尝试解包该对象。如果成功解包,则返回原生的JDBC数据源对象,否则返回null。
使用DataSourceUnwrapper可以方便地获取到原生的JDBC数据源对象,以便于在需要直接操作JDBC的场景中使用,例如执行原生SQL语句、使用JdbcTemplate等。
public static <T> T unwrap(DataSource dataSource, Class<T> target) {
if (target.isInstance(dataSource)) {
return target.cast(dataSource);
} else {
T unwrapped = safeUnwrap(dataSource, target);
if (unwrapped != null) {
return unwrapped;
} else {
if (DELEGATING_DATA_SOURCE_PRESENT) {
DataSource targetDataSource = DataSourceUnwrapper.DelegatingDataSourceUnwrapper.getTargetDataSource(dataSource);
if (targetDataSource != null) {
return unwrap(targetDataSource, target);
}
}
if (AopUtils.isAopProxy(dataSource)) {
Object proxyTarget = AopProxyUtils.getSingletonTarget(dataSource);
if (proxyTarget instanceof DataSource) {
return unwrap((DataSource)proxyTarget, target);
}
}
return null;
}
}
}
- 首先,通过判断目标类型是否是DataSource对象的实例,如果是,则直接将DataSource对象转换为目标类型并返回。
- 如果目标类型不是DataSource对象的实例,则调用safeUnwrap方法尝试解包DataSource对象为目标类型的对象。如果解包成功,则返回解包后的对象。
- 如果通过safeUnwrap方法无法解包DataSource对象,则继续进行其他解包尝试:
- 首先,判断是否存在DelegatingDataSource类(一种常见的DataSource包装类),如果存在,则通过DelegatingDataSourceUnwrapper.getTargetDataSource方法获取目标数据源对象,并递归调用unwrap方法尝试解包目标数据源对象。
- 其次,判断DataSource对象是否是AOP代理对象,如果是,则通过AopProxyUtils.getSingletonTarget方法获取AOP代理对象的原始目标对象,并递归调用unwrap方法尝试解包原始目标对象。
- 如果以上解包尝试都失败,则返回null。
这段代码的目的是为了提供一个通用的方法,能够自动解包不同类型的数据源对象,并获取到目标类型的对象。这样,我们可以方便地在需要使用特定类型的数据源对象时,通过调用该方法进行解包和转换。
5、TomcatDataSourcePoolMetadata
TomcatDataSourcePoolMetadata 是一个用于获取Tomcat连接池元数据的类。它提供了一些方法来获取连接池的相关信息,如最大连接数、空闲连接数、活动连接数等。通过使用 TomcatDataSourcePoolMetadata,我们可以在应用程序中获取和监控连接池的状态信息,以便于进行性能优化和故障排查。
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package org.springframework.boot.jdbc.metadata;
import org.apache.tomcat.jdbc.pool.ConnectionPool;
import org.apache.tomcat.jdbc.pool.DataSource;
public class TomcatDataSourcePoolMetadata extends AbstractDataSourcePoolMetadata<DataSource> {
public TomcatDataSourcePoolMetadata(DataSource dataSource) {
super(dataSource);
}
public Integer getActive() {
ConnectionPool pool = ((DataSource)this.getDataSource()).getPool();
return pool != null ? pool.getActive() : 0;
}
public Integer getMax() {
return ((DataSource)this.getDataSource()).getMaxActive();
}
public Integer getMin() {
return ((DataSource)this.getDataSource()).getMinIdle();
}
public String getValidationQuery() {
return ((DataSource)this.getDataSource()).getValidationQuery();
}
public Boolean getDefaultAutoCommit() {
return ((DataSource)this.getDataSource()).isDefaultAutoCommit();
}
}
这段代码是一个名为TomcatDataSourcePoolMetadata的类,继承自AbstractDataSourcePoolMetadata类,用于获取Tomcat数据源的连接池元数据。
代码的主要功能是通过传入的DataSource对象,获取连接池的活动连接数、最大连接数、最小连接数、验证查询语句和默认是否自动提交。
具体步骤如下:
- 导入相关的包和类。
- 定义TomcatDataSourcePoolMetadata类,继承自AbstractDataSourcePoolMetadata类。
- 实现TomcatDataSourcePoolMetadata类的构造方法,接收一个DataSource对象作为参数,并调用父类的构造方法。
- 实现getActive()方法,通过调用DataSource对象的getPool()方法获取连接池对象ConnectionPool,然后调用ConnectionPool的getActive()方法获取活动连接数。如果连接池对象为null,则返回0。
- 实现getMax()方法,直接调用DataSource对象的getMaxActive()方法获取最大连接数。
- 实现getMin()方法,直接调用DataSource对象的getMinIdle()方法获取最小连接数。
- 实现getValidationQuery()方法,直接调用DataSource对象的getValidationQuery()方法获取验证查询语句。
- 实现getDefaultAutoCommit()方法,直接调用DataSource对象的isDefaultAutoCommit()方法获取默认是否自动提交。
总结:这段代码是一个用于获取Tomcat数据源连接池元数据的类,通过传入的DataSource对象获取连接池的活动连接数、最大连接数、最小连接数、验证查询语句和默认是否自动提交。
6、AbstractDataSourcePoolMetadata
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package org.springframework.boot.jdbc.metadata;
import javax.sql.DataSource;
public abstract class AbstractDataSourcePoolMetadata<T extends DataSource> implements DataSourcePoolMetadata {
private final T dataSource;
protected AbstractDataSourcePoolMetadata(T dataSource) {
this.dataSource = dataSource;
}
public Float getUsage() {
Integer maxSize = this.getMax();
Integer currentSize = this.getActive();
if (maxSize != null && currentSize != null) {
if (maxSize < 0) {
return -1.0F;
} else {
return currentSize == 0 ? 0.0F : (float)currentSize / (float)maxSize;
}
} else {
return null;
}
}
protected final T getDataSource() {
return this.dataSource;
}
}
这段代码是一个抽象类,用于获取数据源池的元数据。它包含一个泛型参数T,用于表示数据源的类型。这个抽象类实现了DataSourcePoolMetadata接口,并提供了一些方法来获取数据源池的使用情况。
在构造方法中,通过传入一个数据源对象来初始化dataSource成员变量。
getUsage()方法用于计算数据源池的使用率。它首先获取最大连接数和当前活动连接数,如果两者都不为null,则根据以下条件进行计算:
- 如果最大连接数小于0,则返回-1.0F,表示无限制。
- 如果当前活动连接数为0,则返回0.0F,表示没有活动连接。
- 否则,返回当前活动连接数除以最大连接数的比例。
getDataSource()方法用于获取数据源对象。
总结:这段代码是一个抽象类,用于获取数据源池的元数据。它提供了获取数据源池使用率的方法,并包含一个数据源对象作为成员变量。
代码步骤:
- 定义了一个抽象类AbstractDataSourcePoolMetadata,实现了DataSourcePoolMetadata接口。
- 在类中定义了一个泛型成员变量dataSource,表示数据源对象。
- 在构造方法中,通过传入一个数据源对象来初始化dataSource成员变量。
- 实现了getUsage()方法,用于计算数据源池的使用率。首先获取最大连接数和当前活动连接数,根据条件进行计算并返回使用率。
- 实现了getDataSource()方法,用于获取数据源对象。
- 结束。
💕💕 本文由激流原创,原创不易,感谢支持
💕💕喜欢的话记得点赞收藏啊