再看自动装配原理

自动配置

pom.xml

  • spring-boot-dependencies:核心依赖在父工程(spring-boot-starter-parent)中
  • 我们在写或者引入一些Springboot引入依赖的时候,不需要指定版本,就因为有这些版本仓库在这里插入图片描述

启动器

启动器: 就是springboot 的启动场景,比如spring-boot-starter-web 它会帮我们自动导入web环境的所有依赖。springboot会将所有的场景变成一个个的启动器。

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

我们要使用什么功能,只需要找到一个个的启动器即可。具体你需要使用什么启动器在官网查找即可:
[https://docs.spring.io/spring-boot/docs/current/reference/html/using-spring-boot.html#using-boot-starter](https://docs.spring.io/sprin g-boot/docs/current/reference/html/using-spring-boot.html#using-boot-starter)

主程序

//@SpringBootApplication:标注这个类是一个springboot的应用,就是为了启动类下的所有资源被导入
@SpringBootApplication
public class Springboot01HelloworldApplication {
    
    
    //将springboot应用启动
    public static void main(String[] args) {
    
    
        SpringApplication.run(Springboot01HelloworldApplication.class, args);
    }
}

我们从SpringBootApplication注解点进去可以发现@SpringBootConfiguration@EnableAutoConfiguration,这两个主要的注解,我们下面将这两个注解一层层的分析:

@SpringBootConfiguration

@SpringBootConfiguration:springboot的配置

  • @Configuration:spring配置类
  • @Component:说明这也是一个spring 组件

@EnableAutoConfiguration(起关键作用,很重要)

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

@AutoConfigurationPackage: 自动配置包 → @Import({Registrar.class})(导入选择器 ):Registrar.class 作用:将主启动类的所在包及包下面所有子包里面的所有组件扫描到Spring容器 ;

@Import({AutoConfigurationImportSelector.class}): 给容器导入组件
AutoConfigurationImportSelector :自动配置导入选择器,那么它会导入哪些组件的选择器呢?我们点击去这个类看源码:

1、这个类中有一个getCandidateConfigurations方法

// 获得候选的配置
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    
    
    //这里的getSpringFactoriesLoaderFactoryClass()方法
    //返回的就是我们最开始看的启动自动导入配置文件的注解类;EnableAutoConfiguration 
    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;
}

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

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

3、我们继续点击查看 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);
        }
    }
}

4、发现一个多次出现的文件:spring.factories,全局搜索它
在这里插入图片描述

我们根据源头打开spring.factories , 看到了很多自动配置的文件;这就是自动配置根源所在!
在这里插入图片描述
在这里插入图片描述我们再看loadFactoryNames,点进去看我们发现它在另外一个类(SpringFactoriesLoader)中,我们分析此函数。
在这里插入图片描述

经过loadFactoryNames函数,我们就将所有的资源都加载到了配置类中。

自动装配的执行流程图

在这里插入图片描述
这么多自动配置为什么有的没有生效?

是因为我们需要导入对应的starter 它才会生效。而在其中又起到关键性作用的是@ConditionalOnXXX当它里面的所有条件都满足的才会生效。

我们去看一个我们没有导入的start的aop组件,我们发现@ConditionalOnClass里面有些内容是报红的意思就是它没有满足生效的所有条件,所以它不会生效,判断条件成立才回去加载这个类。
在这里插入图片描述

结论

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

1.springboot在启动的时候,从类路径下/META-INF/spring.factories获取指定的值
2.将这些自动装配的类导入容器,自动装配就会生效,帮我进行装配
3.以前我们需要自动配置的东西,现在springboot帮我们做了!
4.整合javaEE,解决方案和自动装配的东西都在spring-boot-autoconfigure-2.5.2.jar这个包
5.他会把所有需要 导入的组件,以类名的方式返回,这些组件就会被添加打容器中
6.容器中也会存在非常多的xxxAutoConfiguration的文件(@Bean),就是这些类给容器中导入了这个场景需要的所有组件,@Configuration,JavaConfig
7.有了自动配置类,免去了我们手动编写配置文件!

SpringApplication

在这里插入图片描述
这个主启动类不是简简单单的main方法,而是开启的一个服务。

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

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

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

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

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

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

查看构造器:

public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) {
    
    
    // ......
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    this.setInitializers(this.getSpringFactoriesInstances();
    this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
    this.mainApplicationClass = this.deduceMainApplicationClass();
}

理解run方法执行流程
在这里插入图片描述

Guess you like

Origin blog.csdn.net/weixin_44742328/article/details/118632003