spring boot tomcat jdbc pool property binding
- 2021-01-14 05:52:43
- OfStack
spring boot tomcat jdbc pool property binding code:
spring:
datasource:
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
## when pool sweeper is enabled, extra idle connection will be closed
max-idle: 5
## when idle connection > min-idle, poolSweeper will start to close
min-idle: 1
initial-size, max-active, max-idle, min-idle, tomcat jdbc datasource, min-idle, tomcat jdbc datasource, tomcat-size max-active max-idle min-idle, min-idle tomcat-jdbc datasource tomcat-size max-active max-idle min-idle
Configured correctly
spring:
datasource:
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
tomcat: ## A single database connection pool , And you have to write tomcat The property configuration of the
initial-size: 1
max-active: 5
## when pool sweeper is enabled, extra idle connection will be closed
max-idle: 5
## when idle connection > min-idle, poolSweeper will start to close
min-idle: 1
Note that the tomcat specific database connection pool configuration property is placed under the spring.datasource.tomcat property for this to take effect.
The source code parsing
spring-boot-autoconfigure-1.5.9.RELEASE-sources.jar!/org/springframework/boot/autoconfigure/jdbc/DataSourceAutoConfiguration.java
@Configuration
@Conditional(PooledDataSourceCondition.class)
@ConditionalOnMissingBean({ DataSource.class, XADataSource.class })
@Import({ DataSourceConfiguration.Tomcat.class, DataSourceConfiguration.Hikari.class,
DataSourceConfiguration.Dbcp.class, DataSourceConfiguration.Dbcp2.class,
DataSourceConfiguration.Generic.class })
@SuppressWarnings("deprecation")
protected static class PooledDataSourceConfiguration {
}
DataSourceConfiguration.Tomcat
spring-boot-autoconfigure-1.5.9.RELEASE-sources.jar!/org/springframework/boot/autoconfigure/jdbc/DataSourceConfiguration.java
/**
* Tomcat Pool DataSource configuration.
*/
@ConditionalOnClass(org.apache.tomcat.jdbc.pool.DataSource.class)
@ConditionalOnProperty(name = "spring.datasource.type", havingValue = "org.apache.tomcat.jdbc.pool.DataSource", matchIfMissing = true)
static class Tomcat extends DataSourceConfiguration {
@Bean
@ConfigurationProperties(prefix = "spring.datasource.tomcat")
public org.apache.tomcat.jdbc.pool.DataSource dataSource(
DataSourceProperties properties) {
org.apache.tomcat.jdbc.pool.DataSource dataSource = createDataSource(
properties, org.apache.tomcat.jdbc.pool.DataSource.class);
DatabaseDriver databaseDriver = DatabaseDriver
.fromJdbcUrl(properties.determineUrl());
String validationQuery = databaseDriver.getValidationQuery();
if (validationQuery != null) {
dataSource.setTestOnBorrow(true);
dataSource.setValidationQuery(validationQuery);
}
return dataSource;
}
}
You can see that the DataSourceProperties here is only spring. The configuration of the direct properties of datasource, such as url, username, password, driverClassName. tomcat has no specific properties.
createDataSource
protected <T> T createDataSource(DataSourceProperties properties,
Class<? extends DataSource> type) {
return (T) properties.initializeDataSourceBuilder().type(type).build();
}
Direct createDataSource out org. apache. tomcat. jdbc. pool. DataSource PoolProperties is also the default configuration
ConfigurationProperties
The magic is here
@ConfigurationProperties(prefix = "spring.datasource.tomcat")
This code, it's in good spring vessel agent bean before returning will spring. datasource. tomcat specified attribute is set to 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));
// If no explicit conversion service is provided we add one so that (at least)
// comma-separated arrays of convertibles can be bound automatically
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);
}
}
Notice that annotation here is
@ConfigurationProperties(prefix = "spring.datasource.tomcat")
, its prefix is
spring.datasource.tomcat PropertiesConfigurationFactory
The targetName is 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);
}
}
Delegate to the doBindPropertiesToTarget method
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 method is used here
getRelaxedTargetNames
private Iterable<String> getRelaxedTargetNames() {
return (this.target != null && StringUtils.hasLength(this.targetName)
? new RelaxedNames(this.targetName) : null);
}
Here new is a single RelaxedNames, which can identify variants of multiple variables
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);
}
That support org. springframework. boot. bind. RelaxedNames @ 6 ef81f31 [name = spring. datasource. tomcat, values = [spring. datasource. tomcat, spring_datasource_tomcat, springDatasourceTomcat, springdatasourcetomcat, SPRING. DATASOURCE. TOMCAT, SPRING_DATASOURCE_TOMCAT, SPRINGDATASOURCETOMCAT]] The configuration of the 7
spring:
datasource:
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
tomcat: ## A single database connection pool , And you have to write tomcat The property configuration of the
initial-size: 1
max-active: 5
## when pool sweeper is enabled, extra idle connection will be closed
max-idle: 5
## when idle connection > min-idle, poolSweeper will start to close
min-idle: 1
0
spring.datasource.tomact This method pulls the property configuration under spring.tomact into the PropertyValues object
RelaxedDataBinder.bind
spring:
datasource:
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
tomcat: ## A single database connection pool , And you have to write tomcat The property configuration of the
initial-size: 1
max-active: 5
## when pool sweeper is enabled, extra idle connection will be closed
max-idle: 5
## when idle connection > min-idle, poolSweeper will start to close
min-idle: 1
1
Finally through getPropertyAccessor () to set the propertyAccessor is org. springframework. boot. bind. RelaxedDataBinder $RelaxedBeanWrapper: wrapping object [org apache. tomcat. jdbc. pool. DataSource @ 6 a84bc2a], also packing org. apache. tomcat. jdbc. pool. DataSource
spring:
datasource:
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
tomcat: ## A single database connection pool , And you have to write tomcat The property configuration of the
initial-size: 1
max-active: 5
## when pool sweeper is enabled, extra idle connection will be closed
max-idle: 5
## when idle connection > min-idle, poolSweeper will start to close
min-idle: 1
2
nestedPa here.setPropertyValue(tokens, pv); The real spring. datasource. tomcat attribute value is set in the nestedPa here is org. springframework. boot. bind. RelaxedDataBinder $RelaxedBeanWrapper: wrapping object [org apache. tomcat. jdbc. pool. DataSource @ 6 a84bc2a] finally is invoked 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);
}
}
It make it is to use class org. springframework. beans. BeanWrapperImpl $BeanPropertyHandler to set
spring:
datasource:
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
tomcat: ## A single database connection pool , And you have to write tomcat The property configuration of the
initial-size: 1
max-active: 5
## when pool sweeper is enabled, extra idle connection will be closed
max-idle: 5
## when idle connection > min-idle, poolSweeper will start to close
min-idle: 1
4
Here you use reflection to find the setXXX method (for example, setMaxActive) and set it in
Configuration of multiple data sources
The above configuration is fine for a single data source, and for multiple data sources, it is configured as follows
spring:
datasource:
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
tomcat: ## A single database connection pool , And you have to write tomcat The property configuration of the
initial-size: 1
max-active: 5
## when pool sweeper is enabled, extra idle connection will be closed
max-idle: 5
## when idle connection > min-idle, poolSweeper will start to close
min-idle: 1
5
Note that additional Settings are added to inject ConfigurationProperties tomcat jdbc pool
spring:
datasource:
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
tomcat: ## A single database connection pool , And you have to write tomcat The property configuration of the
initial-size: 1
max-active: 5
## when pool sweeper is enabled, extra idle connection will be closed
max-idle: 5
## when idle connection > min-idle, poolSweeper will start to close
min-idle: 1
6
Original tomcat configuration will be under data source prefix, on spring. datasource. tomcat or spring. datasource. master. Under tomcat are unable to take effect.