แอสเซมบลีอัตโนมัติของ Springboot เป็นพื้นฐานสำหรับการยกเลิกการบ็อกซ์และข้อกำหนดเบื้องต้นสำหรับ Microservices หัวข้อหลักในครั้งนี้คือการดูว่ามีการใช้งานอย่างไร เราใช้ซอร์สโค้ดเพื่อเข้าใจการประกอบแบบอัตโนมัติ
1.1. เกี่ยวกับ @springbootapplication
เมื่อเราเขียนโครงการ Springboot @SpringBootapplication เป็นคำอธิบายประกอบที่พบบ่อยที่สุด เราสามารถดูซอร์สโค้ด:
/ * * ลิขสิทธิ์ 2012-2017 ผู้แต่งหรือผู้แต่งดั้งเดิม * * ได้รับอนุญาตภายใต้ใบอนุญาต Apache เวอร์ชัน 2.0 ("ใบอนุญาต"); * คุณไม่สามารถใช้ไฟล์นี้ยกเว้นตามใบอนุญาต * คุณอาจได้รับสำเนาใบอนุญาตที่ * * http://www.apache.org/licenses/license-2.0 * * เว้นแต่ว่ากฎหมายที่บังคับใช้หรือตกลงที่จะเป็นลายลักษณ์อักษรซอฟต์แวร์ * แจกจ่ายภายใต้ใบอนุญาตจะถูกแจกจ่ายใน "ตาม" โดยไม่มีการรับประกันหรือเงื่อนไขใด ๆ * ดูใบอนุญาตสำหรับการอนุญาตการกำกับดูแลภาษาเฉพาะและ * ข้อ จำกัด ภายใต้ใบอนุญาต */แพ็คเกจ org.springframework.boot.autoconfigure; นำเข้า java.lang.annotation.documented; นำเข้า java.lang.annotation.elementtype; นำเข้า Java.lang.annotation.inherited; java.lang.annotation.target; นำเข้า org.springframework.boot.springbootconfiguration; นำเข้า org.springframework.boot.context.typeexcludefilter; นำเข้า org.springframework.context.annotation.bean.bean; org.springframework.context.annotation.componentscan.filter; นำเข้า org.springframework.context.annotation.configuration; นำเข้า org.springframework.context.annotation.filtertype; Import org.springframework การกำหนดค่า} คลาสที่ประกาศหนึ่งหรือมากกว่าหนึ่ง * {@link bean @bean} วิธีการและทริกเกอร์ {@link enableautoconfiguration * การกำหนดค่าอัตโนมัติ} และ {@link Componentscan scanning} นี่คือความสะดวก * คำอธิบายประกอบที่เทียบเท่ากับการประกาศ {@code @configuration}, * {@code @enableautoconfiguration} และ {@code @componentscan} * * @author Phillip Webb * @author Stephane Nicoll * @Since 1.2.0 */@Target (ElementType.type) @retention (RETINGIONPICALY.RUNTIME)@DOCERITED@มรดก@SpringBOOTCONFIGURATION@enableAutoconfiguration TypeExCludeFilter.class), @Filter (type = filterType.custom, คลาส = autoconfigurationExcludeFilter.class)}) สาธารณะ @interface Springbootapplication { /*** ไม่รวมคลาสการกำหนดค่าอัตโนมัติที่เฉพาะเจาะจงเช่นนั้นจะไม่ถูกนำไปใช้ * @return คลาสที่จะยกเว้น */ @aliasfor (คำอธิบายประกอบ = enableAutoconFiguration.class, attribute = "exclude") คลาส <?> [] ยกเว้น () ค่าเริ่มต้น {}; /** * ไม่รวมชื่อคลาสการกำหนดค่าอัตโนมัติที่เฉพาะเจาะจงซึ่งจะไม่ถูกนำไปใช้ * * @return ชื่อคลาสเพื่อยกเว้น * @Since 1.3.0 */ @Aliasfor (คำอธิบายประกอบ = enableAutoconFiguration.class, attribute = "excludename") สตริง [] excludename () เริ่มต้น {}; /*** แพ็คเกจพื้นฐานเพื่อสแกนส่วนประกอบที่มีคำอธิบายประกอบ ใช้ {@link #scanbasepackageclasses} * สำหรับทางเลือกที่ปลอดภัยสำหรับชื่อแพ็คเกจที่ใช้สตริง * @return แพ็คเกจฐานเพื่อสแกน * @Since 1.3.0 */ @Aliasfor (คำอธิบายประกอบ = componentScan.class, attribute = "basepackages") สตริง [] scanbasepackages () ค่าเริ่มต้น {}; /** * ทางเลือกประเภทที่ปลอดภัยสำหรับ {@link #ScanBasePackages} สำหรับการระบุแพ็คเกจเพื่อ * สแกนสำหรับส่วนประกอบที่มีคำอธิบายประกอบ แพ็คเกจของแต่ละคลาสที่ระบุจะถูกสแกน * <p> * พิจารณาการสร้างคลาส Marker No-op พิเศษหรืออินเทอร์เฟซในแต่ละแพ็คเกจที่ * ไม่มีวัตถุประสงค์นอกเหนือจากการอ้างอิงโดยแอตทริบิวต์นี้ * @return แพ็คเกจฐานเพื่อสแกน * @Since 1.3.0 */ @Aliasfor (คำอธิบายประกอบ = ComponentScan.class, attribute = "basepackageclasses") คลาส <?> [] scanbasepackageClasses () เริ่มต้น {};};};สิ่งนี้มี @springbootconfiguration, @enableautoconfiguration, @componentscan ที่นี่เนื่องจากไม่มีการระบุแพ็คเกจการสแกนจึงสแกนคลาสทั้งหมดภายใต้ระดับเดียวกับคลาสหรือแพ็คเกจระดับเดียวกันโดยค่าเริ่มต้น นอกจากนี้ @springbootconfiguration คุณสามารถค้นหาผ่านซอร์สโค้ดว่าเป็น @configuration:
/ * * ลิขสิทธิ์ 2012-2016 ผู้แต่งหรือผู้แต่งดั้งเดิม * * ได้รับอนุญาตภายใต้ใบอนุญาต Apache เวอร์ชัน 2.0 ("ใบอนุญาต"); * คุณไม่สามารถใช้ไฟล์นี้ยกเว้นตามใบอนุญาต * คุณอาจได้รับสำเนาใบอนุญาตที่ * * http://www.apache.org/licenses/license-2.0 * * เว้นแต่ว่ากฎหมายที่บังคับใช้หรือตกลงที่จะเป็นลายลักษณ์อักษรซอฟต์แวร์ * แจกจ่ายภายใต้ใบอนุญาตจะถูกแจกจ่ายใน "ตาม" โดยไม่มีการรับประกันหรือเงื่อนไขใด ๆ * ดูใบอนุญาตสำหรับการอนุญาตการกำกับดูแลภาษาเฉพาะและ * ข้อ จำกัด ภายใต้ใบอนุญาต */แพ็คเกจ org.springframework.boot; นำเข้า java.lang.annotation.documented; นำเข้า java.lang.annotation.ElementType; นำเข้า java.lang.annotation.retention org.springframework.context.annotation.configuration;/** * ระบุว่าคลาสมีแอปพลิเคชันสปริงบูต * {@link การกำหนดค่า @configuration} สามารถใช้เป็นทางเลือกแทน * มาตรฐาน {@code @configuration} คำอธิบายประกอบเพื่อให้สามารถพบการกำหนดค่า * โดยอัตโนมัติ (ตัวอย่างเช่นในการทดสอบ) * <p> * แอปพลิเคชันควรรวม <em> หนึ่ง </em> {@code @springbootconfiguration} และ * แอปพลิเคชันการบูตสปริงที่มีสำนวนมากที่สุดจะสืบทอดจาก * {@code @springbootapplication} * * @author Phillip Webb * @Since 1.4.0 */@Target (ElementType.type) @retentionจากนี้เราสามารถอนุมานได้ว่า @springbootapplication นั้นเทียบเท่ากับ @configuration @componentscan @enableautoconfiguration
1.2. @enableautoconfiguration
เมื่อเพิ่มคำอธิบายประกอบนี้แล้วฟังก์ชั่นแอสเซมบลีอัตโนมัติจะเปิดใช้งาน ในการพูดง่าย ๆ สปริงจะพยายามค้นหาถั่วที่กำหนดค่าทั้งหมดภายใต้ ClassPath ของคุณแล้วประกอบพวกเขา แน่นอนเมื่อประกอบถั่วมันจะเริ่มต้นตามกฎการปรับแต่ง (เงื่อนไข) หลายข้อ มาดูซอร์สโค้ดกันเถอะ:
/ * * ลิขสิทธิ์ 2012-2017 ผู้แต่งหรือผู้แต่งดั้งเดิม * * ได้รับอนุญาตภายใต้ใบอนุญาต Apache เวอร์ชัน 2.0 ("ใบอนุญาต"); * คุณไม่สามารถใช้ไฟล์นี้ยกเว้นตามใบอนุญาต * คุณอาจได้รับสำเนาใบอนุญาตที่ * * http://www.apache.org/licenses/license-2.0 * * เว้นแต่ว่ากฎหมายที่บังคับใช้หรือตกลงที่จะเป็นลายลักษณ์อักษรซอฟต์แวร์ * แจกจ่ายภายใต้ใบอนุญาตจะถูกแจกจ่ายใน "ตาม" โดยไม่มีการรับประกันหรือเงื่อนไขใด ๆ * ดูใบอนุญาตสำหรับการอนุญาตการกำกับดูแลภาษาเฉพาะและ * ข้อ จำกัด ภายใต้ใบอนุญาต */แพ็คเกจ org.springframework.boot.autoconfigure; นำเข้า java.lang.annotation.documented; นำเข้า java.lang.annotation.elementtype; นำเข้า Java.lang.annotation.inherited; java.lang.annotation.target; นำเข้า org.springframework.boot.autoconfigure.condition.conditionalonbean; นำเข้า org.springframework.boot.autoconfigure.conditionalonbean; org.springframework.boot.autoconfigure.condition.conditionalonmissingbean; นำเข้า org.springframework.boot.context.embedded.embeddedservletContainerFactory; org.springframework.context.annotation.conditional; นำเข้า org.springframework.context.annotation.configuration; นำเข้า org.springframework.context.annotation.import; พยายามเดาและกำหนดค่าถั่วที่คุณต้องการ คลาสการกำหนดค่าอัตโนมัติมักใช้ * ใช้ตาม classpath ของคุณและถั่วที่คุณกำหนดไว้ ตัวอย่างเช่นถ้าคุณ * มี {@code tomcat-embedded.jar} บน classpath ของคุณคุณมีแนวโน้มที่จะต้องการ * {@link tomcatembeddedservletContainerFactory} (เว้นแต่คุณจะกำหนด * {@link EmbeddedServletContainerForfortory} * <p> * เมื่อใช้ {@link springbootapplication} การกำหนดค่าอัตโนมัติของบริบทคือ * เปิดใช้งานโดยอัตโนมัติและการเพิ่มคำอธิบายประกอบนี้จึงไม่มีผลเพิ่มเติม * <p> * การกำหนดค่าอัตโนมัติพยายามที่จะฉลาดที่สุดเท่าที่จะเป็นไปได้และจะย้อนกลับไปในขณะที่คุณ * กำหนดค่าของคุณเองมากขึ้น คุณสามารถ {@link #ExClude ()} การกำหนดค่า * ใด ๆ ที่คุณไม่ต้องการใช้ (ใช้ {@link #ExCludEname ()} หากคุณไม่สามารถเข้าถึงได้) นอกจากนี้คุณยังสามารถยกเว้นพวกเขาผ่านคุณสมบัติ * {@code spring.autoconfigure.exclude} การกำหนดค่าอัตโนมัติจะถูกนำไปใช้เสมอ * หลังจากลงทะเบียนถั่วที่ผู้ใช้กำหนดแล้ว * <p> * แพ็คเกจของคลาสที่มีคำอธิบายประกอบด้วย {@code @enableautoconfiguration}, * โดยปกติผ่าน {@code @springbootapplication} มีนัยสำคัญเฉพาะและมักจะใช้เป็น 'เริ่มต้น' ตัวอย่างเช่นมันจะถูกใช้เมื่อสแกนสำหรับคลาส {@code @Entity} * โดยทั่วไปขอแนะนำให้คุณวาง {@code @enableautoconfiguration} (ถ้าคุณไม่ได้ใช้ {@code @springbootapplication}) ในแพ็คเกจรูทเพื่อให้สามารถค้นหาแพคเกจย่อย * และคลาสทั้งหมดได้ * <p> * คลาสการกำหนดค่าอัตโนมัติเป็นฤดูใบไม้ผลิปกติ {@link การกำหนดค่า} ถั่ว พวกเขา * ตั้งอยู่โดยใช้กลไก {@link SpringFactoriesLoader} (คีย์กับคลาสนี้) * โดยทั่วไปแล้วถั่วการกำหนดค่าอัตโนมัติคือ {@link เงื่อนไข @Conditional} ถั่ว (ส่วนใหญ่ * มักจะใช้ {@link เงื่อนไข @conditionalonclass} และ * {@link conitionalonmissingbean @conditionalonmissingbean} คำอธิบายประกอบ) * * @author Phillip Webb * @author Stephane Nicoll * @See ConditionalOnBean * @See ConditionalOnMissingBean * @SEE CONDIONALONCLASS * @See AutoconFigurefter * @See SpringBootApplication */@suppresswarnings ("deprecation")@target (ElementType.type) @retention (RetentionPolicy.runtime)@documented@สืบทอด@autoconfigurationPackage@import "Spring.boot.enableautoconfiguration"; /*** ไม่รวมคลาสการกำหนดค่าอัตโนมัติที่เฉพาะเจาะจงซึ่งจะไม่ถูกนำไปใช้ * @return คลาสที่จะยกเว้น */ คลาส <?> [] ยกเว้น () ค่าเริ่มต้น {}; /** * ไม่รวมชื่อคลาสการกำหนดค่าอัตโนมัติที่เฉพาะเจาะจงซึ่งจะไม่ถูกนำไปใช้ * * @return ชื่อคลาสเพื่อยกเว้น * @since 1.3.0 */ string [] excludename () ค่าเริ่มต้น {};}แม้ว่าจะเป็นไปตามความคิดเห็นของเอกสาร แต่ก็แนะนำให้เราดู EnableAutoconFigurationImportSelector แต่คลาสนั้นล้าสมัยในเวอร์ชัน Springboot1.5.x ดังนั้นลองมาดูที่คลาสแม่ของ AutoConfigurationImportSelector:
/ * * ลิขสิทธิ์ 2012-2017 ผู้แต่งหรือผู้แต่งดั้งเดิม * * ได้รับอนุญาตภายใต้ใบอนุญาต Apache เวอร์ชัน 2.0 ("ใบอนุญาต"); * คุณไม่สามารถใช้ไฟล์นี้ยกเว้นตามใบอนุญาต * คุณอาจได้รับสำเนาใบอนุญาตที่ * * http://www.apache.org/licenses/license-2.0 * * เว้นแต่ว่ากฎหมายที่บังคับใช้หรือตกลงที่จะเป็นลายลักษณ์อักษรซอฟต์แวร์ * แจกจ่ายภายใต้ใบอนุญาตจะถูกแจกจ่ายใน "ตาม" โดยไม่มีการรับประกันหรือเงื่อนไขใด ๆ * ดูใบอนุญาตสำหรับภาษาเฉพาะที่ควบคุมการอนุญาตและ * ข้อ จำกัด ภายใต้ใบอนุญาต */แพ็คเกจ org.springframework.boot.autoconfigure; นำเข้า java.io.ioexception; นำเข้า java.util.arraylist; นำเข้า java.util.arrays; นำเข้า java.util.collections; java.util.map; นำเข้า java.util.set; นำเข้า java.util.concurrent.timeUnit; นำเข้า org.apache.commons.logging.log; นำเข้า org.apache.Commons.logging.logfactory; org.springframework.beans.factory.beanclassloaderaware นำเข้า org.springframework.beans.factory.beanfactory; นำเข้า org.springframework.beans.factory.beanfactoryaware; นำเข้า org.springframework.beans.beans.beans org.springframework.beans.factory.config.configurablelistablebeanfactory นำเข้า org.springframework.boot.bind.relaxedpropertyresolver; นำเข้า org.springframework.context.environame org.springframework.context.annotation.deferredimportlector; นำเข้า org.springframework.core.ordered; นำเข้า org.springframework.core.annotation.annotationattributes; org.springframework.core.env.environment; นำเข้า org.springframework.core.io.resourceloader; นำเข้า org.springframework.core.io.support.springfactoryload; org.springframework.core.type.classreading.cachingmetadatareaderfactory นำเข้า org.springframework.core.type.classreading.metadatareaderfactory; นำเข้า org.springframework.util.assert; org.springframework.util.stringutils;/** * {@link deferredimportlector} เพื่อจัดการ {@link enableautoconfiguration * การกำหนดค่าอัตโนมัติ} คลาสนี้ยังสามารถ subclassed หากตัวแปรที่กำหนดเองของ * {@link enableautoconfiguration @enableautoconfiguration} เป็นสิ่งจำเป็น * * @author Phillip Webb * @author Andy Wilkinson * @author Stephane Nicoll * @author Madhura Bhave * @Since 1.3.0 * @See ENABLEAUTOCONFIGURATION */การจัดระดับการทำงาน {สตริงสุดท้ายคงที่ส่วนตัว [] no_imports = {}; logger log สุดท้ายคงที่ส่วนตัว = logfactory .getLog (AutoConFigurationImportSelector.class); Private ConfigurableListableBeanFactory beanfactory; สภาพแวดล้อมภาคเอกชน classloader ส่วนตัว beanclassloader; ResourceLoader ส่วนตัว @Override สตริงสาธารณะ [] SelectImports (AnnotationMetadata AnnotationMetadata) {ถ้า (! isenabled (AnnotationMetadata)) {return no_imports; } ลอง {autoconfigurationMetadata autoconfigurationMetadata = autoconfigurationMetAdataloader .loadMetAdata (this.beanclassloader); AnnotationAttributes แอตทริบิวต์ = getAttributes (AnnotationMetadata); รายการ <String> การกำหนดค่า = getCandidateConfigurations (AnnotationMetadata, แอตทริบิวต์); การกำหนดค่า = RemovedUplicates (การกำหนดค่า); การกำหนดค่า = sort (การกำหนดค่า, autoconfigurationMetadata); ตั้งค่า <string> การยกเว้น = getExclusions (AnnotationMetadata, แอตทริบิวต์); checkexcludedclasses (การกำหนดค่า, ยกเว้น); configurations.removeall (การยกเว้น); การกำหนดค่า = ตัวกรอง (การกำหนดค่า, autoconfigurationMetadata); FireAutoconfigurationImportEvents (การกำหนดค่า, ยกเว้น); ส่งคืนการกำหนดค่า toArray (สตริงใหม่ [configurations.size ()]); } catch (ioexception ex) {โยนใหม่ unlilmandalstateException (ex); }} บูลีนที่ได้รับการป้องกัน isenabled (คำอธิบายประกอบเมตาดาต้า) {return true; } /** * ส่งคืน {@link AnnotationAttributes} จาก * {@link AnnotationMetadata} โดยค่าเริ่มต้นวิธีนี้จะส่งคืนแอตทริบิวต์สำหรับ * {@link #GetAnnotationClass ()} * @param metadata คำอธิบายประกอบข้อมูลเมตาคำอธิบายประกอบ * @@return Annotation แอตทริบิวต์ */ คำอธิบายประกอบที่ได้รับการป้องกัน GetAttributes (คำอธิบายประกอบเมตาดาต้า) {ชื่อสตริง = getAnnotationClass () getName (); AnnotationAttributes แอตทริบิวต์ = คำอธิบายประกอบ annotationAttributes .frommap (metadata.getannotationattributes (ชื่อ, จริง)); assert.notnull (แอตทริบิวต์ "ไม่พบแอตทริบิวต์การกำหนดค่าอัตโนมัติที่พบคือ" + metadata.getClassName () + "คำอธิบายประกอบด้วย" + classutils.getShortname (ชื่อ) + "?"); คุณลักษณะการส่งคืน; } /*** ส่งคืนคลาสคำอธิบายประกอบที่ใช้โดยตัวเลือกที่ใช้ * @return คลาสคำอธิบายประกอบ */ คลาสที่ได้รับการป้องกัน <?> getAnnotationClass () {return enableAutoconFiguration.class; } /*** ส่งคืนชื่อคลาสการกำหนดค่าอัตโนมัติที่ควรพิจารณา โดยค่าเริ่มต้น * วิธีนี้จะโหลดผู้สมัครโดยใช้ {@link SpringFactoriesLoader} ด้วย * {@link #GetSpringFringFortoriesLoaderFactoryClass ()} * @param metadata แหล่งข้อมูลเมตา * @param แอตทริบิวต์ {@link #getattributes (AnnotationMetadata) คำอธิบายประกอบ * แอตทริบิวต์} * @@Return รายการการกำหนดค่าผู้สมัคร */ รายการที่ได้รับการป้องกัน = SpringFactoriesLoader.loadFactoryNames (getSpringFactoriesLoaderFactoryClass (), getBeanClassloader ()); assert.notEmpty (การกำหนดค่า "ไม่พบคลาสการกำหนดค่าอัตโนมัติใน meta-inf/spring.factories ถ้าคุณ" + "ใช้บรรจุภัณฑ์ที่กำหนดเองตรวจสอบให้แน่ใจว่าไฟล์นั้นถูกต้อง"); ส่งคืนการกำหนดค่า; } /** * ส่งคืนคลาสที่ใช้โดย {@link SpringFactoriesLoader} เพื่อโหลดการกำหนดค่า * ผู้สมัคร * @return คลาสโรงงาน */ คลาสที่ได้รับการป้องกัน <?> getSpringFringFringFringFringFerFactoryClass () {return enableAutoconFiguration.class; } โมฆะส่วนตัว checkexCludedClasses (รายการ <String> การกำหนดค่าตั้งค่า <string> exclus) {รายการ <String> InvalIdeXCludes = new ArrayList <String> (ยกเว้น Size ()); สำหรับ (String exclus: exclus) {if (classutils.ispresent (การยกเว้น, getClass (). getClassLoader ()) &&! configurations.contains (การยกเว้น)) {invalidexcludes.add (ยกเว้น); }} if (! invalidexcludes.isEmpty ()) {handleinvalidexcludes (InvalidexCludes); }} /*** จัดการไม่ถูกต้องใด ๆ ที่ไม่ถูกต้องที่ระบุไว้ * @param InvalidexCludes รายการของไม่ถูกต้องยกเว้น (จะมีองค์ประกอบอย่างน้อยหนึ่ง * หนึ่ง *) */ void handleding ที่ได้รับการป้องกันเสมอ สำหรับ (String exclude: InvalidexCludes) {message.append ("/t-") .append (ยกเว้น) .append (string.format ("%n")); } โยนใหม่ unglemalstateException (String. format ("คลาสต่อไปนี้ไม่สามารถยกเว้นได้เพราะพวกเขาคือ" + "คลาสการกำหนดค่าอัตโนมัติ:%n%s", ข้อความ)); } /*** ส่งคืนการยกเว้นใด ๆ ที่ จำกัด การกำหนดค่าผู้สมัคร * @param metadata แหล่งข้อมูลเมตา * @param แอตทริบิวต์ {@link #getattributes (AnnotationMetadata) คำอธิบายประกอบ * แอตทริบิวต์} * @return ยกเว้นเซต */ ชุดที่ไม่มีการป้องกัน LinkedHashSet <String> (); ไม่รวม. addall (aslist (แอตทริบิวต์, "ไม่รวม")); ไม่รวม. addall (array.aslist (attributes.getStringArray ("excludename"))); ไม่รวม. addall (getExcludeautoconfigurationsProperty ()); ไม่รวมกลับมา; } รายการส่วนตัว <String> getExCludeAutoconFigurationsProperty () {ถ้า (getenvironment () อินสแตนซ์ของการกำหนดค่าที่กำหนดค่าได้) {RelaxedPropertyResolver Resolver = ใหม่ RelaxedPropertyResolver แผนที่ <string, Object> properties = resolver.getSubProperties ("ยกเว้น"); if (properties.isempty ()) {return collections.empylist (); } รายการ <String> excludes = new ArrayList <String> (); สำหรับ (map.entry <string, Object> รายการ: properties.entrySet ()) {string name = entry.getKey (); ค่าวัตถุ = entry.getValue (); if (name.isempty () || name.startswith ("[") && value! = null) {excludes.addall (ใหม่ hashset <string> (array.aslist (stringutils .tokenizetoStringArray }} return ไม่รวม; } RelaxedPropertyResolver Resolver = ใหม่ RelaxedPropertyResolver (Getenvironment (), "Spring.autoconfigure"); String [] exclude = Resolver.getProperty ("exclude", string []. class); return (array.aslist (exclude == null? สตริงใหม่ [0]: ยกเว้น)); } รายการส่วนตัว <String> เรียงลำดับ (รายการ <String> การกำหนดค่า, autoconfigurationMetadata autoconfigurationMetadata) พ่น IOException {การกำหนดค่า = ใหม่ autoconfigurationsorter ส่งคืนการกำหนดค่า; } รายการส่วนตัว <String> ตัวกรอง (รายการ <String> การกำหนดค่า, AutoCONFigurationMetAdata autoconFigurationMetAdata) {Long StartTime = System.Nanotime (); สตริง [] ผู้สมัคร = การกำหนดค่า toArray (สตริงใหม่ [configurations.size ()]); บูลีน [] skip = บูลีนใหม่ [ผู้สมัครความยาว]; บูลีนข้าม = false; สำหรับ (AutoconFigurationImportFilter ตัวกรอง: getAutoconFigurationImportFilters ()) {InvokeAwareMethods (ตัวกรอง); บูลีน [] match = filter.match (ผู้สมัคร, autoconfigurationmetadata); สำหรับ (int i = 0; i <match.length; i ++) {if (! match [i]) {skip [i] = true; ข้าม = true; }}}} if (! ข้าม) {return configurations; } รายการ <String> result = new ArrayList <String> (ผู้สมัครความยาว); สำหรับ (int i = 0; i <candidates.length; i ++) {ถ้า (! ข้าม [i]) {result.add (ผู้สมัคร [i]); }} if (logger.istraceenabled ()) {int numberFiltered = configurations.size () - result.size (); logger.trace ("กรอง" + numberfiltered + "คลาสการกำหนดค่าอัตโนมัติใน" + timeunit.nanoseconds.tomillis (system.nanotime () - starttime) + "ms"); } ส่งคืน ArrayList ใหม่ <String> (ผลลัพธ์); } รายการที่ได้รับการป้องกัน <AutoconFigurationImportFilter> getAutoconFigurationImportFilters () {ส่งคืน SpringFactoriesLoad.load.loadFactories (AutoconFigurationImportFilter.class, this.beanclassloader); } metadatareaderFactory ส่วนตัว getMetAdatAreaderFactory () {ลอง {return getBeanFactory (). getBean (SharedMetAdatAreaderFactoryContextInitializer.bean_name, metadatareaderfactory.class); } catch (nosuchbeanDefinitionException ex) {ส่งคืนใหม่ CachingMetAdatAreaderFactory (this.resourceLoader); }} ได้รับการป้องกันสุดท้าย <t> รายการ <t> RemovedUplicates (รายการ <t> รายการ) {ส่งคืน ArrayList ใหม่ <T> (ใหม่ LinkedHashSet <T> (รายการ)); } รายการสุดท้ายที่ได้รับการป้องกัน <string> aslist (AnnotationAttributes แอตทริบิวต์ชื่อสตริง) {string [] value = attributes.getStringArray (ชื่อ); return array.aslist (value == null? สตริงใหม่ [0]: ค่า); } โมฆะส่วนตัว FireAutoconFigurationImportEvents (รายการ <String> การกำหนดค่าตั้งค่า <string> excclusions) {list <autoconFigurationImportListener> ผู้ฟัง = getAutoconFigurationImportListeners (); if (! listeners.isEmpty ()) {autoconfigurationImportEvent event = ใหม่ autoconfigurationImportEvent (นี่คือการกำหนดค่า excclusions); สำหรับ (AutoconFigurationImportListener Listener: ผู้ฟัง) {InvokeAwareMethods (ผู้ฟัง); Listener.onautoconfigurationImportEvent (เหตุการณ์); }}} รายการที่ได้รับการป้องกัน <AutoconFigurationImportListener> getAutoconFigurationImportListeners () {return springfactoriesloader.loadFactories (autoconfigurationImportListener.class, this.beanclassloader); } โมฆะส่วนตัว InvokeAwareMethods (อินสแตนซ์วัตถุ) {ถ้า (อินสแตนซ์อินสแตนซ์ของการรับรู้) {ถ้า (อินสแตนซ์อินสแตนซ์ของ beanclassloaderaware) {((beanclassloaderaware) อินสแตนซ์) .setBeanClassloader } if (อินสแตนซ์อินสแตนซ์ของ BeanFactoryAware) {((BeanFactoryAware) อินสแตนซ์) .setBeanFactory (this.BeanFactory); } if (อินสแตนซ์อินสแตนซ์ของสิ่งแวดล้อม evenurnware) {((((สิ่งแวดล้อม) อินสแตนซ์) .setEnvironment (สิ่งนี้อยู่ในสภาพแวดล้อม); } if (อินสแตนซ์อินสแตนซ์ของ ResourceLoaderAware) {((ResourceLoaderAware) อินสแตนซ์) .setResourceLoader (this.resourceLoader); }}} @Override โมฆะสาธารณะ setBeanFactory (BeanFactory BeanFactory) พ่น beansexception {assert.isinstanceof (configurableListableBeanFactory.class, beanfactory); this.beanfactory = (configurableListableBeanFactory) beanfactory; } ได้รับการป้องกันการกำหนดค่าสุดท้ายของการกำหนดค่าความสามารถในการ getBeanFactory () {return this.beanfactory; } @Override โมฆะสาธารณะ setBeanClassLoader (classloader classloader) {this.beanclassloader = classloader; } protected classloader getBeanClassLoader () {return this.beanclassloader; } @Override โมฆะสาธารณะ setEnvironment (สภาพแวดล้อมสภาพแวดล้อม) {this.environment = สิ่งแวดล้อม; } การป้องกันสภาพแวดล้อมสุดท้าย getenvironment () {return this.environment; } @Override โมฆะสาธารณะ setResourceLoader (ResourceLoader ResourceLoader) {this.resourceLoader = ResourceLoader; } ป้องกัน ResourceLoader สุดท้าย getResourceLoader () {return this.resourceLoader; } @Override สาธารณะ int getOrder () {return ordered.lowest_precedence - 1; -ขั้นแรกคลาสนี้ใช้อินเตอร์เฟส deferredimportSelector ซึ่งสืบทอด ImportSelector:
/ * * ลิขสิทธิ์ 2002-2013 ผู้แต่งหรือผู้เขียนดั้งเดิม * * ได้รับอนุญาตภายใต้ใบอนุญาต Apache เวอร์ชัน 2.0 ("ใบอนุญาต"); * คุณไม่สามารถใช้ไฟล์นี้ยกเว้นตามใบอนุญาต * คุณอาจได้รับสำเนาใบอนุญาตที่ * * http://www.apache.org/licenses/license-2.0 * * เว้นแต่ว่ากฎหมายที่บังคับใช้หรือตกลงที่จะเป็นลายลักษณ์อักษรซอฟต์แวร์ * แจกจ่ายภายใต้ใบอนุญาตจะถูกแจกจ่ายใน "ตาม" โดยไม่มีการรับประกันหรือเงื่อนไขใด ๆ * ดูใบอนุญาตสำหรับภาษาเฉพาะที่ควบคุมการอนุญาตและ * ข้อ จำกัด ภายใต้ใบอนุญาต */แพ็คเกจ org.springframework.context.annotation; นำเข้า org.springframework.core.type.annotationMetadata;/** * อินเทอร์เฟซที่จะนำไปใช้ตามประเภทที่กำหนด @ @ @ @link class} * คลาส (es) * * <p> {@link importselector} อาจใช้สิ่งใด ๆ ต่อไปนี้ * {@link org.springframework.beans.factory.ware Aware} อินเตอร์เฟสและวิธีการ * ของพวกเขาจะถูกเรียกก่อนหน้า {@link #selectimports}: * <ul> สิ่งแวดล้อม} </li> * <li> {@link org.springframework.beans.factory.beanfactoryaware beanfactoryaware} </li> * <li> {@link org.springframework.beans.factory.beanclassloaderaware org.springframework.context.resourceloaderaware ResourceLoaderAware} </li> * </ul> * <p> importSelectors มักจะประมวลผลในลักษณะเดียวกับ {@code @import} * annotations DeferredimportSelector} * สำหรับรายละเอียด) * * @author Chris Beams * @Since 3.1 * @See DeferredImportSelector * @See Import * @See ImportBeanDefinitionRegistrar * @See การกำหนดค่า * /อินเตอร์เฟสสาธารณะ importSelector { /** * เลือกและส่งคืนชื่อของคลาส (es) */ String [] SelectImports (AnnotationMetadata ImportingClassMetadata);}อินเทอร์เฟซนี้ส่วนใหญ่ใช้เพื่อนำเข้ารายการการกำหนดค่าของ @Configuration และ DeferredImportSelector เป็นการนำเข้ารอการตัดบัญชีและจะดำเนินการหลังจาก @Configurations ทั้งหมดถูกประมวลผล
ลองดูที่วิธีการ SelectImport ของ AutoConfigurationImportSelector:
@Override สตริงสาธารณะ [] SelectImports (AnnotationMetadata AnnotationMetadata) {ถ้า (! isenabled (AnnotationMetadata)) {return no_imports; } ลอง {autoconfigurationMetadata autoconfigurationMetadata = autoconfigurationMetAdataloader .loadMetAdata (this.beanclassloader); AnnotationAttributes แอตทริบิวต์ = getAttributes (AnnotationMetadata); รายการ <String> การกำหนดค่า = getCandidateConfigurations (AnnotationMetadata, แอตทริบิวต์); การกำหนดค่า = RemovedUplicates (การกำหนดค่า); การกำหนดค่า = sort (การกำหนดค่า, autoconfigurationMetadata); ตั้งค่า <string> excclusions = getExclusions (AnnotationMetadata, แอตทริบิวต์); checkexcludedclasses (การกำหนดค่า, ยกเว้น); configurations.removeall (การยกเว้น); การกำหนดค่า = ตัวกรอง (การกำหนดค่า, autoconfigurationMetadata); FireAutoconfigurationImportEvents (การกำหนดค่า, excclusions); ส่งคืนการกำหนดค่า toArray (สตริงใหม่ [configurations.size ()]); } catch (ioexception ex) {โยนใหม่ unlilmandalstateException (ex); -
ในตอนแรกวิธีนี้จะพิจารณาว่าจะทำการประกอบอัตโนมัติก่อนจากนั้นอ่านคุณสมบัติที่เกี่ยวข้องของข้อมูลเมตาและข้อมูลเมตาจาก Meta-INF/Spring-Autoconfigure-Metadata
/*** ส่งคืนชื่อคลาสการกำหนดค่าอัตโนมัติที่ควรพิจารณา โดยค่าเริ่มต้น * วิธีนี้จะโหลดผู้สมัครโดยใช้ {@link SpringFactoriesLoader} ด้วย * {@link #GetSpringFringFortoriesLoaderFactoryClass ()} * @param metadata แหล่งข้อมูลเมตา * @param แอตทริบิวต์ {@link #getattributes (AnnotationMetadata) คำอธิบายประกอบ * แอตทริบิวต์} * @@Return รายการการกำหนดค่าผู้สมัคร */ รายการที่ได้รับการป้องกัน = SpringFactoriesLoader.loadFactoryNames (getSpringFactoriesLoaderFactoryClass (), getBeanClassloader ()); assert.notEmpty (การกำหนดค่า "ไม่พบคลาสการกำหนดค่าอัตโนมัติใน meta-inf/spring.factories ถ้าคุณ" + "ใช้บรรจุภัณฑ์ที่กำหนดเองตรวจสอบให้แน่ใจว่าไฟล์นั้นถูกต้อง"); ส่งคืนการกำหนดค่า; } /** * ส่งคืนคลาสที่ใช้โดย {@link SpringFactoriesLoader} เพื่อโหลดการกำหนดค่า * ผู้สมัคร * @return คลาสโรงงาน */ คลาสที่ได้รับการป้องกัน <?> getSpringFringFringFringFringFerFactoryClass () {return enableAutoconFiguration.class; -ที่นี่ฉันได้พบกับคนรู้จักเก่าของเรา - SpringFactoryiesloader ซึ่งจะอ่านการกำหนดค่าของ enableautoconfiguration ภายใต้ meta -inf/spring.factories จากนั้นดำเนินการยกเว้นและการกรองเพื่อให้ได้คลาสที่ต้องประกอบ ในที่สุดปล่อยให้ AutoconFigurationImportListener กำหนดค่าภายใต้ META-INF/Spring.Factories ดำเนินการ Event AutoConFigurationImportEvent, รหัสมีดังนี้:
โมฆะส่วนตัว FireAutoconFigurationImportEvents (รายการ <String> การกำหนดค่าตั้งค่า <string> excclusions) {รายการ <autoconfigurationImportListener> ผู้ฟัง = getAutoconFigurationImportListeners (); if (! listeners.isEmpty ()) {autoconfigurationImportEvent event = ใหม่ autoconfigurationImportEvent (นี่คือการกำหนดค่า excclusions); สำหรับ (AutoconFigurationImportListener Listener: ผู้ฟัง) {InvokeAwareMethods (ผู้ฟัง); Listener.onautoconfigurationImportEvent (เหตุการณ์); }}} รายการที่ได้รับการป้องกัน <AutoconFigurationImportListener> getAutoconFigurationImportListeners () {return springfactoriesloader.loadFactories (autoconfigurationImportListener.class, this.beanclassloader); -ในลิงค์ก่อนหน้านี้เราจำเป็นต้องพิจารณาว่าจะต้องประกอบคลาสใดและเมื่อใดที่คลาสที่ประกอบขึ้นโดยอัตโนมัติจะถูกประมวลผลใน Springboot? มาวิเคราะห์สั้น ๆ :
2.1. วิธีการรีเฟรชของ AbstractApplicationContext:
วิธีนี้เป็นความคิดโบราณ โปรดให้ความสนใจกับวิธีนี้:
// เรียกใช้โปรเซสเซอร์จากโรงงานที่ลงทะเบียนเป็นถั่วในบริบท InvokeBeanFactoryPostProcessors (BeanFactory);
นี่คือกระบวนการของ beanfactorypostprocessor ดังนั้นลองมาดูอินเทอร์เฟซนี้ beandefinitionRegistryRegistryPostProcessor:
/ * * ลิขสิทธิ์ 2002-2010 ผู้แต่งหรือผู้แต่งดั้งเดิม * * ได้รับอนุญาตภายใต้ใบอนุญาต Apache เวอร์ชัน 2.0 ("ใบอนุญาต"); * คุณไม่สามารถใช้ไฟล์นี้ยกเว้นตามใบอนุญาต * คุณอาจได้รับสำเนาใบอนุญาตที่ * * http://www.apache.org/licenses/license-2.0 * * เว้นแต่ว่ากฎหมายที่บังคับใช้หรือตกลงที่จะเป็นลายลักษณ์อักษรซอฟต์แวร์ * แจกจ่ายภายใต้ใบอนุญาตจะถูกแจกจ่ายใน "ตาม" โดยไม่มีการรับประกันหรือเงื่อนไขใด ๆ * ดูใบอนุญาตสำหรับภาษาเฉพาะที่ควบคุมการอนุญาตและ * ข้อ จำกัด ภายใต้ใบอนุญาต */package org.springframework.beans.factory.support;import org.springframework.beans.BeansException;import org.springframework.beans.factory.config.BeanFactoryPostProcessor;/** * Extension to the standard {@link BeanFactoryPostProcessor} SPI, allowing for * the registration of further 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 类
该类主要处理@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. ฉันหวังว่ามันจะเป็นประโยชน์กับคุณ หากคุณมีคำถามใด ๆ โปรดฝากข้อความถึงฉันและบรรณาธิการจะตอบกลับคุณทันเวลา!