詳談springboot啟動類的@SpringBootApplication注解


前幾天我們學會了如何創建springboot項目今天我們說一下他是怎么運行的為什么不需要我們再去編寫繁重的配置文件的

@SpringBootApplication

首先我們看一下這個注解,他是用來標注在主程序的,表明他是一個springboot項目

@Target({ElementType.TYPE})
  @Retention(RetentionPolicy.RUNTIME)
  @Documented
  @Inherited
  @SpringBootConfiguration
  @EnableAutoConfiguration
  @ComponentScan(
      excludeFilters = {
        @Filter(type = FilterType.CUSTOM,classes = {TypeExcludeFilter.class}),    
        @Filter( type = FilterType.CUSTOM,classes = {AutoConfigurationExcludeFilter.class})
      }
  )
  public @interface SpringBootApplication {
}

 

點進@SpringBootApplication注解后我們重點關注最后三個注解

@ComponentScan(包掃描)
  • component是組件,scan是掃描,所以這個注解的含義就是用來掃描組件的,

  • componentScan就是掃描所標注的類所在包下的所有需要注入的組件,將其注入,這里他是在@SpringBootApplication 中體現的,所以這個注解會自動注入所有在主程序所在包下的組件

  • 以前在ssm項目中我們需要去配置我們的包掃描

  
<context:component-scan base-package="com.xxx"></context:component-scan>

 

@EnableAutoConfiguration(開啟自動裝配)
@Target({ElementType.TYPE})
  @Retention(RetentionPolicy.RUNTIME)
  @Documented
  @Inherited
  @AutoConfigurationPackage
  @Import({AutoConfigurationImportSelector.class})
  public @interface EnableAutoConfiguration {
      String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
  ​
      Class<?>[] exclude() default {};
  ​
      String[] excludeName() default {};
  }

這里我們關注兩個注解

一、@AutoConfigurationPackage

  
 @Target({ElementType.TYPE})
  @Retention(RetentionPolicy.RUNTIME)
  @Documented
  @Inherited
  @Import({Registrar.class})
  public @interface AutoConfigurationPackage {
  }

 

在這個注解中,主要是獲取我們注解所在包下的組件去進行注冊

  
 static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
          Registrar() {
          }
  ​
    //metadata是我們注解所在的元信息
          public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
            //看這里 將我們注解所在包下所有的組件去進行注冊
              AutoConfigurationPackages.register(registry, (new AutoConfigurationPackages.PackageImport(metadata)).getPackageName());
          }
  ​
          public Set<Object> determineImports(AnnotationMetadata metadata) {
              return Collections.singleton(new AutoConfigurationPackages.PackageImport(metadata));
          }
      }

 

register方法:通過AutoConfigurationPackages去對包下的組件進行注冊

  
 private static final String BEAN = AutoConfigurationPackages.class.getName();
  ​
  public static void register(BeanDefinitionRegistry registry, String... packageNames) {
     //先判斷整個BEAN有沒有被注冊
          if (registry.containsBeanDefinition(BEAN)) {
            //獲取bean的定義
              BeanDefinition beanDefinition = registry.getBeanDefinition(BEAN);
            //通過bean獲取構造函數的參數值
              ConstructorArgumentValues constructorArguments = beanDefinition.getConstructorArgumentValues();
            //添加參數值,
              constructorArguments.addIndexedArgumentValue(0, addBasePackages(constructorArguments, packageNames));
          } else {
            //創建一個新的bean的定義
              GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
            //設置bean的類型為AutoConfigurationPackages類型
              beanDefinition.setBeanClass(AutoConfigurationPackages.BasePackages.class);
              beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, packageNames);
              beanDefinition.setRole(2);
            //進行bean的注冊
              registry.registerBeanDefinition(BEAN, beanDefinition);
          }
      }

 

二、@Import({AutoConfigurationImportSelector.class})

裝配我們的 自動配置導入選擇器

我們點進去這個類,下面有一個方法getAutoConfigurationEntry 獲取自動裝配的入口、

  
protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {
          if (!this.isEnabled(annotationMetadata)) {
              return EMPTY_ENTRY;
          } else {
              AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
              List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
              configurations = this.removeDuplicates(configurations);
              Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
              this.checkExcludedClasses(configurations, exclusions);
              configurations.removeAll(exclusions);
              configurations = this.filter(configurations, autoConfigurationMetadata);
              this.fireAutoConfigurationImportEvents(configurations, exclusions);
              return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
          }
      }

 

這個方法他主要是獲取了configurations的一個集合在這我們點進去getCandidateConfigurations方法

  
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
          List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.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;
      }

 

我們首先看他這個紅色報錯信息:在META-INF/spring.factories中沒有找到自動配置類。根據他的錯誤信息我們可以得出他是從META-INF/spring.factories獲取我們的自動配置信息的

我們也可以在點進這個loadFactoryNames這個方法

   public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
          String factoryClassName = factoryClass.getName();
     //調用了下面的方法loadSpringFactories
          return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
      }
  ​
      private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
          MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
          if (result != null) {
              return result;
          } else {
              try {
                //根據類加載器去獲取配置文件里的信息
                  Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
                  LinkedMultiValueMap result = new LinkedMultiValueMap();
  ​
                  while(urls.hasMoreElements()) {
                      URL url = (URL)urls.nextElement();
                      UrlResource resource = new UrlResource(url);
                    //配置信息經過多次轉換最終成為properties形式
                      Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                      Iterator var6 = properties.entrySet().iterator();
                  //循環獲取所有的配置信息
                      while(var6.hasNext()) {
                          Entry<?, ?> entry = (Entry)var6.next();
                          String factoryClassName = ((String)entry.getKey()).trim();
                          String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
                          int var10 = var9.length;
  ​
                          for(int var11 = 0; var11 < var10; ++var11) {
                              String factoryName = var9[var11];
                              result.add(factoryClassName, factoryName.trim());
                          }
                      }
                  }
  ​
                  cache.put(classLoader, result);
                  return result;
              } catch (IOException var13) {
                  throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
              }
          }
      }

 

而這個配置文件在哪里呢

在我們項目的External Libraries中找到我們的spring-boot-autoconfigure中

這樣就完成我們默認的一些自動裝配了

@SpringBootConfiguration

這個方法就比較簡單了,表明了這是一個配置類

  @Target({ElementType.TYPE})
  @Retention(RetentionPolicy.RUNTIME)
  @Documented
  @Configuration
  public @interface SpringBootConfiguration {
  }
 
到這我們三個注解就講完了,有什么問題請大佬們指明謝謝。

有過掙扎,有過失敗,但仍然會爬起來繼續戰斗

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM