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.


Related articles: