spring boot tomcat jdbc pool的属性绑定
下面看下springboottomcatjdbcpool的属性绑定代码,具体代码如下所示:
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 ##whenpoolsweeperisenabled,extraidleconnectionwillbeclosed max-idle:5 ##whenidleconnection>min-idle,poolSweeperwillstarttoclose min-idle:1
使用如上配置,最后发现initial-size,max-active,max-idle,min-idle等配置均无效,生成的tomcatjdbcdatasource还是使用的默认的配置
正确配置
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 ##whenpoolsweeperisenabled,extraidleconnectionwillbeclosed max-idle:5 ##whenidleconnection>min-idle,poolSweeperwillstarttoclose 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("deprecation")
protectedstaticclassPooledDataSourceConfiguration{
}
DataSourceConfiguration.Tomcat
spring-boot-autoconfigure-1.5.9.RELEASE-sources.jar!/org/springframework/boot/autoconfigure/jdbc/DataSourceConfiguration.java
/**
*TomcatPoolDataSourceconfiguration.
*/
@ConditionalOnClass(org.apache.tomcat.jdbc.pool.DataSource.class)
@ConditionalOnProperty(name="spring.datasource.type",havingValue="org.apache.tomcat.jdbc.pool.DataSource",matchIfMissing=true)
staticclassTomcatextendsDataSourceConfiguration{
@Bean
@ConfigurationProperties(prefix="spring.datasource.tomcat")
publicorg.apache.tomcat.jdbc.pool.DataSourcedataSource(
DataSourcePropertiesproperties){
org.apache.tomcat.jdbc.pool.DataSourcedataSource=createDataSource(
properties,org.apache.tomcat.jdbc.pool.DataSource.class);
DatabaseDriverdatabaseDriver=DatabaseDriver
.fromJdbcUrl(properties.determineUrl());
StringvalidationQuery=databaseDriver.getValidationQuery();
if(validationQuery!=null){
dataSource.setTestOnBorrow(true);
dataSource.setValidationQuery(validationQuery);
}
returndataSource;
}
}
可以看到这里的DataSourceProperties仅仅只有spring.datasource直接属性的配置,比如url,username,password,driverClassName。tomcat的具体属性都没有。
createDataSource
protectedTcreateDataSource(DataSourcePropertiesproperties, Classtype){ 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
privatevoidpostProcessBeforeInitialization(Objectbean,StringbeanName,
ConfigurationPropertiesannotation){
Objecttarget=bean;
PropertiesConfigurationFactory
注意,这里的annotation就是@ConfigurationProperties(prefix="spring.datasource.tomcat"),它的prefix是spring.datasource.tomcatPropertiesConfigurationFactory的targetName就是spring.datasource.tomcat
PropertiesConfigurationFactory.bindPropertiesToTarget
spring-boot-1.5.9.RELEASE-sources.jar!/org/springframework/boot/bind/PropertiesConfigurationFactory.java
publicvoidbindPropertiesToTarget()throwsBindException{
Assert.state(this.propertySources!=null,"PropertySourcesshouldnotbenull");
try{
if(logger.isTraceEnabled()){
logger.trace("PropertySources:"+this.propertySources);
}
this.hasBeenBound=true;
doBindPropertiesToTarget();
}
catch(BindExceptionex){
if(this.exceptionIfInvalid){
throwex;
}
PropertiesConfigurationFactory.logger
.error("FailedtoloadPropertiesvalidationbean."
+"YourPropertiesmaybeinvalid.",ex);
}
}
委托给doBindPropertiesToTarget方法
PropertiesConfigurationFactory.doBindPropertiesToTarget
privatevoiddoBindPropertiesToTarget()throwsBindException{
RelaxedDataBinderdataBinder=(this.targetName!=null
?newRelaxedDataBinder(this.target,this.targetName)
:newRelaxedDataBinder(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);
IterablerelaxedTargetNames=getRelaxedTargetNames();
Setnames=getNames(relaxedTargetNames);
PropertyValuespropertyValues=getPropertySourcesPropertyValues(names,
relaxedTargetNames);
dataBinder.bind(propertyValues);
if(this.validator!=null){
dataBinder.validate();
}
checkForBindingErrors(dataBinder);
}
这里借助RelaxedDataBinder.bind方法
getRelaxedTargetNames privateIterablegetRelaxedTargetNames(){ return(this.target!=null&&StringUtils.hasLength(this.targetName) ?newRelaxedNames(this.targetName):null); }
这里new了一个RelaxedNames,可以识别多个变量的变种
RelaxedNames
spring-boot-1.5.9.RELEASE-sources.jar!/org/springframework/boot/bind/RelaxedNames.java privatevoidinitialize(Stringname,Setvalues){ if(values.contains(name)){ return; } for(Variationvariation:Variation.values()){ for(Manipulationmanipulation:Manipulation.values()){ Stringresult=name; result=manipulation.apply(result); result=variation.apply(result); values.add(result); initialize(result,values); } } } /** *Namevariations. */ enumVariation{ NONE{ @Override publicStringapply(Stringvalue){ returnvalue; } }, LOWERCASE{ @Override publicStringapply(Stringvalue){ returnvalue.isEmpty()?value:value.toLowerCase(); } }, UPPERCASE{ @Override publicStringapply(Stringvalue){ returnvalue.isEmpty()?value:value.toUpperCase(); } }; publicabstractStringapply(Stringvalue); }
即支持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 privatePropertyValuesgetPropertySourcesPropertyValues(Setnames, Iterable relaxedTargetNames){ PropertyNamePatternsMatcherincludes=getPropertyNamePatternsMatcher(names, relaxedTargetNames); returnnewPropertySourcesPropertyValues(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 /** *Bindthegivenpropertyvaluestothisbinder'starget. *Thiscallcancreatefielderrors,representingbasicbinding *errorslikearequiredfield(code"required"),ortypemismatch *betweenvalueandbeanproperty(code"typeMismatch"). *
NotethatthegivenPropertyValuesshouldbeathrowawayinstance: *Forefficiency,itwillbemodifiedtojustcontainallowedfieldsifit *implementstheMutablePropertyValuesinterface;else,aninternalmutable *copywillbecreatedforthispurpose.PassinacopyofthePropertyValues *ifyouwantyouroriginalinstancetostayunmodifiedinanycase. *@parampvspropertyvaluestobind *@see#doBind(org.springframework.beans.MutablePropertyValues) */ publicvoidbind(PropertyValuespvs){ MutablePropertyValuesmpvs=(pvsinstanceofMutablePropertyValues)? (MutablePropertyValues)pvs:newMutablePropertyValues(pvs); doBind(mpvs); } /** *Actualimplementationofthebindingprocess,workingwiththe *passed-inMutablePropertyValuesinstance. *@parammpvsthepropertyvaluestobind, *asMutablePropertyValuesinstance *@see#checkAllowedFields *@see#checkRequiredFields *@see#applyPropertyValues */ protectedvoiddoBind(MutablePropertyValuesmpvs){ checkAllowedFields(mpvs); checkRequiredFields(mpvs); applyPropertyValues(mpvs); } /** *Applygivenpropertyvaluestothetargetobject. *
Defaultimplementationappliesallofthesuppliedproperty *valuesasbeanpropertyvalues.Bydefault,unknownfieldswill *beignored. *@parammpvsthepropertyvaluestobebound(canbemodified) *@see#getTarget *@see#getPropertyAccessor *@see#isIgnoreUnknownFields *@see#getBindingErrorProcessor *@seeBindingErrorProcessor#processPropertyAccessException */ protectedvoidapplyPropertyValues(MutablePropertyValuesmpvs){ try{ //Bindrequestparametersontotargetobject. getPropertyAccessor().setPropertyValues(mpvs,isIgnoreUnknownFields(),isIgnoreInvalidFields()); } catch(PropertyBatchUpdateExceptionex){ //UsebinderrorprocessortocreateFieldErrors. for(PropertyAccessExceptionpae:ex.getPropertyAccessExceptions()){ getBindingErrorProcessor().processPropertyAccessException(pae,getInternalBindingResult()); } } } /** *ReturntheunderlyingPropertyAccessorofthisbinder'sBindingResult. */ protectedConfigurablePropertyAccessorgetPropertyAccessor(){ returngetInternalBindingResult().getPropertyAccessor(); }
最后通过getPropertyAccessor()来设置,这个propertyAccessor就是org.springframework.boot.bind.RelaxedDataBinder$RelaxedBeanWrapper:wrappingobject[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
publicvoidsetPropertyValues(PropertyValuespvs,booleanignoreUnknown,booleanignoreInvalid)
throwsBeansException{
ListpropertyAccessExceptions=null;
ListpropertyValues=(pvsinstanceofMutablePropertyValues?
((MutablePropertyValues)pvs).getPropertyValueList():Arrays.asList(pvs.getPropertyValues()));
for(PropertyValuepv:propertyValues){
try{
//ThismethodmaythrowanyBeansException,whichwon'tbecaught
//here,ifthereisacriticalfailuresuchasnomatchingfield.
//Wecanattempttodealonlywithlessseriousexceptions.
setPropertyValue(pv);
}
catch(NotWritablePropertyExceptionex){
if(!ignoreUnknown){
throwex;
}
//Otherwise,justignoreitandcontinue...
}
catch(NullValueInNestedPathExceptionex){
if(!ignoreInvalid){
throwex;
}
//Otherwise,justignoreitandcontinue...
}
catch(PropertyAccessExceptionex){
if(propertyAccessExceptions==null){
propertyAccessExceptions=newLinkedList();
}
propertyAccessExceptions.add(ex);
}
}
//Ifweencounteredindividualexceptions,throwthecompositeexception.
if(propertyAccessExceptions!=null){
PropertyAccessException[]paeArray=
propertyAccessExceptions.toArray(newPropertyAccessException[propertyAccessExceptions.size()]);
thrownewPropertyBatchUpdateException(paeArray);
}
}
@Override
publicvoidsetPropertyValue(PropertyValuepv)throwsBeansException{
PropertyTokenHoldertokens=(PropertyTokenHolder)pv.resolvedTokens;
if(tokens==null){
StringpropertyName=pv.getName();
AbstractNestablePropertyAccessornestedPa;
try{
nestedPa=getPropertyAccessorForPropertyPath(propertyName);
}
catch(NotReadablePropertyExceptionex){
thrownewNotWritablePropertyException(getRootClass(),this.nestedPath+propertyName,
"Nestedpropertyinpath'"+propertyName+"'doesnotexist",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:wrappingobject[org.apache.tomcat.jdbc.pool.DataSource@6a84bc2a]最后是调用AbstractNestablePropertyAccessor.processLocalProperty
AbstractNestablePropertyAccessor.processLocalProperty
spring-beans-4.3.13.RELEASE-sources.jar!/org/springframework/beans/AbstractNestablePropertyAccessor.java
privatevoidprocessLocalProperty(PropertyTokenHoldertokens,PropertyValuepv){
PropertyHandlerph=getLocalPropertyHandler(tokens.actualName);
if(ph==null||!ph.isWritable()){
if(pv.isOptional()){
if(logger.isDebugEnabled()){
logger.debug("Ignoringoptionalvalueforproperty'"+tokens.actualName+
"'-propertynotfoundonbeanclass["+getRootClass().getName()+"]");
}
return;
}
else{
throwcreateNotWritablePropertyException(tokens.canonicalName);
}
}
ObjectoldValue=null;
try{
ObjectoriginalValue=pv.getValue();
ObjectvalueToApply=originalValue;
if(!Boolean.FALSE.equals(pv.conversionNecessary)){
if(pv.isConverted()){
valueToApply=pv.getConvertedValue();
}
else{
if(isExtractOldValueForEditor()&&ph.isReadable()){
try{
oldValue=ph.getValue();
}
catch(Exceptionex){
if(exinstanceofPrivilegedActionException){
ex=((PrivilegedActionException)ex).getException();
}
if(logger.isDebugEnabled()){
logger.debug("Couldnotreadpreviousvalueofproperty'"+
this.nestedPath+tokens.canonicalName+"'",ex);
}
}
}
valueToApply=convertForProperty(
tokens.canonicalName,oldValue,originalValue,ph.toTypeDescriptor());
}
pv.getOriginalPropertyValue().conversionNecessary=(valueToApply!=originalValue);
}
ph.setValue(this.wrappedObject,valueToApply);
}
catch(TypeMismatchExceptionex){
throwex;
}
catch(InvocationTargetExceptionex){
PropertyChangeEventpropertyChangeEvent=newPropertyChangeEvent(
this.rootObject,this.nestedPath+tokens.canonicalName,oldValue,pv.getValue());
if(ex.getTargetException()instanceofClassCastException){
thrownewTypeMismatchException(propertyChangeEvent,ph.getPropertyType(),ex.getTargetException());
}
else{
Throwablecause=ex.getTargetException();
if(causeinstanceofUndeclaredThrowableException){
//Mayhappene.g.withGroovy-generatedmethods
cause=cause.getCause();
}
thrownewMethodInvocationException(propertyChangeEvent,cause);
}
}
catch(Exceptionex){
PropertyChangeEventpce=newPropertyChangeEvent(
this.rootObject,this.nestedPath+tokens.canonicalName,oldValue,pv.getValue());
thrownewMethodInvocationException(pce,ex);
}
}
它使其是使用classorg.springframework.beans.BeanWrapperImpl$BeanPropertyHandler来设置
BeanWrapperImpl$BeanPropertyHandler.setValue
spring-beans-4.3.13.RELEASE-sources.jar!/org/springframework/beans/BeanWrapperImpl.java
@Override
publicvoidsetValue(finalObjectobject,ObjectvalueToApply)throwsException{
finalMethodwriteMethod=(this.pdinstanceofGenericTypeAwarePropertyDescriptor?
((GenericTypeAwarePropertyDescriptor)this.pd).getWriteMethodForActualAccess():
this.pd.getWriteMethod());
if(!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())&&!writeMethod.isAccessible()){
if(System.getSecurityManager()!=null){
AccessController.doPrivileged(newPrivilegedAction(){
@Override
publicObjectrun(){
writeMethod.setAccessible(true);
returnnull;
}
});
}
else{
writeMethod.setAccessible(true);
}
}
finalObjectvalue=valueToApply;
if(System.getSecurityManager()!=null){
try{
AccessController.doPrivileged(newPrivilegedExceptionAction(){
@Override
publicObjectrun()throwsException{
writeMethod.invoke(object,value);
returnnull;
}
},acc);
}
catch(PrivilegedActionExceptionex){
throwex.getException();
}
}
else{
writeMethod.invoke(getWrappedInstance(),value);
}
}
}
这里利用反射找出setXXX方法(比如setMaxActive),然后设置进去
多数据源的配置
上面的配置对于单数据源来说是没有问题的,对于多数据源,则配置如下
@Configuration
publicclassMasterDatasourceConfig{
@Bean("masterDataSource")
@ConfigurationProperties(prefix="spring.datasource.master")
publicDataSourcemasterDataSource(){
returnDataSourceBuilder.create().build();
}
}
注意,这里要添加ConfigurationProperties注入tomcatjdbcpool的额外设置
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 ##whenpoolsweeperisenabled,extraidleconnectionwillbeclosed max-idle:5 ##whenidleconnection>min-idle,poolSweeperwillstarttoclose min-idle:1
原先tomcat的配置都要放在数据源前缀的底下,放在spring.datasource.tomcat或者spring.datasource.master.tomcat底下均无法生效。
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。