springBoot的启动

自动配置注解的逻辑: 在启动类的基础上,导入了springboot的大量自动配置类,以至于自己不用关心配置实现过程(约定大于配置)

大量自动配置类是如何导入的?

Spring提供了一个SpringFactories功能(SPI: service provider interface ),读取固定文件META-INF/spring.factories,按照格式配置的内容会被加载,对应EnableAutConfiguration就是springboot要使用的自动配置类.

交给一个导入的Selector,通过类的全路径名称,实现配置类的加载,通过条件的判断,筛选满足的配置类.

所有的自动配置类都是springboot完成的? 不是,有第三方扩展的

mybatis-spring-boot-starter: mybatis-spring-boot-autoconfigure

dubbo-spring-boot-starter: dubbo-spring-boot-autoconfigure

knife4j-spring-boot-starter:knife4j-spring-boot-autoconfigure

所有的自动配置类都需要加载? 不是,加载不加载取决于条件注解是否满足

@ConditionalOnClass

@ConditionalOnMissingClass

@ConditionalOnBean

@ConditionalOnMissingBean

@ConditionalOnProperty 注解属性

prefix 前缀

name/value 属性名

havingValue 是否有值并且相等

matchIfMissing 如果没有这个属性,是否匹配(如果matchIfMissing 值为true,则表示没有)

spring.factories作用:

————————————————

在Spring Boot启动时,它会扫描classpath下所有的spring.factories文件,加载其中的自动配置类,并将它们注入到Spring ApplicationContext中,使得项目能够自动运行。该文件的格式为键值对,键是自动配置类的全限定名,值是该自动配置类所对应的配置类的全限定名。

一、主要的注解:

@SpringBootApplication是springBoot最核心的一个组合注解 ,  其组合了如下注解:
  • SpringApplicationConfiguration: 包装了一个@Configuration的注解,所以有这个注解的类本身就是一个配置类(启动类是个配置).

  • ComponentScan: 扫描 自定义的各种注解所在的包,比如service controller 全局异常捕获(加载和扫描我们自己的业务逻辑).

  • EnableAutoConfiguration: 开启自动配置加载(自动加载配置文件)

二、启动步骤:

2.1.导入选择器:

  • @SpringBootApplication注解上标注的@EnableAutoConfiguration用Import导入了一个选择器类AutoConfigurationImportSelector
    

@Import(AutoConfigurationImportSelector.class)

2.2.执行selectImports方法:

执行选择器的selectImports方法

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

方法返回结果是一个数组String[] ,元素是自动配置类全路径名称.

springBoot的启动_第1张图片

然后就会导入这些配置类(只导入符合条件的配置类<会用@Condition注解设置条件>)(这些配置类叫做自动配置类)。

问题:如此多的配置类,不可能在一个项目中全部使用.

要进行过滤和筛选,哪些有效,哪些无效.

3.4.3 条件注解@Conditional选择

在众多的自动配置类中,就需要满足一定条件,才加载此配置类.(不满足条件,配置类不加载的.)

这个功能是spring提供的条件注解,在springboot做了衍生.

一个springboot工程启动,如果观察哪些配置满足条件,可以使用debug模式,对应root范围.

 springBoot的启动_第2张图片

会在日志中出现提示:  matched configuration或unmatched configuration, 提示哪些条件没有满足.

下边是一些衍生注解:

  • @ConditionalOnClass: 标注在类上的注解和标注在方法上的注解,标注在类上 条件满足(内存中有满足条件的类)类加载, 标注在方法上条件满足方法加载.不满足,则不加载.

指定class必须在依赖环境中存在.存在则满足,不存在则不满足

  • @ConditionalOnMissingClass:与上面的条件逻辑是相反的.存在则不满足,不存在则满足

  • @ConditionalOnBean:类注解和方法注解, 某个限定条件的bean对象在容器中存在 则满足,不存在则不满足

  • @ConditionalOnMissingBean:与上面条件逻辑相反.一般是留给自定义扩展的

  • @ConditionalOnProperty:标注在类上的注解和标注在方法上的注解,标注在类上 条件满足(内存中有满足条件的属性<变量>)就加载此配置类,否则不加载

注意: 这些条件注解可以添加在配置类上也可以加在方法上(条件满足执行方法)

测试案例: 底下是一个用来测试@ConditionalOnProperty和@ConditionalOnMissingClass的案例

1.配置了@ConditionalOnProperty的配置类:

package cn.zyq.stater.config2;

import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Configuration;

@Configuration
//ConditionalOnProperty 描述:  test4.username=root
//  属性有test4.password=root 的属性数据 条件则满足,没有,或者值不是123456都不满足
//- 使用入口配置类扫描这个测试
//matchIfMissing表示如果条件不满足要不要执行(如果matchIfMissing = true表示test4.password=root 不存在也表示Condition满足)
@ConditionalOnProperty(prefix = "test4", value = "password" ,havingValue = "root" ,matchIfMissing = true)
public class Config6_OnCondition {
    public Config6_OnCondition(){
        System.out.println("条件配置类Config6_OnCondition ,条件满足, 加载此类");
    }
}

2.扫描此配置类的Config6_OnCondition2:

package cn.zyq.stater.config2;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

@Configuration
@ComponentScan(basePackages= {"cn.zyq.stater.config2"})//扫描Config6_OnCondition并创建此配置类的对象
@PropertySource(value = "classpath:jdbc.properties",ignoreResourceNotFound = false)
public class Config6_OnCondition2 {

}

3. jdbc.properties:

test4.username=root
test4.password=root

4.测试类:

package cn.zyq.stater;

import cn.zyq.stater.config2.Config6_OnCondition2;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Test6 {

    @Test
    public void test6(){
        AnnotationConfigApplicationContext ac3=new AnnotationConfigApplicationContext(Config6_OnCondition2.class);
    }

}

3.4.4.导入的众多配置类位置:

通过selector导入String[]数组中的配置类, 在哪里? (springboot如果将其写死.没有扩展的空间了).

springboot提供了自动配置的导入逻辑,需要准备一个META-INF/spring.factories的文件,这个文件的格式,可以参考spring-boot-autoconfigure中的内容.

springBoot的启动_第3张图片

 springBoot的启动_第4张图片

加载自动配置的逻辑全部介绍完了.

总结:

  1. 启动类的核心注解(EnableAutoConfiguration)

  2. 叫Enable的注解都在导入

  3. Import注解导入一个selector选择器(返回一堆配置类的全路径名称String[])  读取META-INF/spring.factories的文件中的key和value

@Import(AutoConfigurationImportSelector.class)

     注意: 并不是所有String[]中的自动配置类都加载,需要满足条件注解

  

三、自动配置案例:

3.1.META-INF/spring.factories 

对应代码:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
cn.zyq.stater.config3.autoconfig.ZyqAutoConfigure

 3.2.自动配置类:

springBoot的启动_第5张图片

对应代码:

package cn.zyq.stater.config3.autoconfig;

import cn.zyq.stater.bean.User1;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

@Configuration//本类作为配置类,并且在spring.factories中进行配置了,如果本项目作为jar引入到其他项目中了
//即使不启动本项目, 引入本jar的项目启动时会根据spring.factories加载本配置类.然后根据 @ConditionalOnProperty判断是否会执行createUser1方法
@PropertySource(value = "jdbc.properties", ignoreResourceNotFound=false)
@Slf4j
public class ZyqAutoConfigure {

    //本类ZyqAutoConfigure作为配置类是肯定会被加载到内存的,但本方法createUser1()在加载时是否会执行要看@ConditionalOnProperty条件是否成立
    @Bean
    //首先要加载了本配置类,  然后还要条件满足,  就会创建Bean对象(User1类型),(条件不满足,user对象不创建<需要用到jdbc.properties中的配置信息>)
    @ConditionalOnProperty(prefix = "zyq", value = "enable", havingValue = "true" , matchIfMissing = false)
    public User1 createUser1(){
        log.info("条件满足1,createUser1()创建User1对象");
        System.err.println("条件满足2,createUser1()创建User1对象");
        return  new User1();
    }

}

 项目主启动类运行时就会自动加载spring.factories , 然后就会加载ZyqAutoConfigure配置类,然后会根据条件执行createUser1()方法。

你可能感兴趣的:(springboot,spring,boot,java,spring)