spring-boot-autoconfigure-1.5.9.RELEASE-sources.jar!/org/springframework/boot/autoconfigure/jdbc/DataSourceAutoConfiguration.java
spring-boot-autoconfigure-1.5.9.RELEASE-sources.jar!/org/springframework/boot/autoconfigure/jdbc/DataSourceConfiguration.java
, matchIfMissing = true)
static class Tomcat extends DataSourceConfiguration {
)
public org.apache.tomcat.jdbc.pool.DataSource dataSource(
DataSourceProperties properties) {
(
properties, org.apache.tomcat.jdbc.pool.DataSource.class);
));
String
validationQuery
=
databaseDriver
.getValidationQuery
();
if
(validationQuery != null) {
dataSource
.setTestOnBorrow
(true);
dataSource
.setValidationQuery
(validationQuery);
return
dataSource
;
}
复制代码
可以看到这里的DataSourceProperties仅仅只有spring.datasource直接属性的配置,比如url,username,password,driverClassName。tomcat的具体属性都没有。
createDataSource
protected <T> T createDataSource(DataSourceProperties properties,
Class<? extends DataSource> type) {
return (T) properties.initializeDataSourceBuilder().type(type).build();
}复制代码
直接createDataSource出来的org.apache.tomcat.jdbc.pool.DataSource的PoolProperties也是默认的配置
ConfigurationProperties
具体的魔力就在于@ConfigurationProperties(prefix = "spring.datasource.tomcat")这段代码,它在spring容器构造好代理bean返回之前会将spring.datasource.tomcat指定的属性设置到org.apache.tomcat.jdbc.pool.DataSource
spring-boot-1.5.9.RELEASE-sources.jar!/org/springframework/boot/context/properties/ConfigurationPropertiesBindingPostProcessor.java
private void postProcessBeforeInitialization(Object bean, String beanName,
ConfigurationProperties annotation) {
Object target = bean;
PropertiesConfigurationFactory<Object> factory = new PropertiesConfigurationFactory<Object>(
target);
factory.setPropertySources(this.propertySources);
factory.setValidator(determineValidator(bean));
factory.setConversionService(this.conversionService == null
? getDefaultConversionService() : this.conversionService);
if (annotation != null) {
factory.setIgnoreInvalidFields(annotation.ignoreInvalidFields());
factory.setIgnoreUnknownFields(annotation.ignoreUnknownFields());
factory.setExceptionIfInvalid(annotation.exceptionIfInvalid());
factory.setIgnoreNestedProperties(annotation.ignoreNestedProperties());
if (StringUtils.hasLength(annotation.prefix())) {
factory.setTargetName(annotation.prefix());
try {
factory.bindPropertiesToTarget();
catch (Exception ex) {
String targetClass = ClassUtils.getShortName(target.getClass());
throw new BeanCreationException(beanName, "Could not bind properties to "
+ targetClass + " (" + getAnnotationDetails(annotation) + ")", ex);
}复制代码
注意,这里的annotation就是@ConfigurationProperties(prefix = "spring.datasource.tomcat"),它的prefix是spring.datasource.tomcat
PropertiesConfigurationFactory的targetName就是spring.datasource.tomcat
PropertiesConfigurationFactory.bindPropertiesToTarget
spring-boot-1.5.9.RELEASE-sources.jar!/org/springframework/boot/bind/PropertiesConfigurationFactory.java
public void bindPropertiesToTarget() throws BindException {
Assert.state(this.propertySources != null, "PropertySources should not be null");
try {
if (logger.isTraceEnabled()) {
logger.trace("Property Sources: " + this.propertySources);
this.hasBeenBound = true;
doBindPropertiesToTarget();
catch (BindException ex) {
if (this.exceptionIfInvalid) {
throw ex;
PropertiesConfigurationFactory.logger
.error("Failed to load Properties validation bean. "
+ "Your Properties may be invalid.", ex);
}复制代码
委托给doBindPropertiesToTarget方法
PropertiesConfigurationFactory.doBindPropertiesToTarget
private void doBindPropertiesToTarget() throws BindException {
RelaxedDataBinder dataBinder = (this.targetName != null
? new RelaxedDataBinder(this.target, this.targetName)
: new RelaxedDataBinder(this.target));
if (this.validator != null
&& this.validator.supports(dataBinder.getTarget().getClass())) {
dataBinder.setValidator(this.validator);
if (this.conversionService != null) {
dataBinder.setConversionService(this.conversionService);
dataBinder.setAutoGrowCollectionLimit(Integer.MAX_VALUE);
dataBinder.setIgnoreNestedProperties(this.ignoreNestedProperties);
dataBinder.setIgnoreInvalidFields(this.ignoreInvalidFields);
dataBinder.setIgnoreUnknownFields(this.ignoreUnknownFields);
customizeBinder(dataBinder);
Iterable<String> relaxedTargetNames = getRelaxedTargetNames();
Set<String> names = getNames(relaxedTargetNames);
PropertyValues propertyValues = getPropertySourcesPropertyValues(names,
relaxedTargetNames);
dataBinder.bind(propertyValues);
if (this.validator != null) {
dataBinder.validate();
checkForBindingErrors(dataBinder);
}复制代码
这里借助RelaxedDataBinder.bind方法
getRelaxedTargetNames
private Iterable<String> getRelaxedTargetNames() {
return (this.target != null && StringUtils.hasLength(this.targetName)
? new RelaxedNames(this.targetName) : null);
}复制代码
这里new了一个RelaxedNames,可以识别多个变量的变种
RelaxedNames
spring-boot-1.5.9.RELEASE-sources.jar!/org/springframework/boot/bind/RelaxedNames.java
private void initialize(String name, Set<String> values) {
if (values.contains(name)) {
return;
for (Variation variation : Variation.values()) {
for (Manipulation manipulation : Manipulation.values()) {
String result = name;
result = manipulation.apply(result);
result = variation.apply(result);
values.add(result);
initialize(result, values);
* Name variations.
enum Variation {
NONE {
@Override
public String apply(String value) {
return value;
LOWERCASE {
@Override
public String apply(String value) {
return value.isEmpty() ? value : value.toLowerCase();
UPPERCASE {
@Override
public String apply(String value) {
return value.isEmpty() ? value : value.toUpperCase();
public abstract String apply(String value);
}复制代码
即支持org.springframework.boot.bind.RelaxedNames@6ef81f31[name=spring.datasource.tomcat,values=[spring.datasource.tomcat, spring_datasource_tomcat, springDatasourceTomcat, springdatasourcetomcat, SPRING.DATASOURCE.TOMCAT, SPRING_DATASOURCE_TOMCAT, SPRINGDATASOURCETOMCAT]]这7中配置的写法
getPropertySourcesPropertyValues
private PropertyValues getPropertySourcesPropertyValues(Set<String> names,
Iterable<String> relaxedTargetNames) {
PropertyNamePatternsMatcher includes = getPropertyNamePatternsMatcher(names,
relaxedTargetNames);
return new PropertySourcesPropertyValues(this.propertySources, names, includes,
this.resolvePlaceholders);
}复制代码
这个方法会把spring.datasource.tomact底下的属性配置拉取到PropertyValues对象里头
RelaxedDataBinder.bind
spring-boot-1.5.9.RELEASE-sources.jar!/org/springframework/boot/bind/RelaxedDataBinder.java的bind方法调用的是父类的方法spring-context-4.3.13.RELEASE-sources.jar!/org/springframework/validation/DataBinder.java
* Bind the given property values to this binder's target.
* <p>This call can create field errors, representing basic binding
* errors like a required field (code "required"), or type mismatch
* between value and bean property (code "typeMismatch").
* <p>Note that the given PropertyValues should be a throwaway instance:
* For efficiency, it will be modified to just contain allowed fields if it
* implements the MutablePropertyValues interface; else, an internal mutable
* copy will be created for this purpose. Pass in a copy of the PropertyValues
* if you want your original instance to stay unmodified in any case.
* @param pvs property values to bind
* @see #doBind(org.springframework.beans.MutablePropertyValues)
public void
bind
(PropertyValues pvs) {
MutablePropertyValues mpvs = (pvs instanceof MutablePropertyValues) ?
(MutablePropertyValues) pvs : new MutablePropertyValues(pvs);
doBind
(mpvs);
* Actual implementation of the binding process, working with the
* passed-in MutablePropertyValues instance.
* @param mpvs the property values to bind,
* as MutablePropertyValues instance
* @see #checkAllowedFields
* @see #checkRequiredFields
* @see #applyPropertyValues
protected void
doBind
(MutablePropertyValues mpvs) {
checkAllowedFields
(mpvs);
checkRequiredFields
(mpvs);
applyPropertyValues
(mpvs);
* Apply given property values to the target object.
* <p>Default implementation applies all of the supplied property
* values as bean property values. By default, unknown fields will
* be ignored.
* @param mpvs the property values to be bound (can be modified)
* @see #getTarget
* @see #getPropertyAccessor
* @see #isIgnoreUnknownFields
* @see #getBindingErrorProcessor
* @see BindingErrorProcessor#processPropertyAccessException
protected void
applyPropertyValues
(MutablePropertyValues mpvs) {
try {
getPropertyAccessor
()
.setPropertyValues
(mpvs, isIgnoreUnknownFields(),
isIgnoreInvalidFields
());
catch (PropertyBatchUpdateException ex) {
for (PropertyAccessException pae : ex.getPropertyAccessExceptions()) {
getBindingErrorProcessor
()
.processPropertyAccessException
(pae, getInternalBindingResult());
* Return the underlying PropertyAccessor of this binder's BindingResult.
protected ConfigurablePropertyAccessor
getPropertyAccessor
() {
return
getInternalBindingResult
()
.getPropertyAccessor
();
}
复制代码
最后通过getPropertyAccessor()来设置,这个propertyAccessor就是org.springframework.boot.bind.RelaxedDataBinder$RelaxedBeanWrapper: wrapping object [org.apache.tomcat.jdbc.pool.DataSource@6a84bc2a],也就包装的org.apache.tomcat.jdbc.pool.DataSource
AbstractPropertyAccessor.setPropertyValues
spring-beans-4.3.13.RELEASE-sources.jar!/org/springframework/beans/AbstractPropertyAccessor.java
@Override
public void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown, boolean ignoreInvalid)
throws BeansException {
List<PropertyAccessException> propertyAccessExceptions = null;
List<PropertyValue> propertyValues = (pvs instanceof MutablePropertyValues ?
((MutablePropertyValues) pvs).getPropertyValueList() : Arrays.asList(pvs.getPropertyValues()));
for (PropertyValue pv : propertyValues) {
try {
setPropertyValue(pv);
catch (NotWritablePropertyException ex) {
if (!ignoreUnknown) {
throw ex;
catch (NullValueInNestedPathException ex) {
if (!ignoreInvalid) {
throw ex;
catch (PropertyAccessException ex) {
if (propertyAccessExceptions == null) {
propertyAccessExceptions = new LinkedList<PropertyAccessException>();
propertyAccessExceptions.add(ex);
if (propertyAccessExceptions != null) {
PropertyAccessException[] paeArray =
propertyAccessExceptions.toArray(new PropertyAccessException[propertyAccessExceptions.size()]);
throw new PropertyBatchUpdateException(paeArray);
@Override
public void setPropertyValue(PropertyValue pv) throws BeansException {
PropertyTokenHolder tokens = (PropertyTokenHolder) pv.resolvedTokens;
if (tokens == null) {
String propertyName = pv.getName();
AbstractNestablePropertyAccessor nestedPa;
try {
nestedPa = getPropertyAccessorForPropertyPath(propertyName);
catch (NotReadablePropertyException ex) {
throw new NotWritablePropertyException(getRootClass(), this.nestedPath + propertyName,
"Nested property in path '" + propertyName + "' does not exist", ex);
tokens = getPropertyNameTokens(getFinalPath(nestedPa, propertyName));
if (nestedPa == this) {
pv.getOriginalPropertyValue().resolvedTokens = tokens;
nestedPa.setPropertyValue(tokens, pv);
else {
setPropertyValue(tokens, pv);
}复制代码
这里的nestedPa.setPropertyValue(tokens, pv);真正把spring.datasource.tomcat的属性值设置进去
这里的nestedPa就是org.springframework.boot.bind.RelaxedDataBinder$RelaxedBeanWrapper: wrapping object [org.apache.tomcat.jdbc.pool.DataSource@6a84bc2a]
最后是调用AbstractNestablePropertyAccessor.processLocalProperty
AbstractNestablePropertyAccessor.processLocalProperty
spring-beans-4.3.13.RELEASE-sources.jar!/org/springframework/beans/AbstractNestablePropertyAccessor.java
private void processLocalProperty(PropertyTokenHolder tokens, PropertyValue pv) {
PropertyHandler ph = getLocalPropertyHandler(tokens.actualName)
if (ph == null || !ph.isWritable()) {
if (pv.isOptional()) {
if (logger.isDebugEnabled()) {
logger.debug("Ignoring optional value for property '" + tokens.actualName +
"' - property not found on bean class [" + getRootClass().getName() + "]")
return
else {
throw createNotWritablePropertyException(tokens.canonicalName)
Object oldValue = null
try {
Object originalValue = pv.getValue()
Object valueToApply = originalValue
if (!Boolean.FALSE.equals(pv.conversionNecessary)) {
if (pv.isConverted()) {
valueToApply = pv.getConvertedValue()
else {
if (isExtractOldValueForEditor() && ph.isReadable()) {
try {
oldValue = ph.getValue()
catch (Exception ex) {
if (ex instanceof PrivilegedActionException) {
ex = ((PrivilegedActionException) ex).getException()
if (logger.isDebugEnabled()) {
logger.debug("Could not read previous value of property '" +
this.nestedPath + tokens.canonicalName + "'", ex)
valueToApply = convertForProperty(
tokens.canonicalName, oldValue, originalValue, ph.toTypeDescriptor())
pv.getOriginalPropertyValue().conversionNecessary = (valueToApply != originalValue)
ph.setValue(this.wrappedObject, valueToApply)
catch (TypeMismatchException ex) {
throw ex
catch (InvocationTargetException ex) {
PropertyChangeEvent propertyChangeEvent = new PropertyChangeEvent(
this.rootObject, this.nestedPath + tokens.canonicalName, oldValue, pv.getValue())
if (ex.getTargetException() instanceof ClassCastException) {
throw new TypeMismatchException(propertyChangeEvent, ph.getPropertyType(), ex.getTargetException())
else {
Throwable cause = ex.getTargetException()
if (cause instanceof UndeclaredThrowableException) {
// May happen e.g. with Groovy-generated methods
cause = cause.getCause()
throw new MethodInvocationException(propertyChangeEvent, cause)
catch (Exception ex) {
PropertyChangeEvent pce = new PropertyChangeEvent(
this.rootObject, this.nestedPath + tokens.canonicalName, oldValue, pv.getValue())
throw new MethodInvocationException(pce, ex)
}复制代码
它使其是使用class org.springframework.beans.BeanWrapperImpl$BeanPropertyHandler来设置
BeanWrapperImpl$BeanPropertyHandler.setValue
spring-beans-4.3.13.RELEASE-sources.jar!/org/springframework/beans/BeanWrapperImpl.java
@Override
public void setValue(final Object object, Object valueToApply) throws Exception {
final Method writeMethod = (this.pd instanceof GenericTypeAwarePropertyDescriptor ?
((GenericTypeAwarePropertyDescriptor) this.pd).getWriteMethodForActualAccess() :
this.pd.getWriteMethod());
if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers()) && !writeMethod.isAccessible()) {
if (System.getSecurityManager() != null) {
AccessController.doPrivileged(new PrivilegedAction<Object>() {
@Override
public Object run() {
writeMethod.setAccessible(true);
return null;
else {
writeMethod.setAccessible(true);
final Object value = valueToApply;
if (System.getSecurityManager() != null) {
try {
AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
@Override
public Object run() throws Exception {
writeMethod.invoke(object, value);
return null;
}, acc);
catch (PrivilegedActionException ex) {
throw ex.getException();
else {
writeMethod.invoke(getWrappedInstance(), value);
}复制代码
这里利用反射找出setXXX方法(
比如setMaxActive
),然后设置进去
多数据源的配置
上面的配置对于单数据源来说是没有问题的,对于多数据源,则配置如下
@Configuration
public class MasterDatasourceConfig {
@Bean("masterDataSource")
@ConfigurationProperties(prefix = "spring.datasource.master")
public DataSource masterDataSource() {
return DataSourceBuilder.create().build();
} 复制代码
注意,这里要添加ConfigurationProperties注入tomcat jdbc pool的额外设置
spring:
datasource:
master:
type: org.apache.tomcat.jdbc.pool.DataSource
driver-class-name: org.postgresql.Driver
url: jdbc:postgresql://192.168.99.100:5432/postgres?connectTimeout=6000&socketTimeout=6000
username: postgres
password: postgres
jmx-enabled: true
initial-size: 1
max-active: 5
max-idle: 5
min-idle: 1复制代码
原先tomcat的配置都要放在数据源前缀的底下,放在spring.datasource.tomcat或者spring.datasource.master.tomcat底下均无法生效。
spirngboot的自动配置是挺方便的,但是在实际应用的场景下还需要了解底层机制才可以,否则容易出来配置假象,以为配置对了,实际没生效。
boot-features-configure-datasource