Vamos dar uma olhada no código de ligação da propriedade do Spring Boot Tomcat JDBC Pool. O código específico é o seguinte:
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-ENBLEFIDO: TRUE SIZEIRO INICIAL: 1 MAX-ACTIVE: 5 ## Quando o suéter de piscina estiver ativado, a conexão ociosa extra será fechada no máximo: 5 ## Quando a conexão ociosa> min-Idle, Poolsweeper começará a fechar o min-Idle: 1
Usando a configuração acima, finalmente descobri que as configurações de tamanho inicial, max-ativo, Max-Idle, Min-Idle e outras são inválidas. O DataSource gerado TomCat JDBC ainda é a configuração padrão usada.
Configuração correta
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-ENBLEFIDO: TRUE TOMCAT: ## Um único pool de conexão de banco de dados, e a configuração da propriedade Tomcat deve ser gravada para entrar em vigor: 1 max-ativo: 5 ## Quando o varredor do pool estiver ativado, a conexão ociosa extra será fechada no máximo: o Idle: 5 ##, quando a conexão com o IDLE> Min-IDLE, o poolweeper começará a fechar o MAX-IDLE.
Observe que as propriedades de configuração do pool de conexão do banco de dados TOMCAT específico são colocadas sob a propriedade Spring.DataSource.Tomcat para que possam entrar em vigor.
Análise do código -fonte
spring-boot-autoconfigure-1.5.9.release-sources.jar!/org/springframework/boot/autoconfigure/jdbc/datasourceautoconfiguration.java@configuration @condicional (pooleddatasourceCondition.cllass) @conditionalmbousbousbousbousbousbousBondCondCondCondition.cllass) @conditionalM (pooleddatasourceCondition.class) XadataSource.class}) @import ({DataSourceConfiguration.tomcat.class, DataSourceConfiguration.hikari.class, datasourceconfiguration.dbcp.class, datasourConfiguration.dbcp2.class) @Suppresswarnings ("deprecação") Classe estática protegida pooledDataSourceConfiguration {}DataSourceConfiguration.tomcat
Spring-Boot-Autoconfigure-1.5.9.Release-sources.jar! /org/springframework/boot/autoconfigure/jdbc/dataSourceConfiguration.java/*** Tomcat Pool DataSource Configuração. */ @ConditionalOnClass (org.apache.tomcat.jdbc.pool.datasource.class) @conditionalonproperty (name = "spring.dataSource.type", tendovalue = "org.apache.tomcat.jdbc.pool.datsource",, "pache.tomcat.jdbc.pool.datsource ",,, "pache.tomcat.jdbc.pool.datsource",, "pache.tomcat.jdbc.pool.datas DataSourceConfiguration {@Bean @ConfigurationProperties (prefix = "spring.dataSource.tomcat") public org.apache.tomcat.jdbc.pool.datasource DataSource (DataSourceProperties) crioutasource (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); } retornar DataSource; }}Você pode ver que os DataSourceProperties aqui têm apenas a configuração do Spring.DataSource Direct Atributes, como URL, nome de usuário, senha, DriverClassName. O Tomcat não possui propriedades específicas.
CriadoTAsource
Protegido <T> t criadoTAsource (Propriedades do DataSourceProperties, classe <? Extende o DataSource> tipo) {return (t) Properties.initializedataSourceBuilder (). Type (Type) .build (); }O org.apache.tomcat.jdbc.pool.dataSource PoolProperties que criou diretamente o CreatedAsource também é a configuração padrão.
ConfigurationProperties
A mágica específica está no código @ConfigurationProperties(prefix = "spring.datasource.tomcat") . Antes que o contêiner da mola construa o feijão proxy e retorne, ele definirá o atributo especificado por spring.datasource.tomcat para org.apache.tomcat.jdbc.pool.datasource
spring-boot-1.5.9.release sources.jar! /org/springframework/boot/context/properties/configurationpropertiesbindingpostprocessor.javapriv; void pós-processando parainitialização (objeto ben, string beanname, bate-seropserties PropertiesConfigurationFactory <ject> factory = new PropertiesConfigurationFactory <ject> (Target); Factory.SetPropertySources (this.PropertySources); Factory.SetValidator (DetermetValidator (Bean)); // Se nenhum serviço de conversão explícito for fornecido, adicionamos um para que (pelo menos) // Matrizes de conversíveis separados por vírgula possam ser ligados automaticamente fábrica.setConversionService (this.ConversionService == null? GetDefaultConversionService (): this.ConversionService); if (anotação! = null) {Factory.SetInorEInValidFields (ANNOTATION.IGNOREIRVALIDFIELDS ()); Factory.SetInorEunkNownFields (ANNOTATION.IGNOREUNKNOwnfields ()); Factory.SetexceptionInValId (AnoTation.ExceptionIfInValid ()); Factory.SetIngorEretestroperties (ANNOTATION.IGNORERENTESTETPERTIES ()); if (stringUtils.haslength (anoTation.Prefix ())) {Factory.SetTargetName (anoTation.Prefix ()); }} tente {factory.bindPropertiestOTarget (); } catch (Exceção ex) {string TargetClass = Classutils.getShortName (Target.getClass ()); lançar o novo beancreationException (nome do beann, "não pôde vincular propriedades a" + TargetClass + "(" + getAnnotationDetails (anotação) + ")", ex); }} Observe que a anotação aqui é @ConfigurationProperties(prefix = "spring.datasource.tomcat") , seu prefixo é spring.datasource.tomcat PropertiesConfigurationFactory TargetName é spring.datasource.tomcat
PropertiesConfigurationFactory.bindPropertiesToTargetspring-boot-1.5.9.RELEASE-sources.jar!/org/springframework/boot/bind/PropertiesConfigurationFactory.javapublic void bindPropertiesToTarget() throws BindException { Assert.state(this.propertySources != null, "PropertySources should not be nulo"); tente {if (logger.istraceEnabled ()) {Logger.Trace ("Fontes da propriedade:" + this.propertysources); } this.hasbeenbound = true; DobindPropertiestOTarget (); } catch (bindException ex) {if (this.ExceptionIfInValid) {tiro ex; } PropertiesConfigurationFactory.Logger .Error ("Falha ao carregar o Bean de validação de propriedades." + "Suas propriedades podem ser inválidas.", Ex); }}Delegado ao método DobindProperSiestarget
PropertiesConfigurationFactory.DOBindPropertiestOtTegetPrivate void DOBindProperSTiestOTetget () lança o BindException {RelaxedDatabinder Databinder = (this.TargetName! 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.setIngorEretedProperties (this.ignorerentestestroperties); Databinder.setInorEInValidFields (this.ignoreinValidfields); Databinder.SetInorEunkNownFields (this.ignoreunknownfields); PersonalizeBinder (banco de dados); Iterable <string> relaxedTargetNames = getRelaxedTargetNames (); Set <string> nomes = getNames (relaxedTargetNames); PropertyValues PropertSValues = getPropertySourcesPropertyValues (nomes, nomes relaxados); Databinder.bind (PropertyValues); if (this.validator! = null) {databinder.validate (); } checkForBindingErrors (banco de dados); }Aqui, use o método RelaxedDatabinder.Bind
getRelaxedTargetNamesprivate iterable <string> getReLaxedTargetNames () {return (this.target! = null && stringUtils.haslength (this.targetName)? New RelaxedNames (this.TargetName): null); }Aqui estão novos nomes relaxados que podem identificar variantes de várias variáveis
Nomes relaxados
spring-boot-1.5.9.Release sources.jar! /org/springframework/boot/bind/relaxednames.javaprivate void Initialize (nome da string, set <tring> valores) {if (valores.contains (name)) {return; } para (variação variação: variação.values ()) {for (manipulação manipulação: manipulação.values ()) {string resultado = name; resultado = manipulação.Apply (resultado); resultado = variação.Apply (resultado); valores.add (resultado); inicializar (resultado, valores); }}} /*** Variações de nome. */ enum variação {nenhum {@Override public String Aplicar (String Value) {retornar valor; }}, Minúsculo {@Override public String Aplicar (String value) {return value.isempty ()? valor: value.tolowerCase (); }}, Uppercase {@Override public String Aplicar (String value) {return value.isempty ()? Valor: value.touppercase (); }}; string abstrata pública Aplicar (valor da string); }That is, the writing method of configuration in 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]] é suportado.
getPropertySourcesPropertyValuesPrivate PropertyValues getPropertySourcesPropertyValues (Set <String> nomes, iterable <string> relaxedTargetNames) {PropertyNeRePatternsMatcher Incluir = getPropertyNamePatternsmatcher (names, relaxedTargetNames); devolver novos PropertySourcesPropertyValues (this.PropertySources, Nomes, inclui, this.ResolvePLAREPLES); }Este método puxará a configuração da propriedade em Spring.DataSource.Tomact para o objeto PropertyValues
RelaxedDatabinder.bind
spring-boot-1.5.9.Release Sources.jar! /org/springframework/boot/bind/relaxeddatabinder.java's bind Method chama o método da classe parental Spring-Context-4.3.13.release-sources.jar! * <p> Esta chamada pode criar erros de campo, representando erros de ligação básica * como um campo necessário (código "necessário") ou digite incompatibilidade * entre o valor e a propriedade Bean (código "TypeMismatch"). * <p> Observe que os valores de propriedade fornecidos devem ser uma instância descartável: * Para eficiência, ele será modificado para conter apenas campos permitidos se * implementar a interface MutablePropertyValues; caso contrário, uma cópia interna mutável será criada para esse fim. Passe em uma cópia dos PropertyValues* se você deseja que sua instância original permaneça não modificada em qualquer caso. * @param valores de propriedade PVS para ligar * @see #DOBind (org.springframework.beans.mutablePropertyValues) */ public void bind (PropertyValues pvs) {mutablePropertyValues MPVS = (PVS Instância de MutablePropertyValues)? (MutablePropertyValues) PVs: New MutablePropertyValues (PVs); Dobind (MPVS); } /** * A implementação real do processo de ligação, trabalhando com a instância * passada MutablePropertyValues. * @param mpvs os valores da propriedade para se ligar, * como mutablePropertyValues Instância * @see #checkallowedfields * @see #checkrequiredfields * @see #applyPropertyValues */ Protect DOBIND (MutablePropertyValues mpvs) {checkedfields (mpvs); CheckRequiredFields (MPVS); aplicar propertyValues (MPVs); } /*** Aplique determinados valores da propriedade ao objeto de destino. * <p> A implementação padrão aplica todos os valores da propriedade fornecida * como valores da propriedade Bean. Por padrão, os campos desconhecidos serão * ignorados. * @param mpvs Os valores da propriedade a serem ligados (podem ser modificados) * @see #getTarget * @see #getPropertyAccessor * @see #isignoreunknownfields * @see #getBindingErrorRorProcessor * @see bindingErrorProcessor tente {// vinte parâmetros de solicitação no objeto de destino. getPropertyAccessor (). SetPropertyValues (MPVs, isignoreunknownfields (), isignoreinValidfields ()); } Catch (PropertyBatchUpDateException Ex) {// Use o processador de erro de ligação para criar Fielderrors. para (PropertyAccessException PAE: Ex.getPropertyAccessExceptions ()) {getBindingErrorProcessor (). ProcessPropertyAccessException (PAE, GetInternalBindingResult ()); }}} /*** Retorne o PropertyAccessor subjacente do BindingResult deste fichário. */ Protected ConfigurucuryPropertyAccessor getPropertyAccessor () {return getInternalbindingResult (). getPropertyAccessor (); }Finalmente, é definido por getPropertyAccessor (). This propertyAccessor is org.springframework.boot.bind.RelaxedDataBinder$RelaxedBeanWrapper: wrapping object [org.apache.tomcat.jdbc.pool.DataSource@6a84bc2a], which is the wrapped org.apache.tomcat.jdbc.pool.DataSource
AbstractPropertyAccessor.setPropertyValuesspring-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<PropertyAccessException> PropertyAccessExceptions = NULL; Lista <PROPRIETÍVEL> PropertyValues = (PVS Instância de MutablePropertyValues? ((MutablePropertyValues) PVS) .getPropertyValuelist (): Arrays.asList (pvs.getPropertyValues ())); para (PropertyValue PV: PropertyValues) {Try {// Este método pode lançar qualquer beansexception, que não será capturado // aqui, se houver uma falha crítica, como nenhum campo correspondente. // Podemos tentar lidar apenas com exceções menos graves. setPropertyValue (PV); } catch (nãoWrityPropertyException ex) {if (! ignoreunknown) {tiro ex; } // Caso contrário, basta ignorá -lo e continuar ...} catch (nullValueInnestedPathException ex) {if (! IgnoreinValid) {tiro ex; } // Caso contrário, basta ignorá -lo e continuar ...} Catch (PropertyAccessException Ex) {if (PropertyAccessExceptions == NULL) {PropertyAcceSexceptions = new LinkedList <PROPERECESCESSCECCECCECTION> (); } PropertyAccessExceptions.add (Ex); }} // Se encontrarmos exceções individuais, jogue a exceção composta. if (PropertyAccessExceptions! = NULL) {PropertyAccessException [] paearray = PropertyAccessExceptions.ToArray (new PropertyAccessException [PropertyAccessExceptions.size ()]); lançar o novo PropertyBatchUpDateException (paearray); }} @Override public void setPropertyValue (PropertyValue PV) lança beansexception {PropertyTokenHolder Tokens = (PropertyTokenHolder) pv.ResolvedTokens; if (tokens == null) {String PropertyName = pv.getName (); AbstractNestablePropertyAccessor Nestestpa; tente {Nestedpa = getPropertyAccessorForPropertyPath (PropertyName); } Catch (NotLeAtablePropertyException Ex) {THROW NOVO NOTWRITEPROPROPERTYEXCECTION (GetRootClass (), this.nestedPath + PropertyName, "Propriedade aninhada no path '" + PropertyName + "' não existe", ex); } tokens = getPropertyNameTokens (getFinalPath (NestedPA, PropertyName)); if (Nestedpa == this) {pv.getoriginalPropertyValue (). ResolvedTokens = tokens; } Nestestpa.setPropertyValue (tokens, PV); } else {setPropertyValue (tokens, PV); }}Aqui Nestedpa.setPropertyValue (tokens, PV); The real setting of spring.datasource.tomcat property value is org.springframework.boot.bind.RelaxedDataBinder$RelaxedBeanWrapper: wrapping object [org.apache.tomcat.jdbc.pool.DataSource@6a84bc2a] Finally, call AbstractNestablePropertyAccessor.processLocalProperty
AbstractNestablePropertyAccessor.processlocalPropertySpring-beans-4.3.13.Release-sources.jar! /Org/springframework/beans/abstractnestablepromertyaccessor.javaprivate void ProcessLocalProperty (PropertyTOKNEDER PHEKENS, PROPRIEDADEMEVELUE, PROPROMENDO) getLocalPropertyHandler (tokens.actualName); if (ph == null ||! ph.isWritable ()) {if (pv.isoptional ()) {if (logger.isdebugenabled ()) {logger.debug ("ignorando o valor opcional para a propriedade '" + tokens.actualname + "' - não encontrado na classe" " +" + getroot " + getroot" + getroot " + getroot" + getroot " +" + ". } retornar; } else {tiro createNotWrityPropertyException (tokens.canonicalname); }} Objeto OldValue = null; tente {objeto originalValue = pv.getValue (); Objeto valuetopply = originalValue; if (! boolean.false.equals (pv.conversãonecessary)) {if (pv.isconverted ()) {valuetopply = pv.getConvertedValue (); } else {if (isextractoldValueForeditor () && ph.isReAtable ()) {try {OldValue = ph.getValue (); } catch (Exceção ex) {if (ex instância de privilégio privilegiado) {ex = ((privilegedActionException) ex) .Getexception (); } if (logger.isdebugenabled ()) {logger.debug ("não conseguiu ler o valor anterior de propriedade '" + this.nestedPath + tokens.CanonicalName + "'", ex); }} valueToApply = ConvertForProperty (tokens.canonicalname, OldValue, OriginalValue, Ph.totypeDescriptor ()); } pv.getoriginalPropertyValue (). conversionNeCesary = (valuetoApply! = OriginalValue); } ph.setValue (this.wrapdObject, valuetoApply); } catch (typeMismatchException ex) {tiro 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 caus = ex.gettargetexception (); if (causa instância do não-declarado exceception) {// pode acontecer, por exemplo, com métodos groovy generados causar = caus.getcause (); } lança o novo MethodInvocationException (PropertyChangeEvent, Causa); }} Catch (Exceção ex) {PropertyChangeEvent PCE = new PropertyChangeEvent (this.rootoBject, this.nestedPath + tokens.CanonicalName, OldValue, pv.getValue ()); lançar novo MethodInvocationException (PCE, Ex); }}Isso o faz definir usando a classe org.springframework.beans.beanwrapperImpl $ beanpropertyhandler
BeanwrapperImpl$BeanPropertyHandler.setValuespring-beans-4.3.13.release-sources.jar!/org/springframework/beans/beanwrapperImpl.java@override public void setValue (objeto final, objeto objeto (objeto ((GenerictyPeawarePropertyDescriptor) this.pd) .getWriteMethodForActualAccess (): this.pd.getWriteMethod ()); if (! modifier.ispublic (writeMethod.getDecLaringClass (). getModifiers ()) &&! writemethod.isaccessible ()) {if (system.getSecurityManager ()! writeMethod.setAccessible (true); } else {writemethod.setAccessible (true); }} Valor do objeto final = valueToApply; if (system.getSecurityManager ()! = null) {tente {accessController.Doprivileged (new PrivilegedExceptionAction <ject> () {@Override public Object run () lança exceção {writeMethod.invoke (objeto, value); retornar null;}}, acc); } catch (privilegedActionException ex) {tiro ex.Getexception (); }} else {writemethod.invoke (getWrappedInstance (), valor); }}}}Aqui usamos a reflexão para encontrar o método setxxx (como o setMaxactive) e depois o definimos em
Configuração de origem multi-dados
A configuração acima não é problemática para uma única fonte de dados. Para várias fontes de dados, a configuração é a seguinte
@ConfigurationPublic Classe MasterDataSourceConfig {@Bean ("MasterDataSource") @ConfigurationProperties (prefix = "spring.dataSource.master") public DataSourcece MasterDataSource () {Return DataSourCuilder.create (). }}Observe que você precisa adicionar configurações adicionais para injetar o ConfigurationProperties no TomCat JDBC Pool
Spring: DataSource: Mestre: Tipo: org.apache.tomcat.jdbc.pool.dataSource Driver-class-name: org.postgresql.driver URL: JDBC: PostGresql: //192.168.99.100: 5432/PostGres? JMX-habilitado: true# tomcat: ## Para várias fontes de dados, precisamos remover o tomcat aqui e colocá-los sob o prefixo do tamanho inicial da fonte de dados: 1 max-ativo: 5 ## Quando o sweeper da piscina estiver ativado, a conexão extra-lenta será fechada no max-idle: 5 ##, quando a conexão de 1 °
A configuração original do TomCat deve ser colocada no prefixo da fonte de dados e não pode entrar em vigor sob a primavera.dataSource.tomcat ou spring.datasource.master.tomcat.