SpringBoot中的数据库连接池

升级发现问题

有一个项目之前使用的SpringBoot的版本是1.5.6.RELEASE, 现在准备升级到2.1.0.RELEASE,突然发现数据库连接不上了,排查了一下发现是由于SpringBoot版本在2.0.0.RELEASE后的默认数据源不是tomcat的数据库连接池了;而项目正好没有指定数据库连接池,使用的是1.5.6.RELEASE默认的tomcat的连接池。

以下就以1.5.6.RELEASE和2.1.0.RELEASE为例:

在Spring Boot2.0.0.RELEASE版本之前

选择数据库连接池实现的判断逻辑:

  1. 检查tomcat的数据库连接池实现是否可用,如可用,则启用。使用spring.datasource.tomcat.*可以控制连接池的行为。
  2. 检查hikari是否可用,如可用,则启用。使用spring.datasource.hikari.*可以控制连接池的行为。
  3. 检查dbcp是否可用,如可用,则启用;该连接池被Spring Boot标记为deprecated,不建议在生产环境使用该连接池的实现。
  4. 检查dbcp2是否可用,如可用,则启用。使用spring.datasource.dbcp2.*可以控制连接池的行为。

代码在spring-boot-autoconfigure-1.5.6.RELEASE.jar中:

 * Copyright 2012-2016 the original author or authors.

package org.springframework.boot.autoconfigure.jdbc;

import java.util.HashMap;
import java.util.Map;

import javax.sql.DataSource;

import org.springframework.beans.BeanUtils;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.boot.bind.RelaxedDataBinder;
import org.springframework.boot.jdbc.DatabaseDriver;
import org.springframework.util.ClassUtils;

/**
 * Convenience class for building a {@link DataSource} with common implementations and
 * properties. If Tomcat, HikariCP or Commons DBCP are on the classpath one of them will
 * be selected (in that order with Tomcat first). In the interest of a uniform interface,
 * and so that there can be a fallback to an embedded database if one can be detected on
 * the classpath, only a small set of common configuration properties are supported. To
 * inject additional properties into the result you can downcast it, or use
 * {@code @ConfigurationProperties}.
 *
 * @author Dave Syer
 * @since 1.1.0
 */
public class DataSourceBuilder {

	private static final String[] DATA_SOURCE_TYPE_NAMES = new String[] {
			"org.apache.tomcat.jdbc.pool.DataSource",
			"com.zaxxer.hikari.HikariDataSource",
			"org.apache.commons.dbcp.BasicDataSource", // deprecated
			"org.apache.commons.dbcp2.BasicDataSource" };

	private Class<? extends DataSource> type;

	private ClassLoader classLoader;

	private Map<String, String> properties = new HashMap<String, String>();

	public static DataSourceBuilder create() {
		return new DataSourceBuilder(null);
	}

	public static DataSourceBuilder create(ClassLoader classLoader) {
		return new DataSourceBuilder(classLoader);
	}

	public DataSourceBuilder(ClassLoader classLoader) {
		this.classLoader = classLoader;
	}

	public DataSource build() {
		Class<? extends DataSource> type = getType();
		DataSource result = BeanUtils.instantiate(type);
		maybeGetDriverClassName();
		bind(result);
		return result;
	}

	private void maybeGetDriverClassName() {
		if (!this.properties.containsKey("driverClassName")
				&& this.properties.containsKey("url")) {
			String url = this.properties.get("url");
			String driverClass = DatabaseDriver.fromJdbcUrl(url).getDriverClassName();
			this.properties.put("driverClassName", driverClass);
		}
	}

	private void bind(DataSource result) {
		MutablePropertyValues properties = new MutablePropertyValues(this.properties);
		new RelaxedDataBinder(result).withAlias("url", "jdbcUrl")
				.withAlias("username", "user").bind(properties);
	}

	public DataSourceBuilder type(Class<? extends DataSource> type) {
		this.type = type;
		return this;
	}

	public DataSourceBuilder url(String url) {
		this.properties.put("url", url);
		return this;
	}

	public DataSourceBuilder driverClassName(String driverClassName) {
		this.properties.put("driverClassName", driverClassName);
		return this;
	}

	public DataSourceBuilder username(String username) {
		this.properties.put("username", username);
		return this;
	}

	public DataSourceBuilder password(String password) {
		this.properties.put("password", password);
		return this;
	}

	@SuppressWarnings("unchecked")
	public Class<? extends DataSource> findType() {
		if (this.type != null) {
			return this.type;
		}
		for (String name : DATA_SOURCE_TYPE_NAMES) {
			try {
				return (Class<? extends DataSource>) ClassUtils.forName(name,
						this.classLoader);
			}
			catch (Exception ex) {
				// Swallow and continue
			}
		}
		return null;
	}

	private Class<? extends DataSource> getType() {
		Class<? extends DataSource> type = findType();
		if (type != null) {
			return type;
		}
		throw new IllegalStateException("No supported DataSource type found");
	}

}

顺序按照DATA_SOURCE_TYPE_NAMES 数组中的顺序:

private static final String[] DATA_SOURCE_TYPE_NAMES = new String[] {
			"org.apache.tomcat.jdbc.pool.DataSource",
			"com.zaxxer.hikari.HikariDataSource",
			"org.apache.commons.dbcp.BasicDataSource", // deprecated
			"org.apache.commons.dbcp2.BasicDataSource" };

在Spring Boot2.0.0.RELEASE版本之后

选择数据库连接池实现的判断逻辑:

  1. 检查hikari是否可用,如可用,则启用。使用spring.datasource.hikari.*可以控制连接池的行为。
  2. 检查tomcat的数据库连接池实现是否可用,如可用,则启用。使用spring.datasource.tomcat.*可以控制连接池的行为。
  3. 检查dbcp2是否可用,如可用,则启用。使用spring.datasource.dbcp2.*可以控制连接池的行为。

代码在spring-boot-2.1.0.RELEASE.jar中:

 * Copyright 2012-2018 the original author or authors.

package org.springframework.boot.jdbc;

import java.util.HashMap;
import java.util.Map;

import javax.sql.DataSource;

import org.springframework.beans.BeanUtils;
import org.springframework.boot.context.properties.bind.Bindable;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.context.properties.source.ConfigurationPropertyName;
import org.springframework.boot.context.properties.source.ConfigurationPropertyNameAliases;
import org.springframework.boot.context.properties.source.ConfigurationPropertySource;
import org.springframework.boot.context.properties.source.MapConfigurationPropertySource;
import org.springframework.util.ClassUtils;

/**
 * Convenience class for building a {@link DataSource} with common implementations and
 * properties. If HikariCP, Tomcat or Commons DBCP are on the classpath one of them will
 * be selected (in that order with Hikari first). In the interest of a uniform interface,
 * and so that there can be a fallback to an embedded database if one can be detected on
 * the classpath, only a small set of common configuration properties are supported. To
 * inject additional properties into the result you can downcast it, or use
 * {@code @ConfigurationProperties}.
 *
 * @param <T> type of DataSource produced by the builder
 * @author Dave Syer
 * @author Madhura Bhave
 * @since 2.0.0
 */
public final class DataSourceBuilder<T extends DataSource> {

	private static final String[] DATA_SOURCE_TYPE_NAMES = new String[] {
			"com.zaxxer.hikari.HikariDataSource",
			"org.apache.tomcat.jdbc.pool.DataSource",
			"org.apache.commons.dbcp2.BasicDataSource" };

	private Class<? extends DataSource> type;

	private ClassLoader classLoader;

	private Map<String, String> properties = new HashMap<>();

	public static DataSourceBuilder<?> create() {
		return new DataSourceBuilder<>(null);
	}

	public static DataSourceBuilder<?> create(ClassLoader classLoader) {
		return new DataSourceBuilder<>(classLoader);
	}

	private DataSourceBuilder(ClassLoader classLoader) {
		this.classLoader = classLoader;
	}

	@SuppressWarnings("unchecked")
	public T build() {
		Class<? extends DataSource> type = getType();
		DataSource result = BeanUtils.instantiateClass(type);
		maybeGetDriverClassName();
		bind(result);
		return (T) result;
	}

	private void maybeGetDriverClassName() {
		if (!this.properties.containsKey("driverClassName")
				&& this.properties.containsKey("url")) {
			String url = this.properties.get("url");
			String driverClass = DatabaseDriver.fromJdbcUrl(url).getDriverClassName();
			this.properties.put("driverClassName", driverClass);
		}
	}

	private void bind(DataSource result) {
		ConfigurationPropertySource source = new MapConfigurationPropertySource(
				this.properties);
		ConfigurationPropertyNameAliases aliases = new ConfigurationPropertyNameAliases();
		aliases.addAliases("url", "jdbc-url");
		aliases.addAliases("username", "user");
		Binder binder = new Binder(source.withAliases(aliases));
		binder.bind(ConfigurationPropertyName.EMPTY, Bindable.ofInstance(result));
	}

	@SuppressWarnings("unchecked")
	public <D extends DataSource> DataSourceBuilder<D> type(Class<D> type) {
		this.type = type;
		return (DataSourceBuilder<D>) this;
	}

	public DataSourceBuilder<T> url(String url) {
		this.properties.put("url", url);
		return this;
	}

	public DataSourceBuilder<T> driverClassName(String driverClassName) {
		this.properties.put("driverClassName", driverClassName);
		return this;
	}

	public DataSourceBuilder<T> username(String username) {
		this.properties.put("username", username);
		return this;
	}

	public DataSourceBuilder<T> password(String password) {
		this.properties.put("password", password);
		return this;
	}

	@SuppressWarnings("unchecked")
	public static Class<? extends DataSource> findType(ClassLoader classLoader) {
		for (String name : DATA_SOURCE_TYPE_NAMES) {
			try {
				return (Class<? extends DataSource>) ClassUtils.forName(name,
						classLoader);
			}
			catch (Exception ex) {
				// Swallow and continue
			}
		}
		return null;
	}

	private Class<? extends DataSource> getType() {
		Class<? extends DataSource> type = (this.type != null) ? this.type
				: findType(this.classLoader);
		if (type != null) {
			return type;
		}
		throw new IllegalStateException("No supported DataSource type found");
	}

}

顺序也是按照DATA_SOURCE_TYPE_NAMES 数组中的顺序:

private static final String[] DATA_SOURCE_TYPE_NAMES = new String[] {
			"com.zaxxer.hikari.HikariDataSource",
			"org.apache.tomcat.jdbc.pool.DataSource",
			"org.apache.commons.dbcp2.BasicDataSource" };

建议

使用时最好指定数据源格式,防止今后的升级中默认数据源的变动带来的不确定。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值