精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

五分鐘說清楚 Spring Boot的自動配置原理

開發 架構
Spring Boot沒有火起來之前,使用SSM架構的項目那是相當的多,現在也有不少項目還是使用這種架構。

[[381634]]

前言

Spring Boot沒有火起來之前,使用SSM架構的項目那是相當的多,現在也有不少項目還是使用這種架構。在使用SSM架構的時候,大家是否還記得大量配置的煩惱郁悶,各種配置,搞得人都不是很爽。各種配置掃描,如果想添加一個新的依賴,還得添加各種配置。這種大量配置的工作不進浪費時間,最主要的是會產生各種坑。

自從有了 Spring Boot 之后,咱們就爽爽的!各種零配置開箱即用,而我們之所以開發起來能夠這么爽,自動配置的功勞少不了,今天我們就一起來討論一下 SpringBoot 自動配置原理。

快速了解 SpringBoot 源碼常用注解

我們先對相關基本的注解進行說明,熟悉了這些注解,有利于我們后面更好的閱讀源碼。只要搞清楚了這些注解,遠嗎也就變得沒那么難了。加油!少年~

組合注解

當可能大量同時使用到幾個注解到同一個類上,就可以考慮將這幾個注解到別的注解上。被注解的注解我們就稱之為組合注解。

  • 元注解:可以注解到別的注解上的注解。
  • 組合注解:被注解的注解我們就稱之為組合注解。

@Value

@Value注解有Spring提供,并非是Spring Boot中的,該注解存在于spring-beans.jar中。

  1. @Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE}) 
  2. @Retention(RetentionPolicy.RUNTIME) 
  3. @Documented 
  4. public @interface Value { 
  5.  /** 
  6.   * The actual value expression: for example {@code #{systemProperties.myProp}}. 
  7.   * 比如我們配置項address=ZhongguoGuizhou,這里的value=address 
  8.   */ 
  9.  String value(); 

@Value也相當于傳統 xml 配置文件中的 value 字段。

假設存在代碼:

  1. @Component  
  2. public class Person {  
  3.  
  4. @Value("i am name")  
  5. private String name;  
  6.  

上面代碼等價于的配置文件:

  1. <bean class="Person">  
  2.   <property name ="name" value="i am name"></property> 
  3. </bean>  

我們知道配置文件中的 value 的取值可以是:

  • 字面量
  • 通過 ${key} 方式從環境變量中獲取值
  • 通過 ${key} 方式全局配置文件中獲取值
  • #{SpEL}

所以,我們就可以通過 @Value(${key}) 的方式獲取全局配置文件中的指定配置項。

使用@Value有三個缺點:

  • 配置屬性不統一,沒有結構。
  • 注入麻煩每個屬性都要寫配置名,和屬性名。(只要有重復的工作,就應該重構)
  • 配置零散在項目中各處

@ConfigurationProperties 注解

該注解有Spring Boot提供,在spring-boot.jar包中

org.springframework.boot.context.properties;目錄下:

  1. @Target({ElementType.TYPE, ElementType.METHOD}) 
  2. @Retention(RetentionPolicy.RUNTIME) 
  3. @Documented 
  4. public @interface ConfigurationProperties { 
  5.     //前綴 
  6.     @AliasFor("prefix"
  7.     String value() default ""
  8.  
  9.     @AliasFor("value"
  10.     String prefix() default "";  
  11.     //... 

如果我們要去獲取很多配置項,比如:賬號、密碼、地址等一堆配置項時,如果我們還是用@Value得一個一個去獲取配置項,是不是覺得很low呀,所以這時候我們就可以考慮使用@ConfigurationProperties。

標有 @ConfigurationProperties 的類的所有屬性和配置文件中相關的配置項進行綁定。(默認從全局配置文件中獲取配置值),綁定之后我們就可以通過這個類去訪問全局配置文件中的屬性值了。

下面看一個實例:

第1步:在主配置文件中添加如下配置

  1. pay.account=java后端技術全棧  
  2. pay.password=tj20120622 
  3. pay.url=http://woaijava.cc 

第2步:創建配置類,由于篇幅問題這里省略了 setter、getter 方法,但是實際開發中這個是必須的,否則無法成功注入。另外,@Component 這個注解也還是需要添加的。

  1. @Component  
  2.     @ConfigurationProperties(prefix = "pay")  
  3.     public class PayInfo {  
  4.      
  5.     private String account;  
  6.     private Integer password;  
  7.     private String url;  
  8.      
  9.     }  

這里 @ConfigurationProperties 有一個 prefix 參數,主要是用來指定該配置項在配置文件中的前綴。

第3步:測試,在 Spring Boot 環境中,編寫個測試方法,注入PayInfo類,即可通過 PayInfo對象取到配置文件的值。

@Import 【Spring 提供】

@Import 是由Spring提供的注解,支持導入普通 java 類,并將其聲明成一個bean。主要用于將多個分散的 java config 配置類融合成一個更大的 config 類。

  • @Import 注解在 4.2 之前只支持導入配置類。
  • 在4.2之后 @Import 注解支持導入普通的 java 類,并將其聲明成一個 bean。

@Import 三種使用方式

  • 直接導入普通的 Java 類。
  • 配合自定義的 ImportSelector 使用。
  • 配合 ImportBeanDefinitionRegistrar 使用。

第一種方式:直接導入普通的 Java 類

第1步:創建一個普通的 Java 類。

  1. public class Circle {  
  2.      public void sayHi() {  
  3.        System.out.println("Circle sayHi()");  
  4.      }  
  5.    }  

第2步:創建一個配置類,里面沒有顯式聲明任何的 Bean,然后將剛才創建的 Circle 導入。

  1. @Import({Circle.class})  
  2.     @Configuration  
  3.     public class MainConfig {  
  4.      
  5.     }  

第3步:創建測試類。

  1. public static void main(String[] args) {  
  2.     
  3.    ApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);  
  4.    Circle circle = context.getBean(Circle.class);  
  5.    circle.sayHi();  
  6.     
  7.    }  

第4步:運行結果:

  1. Circle sayHi() 

可以看到我們順利的從 IOC 容器中獲取到了 Circle 對象,證明我們在配置類中導入的 Circle 類,確實被聲明為了一個 Bean。

第二種方式:配合自定義的 ImportSelector 使用

ImportSelector 是一個接口,該接口中只有一個 selectImports 方法,用于返回全類名數組。所以利用該特性我們可以給容器動態導入 N 個 Bean。

第1步:創建普通 Java 類 Triangle。

  1. public class Triangle {  
  2.         public void sayHi(){  
  3.         System.out.println("Triangle sayHi()");  
  4.         }  
  5.     } 

第2步:創建 ImportSelector 實現類,selectImports 返回 Triangle 的全類名。

  1. public class MyImportSelector implements ImportSelector {  
  2.        @Override  
  3.        public String[] selectImports(AnnotationMetadata annotationMetadata) {  
  4.        return new String[]{"annotation.importannotation.waytwo.Triangle"};  
  5.        }  
  6.    }  

第3步:創建配置類,在原來的基礎上還導入了 MyImportSelector。

  1. @Import({Circle.class,MyImportSelector.class})  
  2.     @Configuration  
  3.     public class MainConfigTwo {  
  4.      
  5.     }  

第4步:創建測試類

  1. public static void main(String[] args) {  
  2.    
  3.       ApplicationContext context = new AnnotationConfigApplicationContext(MainConfigTwo.class);  
  4.       Circle circle = context.getBean(Circle.class);  
  5.       Triangle triangle = context.getBean(Triangle.class);  
  6.       circle.sayHi();  
  7.       triangle.sayHi();  
  8.    
  9.   }  

第5步:運行結果:

Circle sayHi()

Triangle sayHi()

可以看到 Triangle 對象也被 IOC 容器成功的實例化出來了。

第三種方式:配合 ImportBeanDefinitionRegistrar 使用

ImportBeanDefinitionRegistrar 也是一個接口,它可以手動注冊bean到容器中,從而我們可以對類進行個性化的定制。(需要搭配 @Import 與 @Configuration 一起使用。)

第1步:創建普通 Java 類 Rectangle。

  1. public class Rectangle {  
  2.     public void sayHi() {  
  3.     System.out.println("Rectangle sayHi()");  
  4.     }  

第2步:創建 ImportBeanDefinitionRegistrar 實現類,實現方法直接手動注冊一個名叫 rectangle 的 Bean 到 IOC 容器中。

  1. public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {  
  2.      
  3.     @Override  
  4.     public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {  
  5.      
  6.     RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Rectangle.class);  
  7.     // 注冊一個名字叫做 rectangle 的 bean  
  8.     beanDefinitionRegistry.registerBeanDefinition("rectangle", rootBeanDefinition);  
  9.     }  
  10.      
  11. }  

第3步:創建配置類,導入 MyImportBeanDefinitionRegistrar 類。

  1. @Import({Circle.class, MyImportSelector.class, MyImportBeanDefinitionRegistrar.class})  
  2. @Configuration  
  3. public class MainConfigThree {  
  4. }  

第4步:創建測試類。

  1. public static void main(String[] args) {  
  2.      
  3. ApplicationContext context = new AnnotationConfigApplicationContext(MainConfigThree.class);  
  4.     Circle circle = context.getBean(Circle.class);  
  5.     Triangle triangle = context.getBean(Triangle.class);  
  6.     Rectangle rectangle = context.getBean(Rectangle.class);  
  7.     circle.sayHi();  
  8.     triangle.sayHi();  
  9.     rectangle.sayHi();  

第5步:運行結果

Circle sayHi()

Triangle sayHi()

Rectangle sayHi()

由此看一看到,Rectangle 對象也被注冊進來了。

@Conditional 【Spring提供】

@Conditional 注釋可以實現只有在特定條件滿足時才啟用一些配置。

下面看一個簡單的例子:

第1步:創建普通 Java 類 ConditionBean,該類主要用來驗證 Bean 是否成功加載。

  1. public class ConditionBean {  
  2.     public void sayHi() {  
  3.       System.out.println("ConditionBean sayHi()");  
  4.     }  
  5. }  

第2步:創建 Condition 實現類,@Conditional 注解只有一個 Condition 類型的參數,Condition 是一個接口,該接口只有一個返回布爾值的 matches() 方法,該方法返回 true 則條件成立,配置類生效。反之,則不生效。在該例子中我們直接返回 true。

  1. public class MyCondition implements Condition {  
  2.      
  3.     @Override  
  4.     public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {  
  5.       return true;  
  6.     }  
  7. }  

第3步:創建配置類,可以看到該配置的 @Conditional 傳了我們剛才創建的 Condition 實現類進去,用作條件判斷。

  1. @Configuration  
  2. @Conditional(MyCondition.class)  
  3. public class ConditionConfig {  
  4.     @Bean  
  5.     public ConditionBean conditionBean(){  
  6.      return new ConditionBean();  
  7.     }  
  8. }  

第4步:編寫測試方法。

  1. public static void main(String[] args) {  
  2.   ApplicationContext context = new AnnotationConfigApplicationContext(ConditionConfig.class);  
  3.   ConditionBean conditionBean = context.getBean(ConditionBean.class);  
  4.   conditionBean.sayHi();  
  5. }  

第5步:結果分析

因為 Condition 的 matches 方法直接返回了 true,配置類會生效,我們可以把 matches 改成返回 false,則配置類就不會生效了。

除了自定義 Condition,Spring 還為我們擴展了一些常用的 Condition。常用注解,可以參考:

SpringBoot 啟動過程

在看源碼的過程中,我們會看到以下四個類的方法經常會被調用,我們需要對一下幾個類有點印象:

  • ApplicationContextInitializer
  • ApplicationRunner
  • CommandLineRunner
  • SpringApplicationRunListener

下面開始源碼分析,先從 SpringBoot 的啟動類的 run() 方法開始看,以下是調用鏈:SpringApplication.run() -> run(new Class[]{primarySource}, args) -> new SpringApplication(primarySources)).run(args)。

一直在run,終于到重點了,我們直接看 new SpringApplication(primarySources)).run(args) 這個方法。

上面的方法主要包括兩大步驟:

  • 創建 SpringApplication 對象。
  • 運行 run() 方法。

創建 SpringApplication 對象

  1. public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) {  
  2.      
  3.     this.sources = new LinkedHashSet();  
  4.     this.bannerMode = Mode.CONSOLE;  
  5.     this.logStartupInfo = true;  
  6.     this.addCommandLineProperties = true;  
  7.     this.addConversionService = true;  
  8.     this.headless = true;  
  9.     this.registerShutdownHook = true;  
  10.     this.additionalProfiles = new HashSet();  
  11.     this.isCustomEnvironment = false;  
  12.     this.resourceLoader = resourceLoader;  
  13.     Assert.notNull(primarySources, "PrimarySources must not be null");  
  14.     // 保存主配置類(這里是一個數組,說明可以有多個主配置類)  
  15.     this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));  
  16.     // 判斷當前是否是一個 Web 應用  
  17.     this.webApplicationType = WebApplicationType.deduceFromClasspath();  
  18.     // 從類路徑下找到 META/INF/Spring.factories 配置的所有 ApplicationContextInitializer,然后保存起來  
  19.     this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));  
  20.     // 從類路徑下找到 META/INF/Spring.factories 配置的所有 ApplicationListener,然后保存起來  
  21.     this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));  
  22.     // 從多個配置類中找到有 main 方法的主配置類(只有一個)  
  23.     this.mainApplicationClass = this.deduceMainApplicationClass();  
  24.      
  25. }  

運行 run() 方法

  1. public ConfigurableApplicationContext run(String... args) {  
  2.      
  3.     // 創建計時器  
  4.     StopWatch stopWatch = new StopWatch();  
  5.     stopWatch.start();  
  6.     // 聲明 IOC 容器  
  7.     ConfigurableApplicationContext context = null;  
  8.     Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();  
  9.     this.configureHeadlessProperty();  
  10.     // 從類路徑下找到 META/INF/Spring.factories 獲取 SpringApplicationRunListeners  
  11.     SpringApplicationRunListeners listeners = this.getRunListeners(args);  
  12.     // 回調所有 SpringApplicationRunListeners 的 starting() 方法  
  13.     listeners.starting();  
  14.     Collection exceptionReporters;  
  15.     try {  
  16.     // 封裝命令行參數  
  17.     ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);  
  18.     // 準備環境,包括創建環境,創建環境完成后回調 SpringApplicationRunListeners#environmentPrepared()方法,表示環境準備完成  
  19.     ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);  
  20.     this.configureIgnoreBeanInfo(environment);  
  21.     // 打印 Banner  
  22.     Banner printedBanner = this.printBanner(environment);  
  23.     // 創建 IOC 容器(決定創建 web 的 IOC 容器還是普通的 IOC 容器)  
  24.     context = this.createApplicationContext();  
  25.     exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);  
  26.     /* 
  27.      * 準備上下文環境,將 environment 保存到 IOC 容器中,并且調用 applyInitializers() 方法 
  28.      * applyInitializers() 方法回調之前保存的所有的 ApplicationContextInitializer 的 initialize() 方法 
  29.      * 然后回調所有的 SpringApplicationRunListener#contextPrepared() 方法  
  30.      * 最后回調所有的 SpringApplicationRunListener#contextLoaded() 方法  
  31.      */ 
  32.     this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);  
  33.     // 刷新容器,IOC 容器初始化(如果是 Web 應用還會創建嵌入式的 Tomcat),掃描、創建、加載所有組件的地方  
  34.     this.refreshContext(context);  
  35.     // 從 IOC 容器中獲取所有的 ApplicationRunner 和 CommandLineRunner 進行回調  
  36.     this.afterRefresh(context, applicationArguments);  
  37.     stopWatch.stop();  
  38.     if (this.logStartupInfo) {  
  39.     (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);  
  40.     }  
  41.     // 調用 所有 SpringApplicationRunListeners#started()方法  
  42.     listeners.started(context);  
  43.     this.callRunners(context, applicationArguments);  
  44.     } catch (Throwable var10) {  
  45.     this.handleRunFailure(context, var10, exceptionReporters, listeners);  
  46.     throw new IllegalStateException(var10);  
  47.     }  
  48.     try {  
  49.     listeners.running(context);  
  50.     return context;  
  51.     } catch (Throwable var9) {  
  52.     this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);  
  53.     throw new IllegalStateException(var9);  
  54.     }  
  55. }  

小結

run() 階段主要就是回調本節開頭提到過的4個監聽器中的方法與加載項目中組件到 IOC 容器中,而所有需要回調的監聽器都是從類路徑下的 META-INF/Spring.factories中獲取,從而達到啟動前后的各種定制操作。

SpringBoot 自動配置原理

@SpringBootApplication 注解

SpringBoot 項目的一切都要從 @SpringBootApplication 這個注解開始說起。

@SpringBootApplication 標注在某個類上說明:

  • 這個類是 SpringBoot 的主配置類。
  • SpringBoot 就應該運行這個類的 main 方法來啟動 SpringBoot 應用。

該注解的定義如下:

  1. @SpringBootConfiguration  
  2. @EnableAutoConfiguration  
  3. @ComponentScan(  
  4. excludeFilters = {@Filter(  
  5.   type = FilterType.CUSTOM,  
  6.   classes = {TypeExcludeFilter.class}  
  7. ), @Filter(  
  8.     type = FilterType.CUSTOM,  
  9.     classes = {AutoConfigurationExcludeFilter.class}  
  10. )}  
  11. )  
  12. public @interface SpringBootApplication {  
  13.      

可以看到SpringBootApplication 注解是一個組合注解(關于組合注解文章的開頭有講到),其主要組合了一下三個注解:

  • @SpringBootConfiguration:該注解表示這是一個 Spring Boot 的配置類,其實它就是一個 @Configuration 注解而已。
  • @ComponentScan:開啟組件掃描。
  • @EnableAutoConfiguration:從名字就可以看出來,就是這個類開啟自動配置的。嗯,自動配置的奧秘全都在這個注解里面。

@EnableAutoConfiguration 注解

先看該注解是怎么定義的:

  1. @AutoConfigurationPackage  
  2. @Import({AutoConfigurationImportSelector.class})  
  3. public @interface EnableAutoConfiguration {  

@AutoConfigurationPackage

從字面意思理解就是自動配置包。點進去可以看到就是一個 @Import 注解:@Import({Registrar.class}),導入了一個 Registrar 的組件。關于 @Import 的用法文章上面也有介紹哦。

我們在 Registrar 類中的 registerBeanDefinitions 方法上打上斷點,可以看到返回了一個包名,該包名其實就是主配置類所在的包。

一句話:@AutoConfigurationPackage 注解就是將主配置類(@SpringBootConfiguration標注的類)的所在包及下面所有子包里面的所有組件掃描到Spring容器中。所以說,默認情況下主配置類包及子包以外的組件,Spring 容器是掃描不到的。

@Import({AutoConfigurationImportSelector.class})

該注解給當前配置類導入另外的 N 個自動配置類。(該注解詳細用法上文有提及)。

配置類導入規則

那具體的導入規則是什么呢?我們來看一下源碼。在開始看源碼之前,先啰嗦兩句。就像小馬哥說的,我們看源碼不用全部都看,不用每一行代碼都弄明白是什么意思,我們只要抓住關鍵的地方就可以了。

我們知道 AutoConfigurationImportSelector 的 selectImports就是用來返回需要導入的組件的全類名數組的,那么如何得到這些數組呢?

在 selectImports 方法中調用了一個 getAutoConfigurationEntry() 方法。

由于篇幅問題我就不一一截圖了,我直接告訴你們調用鏈:在 `getAutoConfigurationEntry() -> getCandidateConfigurations() -> loadFactoryNames()``。

在這里 loadFactoryNames()方法傳入了 EnableAutoConfiguration.class 這個參數。先記住這個參數,等下會用到。

loadFactoryNames() 中關鍵的三步:

  • 從當前項目的類路徑中獲取所有 META-INF/spring.factories 這個文件下的信息。
  • 將上面獲取到的信息封裝成一個 Map 返回。
  • 從返回的 Map 中通過剛才傳入的 EnableAutoConfiguration.class 參數,獲取該 key 下的所有值。

META-INF/spring.factories 探究

聽我這樣說完可能會有點懵,我們來看一下 META-INF/spring.factories 這類文件是什么就不懵了。當然在很多第三方依賴中都會有這個文件,一般每導入一個第三方的依賴,除了本身的jar包以外,還會有一個 xxx-spring-boot-autoConfigure,這個就是第三方依賴自己編寫的自動配置類。我們現在就以 spring-boot-autocongigure 這個依賴來說。

可以看到 EnableAutoConfiguration 下面有很多類,這些就是我們項目進行自動配置的類。

一句話:將類路徑下META-INF/spring.factories 里面配置的所有 EnableAutoConfiguration 的值加入到 Spring 容器中。

HttpEncodingAutoConfiguration

通過上面方式,所有的自動配置類就被導進主配置類中了。但是這么多的配置類,明顯有很多自動配置我們平常是沒有使用到的,沒理由全部都生效吧。

接下來我們以 HttpEncodingAutoConfiguration為例來看一個自動配置類是怎么工作的。為啥選這個類呢?主要是這個類比較的簡單典型。

先看一下該類標有的注解:

  1. @Configuration  
  2. @EnableConfigurationProperties({HttpProperties.class})  
  3. @ConditionalOnWebApplication(  
  4.  type = Type.SERVLET  
  5. )  
  6. @ConditionalOnClass({CharacterEncodingFilter.class})  
  7. @ConditionalOnProperty(  
  8.   prefix = "spring.http.encoding",  
  9.   value = {"enabled"},  
  10.   matchIfMissing = true  
  11. )  
  12. public class HttpEncodingAutoConfiguration {  
  13.      
  • @Configuration:標記為配置類。
  • @ConditionalOnWebApplication:web應用下才生效。
  • @ConditionalOnClass:指定的類(依賴)存在才生效。
  • @ConditionalOnProperty:主配置文件中存在指定的屬性才生效。
  • @EnableConfigurationProperties({HttpProperties.class}):啟動指定類的ConfigurationProperties功能;將配置文件中對應的值和 HttpProperties 綁定起來;并把 HttpProperties 加入到 IOC 容器中。

因為@EnableConfigurationProperties({HttpProperties.class})把配置文件中的配置項與當前 HttpProperties 類綁定上了。

然后在HttpEncodingAutoConfiguration 中又引用了 HttpProperties ,所以最后就能在 HttpEncodingAutoConfiguration中使用配置文件中的值了。

最終通過 @Bean 和一些條件判斷往容器中添加組件,實現自動配置。(當然該Bean中屬性值是從 HttpProperties 中獲取)

HttpProperties

HttpProperties 通過 @ConfigurationProperties 注解將配置文件與自身屬性綁定。

所有在配置文件中能配置的屬性都是在 xxxProperties 類中封裝著;配置文件能配置什么就可以參照某個功能對應的這個屬性類。

  1. @ConfigurationProperties(  
  2.   prefix = "spring.http"  
  3. )// 從配置文件中獲取指定的值和bean的屬性進行綁定  
  4. public class HttpProperties {  

總結

  • SpringBoot啟動會加載大量的自動配置類。
  • 我們看需要的功能有沒有SpringBoot默認寫好的自動配置類。
  • 我們再來看這個自動配置類中到底配置了那些組件(只要我們要用的組件有,我們就不需要再來配置了)。
  • 給容器中自動配置類添加組件的時候,會從properties類中獲取某些屬性。我們就可以在配置文件中指定這些屬性的值。xxxAutoConfiguration:自動配置類給容器中添加組件。xxxProperties:封裝配置文件中相關屬性。

用心看的小伙伴應該發現了,其實很多需要待加載的類都放在類路徑下的META-INF/Spring.factories文件下,而不是直接寫死這代碼中,這樣做就可以很方便我們自己或第三方去z做擴展。

參考:

kil51.cn/JTg9D

blog.51cto.com/4247649/2118354

www.cnblogs.com/duanxz/p/3787757.html

www.jianshu.com/p/e22b9fef311c

blog.csdn.net/qq_26525215/article/details/53523970

http://www.woaijava.cc

本文轉載自微信公眾號「Java后端技術全棧」,可以通過以下二維碼關注。轉載本文請聯系Java后端技術全棧公眾號。

 

責任編輯:武曉燕 來源: Java后端技術全棧
相關推薦

2018-11-28 11:08:30

并查集集合數據結構

2012-10-12 09:23:04

JavaEEUMLObject

2024-09-23 05:10:00

微服務CORSSpringBoot

2019-02-21 16:24:28

5G火車站設備

2021-12-01 06:50:50

Docker底層原理

2022-05-30 08:05:11

架構

2023-10-09 16:35:19

方案Spring支付

2020-03-02 15:17:37

云原生CNCF容器

2023-01-26 01:09:31

配置數據源參數

2019-07-04 09:13:04

中臺百度團隊

2021-02-25 08:21:38

高可用風險故障

2021-11-08 18:37:45

MySQL解碼測試

2023-10-06 19:21:49

Initializr應用Spring

2019-09-26 09:24:01

GC原理調優

2020-10-29 10:35:53

Nginx架構服務器

2020-05-12 09:10:24

瀏覽器服務器網絡

2022-02-16 19:42:25

Spring配置開發

2020-12-18 07:33:20

SpringSchedule組件

2022-11-11 15:49:41

MySQL隔離

2019-10-21 08:51:41

分布式事務CAPAP
點贊
收藏

51CTO技術棧公眾號

日本少妇aaa| 人妻夜夜添夜夜无码av| 在线观看xxxx| 91精品电影| 亚洲大胆人体视频| 国产无套内射久久久国产| 91精品大全| 成人综合在线视频| 日本不卡免费高清视频| 2017亚洲天堂| 欧美巨大xxxx| 欧美男人的天堂一二区| 日韩一级性生活片| 婷婷在线视频观看| 91视频观看免费| 91最新在线免费观看| 在线观看日本视频| 国产精品99一区二区| 亚洲一区www| www.四虎精品| av亚洲一区二区三区| 亚洲国产你懂的| 亚洲精品在线观看免费| 婷婷久久久久久| 国产乱码一区二区三区| 国产精品海角社区在线观看| 久久久久免费看| 久久美女精品| 亚洲人成伊人成综合网久久久| 深夜做爰性大片蜜桃| 精品久久在线| 午夜久久久影院| 桥本有菜av在线| 国产对白叫床清晰在线播放| 99久久精品国产观看| 亚洲aⅴ日韩av电影在线观看| 波多野结衣视频网址| 亚洲三级影院| 欧美大胆a视频| 亚洲一二三四五六区| 精品理论电影| 亚洲人午夜精品免费| 久久久国产精品无码| 91久久精品无嫩草影院| 91精品国产综合久久久久| 欧美婷婷精品激情| 91精品店在线| 欧美午夜精品久久久| 国产成人亚洲精品无码h在线| 国产调教在线| 五月婷婷激情综合| 日韩日韩日韩日韩日韩| 国内高清免费在线视频| 亚洲专区一二三| 免费特级黄色片| av漫画网站在线观看| 亚洲福利视频一区二区| 隔壁人妻偷人bd中字| 羞羞的网站在线观看| 亚洲综合一二区| 无码 制服 丝袜 国产 另类| 99色在线观看| 日韩人在线观看| 欧在线一二三四区| 国产三级一区| 五月天激情综合网| 国产女主播喷水视频在线观看| 在线不卡日本| 日韩中文字幕不卡视频| 国产午夜精品福利| 国内精品久久久久久久影视简单| 日韩毛片久久久| 午夜精品三级久久久有码| 欧美第一在线视频| 日韩视频免费直播| 逼特逼视频在线观看| 精品伊人久久久| 亚洲欧美成人一区二区在线电影| 妺妺窝人体色WWW精品| 日韩av片子| 欧美wwwxxxx| 精品一区免费观看| 丝袜国产日韩另类美女| 国产欧美日韩精品在线观看| 精品人妻少妇AV无码专区| 国产成人在线观看| 欧美aaaaa喷水| av网站无病毒在线| 亚洲精品国产a久久久久久| 很污的网站在线观看| 在线观看v片| 欧美日韩小视频| 韩国三级hd中文字幕有哪些| 外国成人在线视频| 日韩中文字幕在线看| 精品肉丝脚一区二区三区| 久久福利毛片| 亚洲一区二区三区四区视频| 午夜黄色小视频| 国产精品二三区| 免费看国产一级片| 高清久久一区| 亚洲美女av在线播放| 男人与禽猛交狂配| 日韩精品一区第一页| 51精品国产人成在线观看| 欧美精品久久久久久久久久丰满| 久久久久成人黄色影片| 穿情趣内衣被c到高潮视频| 日韩伦理在线| 777色狠狠一区二区三区| 日韩精品一区二区三区高清免费| 色喇叭免费久久综合网| 97免费中文视频在线观看| 中文字幕第一页在线播放| 国产69精品久久99不卡| 亚洲成人18| 日本乱码一区二区三区不卡| 91精品国产91久久综合桃花| 美女洗澡无遮挡| 欧美午夜电影在线观看| 国产在线播放不卡| 日本大片在线观看| 亚洲国产一区视频| 日韩av片专区| 欧美精品系列| 2023亚洲男人天堂| 免费观看的毛片| 亚洲卡通动漫在线| 成人免费毛片播放| 久久午夜影院| 欧美激情一区二区三级高清视频| 这里只有精品免费视频| 97精品久久久久中文字幕| 影音先锋成人资源网站| yw.尤物在线精品视频| 日韩精品中文字幕在线播放| 久久这里只有精品国产| 国产成人午夜99999| 中文字幕av日韩精品| 国产精品久久亚洲不卡| 一本色道久久综合狠狠躁篇的优点 | 国产精品成人av久久| 麻豆精品视频在线观看免费| 欧洲一区二区日韩在线视频观看免费 | 女同性恋一区二区三区| 中文字幕一区二区精品区| 国产伦精品免费视频| 在线免费观看黄色网址| 欧美三级三级三级| 网爆门在线观看| 日韩成人av影视| 欧美一级爱爱| 精品无人乱码一区二区三区| 国产香蕉97碰碰久久人人| 日韩中文字幕高清| 欧美激情综合在线| 中文字幕 91| 久久一区二区三区喷水| 成人黄色中文字幕| 天堂va在线| 精品国产自在久精品国产| 国产男女猛烈无遮挡在线喷水| 精品综合免费视频观看| 久久久久亚洲av无码专区喷水| 精品视频在线一区| 欧美富婆性猛交| 污视频软件在线观看| 色综合天天综合网天天狠天天| 99久久人妻无码精品系列| 日本欧美一区二区在线观看| 亚洲mv在线看| 玖玖精品一区| 国语自产精品视频在线看一大j8| 日日夜夜精品免费| 色婷婷综合久久久久中文 | 一区二区三区成人| a级一a一级在线观看| 视频在线观看一区| 一区二区三区四区五区视频| 日本久久伊人| 欧美在线中文字幕| 午夜小视频在线| 日韩精品中文字幕一区二区三区 | 亚洲国产激情| 日本婷婷久久久久久久久一区二区 | av在线不卡电影| 能看的毛片网站| 久久伦理在线| 精品999在线观看| 四虎影视4hu4虎成人| 九九热最新视频//这里只有精品| 色网站免费观看| 欧美性色黄大片| 久久综合色综合| 久久久精品免费网站| 亚洲天堂网站在线| 久久成人在线| 日本福利视频网站| jiujiure精品视频播放| 不卡视频一区二区三区| 国产另类xxxxhd高清| 久久99久久99精品免观看粉嫩| 毛片网站在线观看| 精品区一区二区| 亚洲视屏在线观看| 午夜a成v人精品| √天堂中文官网8在线| 久久尤物电影视频在线观看| 香蕉视频xxxx| 日韩高清一区二区| 青青青免费在线| 亚洲啊v在线观看| 茄子视频成人在线观看| 高清日韩欧美| 成人国产精品一区二区| 一区二区三区四区日本视频| 欧美另类高清videos| 1024视频在线| 亚洲男人av电影| 欧美天堂在线视频| 日韩三级视频在线看| 国产一级精品毛片| 久草福利资源在线| 97超碰免费在线| 日韩最新免费不卡| 久久精品国产亚洲a∨麻豆| 欧美精品一区二区三区蜜桃| 国产精品污视频| 欧美性猛交xxxx乱大交退制版| 一级片免费网址| 亚洲精品中文字幕乱码三区| 国产又粗又猛又爽又黄的视频小说| 99re66热这里只有精品3直播| 色哟哟网站在线观看| 久久成人18免费观看| 天天干天天综合| 日韩不卡一区二区| 波多野结衣天堂| 午夜综合激情| 日韩精品视频久久| 一本久道久久综合狠狠爱| 国产精品久久国产| 国内视频精品| 日韩精品在线中文字幕| 激情久久中文字幕| 日本福利视频一区| 亚洲日本激情| 日日碰狠狠添天天爽超碰97| 影院欧美亚洲| 每日在线更新av| 欧美专区一区二区三区| www.日日操| 日韩avvvv在线播放| 亚洲欧美久久久久| 国内精品国产成人| 久久精品一卡二卡| 99精品视频一区二区三区| 男人的天堂影院| 91社区在线播放| 波多野在线播放| 国产精品国产自产拍高清av | 欧美a一欧美| 久久波多野结衣| 欧美精美视频| 一区二区精品视频| 亚洲五月综合| youjizz.com在线观看| 亚洲人成在线影院| 激情五月开心婷婷| 麻豆久久久久久久| theporn国产精品| 国产不卡视频在线播放| 亚洲啪av永久无码精品放毛片| 99在线热播精品免费| www.久久av| 国产精品久久久久久久岛一牛影视 | 在线不卡中文字幕| 国产三级视频在线播放| 精品乱人伦一区二区三区| 五十路在线视频| 中文字幕国产精品久久| 精产国品自在线www| 久久久女人电视剧免费播放下载| 激情国产在线| 国产在线拍偷自揄拍精品| 亚洲一区 二区| 欧美激情国产日韩| 99久久99视频只有精品| 男人添女荫道口女人有什么感觉| 蜜桃av一区| 911av视频| 2024国产精品| 小早川怜子一区二区的演员表| 亚洲18女电影在线观看| 中文字幕av久久爽| 欧美精品一区二区三区一线天视频| 精品资源在线看| 欧美超级免费视 在线| 在线观看网站免费入口在线观看国内 | 五月天婷婷在线视频| 欧美激情在线一区| 成人在线免费电影网站| 国产呦系列欧美呦日韩呦| 日韩在线观看一区| 久久黄色片视频| 激情成人午夜视频| 丰满少妇在线观看资源站| 亚洲精品综合在线| 中文字幕在线观看精品| 日韩电影免费观看中文字幕| 色三级在线观看| 91大神福利视频在线| 亚洲日本va午夜在线电影| 亚洲精品8mav| 午夜在线视频观看日韩17c| 久久久久亚洲AV成人网人人小说| 国产精品国产精品国产专区不蜜 | 国产原创视频在线观看| 国产精品xxx视频| 精品国产导航| www.日本三级| 国产一区二区三区观看| 日本免费www| 欧美日韩亚洲视频一区| 欧美性受xxxx狂喷水| 色综合久久88色综合天天看泰| 国模私拍国内精品国内av| 欧美激情论坛| 亚洲制服av| 一本加勒比波多野结衣| 亚洲午夜免费福利视频| 99精品在线视频观看| 日韩在线视频观看正片免费网站| 我爱我色成人网| 欧美精品一区二区三区在线看午夜 | 91精品在线免费| 午夜在线视频| 国产精品678| 国产一区二区三区日韩精品 | 六月丁香综合在线视频| 99久久久无码国产精品衣服| 欧美性猛交xxxx久久久| 色视频精品视频在线观看| 97人人模人人爽人人喊中文字| 91蝌蚪精品视频| 国产av人人夜夜澡人人爽麻豆 | a级在线免费观看| 色婷婷av一区| 大地资源中文在线观看免费版| 日本乱人伦a精品| 九九综合九九| www.日日操| 国产精品久久久久aaaa樱花| 中文天堂在线视频| 精品国产一区二区三区久久久狼| 全球最大av网站久久| 一区二区在线高清视频| 国产最新精品免费| 日本妇女毛茸茸| 337p日本欧洲亚洲大胆精品| 久久青草伊人| 久久国产精品-国产精品| 免费看的黄色欧美网站| 国产aaaaaaaaa| 欧美一三区三区四区免费在线看| 在线观看三级视频| 国内精品视频免费| 久久一区欧美| 999精品视频在线观看播放| 日韩欧美视频在线| missav|免费高清av在线看| 久久国产精品亚洲va麻豆| 青青草国产成人99久久| 手机av在线看| 91精品福利视频| av色图一区| 国产精品中文久久久久久久| 青青一区二区三区| 色呦色呦色精品| 亚洲日韩欧美一区二区在线| 99热这里只有精品99| 国内精品400部情侣激情| 亚洲69av| 熟妇无码乱子成人精品| 午夜精品久久久久久久99水蜜桃| 麻豆app在线观看| 国产在线久久久| 国产精品一二| 中文乱码字幕高清一区二区| 精品国产乱码久久久久久免费| 日韩免费va| 国产情侣第一页| 久久久国际精品| 亚洲av无码乱码国产麻豆| 国产mv久久久| 国内精品久久久久久久影视蜜臀 | 麻豆影视在线| 99热在线播放| 日本欧美一区二区|