spring boot tomcat jdbc pool的属性绑定

网友投稿 288 2023-02-21


spring boot tomcat jdbc pool的属性绑定

下面看下spring boot 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

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还是使用的默认的配置

正确配置

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: ## 单个数据库连接池,而且得写上tomcat的属性配置才可以生效

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

注意,这里把具体tomcat数据库连接池的配置属性放到了spring.datasource.tomcat属性下面,这样才可以生效。

源码解析

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("dhttp://eprecation")

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;

}

}

可以看到这里的DataSourceProperties仅仅只有spring.datasource直接属性的配置,比如url,username,password,driverClassName。tomcat的具体属性都没有。

createDataSource

protected 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 factory = new PropertiesConfigurationFactory(

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);

}

}

注意,这里的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 relaxedTargetNames = getRelaxedTargetNames();

Set names = getNames(relaxedTargetNames);

PropertyValues propertyValues = getPropertySourcesPropertyValues(names,

relaxedTargetNames);

dataBinder.bind(propertyValues);

if (this.validator != null) {

dataBinder.validate();

}

checkForBindingErrors(dataBinder);

}

这里借助RelaxedDataBinder.bind方法

getRelaxedTargetNames

private Iterable 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 values) {

if (values.contains(name)) {

return;

}

for (Variation variation : Variation.values()) {

for http://(Manipulation manipulation : Manipulation.values()) {

String result = name;

result = manipulation.apply(result);

result = variation.apply(result);

values.add(result);

initialize(result, valhttp://ues);

}

}

}

/**

* 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 names,

Iterable 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.

*

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").

*

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.

*

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 {

// Bind request parameters onto target object.

getPropertyAccessor().setPropertyValues(mpvs, isIgnoreUnknownFields(), isIgnoreInvalidFields());

}

catch (PropertyBatchUpdateException ex) {

// Use bind error processor to create FieldErrors.

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 propertyAccessExceptions = null;

List propertyValues = (pvs instanceof MutablePropertyValues ?

((MutablePropertyValues) pvs).getPropertyValueList() : Arrays.asList(pvs.getPropertyValues()));

for (PropertyValue pv : propertyValues) {

try {

// This method may throw any BeansException, which won't be caught

// here, if there is a critical failure such as no matching field.

// We can attempt to deal only with less serious exceptions.

setPropertyValue(pv);

}

catch (NotWritablePropertyException ex) {

if (!ignoreUnknown) {

throw ex;

}

// Otherwise, just ignore it and continue...

}

catch (NullValueInNestedPathException ex) {

if (!ignoreInvalid) {

throw ex;

}

// Otherwise, just ignore it and continue...

}

catch (PropertyAccessException ex) {

if (propertyAccessExceptions == null) {

propertyAccessExceptions = new LinkedList();

}

propertyAccessExceptions.add(ex);

}

}

// If we encountered individual exceptions, throw the composite exception.

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() {

@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() {

@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

# tomcat: ## 多数据源的话,这里要去掉tomcat,通通放在数据源前缀下面

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

原先tomcat的配置都要放在数据源前缀的底下,放在spring.datasource.tomcat或者spring.datasource.master.tomcat底下均无法生效。


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:MyBatis3用log4j在控制台输出SQL的方法示例
下一篇:微信小程序App生命周期详解
相关文章

 发表评论

暂时没有评论,来抢沙发吧~