springboot创建,自动装配原理分析,run方法启动

使用IDEA快速创建一个springboot项目

创建Spring Initializr,然后一直下一步下一步直至完成

选择web,表示创建web项目

运行原理分析

我们先来看看pom.xml文件

核心依赖的父项目如下:

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

进入spring-boot-starter-parent,可以看到管理SpringBoot应用里面所有依赖版本的地方和配置加载的地方。

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-dependencies</artifactId>//管理SpringBoot应用里面所有依赖版本的地方
    <version>2.2.1.RELEASE</version>
    <relativePath>../../spring-boot-dependencies</relativePath>
</parent>

在进入spring-boot-dependencies,就可以看到所依赖的所有版本了,由于太过,这里只截取部分。

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

springboot自动配置原理分析

主程序

//@SpringBootApplication 来标注一个主程序类,说明这是一个Spring Boot应用
@SpringBootApplication
public class Springboot01Application {
    public static void main(String[] args) {
        SpringApplication.run(Springboot01Application.class, args);
    }
}
@SpringBootApplication意义:SpringBoot应用标注在某个类上说明这个类是SpringBoot的主配置类,SpringBoot就应该运行这个类的main方法来启动SpringBoot应用;
进入@SpringBootApplication会发现有三个重要的注解
  • @SpringBootConfiguration:SpringBoot的配置类 ;标注在某个类上 , 表示这是一个SpringBoot的配置类;
  • @EnableAutoConfiguration:开启自动配置功能
  • @ComponentScan:自动扫描并加载符合条件的组件或者bean , 将这个bean定义加载到IOC容器中 ;

下面我们依次看一下这三个注解

@SpringBootConfiguration

进入@SpringBootConfiguration后,会看到@Configuration这个注解。

@Configuration:配置类上来标注这个注解,说明这是一个配置类 ,配置类,即:配置文件

在进入@Configuration后,会看到@Component注解,这个注解大家都很熟悉了吧,说明容器中的一个组件,也就是说启动类本身也是Spring中的一个组件而已,负责启动应用而已

返回来我们再看@ComponentScan

@ComponentScan

@ComponentScan在Spring中非常重要,它对应XML配置中的元素。功能就是自动扫描并加载符合条件的组件或者bean,将这个bean定义加载到IOC容器中 ;

接下来在看最重要的一个@EnableAutoConfiguration

@EnableAutoConfiguration

@EnableAutoConfiguration的作用是:开启自动配置功能,无须我们在自己配置,自动就告诉SpringBoot开启自动配置功能,这样自动配置才能生效;

进入@EnableAutoConfiguration后会发下有两个注解,@AutoConfigurationPackage和@Import(AutoConfigurationImportSelector.class)

@AutoConfigurationPackage //自动配置包
@Import(AutoConfigurationImportSelector.class) //导入哪些组件的选择器
public @interface EnableAutoConfiguration {
      ... ...
}

进入@AutoConfigurationPackage(自动配置包),有一个@Import(AutoConfigurationPackages.Registrar.class)。

@Import注解大家都知道,是Spring底层注解, 给容器中导入一个组件。

Registrar.class 将主配置类【即@SpringBootApplication标注的类】的所在包及包下面所有子包里面的所有组件扫描到Spring容器 ;

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

}

接下返回@EnableAutoConfiguration看下@Import(AutoConfigurationImportSelector.class)(给容器导入组件)。

AutoConfigurationImportSelector:自动配置导入选择器。

我们来思考下,它会导入哪些组件的选择器呢?进入这个类,我们来看源码

在类中找到这样一个方法

// 获得候选的配置
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;
}

getSpringFactoriesLoaderFactoryClass()

这个方法又调用了 SpringFactoriesLoader 类的静态方法!我们进入SpringFactoriesLoader类loadFactoryNames() 方法

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

我们继续点击查看 loadSpringFactories 方法

private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
    //获得classLoader , 我们返回可以看到这里得到的就是EnableAutoConfiguration标注的类本身
        MultiValueMap<String, String> result = cache.get(classLoader);
        if (result != null) {
            return result;
        }
        try {
        //去获取一个资源 "META-INF/spring.factories"
            Enumeration<URL> urls = (classLoader != null ?
                    classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
                    ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
            result = new LinkedMultiValueMap<>();
        //将读取到的资源遍历,封装成为一个Properties
            while (urls.hasMoreElements()) {
                URL url = urls.nextElement();
                UrlResource resource = new UrlResource(url);
                Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                for (Map.Entry<?, ?> entry : properties.entrySet()) {
                    String factoryTypeName = ((String) entry.getKey()).trim();
                    for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
                        result.add(factoryTypeName, factoryImplementationName.trim());
                    }
                }
            }
            cache.put(classLoader, result);
            return result;
        }
        catch (IOException ex) {
            throw new IllegalArgumentException("Unable to load factories from location [" +
                    FACTORIES_RESOURCE_LOCATION + "]", ex);
        }
}

我们进入FACTORIES_RESOURCE_LOCATION

看到了这个路径:META-INF/spring.factories
FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"
接下来我们就找一下这个配置文件
找到spring-boot-autoconfigure-2.2.1.RELEASE.jar - META-INF - spring.factories文件

这些都是自动配置根源

进入我们熟悉的WebMvcAutoConfiguration,都是JavaConfig配置类,而且都注入了一些Bean

 总结

springboot所有自动配置都是在启动的时候扫描并加载:spring.factories所有的自动配置类都在这里面,但不一定生效,要判断条件是否成立,只要导入对应的start,就有对应的启动器了,有了启动器,我们自动装配就会生效,然后就配置成功。

结论

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

流程图:https://www.processon.com/view/link/5de763fee4b00e6d9017b5e5

run()方法

@SpringBootApplication
public class Springboot01Application {
    public static void main(String[] args) {
        //该方法返回一个ConfigurableApplicationContext对象
        //参数一:应用入口的类     参数类:命令行参数
        SpringApplication.run(Springboot01Application.class, args);
    }
}    

run()做了四件事:

  1. 推断应用的类型是普通的项目还是Web项目
  2. 查找并加载所有可用初始化器,设置到initializers属性中
  3. 找出所有的应用程序监听器,设置到listeners属性中
  4. 推断并设置main方法的定义类,找到运行的主类

posted @ 2019-12-04 14:22  如何下笔呢  阅读(595)  评论(0编辑  收藏  举报
levels of contents