首頁 > 軟體

最新springboot中必須要了解的自動裝配原理

2022-05-20 13:12:13

1.pom.xml

父 依 賴 textcolor{orange}{父依賴} 父依賴

spring-boot-dependencies:核心依賴都在父工程中

這裡ctrl+左鍵,點選之後我們可以看到父依賴

這個裡面主要是管理專案的資源過濾及外掛,我們發現他還有一個父依賴

看看下面這個,熟悉嗎?

再點進去,我們發現有很多的依賴。這就是SpringBoot的版本控制中心。

這個地方才是真正管理SpringBoot應用裡面所有依賴的地方,也就是版本控制中心。

我們在寫或引入一些SpringBoot依賴的時候,不需要指定版本,就是因為有這些版本倉庫。

2.啟動器

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-web</artifactId>
</dependency>

啟動器就是springboot的啟動場景 textcolor{red}{啟動器就是SpringBoot的啟動場景} 啟動器就是SpringBoot的啟動場景

springboot-boot-starter-xxx:就是spring-boot的場景啟動器

spring-boot-starter-web:幫我們匯入了web模組正常執行所依賴的元件;也就是自動匯入web環境所有的依賴

SpringBoot將所有的功能場景都抽取出來,做成一個個的starter (啟動器);

要用什麼功能就匯入什麼樣的場景啟動器:只需要在專案中引入這些starter即可,所有相關的依賴都會匯入進來 ;

我們未來也可以自己自定義 starter;

3.主程式

//程式的主入口
//@SpringBootApplication:標註這個類是一個springBoot的應用
@SpringBootApplication
public class HelloSpringBootApplication {
   public static void main(String[] args) {
      //將springBoot應用啟動
      SpringApplication.run(HelloSpringBootApplication.class, args);
   }
}

看著如此的簡單,它就是通過反射載入這個類的物件,這是表面意思,我們看不到它為啥啟動。

首先我們來看

3.1註解

@SpringBootApplication

我們點選@SpringBootApplication後可以看到有這麼幾個註解

結論:springBoot所有的自動設定都是在啟動的時候掃描並載入:spring.factories所有的自動設定類都在這裡面,但是不一定生效,要判斷條件是否成立,只要匯入了對應的start,就有了對應的啟動器,有了啟動器,自動裝配就是生效,之後設定成功

@ComponentScan

這個註解在Spring中非常重要,對應的是XML設定中的元素。

作用:自動掃描並載入符合條件的元件或者bean,將這個bean定義載入到IOC容器中

@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })

@SpringBootConfiguration

作用:SpringBoot的設定類 ,標註在某個類上 , 表示這是一個SpringBoot的設定類;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@Indexed
public @interface SpringBootConfiguration {
    @AliasFor(
        annotation = Configuration.class
    )
    boolean proxyBeanMethods() default true;
}

這裡的@Configuration說明這是一個設定類,這個設定類就是對應Spring的xml組態檔

@Component說明,啟動類本身也是Spring中的一個元件,負責啟動應用。

@EnableAutoConfiguration

作用:開啟自動設定功能

點進去後會看到

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)

然後我們發現了@AutoConfigurationPackage它的作用是自動設定包

@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
}

@import :Spring底層註解@import , 給容器中匯入一個元件

AutoConfigurationPackages.Registrar.class 作用:將主啟動類的所在包及包下面所有子包裡面的所有元件掃描到Spring容器 ;

我們退回上一步看一下這個註解

@Import({AutoConfigurationImportSelector.class}) :給容器匯入元件 ;

AutoConfigurationImportSelector :自動設定匯入選擇器,在這個類中有這麼一個方法

/**
 * Return the auto-configuration class names that should be considered. By default
 * this method will load candidates using {@link SpringFactoriesLoader} with
 * {@link #getSpringFactoriesLoaderFactoryClass()}.
 * @param metadata the source metadata
 * @param attributes the {@link #getAttributes(AnnotationMetadata) annotation
 * attributes}
 * @return a list of candidate configurations
 */
//獲得候選的設定
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    //這裡的getSpringFactoriesLoaderFactoryClass()
    //返回的是我們最開是看到啟動自動匯入組態檔的註解類;EnableAutoConfiguration
   List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
         getBeanClassLoader());
   Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
         + "are using a custom packaging, make sure that file is correct.");
   return configurations;
}

在上面這個方法中呼叫了SpringFactoriesLoader這個類中的靜態方法,我們檢視一下這個類中的loadFactoryNames這個方法。

public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
    ClassLoader classLoaderToUse = classLoader;
    if (classLoader == null) {
        classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
    }

    String factoryTypeName = factoryType.getName();
    return (List)loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}

發現他又呼叫了loadSpringFactories這個方法,我們繼續看

private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
    Map<String, List<String>> result = (Map)cache.get(classLoader);
    if (result != null) {
        return result;
    } else {
        HashMap result = new HashMap();
        try {
            Enumeration urls = classLoader.getResources("META-INF/spring.factories");
            while(urls.hasMoreElements()) {
                URL url = (URL)urls.nextElement();
                UrlResource resource = new UrlResource(url);
                Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                Iterator var6 = properties.entrySet().iterator();
                while(var6.hasNext()) {
                    Entry<?, ?> entry = (Entry)var6.next();
                    String factoryTypeName = ((String)entry.getKey()).trim();
                    String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
                    String[] var10 = factoryImplementationNames;
                    int var11 = factoryImplementationNames.length;
                    for(int var12 = 0; var12 < var11; ++var12) {
                        String factoryImplementationName = var10[var12];
                        ((List)result.computeIfAbsent(factoryTypeName, (key) -> {
                            return new ArrayList();
                        })).add(factoryImplementationName.trim());
                    }
                }
            }
            result.replaceAll((factoryType, implementations) -> {
                return (List)implementations.stream().distinct().collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
            });
            cache.put(classLoader, result);
            return result;
        } catch (IOException var14) {
            throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var14);
        }
    }
}

這裡我們發現多次出現了一個叫spring.factoriestextcolor{red}{這裡我們發現多次出現了一個叫spring.factories} 這裡我們發現多次出現了一個叫spring.factories

3.2 spring.factories

隨便點一個看看JerseyAutoConfiguration

會發現這都是javaConfig設定類,而且都注入了一些Bean。

所以,自動設定真正實現是從classpath中搜尋所有的META-INF/spring.factories組態檔 ,並將其中對應的 org.springframework.boot.autoconfigure. 包下的設定項,通過反射範例化為對應標註了 @Configuration的JavaConfig形式的IOC容器設定類 , 然後將這些都彙總成為一個範例並載入到IOC容器中。

4. 結論

  • springboot在啟動的時候,從類路徑下/META-INF/spring.factories獲取指定的值;
  • 將這些自動設定的類匯入容器,自動設定就會生效,進行自動設定;
  • 以前需要自動設定的東西,現在springboot幫忙做了;
  • 整合JavaEE,解決方案和自動設定的東西都在spring-boot-autoconfigure-2.5.7.jar這個包下
  • 他會把所有需要匯入的元件,以類名的方式返回,這些元件就會被新增到容器中
  • 容器中也會存在非常多的xxxAutoConfiguration的檔案(@Bean),就是這些類給容器中的匯入這個場景需要的所有元件,並自動設定。@Configuration,javaCOnfig
  • 有了自動設定類,免去了我們手動編寫組態檔的工作。

到此這篇關於最新springboot中必須要了解的自動裝配原理的文章就介紹到這了,更多相關springboot自動裝配原理內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com