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(); Set names=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; List propertyValues=(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(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。