Perakitan otomatis Springboot adalah dasar untuk unboxing dan prasyarat untuk layanan mikro. Topik utama kali ini adalah melihat bagaimana itu diimplementasikan. Kami menggunakan kode sumber untuk memahami seluk beluk perakitan otomatis.
1.1. Tentang @springbootapplication
Ketika kami menulis proyek Springboot, @springbootApplication adalah anotasi yang paling umum. Kita dapat melihat kode sumbernya:
/ * * Hak Cipta 2012-2017 Penulis atau penulis asli. * * Dilisensikan di bawah lisensi Apache, versi 2.0 ("lisensi"); * Anda tidak boleh menggunakan file ini kecuali sesuai dengan lisensi. * Anda dapat memperoleh salinan lisensi di * * http://www.apache.org/licenses/license-2.0 * * kecuali diharuskan oleh hukum yang berlaku atau disepakati secara tertulis, perangkat lunak * yang didistribusikan di bawah lisensi didistribusikan atas dasar "sebagaimana adanya", * tanpa jaminan atau ketentuan apa pun, baik yang diungkapkan atau disiratkan. * Lihat lisensi untuk izin tata kelola bahasa tertentu dan * batasan di bawah lisensi. */paket org.springframework.boot.autoconfigure; impor java.lang.annotation.dokumen; impor java.lang.annotation.elementType; impor java.lang.annotation.herherited; impor java.lang.annotation. java.lang.annotation.target; impor org.springframework.boot.springbootconfiguration; impor org.springframework.boot.context.typeexcludefilter; import org.springframework.context.annotation.bean; impor org.springframework.context.annotation.bean; impor org.spramework.context.annotation.bean; Impor org.spramework.context.annotation.bean; Impor org.sgercanponfframork org.springframework.context.annotation.componentscan.filter; impor org.springframework.context.annotation.onfiguration; coonfigasi@* Impor {{@springframework. Itu menyatakan satu atau lebih * {@link bean @bean} metode dan juga memicu {@link enableAutoconfiguration * konfigurasi otomatis} dan {@link componentscan component scanning}. Ini adalah kenyamanan * anotasi yang setara dengan mendeklarasikan {@code @configuration}, * {@code @enableAutoconfiguration} dan {@code @componentscan}. * * @Author Phillip Webb * @author Stephane Nicoll * @since 1.2.0 */@target (elementType.type) @Retention (retentionPolicy.runtime)@didokumentasikan@warisan@springbootconfiguration@enableAutoconfiguration@componentscan (excludefilters = @filter = @filter = components = @filter = @filter. TypeExCludefilter.class), @filter (type = filtertype.custom, class = autoconfigurationexcludefilter.class)}) public @interface springbootApplication { /*** Kecualikan kelas konfigurasi otomatis spesifik sehingga mereka tidak akan pernah diterapkan. * @return kelas untuk mengecualikan */ @aliasfor (annotation = enableAutoconfiguration.class, attribute = "exclude") class <?> [] exclude () default {}; /** * Kecualikan nama kelas konfigurasi otomatis tertentu sehingga tidak akan pernah * diterapkan. * @return Nama kelas untuk mengecualikan * @since 1.3.0 */ @aliasfor (annotation = enableAutoconfiguration.class, attribute = "excludename") string [] excludename () default {}; /*** Paket dasar untuk dipindai untuk komponen beranotasi. Gunakan {@link #scanbasepackageClasses} * untuk alternatif jenis yang aman untuk nama paket berbasis string. * Paket dasar @return untuk dipindai * @since 1.3.0 */ @aliasfor (annotation = componentscan.class, atribut = "Basepackages") string [] scanbasepackages () default {}; /** * Alternatif jenis-aman untuk {@link #scanbasepackages} untuk menentukan paket untuk * memindai komponen beranotasi. Paket setiap kelas yang ditentukan akan dipindai. * <p> * Pertimbangkan untuk membuat kelas penanda no-op khusus atau antarmuka di setiap paket yang * tidak memiliki tujuan selain dirujuk oleh atribut ini. * @Return Paket dasar untuk dipindai * @since 1.3.0 */ @aliasfor (annotation = componentscan.class, atribut = "BasepackageClasses") kelas <?> [] scanbasepackageClasses () default {};}Ini berisi @springbootconfiguration, @enableAutoconfiguration, @ComponentScan. Di sini, karena tidak ada paket pemindaian yang ditentukan, ia memindai semua kelas di bawah level yang sama dengan kelas atau paket level yang sama secara default. Selain itu, @springbootconfiguration, Anda dapat mengetahui melalui kode sumber bahwa itu adalah @configuration:
/ * * Hak Cipta 2012-2016 Penulis asli atau penulis. * * Dilisensikan di bawah lisensi Apache, versi 2.0 ("lisensi"); * Anda tidak boleh menggunakan file ini kecuali sesuai dengan lisensi. * Anda dapat memperoleh salinan lisensi di * * http://www.apache.org/licenses/license-2.0 * * kecuali diharuskan oleh hukum yang berlaku atau disepakati secara tertulis, perangkat lunak * yang didistribusikan di bawah lisensi didistribusikan atas dasar "sebagaimana adanya", * tanpa jaminan atau ketentuan apa pun, baik yang diungkapkan atau disiratkan. * Lihat lisensi untuk izin tata kelola bahasa tertentu dan * batasan di bawah lisensi. */paket org.springframework.boot; import java.lang.annotation.Documented; import java.lang.annotation.elementType; import java.lang.annotation.Retensi; impor java.lang.annotation.RetentionPolicy; impor impor java.notation.notation.targaration; org.springframework.context.annotation.configuration;/** * menunjukkan bahwa kelas menyediakan aplikasi boot spring * {@link configuration @configuration}. Dapat digunakan sebagai alternatif untuk anotasi spring * standar {@code @configuration} sehingga konfigurasi dapat ditemukan * secara otomatis (misalnya dalam tes). * <p> * Aplikasi hanya boleh menyertakan <em> satu </em> {@code @springbootconfiguration} dan * sebagian besar aplikasi boot pegas idiomatik akan mewarisi dari * {@code @springbootApplication}. * * @Author Phillip Webb * @since 1.4.0 */@target (elementType.type) @retention (retentionpolicy.runtime)@didokumentasikan@configurationpublic @interface springbootconfiguration {}Dari sini kita dapat menyimpulkan bahwa @springbootApplication setara dengan @configuration @componentscan @enableAutoconfiguration
1.2. @EnableAutoconfiguration
Setelah anotasi ini ditambahkan, fungsi perakitan otomatis akan diaktifkan. Sederhananya, Spring akan mencoba menemukan semua kacang yang dikonfigurasi di bawah classpath Anda dan kemudian merakitnya. Tentu saja, ketika merakit kacang, itu akan diinisialisasi sesuai dengan beberapa aturan kustomisasi (bersyarat). Mari kita lihat kode sumbernya:
/ * * Hak Cipta 2012-2017 Penulis atau penulis asli. * * Dilisensikan di bawah lisensi Apache, versi 2.0 ("lisensi"); * Anda tidak boleh menggunakan file ini kecuali sesuai dengan lisensi. * Anda dapat memperoleh salinan lisensi di * * http://www.apache.org/licenses/license-2.0 * * kecuali diharuskan oleh hukum yang berlaku atau disepakati secara tertulis, perangkat lunak * yang didistribusikan di bawah lisensi didistribusikan atas dasar "sebagaimana adanya", * tanpa jaminan atau ketentuan apa pun, baik yang diungkapkan atau disiratkan. * Lihat lisensi untuk izin tata kelola bahasa tertentu dan * batasan di bawah lisensi. */paket org.springframework.boot.autoconfigure; impor java.lang.annotation.dokumen; impor java.lang.annotation.elementType; impor java.lang.annotation.herherited; impor java.lang.annotation. java.lang.annotation.target; impor org.springframework.boot.autoconfigure.condition.conditionAlonbean; impor org.springframework.boot.autoconfigure.condition.conditionallonbean; impor org.springframework.boot.autoconfigure.condition.conditionAnonmissingbean; impor org.springframework.boot.context.embedded.embeddedServletContainerFactory; impor org.springframework.boot.context.embeded.tomcyded org.springframework. Mencoba menebak dan * mengkonfigurasi kacang yang mungkin Anda butuhkan. Kelas konfigurasi otomatis biasanya * diterapkan berdasarkan classpath Anda dan kacang apa yang telah Anda tentukan. Misalnya, jika Anda * memiliki {@code tomcat-embedded.jar} di classpath Anda, Anda cenderung menginginkan * {@link tomcatembeddedServletContainerFactory} (kecuali Anda telah mendefinisikan sendiri * {@link embeddedServletContainerFactory} bean Anda sendiri). * <p> * Saat menggunakan {@link springbootApplication}, konfigurasi otomatis konteks adalah * diaktifkan secara otomatis dan menambahkan anotasi ini karena itu tidak memiliki efek tambahan. * <p> * Konfigurasi otomatis berusaha menjadi secerdas mungkin dan akan kembali seperti Anda * mendefinisikan lebih banyak konfigurasi Anda sendiri. Anda selalu dapat secara manual {@link #exclude ()} apapun * konfigurasi yang tidak pernah Anda terapkan (gunakan {@link #exCludeName ()} Jika Anda tidak * memiliki akses ke mereka). Anda juga dapat mengecualikannya melalui properti * {@code spring.autoconfigure.exclude}. Konfigurasi otomatis selalu diterapkan * setelah kacang yang ditentukan pengguna terdaftar. * <p> * Paket kelas yang dijelaskan dengan {@code @enableAutoconfiguration}, * biasanya melalui {@code @springbootApplication}, memiliki signifikansi spesifik dan sering digunakan * sebagai 'default'. Misalnya, ini akan digunakan saat memindai untuk kelas {@code @entity}. * Secara umum disarankan agar Anda menempatkan {@code @enableAutoconfiguration} (jika Anda * tidak menggunakan {@code @springbootApplication}) dalam paket root sehingga semua sub-paket * dan kelas dapat dicari. * <p> * Kelas konfigurasi otomatis adalah pegas reguler {@link configuration} beans. Mereka * terletak menggunakan mekanisme {@link springfactoriesloader} (dikunci terhadap kelas ini). * Secara umum kacang konfigurasi otomatis adalah {@link conditional @conditional} beans (sebagian besar * sering menggunakan {@link conditionAlonclass @ConditionAlonclass} dan * {@link conditionAnonMissingBean @ConditionAlonMissingBean} annotations). * * @Author Phillip Webb * @author Stephane Nicoll * @see conditionAnbean * @see conditionAnonMissingBean * @see conditionAnclass * @see autoconfigureafter * @see springbootApplication */@SuppressWarnings ("Deprecation")@Target (ElementType.Type) @retention (retentionpolicy.runtime)@didokumentasikan@warisan@autoconfigurationpackage@import (enableAutoconfigurationImportSelector.class) PUBLIC @Interface EnableAutoconfiguration {string Engeedselector.OverDass @interface EnableAutoconfiguration {string EngededSelector.Overdas "spring.boot.enableAutoconfiguration"; /*** Kecualikan kelas konfigurasi otomatis tertentu sehingga tidak akan pernah diterapkan. * @return kelas untuk mengecualikan */ class <?> [] exclude () default {}; /** * Kecualikan nama kelas konfigurasi otomatis tertentu sehingga tidak akan pernah * diterapkan. * @return Nama kelas untuk mengecualikan * @since 1.3.0 */ string [] excludename () default {};}Meskipun menurut komentar dokumentasi, ia memandu kami untuk melihat EnableAutoconfigurationImportSelector. Tetapi kelas sudah ketinggalan zaman dalam versi springboot1.5.x, jadi mari kita lihat kelas induknya autoconfigurationImportSelector:
/ * * Hak Cipta 2012-2017 Penulis atau penulis asli. * * Dilisensikan di bawah lisensi Apache, versi 2.0 ("lisensi"); * Anda tidak boleh menggunakan file ini kecuali sesuai dengan lisensi. * Anda dapat memperoleh salinan lisensi di * * http://www.apache.org/licenses/license-2.0 * * kecuali diharuskan oleh hukum yang berlaku atau disepakati secara tertulis, perangkat lunak * yang didistribusikan di bawah lisensi didistribusikan atas dasar "sebagaimana adanya", * tanpa jaminan atau ketentuan apa pun, baik yang diungkapkan atau disiratkan. * Lihat lisensi untuk izin yang mengatur bahasa tertentu dan * batasan di bawah lisensi. */paket org.springframework.boot.autoconfigure; impor java.io.ioexception; impor java.util.arraylist; import java.util.arrays; impor java.util.collections; impor java.util.hashset; impor java.util.util.links; java.util.map; impor java.util.set; impor java.util.concurrent.timeunit; impor org.apache.commons.logging.log; impor org.apache.commons.logging.logfactory; impor org.springframe.beans.beanceersExceptory; impor org.springframework.beans.beanse org.springframework.beans.factory.Aware; impor org.springframework.beans.factory.beanclassloaderaware; impor org.springframework.beans.factory.beanfactory; impor org.springframework.beans.factory.beanfactory; org.springframework.beans.factory.nosuchbeandefinitionException; impor org.springframework.beans.factory.config.configurableListableBeanFactory; impor org.spramework.boot.bind.bind.billaxedpropertypropertyrervare.spramework. org.springframework.context.resourceloaderaware; impor org.springframework.context.annotation.deferredImportSelector; impor org.springframework.core.ordered; impor org.springframework.core.annotation.annotationatribute; org.springframework.core.env.configurableenvironment; impor org.springframework.core.env.environment; org.springframework.core.io.Resouroader; impor org.spramework.core.o.sport.sport.sportfacories; org.springframework.core.type.AnnotationMetadata; impor org.springframework.core.type.classreading.cachingmetadataaderfactory; import orgrramewework.core.type.classreading.metadateReDererererererer.spramework.core org.springframework.util.classutils; impor org.springframework.util.stringutils;/** * {@link ditangguhkan IMPORTSelector} untuk menangani {@link enableAutoconfiguration * auto-konfigurasi}. Kelas ini juga dapat disubklasir jika varian khusus dari * {@link enableAutoconfiguration @EnableAutoconfiguration}. dibutuhkan. * * @author Phillip Webb * @author Andy Wilkinson * @author Stephane Nicoll * @author Madhura Bhave * @since 1.3.0 * @see EnableAutoConfiguration */public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {private static final string [] no_imports = {}; logger log final statis privat = logfactory .getLog (autoconfigurationImportSelector.class); Private ConfigurableListableBeanFactory BeanFactory; lingkungan lingkungan pribadi; private classloader beanscaslloader; sumber daya sumber daya swasta; @Override public string [] selectImports (annotationmetadata annotationmetadata) {if (! IsEnabled (annotationmetadata)) {return no_imports; } coba {autoconfigurationMetadata autoconfigurationMetadata = autoconfigurationMetadataLoader .loadmetadata (this.beanclassloader); AnnotationAttributes atribut = getAttributes (annotationmetadata); Daftar <string> konfigurasi = getCandIdateConfigurations (annotationMetadata, atribut); Konfigurasi = Removeduplicates (Konfigurasi); configurations = sort (konfigurasi, autoconfigurationmetadata); Set <string> Exclussions = GetExclusions (AnnotationMetadata, Atribut); CheckexcludededClasses (Configurations, Excuse); configurations.removeall (pengecualian); konfigurasi = filter (konfigurasi, autoconfigurationmetadata); fireautoconfigurationImportEvents (konfigurasi, eksklusi); return configurations.toArray (string baru [configurations.size ()]); } catch (ioException ex) {lempar baru ilegalstateException (ex); }} boolean yang dilindungi terisabilitas (annotationmetadata metadata) {return true; } /** * Kembalikan {@link annotationAttributes} yang sesuai dari * {@link annotationmetadata}. Secara default metode ini akan mengembalikan atribut untuk * {@link #getAnnotationClass ()}. * @param Metadata Metadata anotasi * @Return Annotation Atribut */ AnnotationAttributes GetAttributes (AnnotationMetadata metadata) {string name = getAnnotationClass (). getName (); Atribut AnnotationAttributes = AnnotationAttributes .frommap (metadata.getAnnotationAttributes (name, true))); Assert.notnull (atribut, "tidak ada atribut konfigurasi otomatis yang ditemukan. IS" + metadata.getClassName () + "dianotasi dengan" + classutils.getshortname (name) + "?"); pengembalian atribut; } /*** Mengembalikan kelas anotasi sumber yang digunakan oleh pemilih. * @return kelas anotasi */ kelas terlindungi <?> getAnnotationClass () {return enableAutoconfiguration.class; } /*** Mengembalikan nama kelas konfigurasi otomatis yang harus dipertimbangkan. Secara default * Metode ini akan memuat kandidat menggunakan {@link springFactoriesLoader} dengan * {@link #getspringfactoriesLoaderFactoryClass ()}. * @param metadata Sumber metadata * @param atribut {@link #getAttributes (annotationmetadata) anotasi * atribut} * @return daftar kandidat konfigurasi */ daftar <string> getCandataConfigurations (annotationmetadata MetAdata {annotations {annotation metadata> getCandataConfigurations (annotationmetadata {string> Springfactoryerer.loadFactoryNames (getspringfactoriesloaderfactoryclass (), getBeanClassLoader ()); Assert.notempty (konfigurasi, "tidak ada kelas konfigurasi otomatis yang ditemukan di meta-inf/spring.factories. Jika Anda" + "menggunakan kemasan khusus, pastikan file itu benar."); mengembalikan konfigurasi; } /** * Kembalikan kelas yang digunakan oleh {@link springfactoriesloader} untuk memuat konfigurasi * kandidat. * @return kelas pabrik */ kelas terlindungi <?> GetspringFactoriesLoaderFactoryClass () {return enableAutoconfiguration.class; } private void checkexcludedclasses (daftar <string> konfigurasi, atur <string> eksklusif) {daftar <string> invalidexCludes = new ArrayList <string> (excupions.size ()); untuk (string exclus: exclus) {if (classutils.ispresent (eksklusi, getClass (). getClassLoader ()) &&! configurations.contains (eksklusi)) {invalidexcludes.add (pengecualian); }} if (! InvalidexCludes.isempty ()) {handleInvalidexCludes (InvalidexCludes); }} /*** Tangani tidak ada yang tidak valid yang telah ditentukan. * @param invalidexcludes daftar tidak valid tidak termasuk (akan selalu memiliki setidaknya satu elemen *) */ void handleInvalidexCludes yang dilindungi (Daftar <String> InvalidexCludes) {stringBuilder pesan = stringBuilder baru (); untuk (string tidak termasuk: InvalidexCludes) {message.append ("/t-") .Append (Exclude) .Append (String.Format ("%n")); } Lempar IllegalStateException baru (String .Format ("Kelas-kelas berikut tidak dapat dikecualikan karena mereka" + "bukan kelas konfigurasi otomatis:%n%s", pesan)); } /*** Mengembalikan pengecualian yang membatasi konfigurasi kandidat. * @param metadata the source metadata * @param attributes the {@link #getAttributes(AnnotationMetadata) annotation * attributes} * @return exclusions or an empty set */ protected Set<String> getExclusions(AnnotationMetadata metadata, AnnotationAttributes attributes) { Set<String> excluded = new LinkedHashset <string> (); tidak termasuk.addall (aslist (atribut, "tidak termasuk")); tidak termasuk.addall (arrays.aslist (attributes.getStringArray ("exculdename")))); dikecualikan. pengembalian dikecualikan; } Daftar Privat <String> getExCludeAutOConfigurationsProperty () {if (getEnvironment () instance dari ConfigableAnvironment) {relaxedPropertyRySolver resolver = new rileksPropertyRyolver (this.environment, "spring.autoconfigure."); Peta <string, object> properties = resolver.getSubproperties ("excude"); if (properties.isempty ()) {return collections.emptylist (); } Daftar <String> tidak termasuk = ArrayList baru <String> (); untuk (map.entry <string, objek> entri: properties.entryset ()) {string name = entry.getKey (); Nilai objek = entri.getValue (); if (name.isempty () || name.startswith ("[") && value! = null) {excludes.addall (hashset baru <string> (arrays.aslist (stringutils .tokenizetoStringArray (string.valueof (value), ",")))))); }} return tidak termasuk; } Resolver saksamaPropertyRySolver = new rilekspropertyRysolver (getenvironment (), "spring.autoconfigure."); String [] exclude = resolver.getProperty ("exclude", string []. Class); return (arrays.aslist (tidak termasuk == null? string baru [0]: tidak termasuk)); } Daftar Privat <String> Sortir (Daftar <String> Konfigurasi, AutoConfigurationMetAdata AutoConfigurationMetadata) melempar ioException {configurations = AutoConfigurationsorTer baru (getMetAdataDerFactory (), autoconfigurationMetAdata (getMetArpiority (), autoconfigurationMetAdata). mengembalikan konfigurasi; } Daftar Privat <String> Filter (Daftar <String> Konfigurasi, AutoConfigurationMetAdata AutoConfigurationMetAdata) {Long StartTime = System.nanoTime (); String [] kandidat = configurations.toArray (string baru [configurations.size ()]); boolean [] skip = boolean baru [kandidat.length]; Boolean dilewati = false; untuk (autoconfigurationImportFilter filter: getAutoconfigurationImportFilters ()) {InvokeAwareMethods (filter); boolean [] match = filter.match (kandidat, autoconfigurationmetadata); untuk (int i = 0; i <match.length; i ++) {if (! Match [i]) {lewati [i] = true; dilewati = true; }}}} if (! dilewati) {return configurations; } Daftar <string> result = ArrayList baru <string> (kandidat.length); untuk (int i = 0; i <kandidat.length; i ++) {if (! lewati [i]) {result.add (kandidat [i]); }} if (logger.istraceEnabled ()) {int numberfiltered = configurations.size () - result.size (); Logger.trace ("Difiltered" + NumberFiltered + "Kelas Konfigurasi Otomatis di" + TimeUnit.nanoseconds.tomillis (System.nanoTime () - StartTime) + "MS"); } return new ArrayList <String> (hasil); } Daftar yang Dilindungi <OutoconfigurationImportFilter> getAutoconfigurationImportFilters () {return springFactoriesLoader.LoadFactories (autoconfigurationImportFilter.class, this.beanClassLoader); } private MetadataReaderFactory getMetadataReaderFactory () {coba {return getBeanFactory (). getbean (sharedMetadataaderFactoryContextInitializer.bean_name, metadataaderfactory.class); } catch (nosuchbeandefinitionException ex) {return new CachingMetadataReaderFactory (this.resourceloader); }} final yang dilindungi <T> Daftar <T> Dihapus Ujung (Daftar <T> Daftar) {return new ArrayList <T> (LinkedHashset baru <T> (daftar)); } Daftar akhir yang dilindungi <string> asList (atribut annotationAttributes, name string) {string [] value = attributes.getStringArray (name); return arrays.aslist (value == null? string baru [0]: value); } private void fireautoconfigurationImportEvents (daftar <string> konfigurasi, atur <string> excclusions) {daftar <autoconfigurationImportListener> pendengar = getAutoconfigurationImportListeners (); if (! listeners.isempty ()) {autoconfigurationImportEvent event = autoconfigurationImportEvent baru (ini, konfigurasi, excclusions); untuk (AutoConfigurationImportListener Listener: pendengar) {InvokeAwareMethods (pendengar); listener.onautoconfigurationImportEvent (acara); }}} Daftar yang Dilindungi <AutoConfigurationImportListener> getAutoconfigurationImportListeners () {return springFactoriesLoader.LoadFactories (AutoConfigurationImportListener.class, this.beanClassloader); } private void InvokeAwareMethods (instance objek) {if (instance instance dari nothing) {if (instance instanceof beanClassLoaderAware) {((beansclassloaderAware) instance) .setBeanClassLoader (this.beanClassLoader); } if (instance instance dari beanfactoryAware) {((beanfactoryAware) instance) .setBeanFactory (this.beanFactory); } if (instance instance dari lingkungan lingkungan) {((lingkungan) instance) .setenvironment (this.environment); } if (instance instance dari resourceloaderAware) {((resourceloaderAware) instance) .setResourceloader (this.resourceloader); }}} @Override public void setBeanFactory (beanfactory beanfactory) melempar beansexception {assert.isinstanceof (configurableListableBeanFactory.class, beanfactory); this.beanFactory = (configurableListableBeanFactory) beanfactory; } protected final configurableListableBeanFactory getBeanFactory () {return this.beanFactory; } @Override public void setBeanClassLoader (ClassLoader ClassLoader) {this.beanClassLoader = classLoader; } Protected ClassLoader getBeanClassLoader () {return this.beanClassLoader; } @Override public void setenvironment (lingkungan lingkungan) {this.environment = lingkungan; } lindung lingkungan final getenvironment () {return this.environment; } @Override public void setResourCeloader (Resourceloader Resourceloader) {this.Resourceloader = Resourceloader; } protected final resourceloader getResourceloader () {return this.resourceloader; } @Override public int getorder () {return orded.lowest_precedence - 1; }}Pertama, kelas ini mengimplementasikan Antarmuka DeferedImportSelector, yang mewarisi ImportSelector:
/ * * Hak Cipta 2002-2013 Penulis asli atau penulis. * * Dilisensikan di bawah lisensi Apache, versi 2.0 ("lisensi"); * Anda tidak boleh menggunakan file ini kecuali sesuai dengan lisensi. * Anda dapat memperoleh salinan lisensi di * * http://www.apache.org/licenses/license-2.0 * * kecuali diharuskan oleh hukum yang berlaku atau disepakati secara tertulis, perangkat lunak * yang didistribusikan di bawah lisensi didistribusikan berdasarkan "sebagaimana adanya", * tanpa jaminan atau ketentuan apa pun, baik yang diungkapkan atau disiratkan. * Lihat lisensi untuk izin yang mengatur bahasa tertentu dan * batasan di bawah lisensi. */paket org.springframework.context.annotation; impor org.springframework.core.type.annotationmetadata;/** * Antarmuka yang akan diimplementasikan oleh jenis yang menentukan @{ @tautan} * class (ES) yang harus diimpor berdasarkan kriteria pilihan yang diberikan, biasanya satu atau lebih banyak *. * * <p> Sebuah {@link ImportSelector} dapat mengimplementasikan salah satu dari yang berikut * {@link org.springframework.beans.factory.Aware sadar}, dan metode masing -masing * akan dipanggil sebelum {@link #selectimports}: * <ul> * <li> {@tautan. EnvironmentAware} </li> * <li> {@link org.springframework.beans.factory.beanfactoryAware beanfactoryAware} </li> * <li> {@link org.springframework.beans.factory.beanclassLoaderAware beanclaskLassLoAware.beans.factory.beanclasseLoaderAware beanclaskLassLoaderererererer org.springframework.context.ResourceloaderAware ResourceloaderAware} </li> * </ul> * <p> ImportSelectors biasanya diproses dengan cara yang sama seperti {@code @code @code @code @code @code @code @code @code @code @code @code juga dimungkinkan untuk menunda pemilihan impor sampai semua * @code @code @code @code @code @code @code @code @code @code @code @code juga dimungkinkan untuk melakukan pemilihan impor sampai semua * @code @code @code @code @code @code @code @code @code @code @code @code DeferedImportSelector} * untuk detailnya). * * @Author Chris Beams * @since 3.1 * @See DeferedImportSelector * @LENE Impor * @See ImportBeandFinitionRegistrar * @Leng @See Configuration * /Public Interface ImportSelector { /** Pilih dan kembalikan nama kelas mana (ES) harus diimpor berdasarkan * {@link annotation. */ String [] selectImports (annotationmetadata imporingclassmetadata);}Antarmuka ini terutama digunakan untuk mengimpor item konfigurasi @configuration, dan DeferedImportSelector adalah impor yang ditangguhkan, dan itu akan dieksekusi hanya setelah semua @Configurations diproses.
Mari kita lihat metode selectImport AutoConfigurationImportSelector:
@Override public string [] selectImports (annotationmetadata annotationmetadata) {if (! IsEnabled (annotationmetadata)) {return no_imports; } coba {autoconfigurationMetadata autoconfigurationMetadata = autoconfigurationMetadataLoader .loadmetadata (this.beanclassloader); AnnotationAttributes atribut = getAttributes (annotationmetadata); Daftar <string> konfigurasi = getCandIdateConfigurations (annotationMetadata, atribut); Konfigurasi = Removeduplicates (Konfigurasi); configurations = sort (konfigurasi, autoconfigurationmetadata); Set <string> excclusions = getExclusions (annotationmetadata, atribut); CheckexcludededClasses (Configurations, Excuse); configurations.removeall (pengecualian); konfigurasi = filter (konfigurasi, autoconfigurationmetadata); fireautoconfigurationImportEvents (konfigurasi, excclusions); return configurations.toArray (string baru [configurations.size ()]); } catch (ioException ex) {lempar baru ilegalstateException (ex); }}
Pada awalnya, metode ini pertama-tama akan menentukan apakah akan melakukan perakitan otomatis, dan kemudian membaca sifat-sifat yang relevan dari metadata dan metadata dari meta-inf/spring-autoconfigure-metadata.properties, dan kemudian hubungi metode getCandateconfigurations:
/*** Mengembalikan nama kelas konfigurasi otomatis yang harus dipertimbangkan. Secara default * Metode ini akan memuat kandidat menggunakan {@link springFactoriesLoader} dengan * {@link #getspringfactoriesLoaderFactoryClass ()}. * @param metadata Sumber metadata * @param atribut {@link #getAttributes (annotationmetadata) anotasi * atribut} * @return daftar kandidat konfigurasi */ daftar <string> getCandataConfigurations (annotationmetadata MetAdata {annotations {annotation metadata> getCandataConfigurations (annotationmetadata {string> Springfactoryerer.loadFactoryNames (getspringfactoriesloaderfactoryclass (), getBeanClassLoader ()); Assert.notempty (konfigurasi, "tidak ada kelas konfigurasi otomatis yang ditemukan di meta-inf/spring.factories. Jika Anda" + "menggunakan kemasan khusus, pastikan file itu benar."); mengembalikan konfigurasi; } /** * Kembalikan kelas yang digunakan oleh {@link springfactoriesloader} untuk memuat konfigurasi * kandidat. * @return kelas pabrik */ kelas terlindungi <?> GetspringFactoriesLoaderFactoryClass () {return enableAutoconfiguration.class; }Di sini saya bertemu dengan kenalan lama kami - SpringFactoryiesLoader, yang akan membaca konfigurasi enableAutoconfiguration di bawah meta -inf/spring. Akhirnya, biarkan semua autoconfigurationImportListener dikonfigurasi di bawah meta-inf/spring.
private void fireautoconfigurationImportEvents (daftar <string> konfigurasi, atur <string> excclusions) {list <autoconfigurationImportListener> pendengar = getAutoconfigurationImportListeners (); if (! listeners.isempty ()) {autoconfigurationImportEvent event = autoconfigurationImportEvent baru (ini, konfigurasi, excclusions); untuk (AutoConfigurationImportListener Listener: pendengar) {InvokeAwareMethods (pendengar); listener.onautoconfigurationImportEvent (acara); }}} Daftar yang Dilindungi <AutoConfigurationImportListener> getAutoconfigurationImportListeners () {return springFactoriesLoader.LoadFactories (AutoConfigurationImportListener.class, this.beanClassloader); }Di tautan sebelumnya, kita hanya perlu menentukan kelas mana yang perlu dikumpulkan, dan kapan kelas -kelas yang dirakit secara otomatis ini akan diproses di Springboot? Mari kita analisis secara singkat:
2.1. Metode Refresh dari AbstractApplicationContext:
Metode ini adalah klise. Harap perhatikan metode ini:
// Invoke Processorsorsor Pabrik terdaftar sebagai kacang dalam konteks. InvokeBeanFactoryPostProcessors (BeanFactory);
Berikut adalah proses BeanFactoryPostProcessor, jadi mari kita lihat antarmuka ini BeandefinitionRegistryPostProcessor:
/ * * Hak Cipta 2002-2010 Penulis atau penulis asli. * * Dilisensikan di bawah lisensi Apache, versi 2.0 ("lisensi"); * Anda tidak boleh menggunakan file ini kecuali sesuai dengan lisensi. * Anda dapat memperoleh salinan lisensi di * * http://www.apache.org/licenses/license-2.0 * * kecuali diharuskan oleh hukum yang berlaku atau disepakati secara tertulis, perangkat lunak * yang didistribusikan di bawah lisensi didistribusikan berdasarkan "sebagaimana adanya", * tanpa jaminan atau ketentuan apa pun, baik yang diungkapkan atau disiratkan. * Lihat lisensi untuk izin yang mengatur bahasa tertentu dan * batasan di bawah lisensi. */paket org.springframework.beans.factory.support; impor org.springframework.beans.beansException; org.springframework.beans.factory.config.beanfactorypostororor; bean definitions <i>before</i> regular * BeanFactoryPostProcessor detection kicks in. In particular, * BeanDefinitionRegistryPostProcessor may register further bean definitions * which in turn define BeanFactoryPostProcessor instances. * * @author Juergen Hoeller * @since 3.0.1 * @see org.springframework.context.annotation.ConfigurationClassPostProcessor */public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor { /** * Modify the application context's internal bean definition registration after its * standard initialization. All regular bean definitions will have been loaded, * but no beans will have been instantiated yet. This allows for adding further * bean definitions before the next post-processing phase kicks in. * @param registry the bean definition registry used by the application context * @throws org.springframework.beans.BeansException in case of errors */ void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;}该接口继承了BeanFactoryPostProcessor。
2.2. ConfigurationClassPostProcessor class
该类主要处理@Configuration注解的,它实现了BeanDefinitionRegistryPostProcessor, 那么也间接实现了BeanFactoryPostProcessor,关键代码如下:
@Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) { int factoryId = System.identityHashCode(beanFactory); if (this.factoriesPostProcessed.contains(factoryId)) { throw new IllegalStateException( "postProcessBeanFactory already called on this post-processor against " + beanFactory); } this.factoriesPostProcessed.add(factoryId); if (!this.registriesPostProcessed.contains(factoryId)) { // BeanDefinitionRegistryPostProcessor hook apparently not supported... // Simply call processConfigurationClasses lazily at this point then. processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory); } enhanceConfigurationClasses(beanFactory); beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory)); }/** * Build and validate a configuration model based on the registry of * {@link Configuration} classes. */ public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) { //.....省略部分代码// Parse each @Configuration class ConfigurationClassParser parser = new ConfigurationClassParser( this.metadataReaderFactory, this.problemReporter, this.environment, this.resourceLoader, this.componentScanBeanNameGenerator, registry); Set<BeanDefinitionHolder> candidates = new LinkedHashSet<BeanDefinitionHolder>(configCandidates); Set<ConfigurationClass> alreadyParsed = new HashSet<ConfigurationClass>(configCandidates.size()); do { parser.parse(candidates); parser.validate(); Set<ConfigurationClass> configClasses = new LinkedHashSet<ConfigurationClass>(parser.getConfigurationClasses()); configClasses.removeAll(alreadyParsed); // Read the model and create bean definitions based on its content if (this.reader == null) { this.reader = new ConfigurationClassBeanDefinitionReader( registry, this.sourceExtractor, this.resourceLoader, this.environment, this.importBeanNameGenerator, parser.getImportRegistry()); } this.reader.loadBeanDefinitions(configClasses); alreadyParsed.addAll(configClasses); candidates.clear(); if (registry.getBeanDefinitionCount() > candidateNames.length) { String[] newCandidateNames = registry.getBeanDefinitionNames(); Set<String> oldCandidateNames = new HashSet<String>(Arrays.asList(candidateNames)); Set<String> alreadyParsedClasses = new HashSet<String>(); for (ConfigurationClass configurationClass : alreadyParsed) { alreadyParsedClasses.add(configurationClass.getMetadata().getClassName()); } for (String candidateName : newCandidateNames) { if (!oldCandidateNames.contains(candidateName)) { BeanDefinition bd = registry.getBeanDefinition(candidateName); if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) && !alreadyParsedClasses.contains(bd.getBeanClassName())) { candidates.add(new BeanDefinitionHolder(bd, candidateName)); } } } candidateNames = newCandidateNames; } } while (!candidates.isEmpty()); // ....省略部分代码}其实这里注释已经很清楚了,我们可以清楚的看到解析每一个@ConfigurationClass的关键类是:ConfigurationClassParser,那么我们继续看一看这个类的parse方法:
public void parse(Set<BeanDefinitionHolder> configCandidates) { this.deferredImportSelectors = new LinkedList<DeferredImportSelectorHolder>(); for (BeanDefinitionHolder holder : configCandidates) { BeanDefinition bd = holder.getBeanDefinition(); try { if (bd instanceof AnnotatedBeanDefinition) { parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName()); } else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) { parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName()); } else { parse(bd.getBeanClassName(), holder.getBeanName()); } } catch (BeanDefinitionStoreException ex) { throw ex; } catch (Throwable ex) { throw new BeanDefinitionStoreException( "Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex); } } processDeferredImportSelectors(); }在这里大家留意一下最后一句processDeferredImportSelectors方法,在这里将会对DeferredImportSelector进行处理,这样我们就和AutoConfigurationSelectImporter结合到一起了:
private void processDeferredImportSelectors() { List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors; this.deferredImportSelectors = null; Collections.sort(deferredImports, DEFERRED_IMPORT_COMPARATOR); for (DeferredImportSelectorHolder deferredImport : deferredImports) { ConfigurationClass configClass = deferredImport.getConfigurationClass(); try { String[] imports = deferredImport.getImportSelector().selectImports(configClass.getMetadata()); processImports(configClass, asSourceClass(configClass), asSourceClasses(imports), false); } catch (BeanDefinitionStoreException ex) { throw ex; } catch (Throwable ex) { throw new BeanDefinitionStoreException( "Failed to process import candidates for configuration class [" + configClass.getMetadata().getClassName() + "]", ex); }}}请大家关注这句代码:String[] imports = deferredImport.getImportSelector().selectImports(configClass.getMetadata());在这里deferredImport的类型为DeferredImportSelectorHolder:
private static class DeferredImportSelectorHolder { private final ConfigurationClass configurationClass; private final DeferredImportSelector importSelector; public DeferredImportSelectorHolder(ConfigurationClass configClass, DeferredImportSelector selector) { this.configurationClass = configClass; this.importSelector = selector; } public ConfigurationClass getConfigurationClass() { return this.configurationClass; } public DeferredImportSelector getImportSelector() { return this.importSelector; }}在这个内部类里持有了一个DeferredImportSelector的引用,至此将会执行自动装配的所有操作
1)自动装配还是利用了SpringFactoriesLoader来加载META-INF/spring.factoires文件里所有配置的EnableAutoConfgruation,它会经过exclude和filter等操作,最终确定要装配的类
2) 处理@Configuration的核心还是ConfigurationClassPostProcessor,这个类实现了BeanFactoryPostProcessor, 因此当AbstractApplicationContext执行refresh方法里的invokeBeanFactoryPostProcessors(beanFactory)方法时会执行自动装配
The above is the automatic assembly in SpringBoot introduced to you by the editor. Saya harap ini akan membantu Anda. Jika Anda memiliki pertanyaan, silakan tinggalkan saya pesan dan editor akan membalas Anda tepat waktu!