了解自动配置原理

Mr.LR2022年10月30日
大约 6 分钟

了解自动配置原理

SpringBoot特点

依赖管理

父项目做依赖管理

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.3.4.RELEASE</version>
</parent>

它的父项目

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

在这个父项目中properties定义了很多jar包版本号,因此我们在引入相关依赖时,不用指定版本号

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    如果不想用父依赖的相关版本号,可以pom定义properties然后写相关版本。或者直接在这里写版本
</dependency>

开发导入starter场景启动器

1、见到很多 spring-boot-starter-* : *就某种场景
2、只要引入starter,这个场景的所有常规需要的依赖我们都自动引入
3、SpringBoot所有支持的场景
https://docs.spring.io/spring-boot/docs/current/reference/html/using-spring-boot.html#using-boot-starter
4、见到的  *-spring-boot-starter: 第三方为我们提供的简化开发的场景启动器。
5、所有场景启动器最底层的依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

自动配置

spring-boot-starter-web里面引入了很多依赖

自动配好tomcat

引入tomcat依赖,配置tomcat

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

自动配好springMVC

引入SpringMVC全套组件,自动配好SpringMVC常用组件(功能)

自动配置好Web常见功能,如:字符编码问题

@SpringBootApplication
public class MainApplication {
    public static void main(String[] args) {
        //ioc容器
        ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class);

        //查看容器里面的组件
        String[] beanDefinitionNames = run.getBeanDefinitionNames();

        for(String name:beanDefinitionNames){
            System.out.println(name);
        }

    }
}

输出

image-20220929180647952

默认的包结构

主程序所在包及其下面的所有子包里面的组件都会被默认扫描进来无需以前的包扫描配置

想要改变扫描路径,@SpringBootApplication(scanBasePackages="com.kkrot")

或者@ComponentScan 指定扫描路径

@SpringBootApplication
等同于
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan("com.kkrot.boot")

各种配置拥有默认值

默认配置最终都是映射到某个类上,如:MultipartProperties

配置文件的值最终会绑定每个类上,这个类会在容器中创建对象

按需加载所有自动配置项

非常多的starter 引入了哪些场景这个场景的自动配置才会开启 SpringBoot所有的自动配置功能都在 spring-boot-autoconfigure 包里面

容器功能

组件添加

@Configuration

/**
 * 1、配置类里面使用@Bean标注在方法上给容器注册组件,默认也是单实例的
 * 2、配置类本身也是组件
 * 3、proxyBeanMethods:代理bean的方法
 *      Full(proxyBeanMethods = true)、【保证每个@Bean方法被调用多少次返回的组件都是单实例的】
 *      Lite(proxyBeanMethods = false)【每个@Bean方法被调用多少次返回的组件都是新创建的】
 *      组件依赖(user里面有pet属性)必须使用Full模式默认。其他默认是否Lite模式
 */
@Configuration(proxyBeanMethods = true)
public class MyConfig {

    @Bean
    public User getUser(){
        User user = new User();
        //user组件依赖了Pet组件
        user.setPet(getPet());
        return user;
    }

    @Bean("tom")
    public Pet getPet(){
        Pet pet = new Pet();
        pet.setName("kk");
        return pet;
    }
}
//主启动类
@SpringBootApplication
public class MainApplication {
    public static void main(String[] args) {
        //ioc容器
        ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class);


        //配置类也可以获取
        MyConfig bean  = run.getBean(MyConfig.class);
        System.out.println(bean);
        //如果@Configuration(proxyBeanMethods = true)代理对象调用方法。
        //SpringBoot总会检查这个组件是否在容器中有。
        //保持组件单实例
        User user = bean.getUser();
        User user1 = bean.getUser();
        System.out.println(user == user1);

        //组件依赖,则proxyBeanMethods = true
        User user01 = run.getBean("getUser", User.class);
        Pet tom = run.getBean("tom", Pet.class);

        System.out.println("用户的宠物:"+(user01.getPet() == tom));

    }
}

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

@ComponentScan、@Import

ComponentScan:根据定义的扫描路径,把符合扫描规则的类装配到spring容器中

Import:

  1. @Import通过快速导入的方式实现把实例加入spring的IOC容器中
  2. @Import注解的作用和在使用spring的xml配置时用到的类似。但应注意是@Import在使用时,
  3. 必须要保证能被IOC容器扫描到,所以通常它会和@Configuration或者@ComponentScan配套使用。
  4. @Import在使用时可以声明在JAVA类上,或者作为元注解使用(即声明在其他注解上)
@Import({User.class, DBHelper.class})
@Configuration() 
public class MyConfig {
}

@Conditional

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

@ConditionalOnBean(name = "tom")//当容器中,有tom 下面这些注册才会生效  也可以加在方法上
public class MyConfig {

原生配置文件引入

@ImportResource

引入原生xml配置文件中的相关配置

@ImportResource("classpath:bean.xml")
public class MyConfig {
User user = run.getBean("userXml",User.class);
System.out.println(user);

配置绑定

@ConfigurationProperties

@Component
@ConfigurationProperties(prefix = "mycar")
public class Car {
    ........
}    
mycar.name=BB
mycar.price=10000
@RestController
public class HelloController {

    @Autowired
    private Car car;

    @RequestMapping("/car")
    public String car(){
        return car.toString();
    }
}

@ConfigurationProperties

@EnableConfigurationProperties(Car.class)
//1、开启Car配置绑定功能
//2、把这个Car这个组件自动注册到容器中
public class MyConfig {
}

自动配置原理入门

引导加载自动配置类

@SpringBootApplication 下有三个注解

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

@SpringBootConfiguration

@Configuration。代表当前是一个配置类

@ComponentScan

指定扫描哪些。

EnableAutoConfiguration

这个注解下也有两个注解

@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})

@AutoConfigurationPackage

自动配置包?指定了默认的包规则

@Import(AutoConfigurationPackages.Registrar.class)  //给容器中导入一个组件
public @interface AutoConfigurationPackage {}

//利用Registrar给容器中导入一系列组件
//将指定的一个包下的所有组件导入进来?MainApplication 所在包下。 
//所以也就是为什么 默认扫描的包在主启动类包下

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));
    }
}

@Import({AutoConfigurationImportSelector.class})

1、利用getAutoConfigurationEntry(annotationMetadata);给容器中批量导入一些组件
2、调用List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes)获取到所有需要导入到容器中的配置类
3、利用工厂加载 Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader);得到所有的组件
4、从META-INF/spring.factories位置来加载一个文件。
	默认扫描我们当前系统里面所有META-INF/spring.factories位置的文件
    spring-boot-autoconfigure-2.3.4.RELEASE.jar包里面也有META-INF/spring.factories
    

按需开启自动配置项

虽然我们127个场景的所有自动配置启动的时候默认全部加载。xxxxAutoConfiguration 按照条件装配规则(@Conditional),最终会按需配置。

修改默认配置

SpringBoot默认会在底层配好所有的组件。但是如果用户自己配置了以用户的优先。

例如:如果我们想替换底层的字符集的配置,则在我们自己的配置类里面,加如下代码即可。

@Bean
	@ConditionalOnMissingBean//如果容器中有该bean 则不被注册  以用户配置的优先
	public CharacterEncodingFilter characterEncodingFilter() {
    }

总结:

  • SpringBoot先加载所有的自动配置类 xxxxxAutoConfiguration

  • 每个自动配置类按照条件进行生效,默认都会绑定配置文件指定的值。xxxxProperties里面拿。xxxProperties和配置文件进行了绑定

  • 生效的配置类就会给容器中装配很多组件

  • 只要容器中有这些组件,相当于这些功能就有了

  • 定制化配置

    • 用户直接自己@Bean替换底层的组件
  • 用户去看这个组件是获取的配置文件什么值就去修改。

xxxxxAutoConfiguration ---> 组件 ---> xxxxProperties里面拿值 ----> application.properties

参考

上次编辑于: 2022/11/9 00:17:03
贡献者: liurui_60837