SpringBoot自动配置原理分析

SpringBoot自动配置原理分析

springboot的重点是starter和autoconfiguration
版本号:springboot 2.4.3
学习顺序:

  1. 先了解springboot的starter
  2. 学习注解的作用
  3. 自动配置的原理

1.依赖管理

  • 每个springboot项目都有spring-boot-starter-parent父依赖,但spring-boot-starter-parent中并没有依赖的版本号
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.4.3</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>  
  • 版本号在spring-boot-starter-parent的父项目中,<properties>...</properties>来规定版本号
    -<dependencyManagement>...</dependencyManagement>作为依赖管理,不能被继承,只可以引用
    <parent>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-dependencies</artifactId>
      <version>2.4.3</version>
    </parent>
    
  • starter场景启动器

    官方场景启动器:spring-boot-starter-*

    第三方场景启动器:*-spring-boot-starter

    所有的场景启动器都需要依赖:

  <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
        <version>2.4.3</version>
        <scope>compile</scope>
  </dependency>

2.注解的作用

2.1 @Configuration

​ 配置类也是组件,组件既对象的意思
​ 因为@Configuration中包含@Component

  • 自定义配置类,使用@Bean标注在方法上给容器注册组件,默认也是单实例的。示例:
//自定义配置类
@Configuration
public class MyConfig {
    @Bean   //给容器中添加组件,以方法名作为组件的id,返回类型就是组件类型。返回的值就是组件在容器中的实例
    public User user(){
        return new User("zhangsan",18);
    }
}
//测试代码
@SpringBootApplication
public class SpringbootFasterApplication {
    public static void main(String[] args) {
        ConfigurableApplicationContext run = SpringApplication.run(SpringbootFasterApplication.class, args);
    	//从Spring容器中拿到两个User实例,判断对象是否相等,来判断是否为单实例
        User user1 = run.getBean("user", User.class);
        User user2 = run.getBean("user", User.class);
        System.out.println("组件:"+(user1==user2)); 
    }
}

输出结果:
组件:true
    
    ```
  • proxyBeanMethods:代理bean的方法,默认为true

​ Full(proxyBeanMethods = true)用处:保证每个@Bean方法每次被调用返回的组件都是单实例的

​ Lite(proxyBeanMethods = false)用处:每个@Bean方法每次被调返回的组件都是新创建的
示例:

//配置类中加入proxyBeanMethods = false
@Configuration(proxyBeanMethods = false)
public class MyConfig {
    @Bean                     //给容器中添加组件,以方法名作为组件的id,返回类型就是组件类型。返回的值就是组件在容器中的实例
    public User user(){
        return new User("zhangsan",18);
    }
}

@SpringBootApplication
 public class SpringbootFasterApplication {
     public static void main(String[] args) {
         ConfigurableApplicationContext run = SpringApplication.run(SpringbootFasterApplication.class, args);
      	//从Spring容器中获取myConfig对象,调用MyConfig中的方法,Full(proxyBeanMethods = false时,每次的组件都是新创建的
         MyConfig myConfig = run.getBean(MyConfig.class);
         User user1 = myConfig.user();
         User user2=myConfig.user();
         System.out.println("组件:"+(user1==user2));
     }
 }
 
输出结果:
组件:false

2.2 创建组件的几种注解

@Bean、@Component、@Controller@Service、@Repository

这里新加个@Import注解,可以给容器中自动创建出某个类型的组件、默认组件的名字为全类名。可以使Springboot里自带的类,只需要导入字节码文件。示例:

@Import({User.class})
@Configuration
public class MyConfig {
    @Bean                 
    public User user(){
        return new User("zhangsan",18);
    }
}

2.3 条件装配@Conditional

满足Conditional指定的条件,则进行组件注入

2.4 @ImportResource

可以导入Spring原生配置文件
@ImportResource(“classpath:beans.xml”)
public class MyConfig {}

2.5 配置绑定@ConfigurationProperties

application.properties配置文件

 server.name=mycat
@ConfigurationProperties(prefix = "server")
public class Cat {}
//1、开启Cat配置绑定功能
//2、把这个Cat这个组件自动注册到容器中
@EnableConfigurationProperties(Cat.class)
public class MyConfig {
     @Bean             
      public User user(){
          return new User("zhangsan",18);
      }
 }
  
@SpringBootApplication
public class SpringbootFasterApplication {
    public static void main(String[] args) {
        ConfigurableApplicationContext run = SpringApplication.run(SpringbootFasterApplication.class, args);
        Cat cat =(Cat) run.getBean(Cat.class);
        System.out.println(cat);
    }
}

输出结果:
Cat{name='mycat', age=0}

3.自动配置原理

@SpringBootApplication包含三个注解:

  • @SpringBootConfiguration 代表当前是一个配置类
  • @EnableAutoConfiguration(重要)
  • @ComponentScan 制定扫描哪些

@EnableAutoConfiguration包含:

  • @AutoConfigurationPackage
  • @Import({AutoConfigurationImportSelector.class})

3.1 @AutoConfigurationPackage

作用:

  • 利用Registrar给容器中导入一系列组件
  • 将指定的一个包下的所有组件导入进来。
    源码分析: 断点调试
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {}
  
//点开@AutoConfigurationPackage
@Import({Registrar.class})
public @interface AutoConfigurationPackage {}

点开Registrar.class

static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
      Registrar() {
       }

       public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
           AutoConfigurationPackages.register(registry, (String[])(new AutoConfigurationPackages.PackageImports(metadata)).getPackageNames().toArray(new String[0]));
       }

       public Set<Object> determineImports(AnnotationMetadata metadata) {
           return Collections.singleton(new AutoConfigurationPackages.PackageImports(metadata));
       }
}

AnnotationMetadata metadata代表注解的原信息,@Import({Registrar.class})这个注解标注在AutoConfigurationPackage上,相当于标注在主引导类SpringbootFasterApplication上
在这里插入图片描述
在这里插入图片描述
结论:将包名为com.yulong下的所有组件导入到spring 容器中

3.2@Import(AutoConfigurationImportSelector.class)

利用getAutoConfigurationEntry(annotationMetadata)给容器中批量导入一些组件

public String[] selectImports(AnnotationMetadata annotationMetadata) {
    if (!this.isEnabled(annotationMetadata)) {
        return NO_IMPORTS;
    } else {
        AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
        return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
    }
}


protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(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.getConfigurationClassFilter().filter(configurations);
            this.fireAutoConfigurationImportEvents(configurations, exclusions);
            return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
        }
    }
    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;
    }
 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());
    }
    
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
	Enumeration urls = classLoader.getResources("META-INF/spring.factories");
}
  1. 利用getAutoConfigurationEntry(annotationMetadata);给容器中批量导入一些组件

  2. 调用List configurations =
    getCandidateConfigurations(annotationMetadata, attributes)获取到所有需要导入到容器中的配置类

  3. 利用工厂加载 Map<String, List> loadSpringFactories(@NullableClassLoader classLoader);得到所有的组件

  4. 从META-INF/spring.factories位置来加载一个文件。
    默认扫描我们当前系统里面所有META-INF/spring.factories位置的文件
    spring-boot-autoconfigure-2.4.3.RELEASE.jar包里面也有META-INF/spring.factories 在这里插入图片描述虽然我们133个场景的所有自动配置启动的时候默认全部加载。xxxxAutoConfiguration

    按照条件装配规则(@Conditional),最终会按需配置。

starter场景=》xxxAutoConfiguration=》通过xxxAutoConfiguration中的@Bean导入xxx组件=》绑定xxxProperties=》绑定配置文件项

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值