zoukankan      html  css  js  c++  java
  • SpringBoot运行原理初探—自动装配

    SpringBoot运行原理初探

    pom.xml

    父依赖

    直接父依赖主要管理项目的资源过滤及插件!

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.1.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    

    点进去,发现还有一个父依赖

    <parent>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-dependencies</artifactId>
      <version>2.3.1.RELEASE</version>
    </parent>
    

    这里才是真正管理SpringBoot应用里面所有依赖版本的地方,SpringBoot的版本控制中心;

    以后我们导入依赖默认是不需要写版本;但是如果导入的包没有在依赖中管理着就需要手动配置版本了;

    启动器 spring-boot-starter

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

    springboot-boot-starter-xxx:就是spring-boot的场景启动器

    spring-boot-starter-web:帮我们导入了web模块正常运行所依赖的组件;

    SpringBoot将所有的功能场景都抽取出来,做成一个个的starter (启动器),只需要在项目中引入这些starter即可,所有相关的依赖都会导入进来 , 我们要用什么功能就导入什么样的场景启动器即可 ;我们也可以自己自定义 starter;

    主启动类 xxxApplication

    
    //@SpringBootApplication 来标注一个主程序类
    //说明这是一个Spring Boot应用
    @SpringBootApplication
    public class Demo01Application {
    
        public static void main(String[] args) {
            SpringApplication.run(Demo01Application.class, args);
        }
    }
    

    @SpringBootApplication

    作用:标注在某个类上说明这个类是SpringBoot的主启动类 , SpringBoot就应该运行这个类的main方法来启动SpringBoot应用;

    进入这个注解:可以看到上面还有很多其他注解

    @SpringBootConfiguration
    @EnableAutoConfiguration
    @ComponentScan(
        excludeFilters = {@Filter(
        type = FilterType.CUSTOM,
        classes = {TypeExcludeFilter.class}
    ), @Filter(
        type = FilterType.CUSTOM,
        classes = {AutoConfigurationExcludeFilter.class}
    )}
    )
    public @interface SpringBootApplication {
        //.....
    }
    
    @SpringBootConfiguration
    

    作用:SpringBoot的配置类 ,标注在某个类上 , 表示这是一个SpringBoot的配置类;

    我们继续进去这个注解查看

    @Configuration
    public @interface SpringBootConfiguration {
        //......
        @AliasFor(annotation = Configuration.class)
    	boolean proxyBeanMethods() default true;
    }
    

    这里的 @Configuration,说明这是一个配置类 ;

    @AliasFor(annotation = Configuration.class)
    boolean proxyBeanMethods() default true;//允许其它配置类调用这个类的@bean方法
    

    将@Configuration中的proxyBeanMethods()和本注解的proxyBeanMethods()互为别名,简化了配置。

    而在@Configuration中又设置了value属性和Component的value属性互为别名。

    回到 SpringBootApplication 注解中继续看。

    @EnableAutoConfiguration

    @EnableAutoConfiguration :开启自动配置功能

    以前我们需要自己配置的东西,而现在SpringBoot可以自动帮我们配置 ;@EnableAutoConfiguration告诉SpringBoot开启自动配置功能,这样自动配置才能生效;

    点进注解接续查看:

    @AutoConfigurationPackage :自动配置包

    @AutoConfigurationPackage
    @Import(AutoConfigurationImportSelector.class)
    public @interface EnableAutoConfiguration {
    
       String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
    
       Class<?>[] exclude() default {};
    
       String[] excludeName() default {};
    }
    

    先看@AutoConfigurationPackage

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

    Registrar.class 作用:将主启动类的所在包及包下面所有子包里面的所有组件扫描到Spring容器 ;

    再看@Import({AutoConfigurationImportSelector.class}) :给容器导入组件 ;

    AutoConfigurationImportSelector :自动配置导入选择器,那么它会导入哪些组件的选择器呢?我们点击去这个类看源码:

    1. 这个类中有一个这样的方法
    // 获得候选的配置
    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;
    }
    
    1. 这个方法又调用了 SpringFactoriesLoader 类的静态方法!我们进入SpringFactoriesLoader类loadFactoryNames() 方法

      public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
          String factoryTypeName = factoryType.getName();
          //这里它又调用了 loadSpringFactories 方法
          return (List)loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
      }
      
    2. 继续跟踪 loadSpringFactories 方法

      private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
          //获得classLoader , 我们返回可以看到这里得到的就是EnableAutoConfiguration标注的类本身
          MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
          if (result != null) {
              return result;
          } else {
              try {
                  //去获取一个资源 "META-INF/spring.factories"
                  Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
                  LinkedMultiValueMap result = new LinkedMultiValueMap();
      
                  //将读取到的资源遍历,封装成为一个Properties
                  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 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);
              }
          }
      }
      
    3. 发现一个多次出现的文件:spring.factories,全局搜索它

      没搜到,然后我直接根据路径在spring-boot-autoconfigure-2.2.8.RELEASE.jar包下找到了它

    WebMvcAutoConfiguration

    我们在上面的自动配置类随便找一个打开看看,比如 :WebMvcAutoConfiguration

    可以看到这些一个个的都是JavaConfig配置类,而且都注入了一些Bean,所以,自动配置真正实现是从classpath中搜寻所有的META-INF/spring.factories配置文件 ,并将其中对应的 org.springframework.boot.autoconfigure. 包下的配置项,通过反射实例化为对应标注了 @Configuration的JavaConfig形式的IOC容器配置类 , 然后将这些都汇总成为一个实例并加载到IOC容器中。

    @ConditionalOnClass

    spring.factories文件中的配置类怎样判断哪些类是需要加载的呢?

    @ConditionalOnClass注解就是判断这个自动配置类是否需要被加载的,只有当参数中的类全部存在时这个类才会被加载。

    结论:

    1. SpringBoot在启动的时候从类路径下的META-INF/spring.factories中获取EnableAutoConfiguration指定的值
    2. 将这些值作为自动配置类导入容器 , 自动配置类就生效 , 帮我们进行自动配置工作;
    3. 整个J2EE的整体解决方案和自动配置都在springboot-autoconfigure的jar包中;
    4. 它会给容器中导入非常多的自动配置类 (xxxAutoConfiguration), 就是给容器中导入这个场景需要的所有组件 , 并配置好这些组件 ;
    5. 有了自动配置类 , 免去了我们手动编写配置注入功能组件等的工作;

    SpringApplication.run分析

    分析该方法主要分两部分,一部分是SpringApplication的实例化,二是run方法的执行;

    SpringApplication

    这个类主要做了以下四件事情:

    1、推断应用的类型是普通的项目还是Web项目

    2、查找并加载所有可用初始化器 , 设置到initializers属性中

    3、找出所有的应用程序监听器,设置到listeners属性中

    4、推断并设置main方法的定义类,找到运行的主类

    查看构造器:

    public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
        this.resourceLoader = resourceLoader;
        Assert.notNull(primarySources, "PrimarySources must not be null");
        this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
        setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
        setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
        this.mainApplicationClass = deduceMainApplicationClass();
    }
    

    run 方法执行流程

    文章来源:B站地址:https://space.bilibili.com/95256449/

  • 相关阅读:
    PAT (Advanced Level) Practice 1054 The Dominant Color (20 分)
    PAT (Advanced Level) Practice 1005 Spell It Right (20 分) (switch)
    PAT (Advanced Level) Practice 1006 Sign In and Sign Out (25 分) (排序)
    hdu 5114 Collision
    hdu4365 Palindrome graph
    单链表查找最大值、两个递增的链表合并并且去重
    蓝桥杯-最短路 (SPFA算法学习)
    蓝桥杯-最大最小公倍数
    Codeforces-470 div2 C题
    蓝桥杯-地宫取宝
  • 原文地址:https://www.cnblogs.com/junlinsky/p/13170970.html
Copyright © 2011-2022 走看看