Mari kita lihat kode ikatan properti dari Spring Boot Tomcat JDBC Pool. Kode spesifiknya adalah sebagai berikut:
Spring: DataSource: Type: org.apache.tomcat.jdbc.pool.datasource driver-class-name: org.postgresql.driver url: jdbc: postgresql: //192.168.99.100: 5432/postgres? Connecttime = 6000 & SockernoUs = 6000: 6000: 54.000: 5432/postgreseOnteOut = 6000 & SockernoUs = 6000 & SockernoUs = 5432: postgreseUnteout = 6000 & Sockernout = 6000 & Sockernout = 5432: 5432/postgres? JMX-Enabled: Benar Ukuran Awal: 1 Max-Active: 5 ## Saat Pool Sweater Diaktifkan, Koneksi Idle Ekstra Akan Ditutup Max-Idle: 5 ## Saat Koneksi Idle> Min-Idle, Poolsweeper akan mulai menutup Min-idle: 1
Menggunakan konfigurasi di atas, saya akhirnya menemukan bahwa ukuran awal, maksimal, max-idle, min-idle dan konfigurasi lainnya tidak valid. DataSource Tomcat JDBC yang dihasilkan masih merupakan konfigurasi default yang digunakan.
Konfigurasi yang benar
Spring: DataSource: Type: org.apache.tomcat.jdbc.pool.datasource driver-class-name: org.postgresql.driver url: jdbc: postgresql: //192.168.99.100: 5432/postgres? Connecttime = 6000 & SockernoUs = 6000: 6000: 54.000: 5432/postgreseOnteOut = 6000 & SockernoUs = 6000 & SockernoUs = 5432: postgreseUnteout = 6000 & Sockernout = 6000 & Sockernout = 5432: 5432/postgres? JMX-Enabled: True Tomcat: ## Kolam koneksi database tunggal, dan konfigurasi properti Tomcat harus ditulis agar berlaku ukuran awal: 1 Max-Active: 5 ## Ketika Pool Sweeper diaktifkan, koneksi idle ekstra akan ditutup Max-Idle: 5 ## Saat idle koneksi> Min-idle, poolsweeper akan mulai close Min-IDLE CLOSE MIN-IIDLE:
Perhatikan bahwa properti konfigurasi dari kumpulan koneksi basis data TOMCAT spesifik ditempatkan di bawah properti spring.datasource.tomcat sehingga mereka dapat berlaku.
Analisis Kode Sumber
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, DataSourConfiguration.hikari.class, DataSourCeconfiguration.dbcp.class, DataSourConfiguration.dbcp22.class, DataSourcacuration.dbcp2.classourcuras.drascrasser.dbcp2 @SuppressWarnings ("Deprecation") Kelas statis yang dilindungi PooledDataSourCeConfiguration {}DataSourCeConfiguration.tomcat
Spring-Boot-autoconfigure-1.5.9.release-sources.jar! /org/springframework/boot/autoconfigure/jdbc/datasourceconfiguration.java/*** Konfigurasi sumber data kumpulan tomcat. */ @ConditionAlclass (org.apache.tomcat.jdbc.pool.datasource.class) @ConditionAlonProperty (name = "spring.datasource.type", haveValue = "org.apache.tomcat.jdbc.pool.datacing" org.apache.tomcat.jdbc.poolce " DataSourCeConfiguration {@bean @configurationproperties (prefix = "spring.datasource.tomcat") public org.apache.tomcat.jdbc.pool.dataSource DataSource (DataSourceProPerties) {org.apache.tomcoperc.batcool.batcool.ourpool. CreateTaRasource (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; }}Anda dapat melihat bahwa DataSourceProperties di sini hanya memiliki konfigurasi atribut langsung Spring.Datasource, seperti URL, Nama Pengguna, Kata Sandi, DriverClassName. Tomcat tidak memiliki sifat khusus.
CreateTaRasource
Dilindungi <T> t createTaSource (properti DataSourceProperties, kelas <? Extends DataSource> type) {return (t) properties.initializedataSourceBuilder (). Type (type) .build (); }Org.apache.tomcat.jdbc.pool.datasource PoolProperties yang secara langsung membuat RataSource juga merupakan konfigurasi default.
Konfigurasi Konfigurasi
Keajaiban spesifik terletak pada kode @ConfigurationProperties(prefix = "spring.datasource.tomcat") . Sebelum Container Spring membangun kacang proxy dan kembali, itu akan mengatur atribut yang ditentukan oleh spring.datasource.tomcat ke org.apache.tomcat.jdbc.pool.datasource
spring-boot-1.5.9.RELEASE-sources.jar!/org/springframework/boot/context/properties/ConfigurationPropertiesBindingPostProcessor.javaprivate void postProcessBeforeInitialization(Object bean, String beanName, ConfigurationProperties annotation) { Object target = bean; PropertiesConfigurationFactory <BOCPOCT> FACTORY = PROPERTI NEW CONFIGURATIONFACTORY <BOMPERTIF> (Target); factory.setpropertysources (this.propertysources); factory.setValidator (determinevalidator (bean)); // Jika tidak ada layanan konversi eksplisit yang disediakan, kami menambahkan satu sehingga (setidaknya) // array convertible yang dipisahkan secara koma dapat diikat secara otomatis. 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.setargetname (annotation.prefix ()); }} coba {factory.bindproperterestoTarget (); } catch (exception ex) {string targetclass = classutils.getshortname (target.getClass ()); Lempar BeanCreationException baru (Beanname, "tidak dapat mengikat properti ke" + targetClass + "(" + getAnnotationDetails (annotation) + ")", ex); }} Perhatikan bahwa anotasi di sini adalah @ConfigurationProperties(prefix = "spring.datasource.tomcat") , awalannya adalah spring.datasource.tomcat PropertiesConfigurationFactory TargetName adalah spring.datasource.tomcat
PropertiesConfigurationFactory.BindProperterToTargetSpring-boot-1.5.9.release-sources.jar! /Org/springframework/boot/bind/propertiesconfigurationfactory.javapublic void bindpropertiestoTarget () lemparan BindException {nolproperctate {nolsception {nolpropertercate () bindException {nolproperctate () bindException {nolpropertercate () bindException {nolsception. tidak boleh null "); coba {if (logger.istraceEnabled ()) {logger.trace ("Sumber properti:" + this.propertysources); } this.hasbeenbound = true; DobindProperterToTarget (); } catch (bindException ex) {if (this.exceptionifInvalid) {throw ex; } PropertiesConfigurationFactory.logger .Error ("Gagal memuat properti validasi kacang." + "Properti Anda mungkin tidak valid.", Ex); }}Metode Delegasi ke DobindProperterestoTarget
PropertiesConfigurationFactory.DobindProperterToTargetPrivate void DobindProperterToToTarget () melempar BindException {relaxedDataBinder databinder = (this.targetName! = Null? RelelDataDabinder baru (this.target, this. 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 <string> relaxedTargetNames = getRelaxedTargetNames (); Set <string> names = getNames (saksamaTargetNames); PropertyValues PropertyValues = GetPropertySourcesPropertyValues (nama, santai -targetnames); Databinder.Bind (PropertyValues); if (this.validator! = null) {databinder.validate (); } checkForBindingErrors (databinder); }Di sini, gunakan metode santai -relaxabinder.bind
getRelaxedTargetNamesPrivate iterable <string> getRelaxedTargetnames () {return (this.target! = null && stringutils.haslength (this.targetName)? baru santainames (this.targetName): null); }Berikut adalah nama santai baru yang dapat mengidentifikasi varian beberapa variabel
Nama santai
spring-boot-1.5.9.release-sources.jar! /org/springframework/boot/bind/relaxednames.javaprivate void initialize (nama string, set <string> nilai) {if (values.contains (name)) {return; } untuk (variasi variasi: variation.values ()) {for (manipulasi manipulasi: manipulation.values ()) {string result = name; hasil = manipulasi.apply (hasil); hasil = variasi.Apply (hasil); values.add (hasil); inisialisasi (hasil, nilai); }}} /*** Variasi nama. */ variasi enum {none {@Override Public String Apply (nilai string) {nilai pengembalian; }}, Huruf kecil {@Override Public String Apply (nilai string) {return value.isempty ()? nilai: value.tolowercase (); }}, Huruf besar {@Override Public String Apply (nilai string) {return value.isempty ()? nilai: value.touppercase (); }}; string abstrak publik berlaku (nilai string); }Artinya, metode penulisan konfigurasi di org.springframework.boot.bind.relaxednames@6ef81f31 [name = spring.datasource.tomcat, values = [spring.datasource.tomcat, spring_datasource_tomcat, springdataSource.tomcat Spring.datasource.tomcat, spring_datasource_tomcat, springdatasourcetomcat]] didukung.
GetPropertySourcesPropertyValuesPrivate PropertyValues GetPropertySourcesPropertyValues (Set <string> nama, iterable <string> relaxedTargetNames) {PropertiDynamepatternscatcher include = getPropertyNeMePatternscatcher (nama, relaxedTargetNames); Return New PropertySourcesPropertyValues (this.propertysources, nama, termasuk, this.ResolvePlaceHolders); }Metode ini akan menarik konfigurasi properti di bawah spring.datasource.Tomact ke objek PropertyValues
SaksamaDatabinder.Bind
Spring-Boot-1.5.9.release-sources.jar! /org/springframework/boot/bind/relaxeddatabinder.java Metode BIND menyebut metode kelas induknya, target-nilai-nilai yang disalin. * <p> Panggilan ini dapat membuat kesalahan lapangan, mewakili kesalahan pengikatan * dasar seperti bidang yang diperlukan (kode "diperlukan"), atau ketik ketidakcocokan * antara nilai dan properti kacang (kode "Typemishatch"). * <p> Perhatikan bahwa nilai properti yang diberikan harus menjadi contoh yang dibuang: * Untuk efisiensi, itu akan dimodifikasi untuk hanya berisi bidang yang diizinkan jika * mengimplementasikan antarmuka mutablePropertyValues; Lain, salinan internal mute * akan dibuat untuk tujuan ini. Lewati salinan PropertyValues* jika Anda ingin instance asli Anda tetap tidak dimodifikasi dalam hal apa pun. * @param pvs nilai properti untuk mengikat * @see #doBind (org.springframework.beans.mutablePropertyValues) */ public void bind (PropertyValues pvs) {mutablePropertyValues mpvs = (PVS contoh mutablePropertyvalues)? (MutablePropertyValues) PVS: New MutablePropertyValues (PVS); Dobind (MPV); } /** * Implementasi aktual dari proses pengikatan, bekerja dengan contoh * MutablePropertyValues yang disahkan. * @param mpvs Nilai properti untuk mengikat, * sebagai mutablePropertyValues instance * @see #CheckAllowedFields * @See #CheckRequiredFields * @LENE #ApplePlyPropertyValues */ VOID DOBIND (MutablePropertyValues mpvs) {void void (mutablePropertyValues mpvs) {mutableDfal (mutablePropertyues mpvs) {mutableDFiLdS); checkRequiredFields (MPVs); ApplyPropertyValues (MPVs); } /*** Terapkan nilai properti yang diberikan ke objek target. * <p> Implementasi default menerapkan semua nilai properti * yang disediakan sebagai nilai properti kacang. Secara default, bidang yang tidak diketahui akan * diabaikan. * @param mpvs Nilai properti yang akan diikat (dapat dimodifikasi) * @LENE #getTarget * @see #getPropertyAccessor * @LENE #isignoreUnkNOwnFields * @LEE #getBINDINGRORPROCESOR * @See BINDINGRORPROCESOR #ProcessPropertyAcception * @see BindingErrorProcessor #ProcessPropertyAcception * Mpvs) {coba {// Bind parameter permintaan ke objek target. getPropertyacessor (). SetPropertyValues (MPVS, iSignoreUndNOwnFields (), iSignoreInvalidFields ()); } catch (PropertyBatchUpDateException ex) {// Gunakan prosesor Bind Error untuk membuat fielderrors. untuk (PropertyAccessException PAE: ex.getPropertyAccessExceptions ()) {getBindingErrorProcessor (). ProcessPropertyAccessException (PAE, GetInternalBindingResult ()); }}} /*** Mengembalikan properti yang mendasari dari pengikat pengikat pengikat ini. */ Protected ConfigurablePropertyacessor getPropertyAccessor () {return getInternalBindingResult (). getPropertyAccessor (); }Akhirnya, diatur oleh getPropertyAccessor (). PropertyAccessor ini adalah org.springframework.boot.bind.relaxedDatabinder $ relaxedBeanWrapper: wrapping objek [org.apache.tomcat.jdbc.pool.datasource@6a84bc2a], yang merupakan org.apache.tapache.tdata.jdata.
AbstractPropertyacessor.setPropertyValuesspring-beans-4.3.13.release-sources.jar!/org/springframework/beans/abstractpropertyaccessor.java@override public void {propertyidvalues PV Daftar <PropertenAccessException> PropertyAccessExceptions = null; Daftar <propertyValue> PropertyValues = (PVS Instance dari MutablePropertyValues? ((MutablePropertyValues) PVS) .getPropertyValueList (): arrays.aslist (pvs.getPropertyValues ()))); for (PropertyValue PV: PropertyValues) {coba {// Metode ini dapat melempar BeansException apa pun, yang tidak akan ditangkap // di sini, jika ada kegagalan kritis seperti tidak ada bidang yang cocok. // kita dapat berusaha hanya dengan pengecualian yang kurang serius. setPropertyValue (PV); } catch (noTwritablePropertyException ex) {if (! IGNACKNOWN) {throw ex; } // Kalau tidak, abaikan saja dan lanjutkan ...} tangkap (nullValueInnestedPathException ex) {if (! Abaikaninvalid) {throw ex; } // Sebaliknya, abaikan saja dan lanjutkan ...} catch (PropertyAccessException ex) {if (PropertyAccessExceptions == NULL) {PropertyAccessExceptions = new LinkedList <PropertyAccessException> (); } PropertyAccessExceptions.Add (ex); }} // Jika kita menemukan pengecualian individu, lempar pengecualian gabungan. if (PropertyAccessExceptions! = null) {PropertyAccessException [] paearray = PropertyAccessExceptions.toArray (PropertyAccessException baru [PropertyAccessExceptions.size ()]); Lempar PropertyBatchupDateException (Paearray) baru; }} @Override public void setPropertyValue (PropertyValue PV) melempar BeansException {PropertyTokenHolder Tokens = (PropertyTokerHolder) PV.ResolvedTokens; if (tokens == null) {string propertyname = pv.getName (); AbstractNestablePropertyAccessor Nestedpa; coba {nestedpa = getPropertyAccessorForPropertypath (PropertieName); } catch (notreadablePropertyException ex) {lempar new notwritablePropertyException (getRootclass (), this.nestedpath + propertieName, "properti bersarang di jalur '" + propertiName + "tidak ada", ex); } tokens = getPropertynametokens (getFinalPath (nestedpa, propertieName)); if (nestedpa == this) {pv.getoriginalPropertyValue (). SecolvedTokens = token; } nestedpa.setPropertyValue (token, pv); } else {setPropertyValue (token, pv); }}Di sini nestedpa.setPropertyValue (Tokens, PV); Pengaturan nyata dari spring.datasource.tomcat Nilai properti adalah org.springframework.boot.bind.relaxedDatabinder $ rileksBeanWrapper: wrapping objek [org.apache.tomcat.jdbc.pool.datasource@6a84bc2a] Akhirnya Call Abstraction.pool.datasource@6a84bc2a]
AbstractNestablePropertyacessor.processlocalpropertyspring-beans-4.3.13.release-sources.jar! /Org/springframework/beans/abstractnestablePropertyAccessor. getLocalPropertyHandler (Tokens.ActualName); if (ph == null ||! ph.iswritable ()) {if (pv.isoptional ()) {if (logger.isdebugeNabled ()) {logger.debug ("Mengabaikan nilai opsional untuk properti '" + tokens.actualname + " - properti yang tidak ditemukan di kelas beana [" + getroot') (). } kembali; } else {Throw CreateNoTwritablePropertyException (Tokens.CanonicalName); }} Objek oldvalue = null; coba {objek aslivalue = pv.getValue (); Objek valuetoApply = originalValue; if (! boolean.false.equals (pv.conversionNecarsary)) {if (pv.isconverted ()) {valueToApply = pv.getConvertedValue (); } else {if (isExtractOldValueForeditor () && ph.isreadable ()) {coba {oldvalue = ph.getValue (); } catch (exception ex) {if (ex instanceof PrivilEgeDactionException) {ex = ((PrivilEgEndActionException) ex) .getException (); } if (logger.isdebugeNabled ()) {logger.debug ("tidak dapat membaca nilai properti sebelumnya '" + this.nestedpath + tokens.canonicalName + "'", ex); }} valueToApply = convertForProperty (Tokens.CanonicalName, OldValue, OriginalValue, Ph.TotipeDescriptor ()); } pv.getoriginalPropertyValue (). ConversionNecessary = (ValueToApply! = OriginalValue); } ph.setValue (this.wrappedObject, valuetoApply); } catch (TypeMisMatchException ex) {throw ex; } catch (InvocateTargetException ex) {PropertyChangeEvent PropertyChangeEvent = New PropertyChangeEvent (this.rootObject, this.nestedpath + tokens.canonicalName, oldValue, pv.getValue ()); if (ex.getTargetException () instance dari classcastException) {throw new typemismatchException (propertyChangeEvent, ph.getPropertyType (), ex.getTargetException ()); } else {throwable cause = ex.getTargetException (); if (penyebab instance dari undeclaredthrowableException) {// dapat terjadi misalnya dengan metode yang dihasilkan groovy Cause = cause.getCause (); } lempar MethodInVocationException baru (PropertyChangeEvent, Cause); }} catch (Exception ex) {PropertyChangeEvent pce = new PropertyChangeEvent (this.rootObject, this.nestedpath + tokens.canonicalName, oldvalue, pv.getValue ()); Lempar MethodInVocationException baru (PCE, EX); }}Itu membuatnya diatur menggunakan kelas org.springframework.beans.beanwrapperImpl $ beanpropertyhandler
BeanwrapperPl$BeanPropertyHandler.setValuespring-beans-4.3.13.release-sources.jar!/org/springframework/beans/beanwrapperImpl.java@override Metode public void (objek objek, objek valuetoapply) melemparkan pengecualian {writemply {ObjectePyply) Lemparan {Metode WritEphyeF. GenerictypeAwarePropertyDescriptor? if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers()) && !writeMethod.isAccessible()) { if (System.getSecurityManager() != null) { AccessController.doPrivileged(new PrivilegedAction<Object>() { @Override public Object run() { writemethod.setAccessible (true); } else {writemethod.setAccessible (true); }} nilai objek akhir = ValueToApply; if (System.GetSecurityManager ()! = NULL) {coba {accessController.DopRivilEgEd (new PrivilEdExceptionAction <BOMPERTIF> () {@Override Objek Publik Run () melempar pengecualian {writemethod.invoke (objek, nilai); return null;}}, acc); } Catch (PrivilEgeGactionException ex) {throw ex.getException (); }} else {writemethod.invoke (getWrappedInstance (), value); }}}}Di sini kami menggunakan refleksi untuk menemukan metode setxxx (seperti setmaxactive), dan kemudian mengaturnya
Konfigurasi Sumber Multi-Data
Konfigurasi di atas tidak bermasalah untuk sumber data tunggal. Untuk beberapa sumber data, konfigurasinya adalah sebagai berikut
@ConfigurationPublic kelas MasterDataSourCeConfig {@bean ("MasterDataSource") @ConfigurationProperties (prefix = "spring.datasource.master") public datasource MasterDataSource () {return dataSourceBuilder.create (). }}Perhatikan bahwa Anda perlu menambahkan pengaturan tambahan untuk menyuntikkan ConfigurationProperties ke 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/postgresTimeout: 6000 = 6000 & 6000: 5432/Postgres? Connecttime = 6000 & 6000 & 6000: 5432/Postgres? PostgresTimeout = 6000 = 6000 & 6000: 5432/postgres? JMX-Enabled: True# Tomcat: ## Untuk beberapa sumber data, kita perlu menghapus Tomcat di sini dan meletakkannya di bawah awalan sumber data ukuran awal: 1 Max-Active: 5 ## Ketika Pool Sweeper diaktifkan, Koneksi Idle Ekstra akan ditutup MAX-IDLE: 5 ## Koneksi Idle> Min-idle, Min-idle, Min-idle akan mulai Min-mulai akan mulai Min-mulai Mulai Min-mulai Mulai Min-Minper Mulai Min-mulai Min-mulai Min-mulai Min-magiper akan mulai Min-idleper Min-idleper Mulai Min-idle, Min-idle, Min-idle, Min-idle, Min-idle, Min-idle, Min-idle, Min-idle, Min-idle, Min-idle, Min-idle, Min-idle, Min-idle, Min-idle, Min-idle:
Konfigurasi Tomcat asli harus ditempatkan di bawah awalan sumber data, dan tidak dapat berlaku di bawah spring.datasource.tomcat atau spring.datasource.master.tomcat.