basic - bean配置 & 注册 · SpringBoot3
bean
yaml配置Bean
单点注入
@Value("${键名}")
代码块收起展开
email:
user: nash
password: 798596代码块收起展开
@Component
public class EmailProperties{
@Value("${email.user}")
public String user;
@Value("${email.password}")
public String code;
}
结构化注入
利用@ConfigurationProperties(prefix="前缀"),
添加后该类自动将成员变量匹配上yml文件的字段
代码块收起展开
email:
user: nash
password: 123代码块收起展开
@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.配置文件
代码块收起展开
配置文件 common.imports
↓
把要导入的配置类全写进去
↓
ImportSelector 读取这个文件
↓
把类名返回给 Spring
↓
Spring 按这些类名去注册 bean
配置文件导入法里,配置文件保存的是要导入的配置类全限定名,
ImportSelector 负责读取这些类名并返回 String[],
再通过 @Import(ImportSelector实现类.class) 让 Spring 加载这些配置类,最终由配置类中的 @Bean 注册第三方 bean。
3.1 resources/common.imports
此文件即名单表, 里面一般写:
代码块收起展开
com.xxx.AConfig
com.xxx.BConfig
com.xxx.CConfig意即这些类导入到Spring容器中 注意这里写的是配置类的路径, 而不是Bean!!!! Bean一般写在配置类里面作为方法的返回值返回
3.2 CommonImportSelector
核心方法:
代码块收起展开
public String[] selectImports(...)返回的即为我们要导入的类
3.2.1 读配置文件
代码块收起展开
InputStream is = CommonImportSelector.class
.getClassLoader()
.getResourceAsStream("common.imports");此时会读取所有的配置类, 并且把所有@Bean修饰的都去注册为第三方bean
3.2.2 实现ImportSelector接口 在ImportSelector接口的子实现类中重写方法selectImports方法, 并且封装成String[]作为返回值
代码块收起展开
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
代码块收起展开
启动类启动
↓
看到 @Import(CommonImportSelector.class)
↓
执行 CommonImportSelector
↓
读取 common.imports
↓
拿到配置类名
↓
导入这些配置类
↓
配置类里的 @Bean 生效代码块收起展开
@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
这个文件里列了几百个全限定类名,比如图右上角那些 DispatcherServletAutoConfiguration、ServletWebServerFactoryAutoConfiguration 等等,都是候选配置类。
4 每个候选类上有条件注解做过滤
比如 DispatcherServletAutoConfiguration 上有 @ConditionalOnClass(DispatcherServlet.class) — classpath 里有这个类才生效。所以不是所有配置都会被装配,是按条件按需激活的 💡
整体就是:注解触发 → selector 读文件 → 拿到候选列表 → 条件过滤 → 真正注册 Bean