springboot3.5 MySQL 自动装配解析

发布于:2025-07-08 ⋅ 阅读:(20) ⋅ 点赞:(0)

一、配置环境

1. 配置pom.xml

        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
        </dependency>

2、自动配置图解

 

二、相关代码解析

1、自动配置入口: DataSourceAutoConfiguration

@AutoConfiguration(before = SqlInitializationAutoConfiguration.class)
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
@ConditionalOnMissingBean(type = "io.r2dbc.spi.ConnectionFactory")
@EnableConfigurationProperties(DataSourceProperties.class)
@Import({ DataSourcePoolMetadataProvidersConfiguration.class, DataSourceCheckpointRestoreConfiguration.class })
public class DataSourceAutoConfiguration {

	@Configuration(proxyBeanMethods = false)
	@Conditional(EmbeddedDatabaseCondition.class)
	@ConditionalOnMissingBean({ DataSource.class, XADataSource.class })
	@Import(EmbeddedDataSourceConfiguration.class)
	protected static class EmbeddedDatabaseConfiguration {

	}
    //加载条件: 存在DataSource类且未自定义DataSource Bean

	@Configuration(proxyBeanMethods = false)
	@Conditional(PooledDataSourceCondition.class)
	@ConditionalOnMissingBean({ DataSource.class, XADataSource.class })
	@Import({ DataSourceConfiguration.Hikari.class, DataSourceConfiguration.Tomcat.class,
			DataSourceConfiguration.Dbcp2.class, DataSourceConfiguration.OracleUcp.class,
			DataSourceConfiguration.Generic.class, DataSourceJmxConfiguration.class })
	protected static class PooledDataSourceConfiguration {

		@Bean
		@ConditionalOnMissingBean(JdbcConnectionDetails.class)
		PropertiesJdbcConnectionDetails jdbcConnectionDetails(DataSourceProperties properties) {
			return new PropertiesJdbcConnectionDetails(properties);
		}

	}
}

2、驱动类名推导解析:DataSourceProperties#determineDriverClassName

@ConfigurationProperties("spring.datasource")
public class DataSourceProperties implements BeanClassLoaderAware, InitializingBean {

	public String determineDriverClassName() {
		String driverClassName = findDriverClassName();
		if (!StringUtils.hasText(driverClassName)) {
			throw new DataSourceBeanCreationException("Failed to determine a suitable driver class", this,
					this.embeddedDatabaseConnection);
		}
		return driverClassName;
	}

	String findDriverClassName() {
         // 根据用户application.properties 里面的配置。 如spring.datasource.driver-class-name
		if (StringUtils.hasText(this.driverClassName)) {
			Assert.state(driverClassIsLoadable(), () -> "Cannot load driver class: " + this.driverClassName);
			return this.driverClassName;
		}
		String driverClassName = null;
          // 通过application.properties 里面的spring.datasource.url获取
		if (StringUtils.hasText(this.url)) {
			driverClassName = DatabaseDriver.fromJdbcUrl(this.url).getDriverClassName();
		}
		if (!StringUtils.hasText(driverClassName)) {
			driverClassName = this.embeddedDatabaseConnection.getDriverClassName();
		}
		return driverClassName;
	}
}

3、驱动名称解析. DatabaseDriver

public enum DatabaseDriver {

	public static DatabaseDriver fromJdbcUrl(String url) {
// 通过配置的url 解析: url: jdbc:mysql://127.0.0.1:3306/user?useUnicode=true&characterEncoding=utf8
		if (StringUtils.hasLength(url)) {
			Assert.isTrue(url.startsWith("jdbc"), "'url' must start with \"jdbc\"");
			String urlWithoutPrefix = url.substring("jdbc".length()).toLowerCase(Locale.ENGLISH);
			for (DatabaseDriver driver : values()) {
				for (String urlPrefix : driver.getUrlPrefixes()) {
					String prefix = ":" + urlPrefix + ":";
					if (driver != UNKNOWN && urlWithoutPrefix.startsWith(prefix)) {
						return driver;
					}
				}
			}
		}
		return UNKNOWN;
	}
}

4、数据源、连接池配置: HikariDataSource

abstract class DataSourceConfiguration {

	@Configuration(proxyBeanMethods = false)
	@ConditionalOnClass(HikariDataSource.class)
	@ConditionalOnMissingBean(DataSource.class)
	@ConditionalOnProperty(name = "spring.datasource.type", havingValue = "com.zaxxer.hikari.HikariDataSource",
			matchIfMissing = true)
	static class Hikari {

		@Bean
		static HikariJdbcConnectionDetailsBeanPostProcessor jdbcConnectionDetailsHikariBeanPostProcessor(
				ObjectProvider<JdbcConnectionDetails> connectionDetailsProvider) {
			return new HikariJdbcConnectionDetailsBeanPostProcessor(connectionDetailsProvider);
		}

		@Bean
		@ConfigurationProperties("spring.datasource.hikari")
		HikariDataSource dataSource(DataSourceProperties properties, JdbcConnectionDetails connectionDetails,
				Environment environment) {
			String dataSourceClassName = environment.getProperty("spring.datasource.hikari.data-source-class-name");
			HikariDataSource dataSource = createDataSource(connectionDetails, HikariDataSource.class,
					properties.getClassLoader(), dataSourceClassName == null);
			if (StringUtils.hasText(properties.getName())) {
				dataSource.setPoolName(properties.getName());
			}
			return dataSource;
		}

	}
}

5、驱动加载原理:

JDBC 4.0+ 自动注册机制 (SPI)
  • 驱动JAR中的声明

    MySQL驱动JAR(mysq-connector-j-9.2.0.jar)包含文件:META-INF/services/java.sql.Driver

    内容:com.mysql.cj.jdbc.Driver

  • DriverManager 自动加载

    首次调用DriverManager.getConnection()时,会自动扫描并注册所有META-INF/services下的驱动实现类。

  • DriverManager 的初始化逻辑

  • 代码位置java.sql.DriverManager#getConnection

  • 关键方法:ensureDriversInitialized() 

public class DriverManager {
    @CallerSensitive
    public static Connection getConnection(String url,
        java.util.Properties info) throws SQLException {

        return (getConnection(url, info, Reflection.getCallerClass()));
    }


/*
     * Load the initial JDBC drivers by checking the System property
     * jdbc.drivers and then use the {@code ServiceLoader} mechanism
     */
     // 通过SPI 加载mysql driver
    @SuppressWarnings("removal")
    private static void ensureDriversInitialized() {
        if (driversInitialized) {
            return;
        }

        synchronized (lockForInitDrivers) {
            if (driversInitialized) {
                return;
            }
            String drivers;
            try {
                drivers = AccessController.doPrivileged(new PrivilegedAction<String>() {
                    public String run() {
                        return System.getProperty(JDBC_DRIVERS_PROPERTY);
                    }
                });
            } catch (Exception ex) {
                drivers = null;
            }
            // If the driver is packaged as a Service Provider, load it.
            // Get all the drivers through the classloader
            // exposed as a java.sql.Driver.class service.
            // ServiceLoader.load() replaces the sun.misc.Providers()

            AccessController.doPrivileged(new PrivilegedAction<Void>() {
                public Void run() {
                    // SPI 加载Driver
                    ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
                    Iterator<Driver> driversIterator = loadedDrivers.iterator();

                    /* Load these drivers, so that they can be instantiated.
                     * It may be the case that the driver class may not be there
                     * i.e. there may be a packaged driver with the service class
                     * as implementation of java.sql.Driver but the actual class
                     * may be missing. In that case a java.util.ServiceConfigurationError
                     * will be thrown at runtime by the VM trying to locate
                     * and load the service.
                     *
                     * Adding a try catch block to catch those runtime errors
                     * if driver not available in classpath but it's
                     * packaged as service and that service is there in classpath.
                     */
                    try {
                        while (driversIterator.hasNext()) {
                            driversIterator.next();
                        }
                    } catch (Throwable t) {
                        // Do nothing
                    }
                    return null;
                }
            });

            println("DriverManager.initialize: jdbc.drivers = " + drivers);

            if (drivers != null && !drivers.isEmpty()) {
                String[] driversList = drivers.split(":");
                println("number of Drivers:" + driversList.length);
                for (String aDriver : driversList) {
                    try {
                        println("DriverManager.Initialize: loading " + aDriver);
                        Class.forName(aDriver, true,
                                ClassLoader.getSystemClassLoader());
                    } catch (Exception ex) {
                        println("DriverManager.Initialize: load failed: " + ex);
                    }
                }
            }

            driversInitialized = true;
            println("JDBC DriverManager initialized");
        }
    }
}
  • 图解:

 

三、总结

1、自动配置:DataSourceAutoConfiguration

2、驱动加载: SPI(机制)

 


网站公告

今日签到

点亮在社区的每一天
去签到