basic - bean配置 & 注册 · SpringBoot3

bean

yaml配置Bean

单点注入

@Value("${键名}")

代码块YAML · 3 行收起展开
email:
	user: nash
	password: 798596
代码块JAVA · 8 行收起展开
@Component 
public class EmailProperties{
	@Value("${email.user}")
	public String user;
	
	@Value("${email.password}")
	public String code;
}

结构化注入

利用@ConfigurationProperties(prefix="前缀"), 添加后该类自动将成员变量匹配上yml文件的字段

代码块YAML · 3 行收起展开
email:
  user: nash
  password: 123
代码块JAVA · 6 行收起展开
@Component
@ConfigurationProperties(prefix = "email")
public class EmailProperties {
    private String user;
    private String password;
}

bean管理

bean的注册

注解说明位置
@Component声明bean的基础注解不属于以下三类时,用此注解
@Controller@Component的衍生注解标注在控制器类上
@Service@Component的衍生注解标注在业务类上
@Repository@Component的衍生注解标注在数据访问类上(由于与mybatis整合,用的少)

流程

默认扫描规则 : Spring Boot 默认扫描“启动类所在包”及其子包

条件注册

注解说明
@ConditionalOnProperty配置文件中存在对应的属性, 才声明 该Bean
@ConditionalOnMissingBean当不存在当前类型的Bean时, 才声明 该Bean
@ConditionalOnClass当前环境存在指定的这个类时, 才声明该Bean

导入第三方bean到容器

bug

第三方bean无妨用@Component及其衍生注解bean

解决

1.配置类 @Configuration + @Bean
  • 写一个Config类并用@Configuration修饰, 启动即扫此类 在配置类编写方法, 方法的返回值即为 第三方bean

  • 第三方bean需要依赖其他已注入的bean时, 将已注入的bean作为形参

2.@Import + @Bean
  • 在启动类加 @Import(配置类.class), 多个可以写成数组@Import({,,,})
  • 如果实在太多可以导入ImportSelector接口的实现类, 然后重写selectImports方法, 返回我们需要导入的包(是一个String[]{"","",""})
  • 耦合度太高, 不够动态
3.配置文件
代码块LUA · 9 行收起展开
配置文件 common.imports

把要导入的配置类全写进去

ImportSelector 读取这个文件

把类名返回给 Spring

Spring 按这些类名去注册 bean

配置文件导入法里,配置文件保存的是要导入的配置类全限定名, ImportSelector 负责读取这些类名并返回 String[], 再通过 @Import(ImportSelector实现类.class) 让 Spring 加载这些配置类,最终由配置类中的 @Bean 注册第三方 bean。

3.1 resources/common.imports

此文件即名单表, 里面一般写:

代码块YAML · 3 行收起展开
com.xxx.AConfig
com.xxx.BConfig
com.xxx.CConfig

意即这些类导入到Spring容器中 注意这里写的是配置类的路径, 而不是Bean!!!! Bean一般写在配置类里面作为方法的返回值返回

3.2 CommonImportSelector

核心方法:

代码块JAVA · 1 行收起展开
public String[] selectImports(...)

返回的即为我们要导入的类

3.2.1 读配置文件

代码块JAVA · 3 行收起展开
InputStream is = CommonImportSelector.class
        .getClassLoader()
        .getResourceAsStream("common.imports");

此时会读取所有的配置类, 并且把所有@Bean修饰的都去注册为第三方bean

3.2.2 实现ImportSelector接口 在ImportSelector接口的子实现类中重写方法selectImports方法, 并且封装成String[]作为返回值

代码块JAVA · 30 行收起展开
public String[] selectImports(AnnotationMetadata importingClassMetadata) {

    List<String> imports = new ArrayList<>(); //每个bean的存类路径
	//1.读取配置文件
    InputStream is = CommonImportSelector.class
            .getClassLoader()
            .getResourceAsStream("common.imports");//配置文件
var br = new BufferedReader(new InputStreamReader(is));//放入
	
	//2.按行读
    String line = null;
    try {
        while ((line = br.readLine()) != null) {
            imports.add(line);
        }
    } catch (IOException e) {
        throw new RuntimeException(e);
    } finally {
        if (br != null) {
            try {
                br.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }
    
	//list->String[]
    return imports.toArray(new String[0]);
}

3.2.3 在启动类 @Import(CommonImportSelector.class) //ImportSelector接口的子实现类.class

代码块LUA · 13 行收起展开
启动类启动

看到 @Import(CommonImportSelector.class)

执行 CommonImportSelector

读取 common.imports

拿到配置类名

导入这些配置类

配置类里的 @Bean 生效
代码块JAVA · 13 行收起展开
@SpringBootApplication
@Import(CommonImportSelector.class)
//让 Spring 启动时调用它的 `selectImports()`。
public class SpringbootRegisterApplication {
    public static void main(String[] args) {
        ApplicationContext context =	 SpringApplication.run(SpringbootRegisterApplication.class, args);

        Country country = context.getBean(Country.class);
        System.out.println(country);

        System.out.println(context.getBean("province"));
    }
}

Bean自动配置原理

1 @SpringBootApplication 是个组合注解 它包含了 @EnableAutoConfiguration,这是自动装配的入口。

2 @EnableAutoConfiguration 上挂着 @Import(AutoConfigurationImportSelector.class)

这个 selector 是核心 — 它实现了 selectImports(...) 方法,Spring 启动时会调用它来决定要导入哪些配置类。

3 selectImports 去读那个文件

就是图里那个路径:META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

这个文件里列了几百个全限定类名,比如图右上角那些 DispatcherServletAutoConfigurationServletWebServerFactoryAutoConfiguration 等等,都是候选配置类。

4 每个候选类上有条件注解做过滤

比如 DispatcherServletAutoConfiguration 上有 @ConditionalOnClass(DispatcherServlet.class) — classpath 里有这个类才生效。所以不是所有配置都会被装配,是按条件按需激活的 💡

整体就是:注解触发 → selector 读文件 → 拿到候选列表 → 条件过滤 → 真正注册 Bean

Pasted image 20260402165037