springBoot2-9----自动配置原理

1、主启动类

package com.boot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan;

/*
* @SpringBootApplication:表示这是一个springboot应用
* */
@SpringBootApplication
public class MyApplication {
    public static void main(String[] args) {
        ConfigurableApplicationContext run = SpringApplication.run(MyApplication.class, args);
        
    }
}

2、SpringBootApplication

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(......)
public @interface SpringBootApplication 

2.1、@SpringBootConfiguration

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.springframework.boot;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.AliasFor;
import org.springframework.stereotype.Indexed;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@Indexed
public @interface SpringBootConfiguration {
    @AliasFor(
        annotation = Configuration.class
    )
    boolean proxyBeanMethods() default true;
}

可以发现SpringBootConfiguration注解的底层是由Configuration注解修饰的,因此有引出来新的问题:

Configuration主要是干什么的?

由**@Configuration**修饰的类就相当于spring中常用的配置文件(bean.xml)

由此可以看出咱使用的SpringBootApplication注解类也是一个配置类

测试

需要的实体类:

package com.boot.pojo;

public class Pet {
    private String name;

    public Pet() {
    }

    public Pet(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Pet{" +
                "name='" + name + '\'' +
                '}';
    }
}
package com.boot;

import com.boot.pojo.Pet;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;

/*
* @SpringBootApplication:表示这是一个springboot应用
* */
@SpringBootApplication
public class MyApplication {

    @Bean
    public Pet getPet(){
        return new Pet("Tom");
    }
    public static void main(String[] args) {
        //返回IOC容器
        ConfigurableApplicationContext run = SpringApplication.run(MyApplication.class, args);

        boolean getPet = run.containsBeanDefinition("getPet");
        System.out.println("是否包含Pet组件:"+getPet);

    }
}
/***
 * OUTPUT:
 * 是否包含Pet组件:true
 */

因此可以得出我们的主启动器下也可以作为配置类使用;为了方便维护,与整洁考虑,但一般不建议把该类当做配置类。

2.2、@EnableAutoConfiguration

@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration 
=======================================================
1、@AutoConfigurationPackage

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

@Import({Registrar.class})
public @interface AutoConfigurationPackage 
//利用Registrar给容器中导入一系列组件
//将指定的一个包下的所有组件导入进来,MainApplication 所在包下。
2、Registrar.class
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
    Registrar() {
    }
/**
     * (String[])(new AutoConfigurationPackages.PackageImports(metadata))
     * .getPackageNames().toArray(new String[0])
     * 该语句会获取你创建的主启动器所在的包名
     */
    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));
    }
}
3、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
package com.boot;

import com.boot.pojo.Pet;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;

/*
* @SpringBootApplication:表示这是一个springboot应用
* */
@SpringBootApplication
public class MyApplication {

    @Bean
    public Pet getPet(){
        return new Pet("Tom");
    }
    public static void main(String[] args) {
        //返回IOC容器
        ConfigurableApplicationContext run = SpringApplication.run(MyApplication.class, args);

        boolean getPet = run.containsBeanDefinition("getPet");
        System.out.println("是否包含Pet组件:"+getPet);

        int beanDefinitionCount = run.getBeanDefinitionCount();
        System.out.println("数量是:"+beanDefinitionCount);

        boolean helloController = run.containsBeanDefinition("helloController");
        System.out.println("是否包含helloController:"+helloController);

    }
    /**
     * 是否包含Pet组件:true
     * 数量是:139
     * 是否包含helloController:true
     */

}

2.3、@ComponentScan

见名知意:指定扫描哪些,Spring注解

@Repeatable(ComponentScans.class)
public @interface ComponentScan 
@Repeatable 注解是用于声明其它类型注解的元注解,来表示这个声明的注解是可重复的。@Repeatable的值是另一个注解,其可以通过这个另一个注解的值来包含这个可重复的注解。
public @interface ComponentScans {

   ComponentScan[] value();

}
//其中,@ComponentScan注解上的元注解@Repeatable中的值,使用了@ComponentScans注解,@ComponentScans注解中包含的值类型是一个@ComponentScan注解的数组!这就解释了官方文档中@Repeatable中值的使用。

关于@Repeatable的详细内容请看:https://blog.csdn.net/weixin_42245133/article/details/99678509

2.4、@Import

用于导入组件

有上述可知一些准备工作都由@SpringBootConfiguration的父注解做好了,所以:

@SpringBootApplication
也可以有下面这三个注解代替:
* @SpringBootConfiguration
* @EnableAutoConfiguration
* @ComponentScan("com.boot")
package com.boot.config;

import com.boot.pojo.People;
import com.boot.pojo.Pet;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

@Import(Pet.class)
@Configuration(proxyBeanMethods = false)
public class MyConfig {

    @Bean("tom")
    public Pet cat(){
        return new Pet("Tom");
    }

    @Bean("mike")
    public People people(){
        People mike = new People("mike");
        mike.setPet(cat());
        return mike;
    }
}
package com.boot;

import com.boot.config.MyConfig;
import com.boot.pojo.People;
import com.boot.pojo.Pet;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;

/*
* @SpringBootApplication:表示这是一个springboot应用
* 也可以有下面这三个注解代替:
* @SpringBootConfiguration
* @EnableAutoConfiguration
* @ComponentScan("com.boot")
* */
//@SpringBootConfiguration
//@EnableAutoConfiguration
//@ComponentScan("com.boot")
@SpringBootApplication
public class MyApplication {


    public static void main(String[] args) {
        //返回IOC容器
        ConfigurableApplicationContext run = SpringApplication.run(MyApplication.class, args);

        String[] names = run.getBeanDefinitionNames();
        for (String name : names) {
            System.out.println(name);
        }

        boolean tom = run.containsBean("tom");
        boolean pet = run.containsBean("com.boot.pojo.Pet");//必须是全限定名,如果是通过import导入的
        System.out.println("是否存在(tom):"+tom);
        System.out.println("是否存在(pet):"+pet);


    }
    /**
     * 两者是否一样:true
     * MyConfig: com.boot.config.MyConfig@757194dc
     * 两者是否一样:false
     * 是/否: false
     */

}

@Import 高级用法: https://www.bilibili.com/video/BV1gW411W7wy?p=8

3、组件

1、@Configuration

指定是否应代理@Bean方法以强制执行Bean生命周期行为,例如,即使在用户代码中直接调用@Bean方法的情况下也返回共享的单例Bean实例。此功能需要方法拦截,通过运行时生成的CGLIB子类实现,该子类具有一些限制,例如不允许配置类及其方法声明final。

默认值为true,允许通过配置类内的直接方法调用进行“bean间引用”,以及外部调用此配置的@bean方法,例如从另一个配置类进行调用。如果由于此特定配置的@Bean方法都是自包含的,并且设计为容器使用的普通工厂方法,因此不需要这样做,请将此标志切换为false,以避免CGLIB子类处理。

关闭bean方法拦截可以有效地单独处理@bean方法,就像在非@Configuration类上声明时一样,也称为“@bean-Lite模式”(参见@bean的javadoc)。因此,它在行为上等同于删除@Configuration原型。

  • 基本使用

  • Full模式与Lite模式

    • 示例
    • 最佳实战
      • 配置 类组件之间无依赖关系用Lite模式加速容器启动过程,减少判断
      • 配置类组件之间有依赖关系,方法会被调用得到之前单实例组件,用Full模式
1、配置类里面使用@Bean标注在方法上给容器注册组件,默认也是单实例的
2、配置类本身也是组件
3、proxyBeanMethods:代理bean的方法
      Full(proxyBeanMethods = true)、【保证每个@Bean方法被调用多少次返回的组件都是单实例的】
      Lite(proxyBeanMethods = false)【每个@Bean方法被调用多少次返回的组件都是新创建的】
      组件依赖必须使用Full模式默认。其他默认是否Lite模式
public @interface Configuration {

   @AliasFor(annotation = Component.class)
   String value() default "";

//默认是true
   boolean proxyBeanMethods() default true;

}

测试一下

1、proxyBeanMethods = true
package com.boot;

import com.boot.pojo.Pet;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
/*
* @SpringBootApplication:表示这是一个springboot应用
* */
@SpringBootApplication
public class MyApplication {
    public static void main(String[] args) {
        //返回IOC容器
        ConfigurableApplicationContext run = SpringApplication.run(MyApplication.class, args);
        //当我们一次请求两个比较他们是否一样
        Pet pet1 = run.getBean("tom", Pet.class);
        Pet pet2 = run.getBean("tom", Pet.class);
        System.out.println("两者是否一样:"+(pet1==pet2));
        
        MyConfig bean = run.getBean(MyConfig.class);
        System.out.println("MyConfig: "+bean);
        
        People people = bean.people();
        People people1 = bean.people();
        System.out.println("两者是否一样:"+(people==people1));
        
        Pet cat = run.getBean("cat", Pet.class);
        People people2 = run.getBean("mike", People.class);
        System.out.println("是/否: "+(people2.getPet()==cat));
    }
    /**
     * 两者是否一样:true
     * MyConfig: com.boot.config.MyConfig$$EnhancerBySpringCGLIB$$8c7ff105@9f6e406
     * 两者是否一样:true
     * 是/否: true
     *
     * 说明是单例,spring容器中只存在一个实例。
     */
}
2、proxyBeanMethods = false
package com.boot;

import com.boot.config.MyConfig;
import com.boot.pojo.People;
import com.boot.pojo.Pet;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;

/*
* @SpringBootApplication:表示这是一个springboot应用
* 也可以有下面这三个注解代替:
* @SpringBootConfiguration
* @EnableAutoConfiguration
* @ComponentScan("com.boot")
* */
//@SpringBootConfiguration
//@EnableAutoConfiguration
//@ComponentScan("com.boot")
@SpringBootApplication
public class MyApplication {


    public static void main(String[] args) {
        //返回IOC容器
        ConfigurableApplicationContext run = SpringApplication.run(MyApplication.class, args);

        //当我们一次请求两个比较他们是否一样
        Pet pet1 = run.getBean("cat", Pet.class);
        Pet pet2 = run.getBean("cat", Pet.class);

        System.out.println("两者是否一样:"+(pet1==pet2));

        MyConfig bean = run.getBean(MyConfig.class);
        System.out.println("MyConfig: "+bean);

        People people = bean.people();
        People people1 = bean.people();
        System.out.println("两者是否一样:"+(people==people1));

        Pet cat = run.getBean("cat", Pet.class);
        People people2 = run.getBean("mike", People.class);
        System.out.println("是/否: "+(people2.getPet()==cat));


    }
    /**
     * 两者是否一样:true
     * MyConfig: com.boot.config.MyConfig@757194dc
     * 两者是否一样:false
     * 是/否: false
     */

}

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

3、@ComponentScan、@Import

4、@Conditional

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

image.png

5、@ImportResource

原生配置文件引入

//如果想引入咱们自己创建的spring配置文件,则需要此注解
@ImportResource("classpath:beans.xml")
public class MyConfig {}

6、配置绑定

pet.name="Tom"
@ConfigurationProperties
package com.boot.pojo;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;


@ConfigurationProperties(prefix = "pet")//这个东西好像有种规范,没仔细研究
public class Pet {
    private String name;

    public Pet() {
    }

    public Pet(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Pet{" +
                "name='" + name + '\'' +
                '}';
    }
}
package com.boot.config;

import com.boot.pojo.People;
import com.boot.pojo.Pet;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;


@Configuration()
@EnableConfigurationProperties(Pet.class)
public class MyConfig {

    @Bean
    public Pet cat(){
        return new Pet();
    }

}
测试
@SpringBootApplication
public class MyApplication {


    public static void main(String[] args) {
        //返回IOC容器
        ConfigurableApplicationContext run = SpringApplication.run(MyApplication.class, args);

        System.out.println(run.getBean("cat"));



    }
    /**
     * Pet{name='"Tom"'}
     */

}

下面是几种搭配:

  • @EnableConfigurationProperties + @ConfigurationProperties
  • @Component + @ConfigurationProperties
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值