Jetons un coup d'œil au code de liaison de la propriété de Spring Boot Tomcat JDBC Pool. Le code spécifique est le suivant:
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-compatible: Vraie-Size: 1 max-active: 5 ## Lorsque le pull de pool est activé, la connexion inactive supplémentaire sera fermée max-idle: 5 ## Lorsque la connexion inactive> Min-Idle, PoolSweeper commencera à fermer Min-Idle: 1
En utilisant la configuration ci-dessus, j'ai finalement constaté que la taille initiale, max-active, max-idle, min-idle et d'autres configurations ne sont pas valides. La source de données Tomcat JDBC générée est toujours la configuration par défaut utilisée.
Configuration correcte
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 compatible: True Tomcat: ## Un seul pool de connexions de base de données et la configuration de la propriété Tomcat doivent être écrites pour prendre effet de taille initiale: 1 max-actif: 5 ## Lorsque le balayeur de pool est activé, la connexion plus inactive sera fermée max-idle: 5 ##
Notez que les propriétés de configuration du pool de connexions de la base de données TomCat spécifique sont placées sous la propriété Spring.datasource.tomcat afin qu'elles puissent prendre effet.
Analyse du code source
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.class}) @SuppressWarnings ("Deprécation") Classe statique protégée PoledDatasourceConfiguration {}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 = "printemps.datasource.type", havrevalue = "org.apache.tomcat.jdbc.pool 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); Databaseriver databasedriver = databaseriver .fromjdbcurl (properties.determinaurl ()); String validationQuery = databaseriver.getValidationQuery (); if (validationQuery! = null) {dataSource.setTestonBorrow (true); DataSource.SetValidationQuery (validationQuery); } return dataSource; }}Vous pouvez voir que les dataSourceProperties ici n'ont que la configuration des attributs directs de Spring.datasource, tels que URL, nom d'utilisateur, mot de passe, driverclassname. Tomcat n'a pas de propriétés spécifiques.
créé
protégé <t> t CreateDataSource (DataSourceProperties Properties, classe <? étend dataSource> type) {return (t) Properties.InitializedAdAtasourceBuilder (). type (type) .build (); }L'org.apache.tomcat.jdbc.pool.datasource PoolProperties qui a directement créé la configuration est également la configuration par défaut.
ConfigurationProperties
La magie spécifique réside dans le code @ConfigurationProperties(prefix = "spring.datasource.tomcat") . Avant que le conteneur de printemps ne construise le bean proxy et le retour, il définira l'attribut spécifié par spring.datasource.tomcat sur org.apache.tomcat.jdbc.pool.datasource
Spring-Boot-1.5.9.release-sources.jar! /org/springframework/boot/context/properties/configurationpropertiesbindingPostProcessor.javaprivate void postprocessbeforeinitialisation (objet bean, string beanname, configurationproperties annotation) {Object Target = bean; PropertiesConfigurationFactory <Bject> Factory = new PropertiesConfigurationFactory <Bject> (Target); factory.setPropertySources (this.propertysources); factory.setValidator (déterminerValidator (bean)); // Si aucun service de conversion explicite n'est fourni, nous en ajoutez un pour que (au moins) // des tableaux de cabriolets séparés des virgules puissent être liés automatiquement Factory.SetConversionService (this.conversionsservice == null? GetDefaultConversionService (): this.ConversionsService); if (annotation! = null) {factory.SetIgnoreInValidFields (annotation.ignoreInvalidFields ()); factory.SetIgnoreUnknownFields (annotation.ignoreUnknownFields ()); factory.setExceptionIFinValid (annotation.ExceptionIFinValid ()); factory.SetIgnoreEntSesdProperties (annotation.ignoreNestedProperties ()); if (stringUtils.hasLength (annotation.prefix ())) {factory.setTargetName (annotation.prefix ()); }} essayez {factory.bindProperStiStotarget (); } catch (exception ex) {String TargetClass = classutils.getShortName (target.getClass ()); Jetez une nouvelle BeanCreationException (beanname, "ne pouvait pas lier les propriétés à" + TargetClass + "(" + GetAnnotationDetails (annotation) + ")", ex); }} Notez que l'annotation ici est @ConfigurationProperties(prefix = "spring.datasource.tomcat") , son préfixe est spring.datasource.tomcat PropertiesConfigurationFactory TargetName est printemps.datasource.tomcat
PropertiesConfigurationFactory.BindProperStiStotOrgetsPring-Boot-1.5.9.Release-Sources.jar! /Org/springframework/boot/bind/propertiesconfigurationfactory.javapublic VOID BindProtitierStices ne pas être nul "); essayez {if (logger.istraceenabled ()) {logger.trace ("Sources de propriété:" + this.propertysources); } this.hasbeenbound = true; dobindProperSticesTotarget (); } catch (bindException ex) {if (this.exceptionIFinValid) {throw ex; } PropertiesConfigurationFactory.logger .Error ("Échec du chargement des propriétés de validation." + "Vos propriétés peuvent être invalides.", Ex); }}Déléguer à la méthode DobindProperSticesTotOrget
PropertiesConfigurationFactory.DobindProperStiStotargetPrivate void dobindProperStiStotOrget () lance BindException {RelaxEdDatabinder databinder = (this.targetName! = Null? New RelaxDatabinder (this.target); if (this.validator! = null && this.validator.supports (databinder.getTarget (). getClass ())) {databinder.setValidator (this.validator); } if (this.ConversionsVice! = null) {databinder.setConversionService (this.ConversionService); } databinder.setAutoGrowCollectionLimit (Integer.max_value); databinder.SetIgnoreEntSesdProperties (this.ignorereSesdProperties); dataBinder.SetIgnoreInValidFields (this.ignoreInValidFields); dataBinder.SetIgnoreUnkNownFields (this.ignoreUnkNownFields); PersumaliseBinder (databinder); ITable <string> relongEdTargetNames = getRelaxEdTargetNames (); Set <string> names = getNames (RelaxEdTargetNames); PropertyValues PropertyValues = GetPropertySourcesProperTyValues (noms, RelaxEdTargetNames); databinder.bind (propriétéValues); if (this.validator! = null) {databinder.validate (); } CheckForBinDiningErrors (databinder); }Ici, utilisez la méthode ReladedDatabinder.bind
getRelaxEdTargetNamesPrivate iTable <string> getRelaxEdTargetNames () {return (this.target! = null && stringUtils.hasLength (this.targetName)? new fastNames (this.targetName): null); }Voici de nouveaux noms détendus qui peuvent identifier les variantes de plusieurs variables
Noms détendus
Spring-boot-1.5.9.release-sources.jar! /org/springframework/boot/bind/relaxEdNames.javaprivate void initialize (name de chaîne, set <string> valeurs) {if (valeurs.contains (name)) {return; } pour (variation variation: variation.values ()) {for (manipulation manipulation: manipulation.values ()) {string result = name; résultat = manipulation.Apply (résultat); résultat = variation.Apply (résultat); valeurs.add (résultat); initialiser (résultat, valeurs); }}} / ** * Nom Variations. * / enum Variation {Aucun {@Override public String applique (String Value) {return Value; }}, Minuscules {@Override public String Apply (String Value) {return Value.iSempty ()? Valeur: valeur.tolowerCase (); }}, UPPERCase {@Override public String Apply (String Value) {return Value.iSempty ()? Valeur: valeur.touppercase (); }}; La chaîne abstraite publique s'applique (valeur de la chaîne); }C'est-à-dire la méthode d'écriture de configuration dans org.springframework.boot.bind.relaxednames@6ef81f31 [name = printemps.datasource.tomcat, valeurs = [printemps.datasource.tomcat, spring_datasource_tomcat, springdatasourcetomcat, springdatasource_tomcat, Printemps.datasource.tomcat, printemps_datasource_tomcat, springdatasourcetomcat]] est pris en charge.
GetPropertySourcesProperTyValuesprivate PropertyValues GetPropertySourcesProperTyValues (Set <string> noms, iTable <String> RelaxEdTargetNames) {PropertyNamePatterNSmatcher inclut = getPropertyNamePatterNSMatcher (noms, détendStargetNames); Renvoie de nouvelles propriétés sur les verspropertyValues (ce.propertysources, noms, inclut, this.ResolplaceHolders); }Cette méthode tirera la configuration de la propriété sous Spring.datasource.tomact dans l'objet PropertyValues
RelosEdDatabinder.bind
Spring-boot-1.5.9.release-sources.jar! /org/springframework/boot/bind/relaxeddatabinder.java's liant la méthode appelle la méthode de la classe parent de printemps-context-4.3.13.release-sources.jar! /org/springframework/validation/databinder.java / ** * Bonnez-vous à des valeurs de propriété données. * <p> Cet appel peut créer des erreurs de champ, représentant des erreurs de liaison de base * comme un champ requis (code "requis"), ou taper la non-match * entre la valeur et la propriété bean (code "tymismatch"). * <p> Notez que les valeurs de propriété données doivent être une instance jetée: * Pour l'efficacité, il sera modifié pour contenir simplement des champs autorisés s'ils * implémentent l'interface MutablePropertyValues; Sinon, une copie interne mutable * sera créée à cet effet. Passez une copie des propriétés des propriétés * Si vous souhaitez que votre instance d'origine reste non modifiée dans tous les cas. * @Param PVS Valeurs de propriétés pour lier * @see #dobind (org.springframework.beans.mutablePropertyValues) * / public void bind (PropertyValues pvs) {mutablepropertyValues mpvs = (instance pvs de mutablepropertyValues)? (MutableProperTyValues) PVS: Nouvelle mutablepropertyValues (PVS); dobind (MPV); } / ** * Implémentation réelle du processus de liaison, en travaillant avec l'instance * passable MutableProperTyValues. * @param mpvs Les valeurs de propriété pour se lier, * comme instance MutableProperTyValues * @see #checkallowedFields * @see #checkrequiredFields * @see #ApplyPropertyValues * / Protected Void dobind (MutablePropertyValues MPVS) {checkAllowEdFields (MPVS); CheckRequiredFields (MPV); ApplicationPropertyValues (MPV); } / ** * Appliquer les valeurs de propriété données à l'objet cible. * <p> L'implémentation par défaut applique toutes les valeurs de propriété fournies * en tant que valeurs de propriété Bean. Par défaut, les champs inconnus seront * ignorés. * @param mpvs Les valeurs de la propriété à lier (peuvent être modifiées) * @see #getTarget * @see #getpropertyaccessor * @see #isigoreunknownfields * @see #getbindingerrorprocessor * @see bindingErrorProcess # processPropertyVelueswe {try {// bind les paramètres de demande sur l'objet cible. getPropertyAccessor (). setPropertyValues (mpvs, isignoreunknownfields (), isignoreInValidFields ()); } catch (propriétéBatchUpDateException ex) {// Utilisez le processeur Bind Error pour créer des champs de champs. pour (PropertyAccessException PAE: ex.getPropertyAccessExceptions ()) {GetBinDingErrorProcessor (). ProcessPropertyAccessException (pae, getInternalBindingResult ()); }}} / ** * Renvoyez le propriété sous-jacent de l'accesseur de ce lien de liaison. * / Protected configurablePropertyAccessor getPropertyAccessor () {return getInternalBindingResult (). getPropertyAccessor (); }Enfin, il est défini par GetPropertyAccessor (). Cet accessor est org.springframework.boot.bind.relaxeddatabinder $ relongBeanWrapper: Emballage objet [org.apache.tomcat.jdbc.pool.datasource@6a84bc2a], qui est le emballé org.apache.tomcat.jdbc.pool.datasource enveloppé org.apache.tomcat.jdbc.pool.datasource emballé
AbstractPropertyAccessor.SetpropertyValuessPring-Beans-4.3.13.release-sources.jar!/org/springframework/beans/abstractPropertyAccessor.java@override Void SetpropertyValues (PropertyValues PVS, Boolean Ignoreunknown, Booolean Ignoreinvalid) TROWSEXCEAGE INLOREUNDNOWN, BOOLEAN IGNOREINVALID) List <propriétéAccessException> PropertyAccessExceptions = null; List <ApertyValue> PropertyValues = (PVS instanceof MutableProperTyValues? ((MutableProperTyValues) PVS) .getPropertyValueList (): arrays.aslist (pvs.getPropertyValues ())); pour (PropertyValue PV: PropertyValues) {try {// Cette méthode peut lancer n'importe quelle conception BeanSexception, qui ne sera pas capturée // ici, s'il y a un échec critique tel que aucun champ correspondant. // Nous pouvons tenter de traiter uniquement d'exceptions moins sérieuses. SetPropertyValue (PV); } catch (notwitablepropertyException ex) {if (! ignoreunknown) {throw ex; } // Sinon, ignorez-le et continuez ...} catch (nullvalueinnestPathException ex) {if (! Ignoreinvalid) {throw ex; } // sinon, ignorez-le et continuez ...} catch (PropertyAccessException ex) {if (propriétéAccessExceptions == NULL) {PropertyAccessExceptions = new LinkedList <ApertyAccessException> (); } PropertyAccessException.Add (ex); }} // Si nous avons rencontré des exceptions individuelles, lancez l'exception composite. if (PropertyAccessExceptions! = null) {PropertyAccessException [] PAEARRAY = PropertyAccessException.toArray (new PropertyAccessException [PropertyAccessException.size ()]); Jetez une nouvelle propriété BatchupDateException (PAEARRAY); }} @Override public void SetProperTyValue (PropertyValue PV) lève BeanSexception {PropertyTokenHolder tokens = (PropertyTokenHolder) Pv.ResolvedTokens; if (tokens == null) {String propertyName = pv.getName (); AbstractNestablePropertyAccessor Nestpa; essayez {netedpa = getPropertyAccessorForProperTypath (PropertyName); } catch (notreadablepropertyException ex) {throw new NotWitablePropertyException (getRootClass (), this.nestpath + propertyName, "Property dans Path '" + PropertyName + "' n'existe pas", ex); } tokens = getProperTyNameTokens (getFinalPath (netedpa, propertyName)); if (netedpa == this) {pv.getoriginalPropertyValue (). RésolvedTokens = tokens; } netedpa.setPropertyValue (tokens, pv); } else {setPropertyValue (tokens, pv); }}Ici nestpa.setpropertyvalue (tokens, pv); Le paramètre réel de la valeur de la propriété Spring.Datasource.Tomcat est org.springframework.boot.bind.relaxeddatabinder $ relongbeanwrapper: objet d'enveloppe [org.apache.tomcat.jdbc.pool.datasource@6a84bc2a] Enfin, abstraitsablepropertyaccessor.
AbstractNestablePropertyAccessor.ProcessLocalPropertySpring-Beans-4.3.13.Release-Sources.jar! /Org/springframework/beans/abstractNestablePropertyAccessor.Javaprivate Void ProcessLocalProperty (Propertiankholder Tokens, PropertyValue PV) oues getLocalPropertyHandler (tokens.actualName); if (ph == null ||! Ph.iswitable ()) {if (pv.isoptional ()) {if (logger.isdebugeNabled ()) {logger.debug ("Ignorer la valeur facultative pour la propriété '" + tokens.actualname + "' - propriété introuvable sur la classe bean [" + getrootclass (). GetName () ")"); } retour; } else {throw CreenoTwitablePropertyException (tokens.canonicalName); }} Objet oldvalue = null; essayez {objet 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 privilEGEGACTIONException) {ex = ((privilEGEDACTACKException) ex) .getException (); } if (logger.isdebugeNable ()) {logger.debug ("n'a pas pu lire la valeur précédente de la propriété '" + this.nestpath + tokens.canonicalName + "'", ex); }} ValuetoApply = convertForProperty (tokens.canonicalName, OldValue, originalValue, Ph.TotyPeDeScriptor ()); } pv.getoriginalPropertyValue (). ConversionNeceSsary = (ValuetoApply! = originalValue); } Ph.SetValue (this.wrappeObject, ValuetoApply); } catch (typeMismatchException ex) {throw ex; } catch (invocationTargetException ex) {PropertyChangeEvent PropertyChangeEvent = new PropertyChangeEvent (this.rootObject, this.nestpath + tokens.canonicalName, oldvalue, pv.getValue ()); if (ex.getTTargetException () instanceof classCastException) {throw new TypeMisMatchException (propriétéChangeEvent, ph.getPropertyType (), ex.getTargetException ()); } else {throwable cause = ex.getTargetException (); if (cause instance of UnteclaredThrowableException) {// peut se produire, par exemple avec des méthodes générées par Groovy Cause = Cause.getCause (); } lancez une nouvelle méthodyInvocationException (PropertyChangeEvent, Cause); }} catch (exception ex) {PropertyChangeEvent pCE = new PropertyChangeEvent (this.rootObject, this.nestpath + tokens.canonicalName, oldvalue, pv.getValue ()); Jetez une nouvelle méthodeInvocationException (PCE, ex); }}Il le fait définir en utilisant la 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 (Final Object Object, Object Valuetoapply) lance Exception {final Method WriteMethod = (this.pd instanceof GenericTypeAwaRepropertyDescriptor? if (! Modifier.ispublic (writemethod.getDeclaringClass (). getModifiers ()) &&! writemethod.isaccessible ()) {if (System.getSecurityManager ()! = null) {AccessController. WriteMethod.setAccessible (true); } else {writemethod.setAccessible (true); }} valeur d'objet final = ValuetoApply; if (System.getSecurityManager ()! = null) {try {AccessController.doprivileged (new privilEgeExceptionAction <objet> () {@Override public objet run () lève exception {writeMeth.Invoke (objet, value); return null;}}, accordem); } catch (privilEGEGACTIONException ex) {throw ex.getException (); }} else {WriteMethod.invoke (getWrapyInstance (), valeur); }}}}Ici, nous utilisons la réflexion pour trouver la méthode setxxx (telle que setMaxActive), puis la définir
Configuration de la source multi-données
La configuration ci-dessus n'est pas problématique pour une seule source de données. Pour plusieurs sources de données, la configuration est la suivante
@ConfigurationPublic classe MasterDataSourceConfig {@Bean ("MasterDataSource") @configurationProperties (prefix = "Spring.Datasource.master") public DataSource MasterDataSource () {return DataSourceBuilder.create (). Build (); }}Notez que vous devez ajouter des paramètres supplémentaires pour injecter la configuration Properties dans le pool Tomcat JDBC
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 JMX compatible: true # tomcat: ## Pour plusieurs sources de données, nous devons supprimer Tomcat ici et les mettre sous le préfixe de la source de données Initial-Size: 1 max-active: 5 ## Lorsque le balayeur de pool est activé, la connexion extra-inactif sera fermé à clôturer MAX-IDLE: 5 ## Connexion Idle> Min-idle, PoolSweeper commencera à clôturer Min-Idle: 1
La configuration TOMCAT d'origine doit être placée sous le préfixe de source de données, et elle ne peut pas prendre effet sous Spring.datasource.tomcat ou printemps.datasource.master.tomcat.