IOC - 注册 bean
注册 bean 的方式
1. @Configuration
+ @Bean
@Configuration
public class MyConfig { // 当前类也会注册成为一个 bean
@Bean
public User user(){ // 注册 bean
retu new User();
}
}
2. @Component
标在哪个类上,哪个类就会注册到容器中
类似功能还有 @Service
、@Repository
、@Controller
。分别对应 业务层、持久层、控制层 的 bean。这三个都具有 MVC 语义
非 MVC 的就使用 @Component
3. @Import
两个场景:1,想把 jar 包里面的某个类注册到容器中;2,一次性导入多个 bean
有三种用法如下,具体使用在文章末尾
@Import({Dog.class,Person.class}) // 注册普通类
@Import(MyImportSelector.class) // 通过 ImportSelector 注册
@Import(MyImportBeanDefinitionRegister.class) // 通过 ImportBeanDefinitionRegistrar 注册
4. @ImportResource
导入 xml 配置文件中的 bean,也可以一次性导入多个 xml 配置文件
-
一个 xml 文件
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="myBean" class="com.example.MyBean" /> </beans>
-
导入这个 xml 中的 bean
@Configuration @ImportResource({"classpath:applicationContext.xml", "classpath:anotherContext.xml"}) public class ImportResourceConfig { }
5. FactoryBean
+ @Component
这是一个接口,实现 getObject
方法通过工厂方式来创建 bean,语义上是用来创建复杂的 bean
-
一个普通的类
public class MyBean { private String message; }
-
导入这个
MyBean
到容器@Component public class MyBeanFactory implements FactoryBean<MyBean> { // getObject 方法返回值就是注入的 bean @Override public MyBean getObject() throws Exception { return new MyBean(); } // 这个方法是指定 bean 的类型,已经有泛型了,为什么还要指定?因为多态,当 MyBean 有多个子类的时候可以指定为具体的子类 @Override public Class<?> getObjectType() { return MyBean.class; } // 是否是单例,true:是单例;false:不是单例 @Override public boolean isSingleton() { return true; } }
6. EnableConfigurationProperties
用于绑定配置文件和 java 对象
-
创建一个 java 对象,用于接收配置文件的配置
@PropertySource("classpath:myApplication.yml") // 指定自定义配置文件位置和名称,如果不指定就是主配置文件(application.yaml 或 application.properties) @ConfigurationProperties(prefix = "person.test") // 配置文件 person.test 的配置绑定到当前类的属性上 @Data // lombok public class MyConfig { private int id; private String name; }
-
配置文件
# 会与绑定 MyConfig.id person.test.id=101 # 会与绑定 MyConfig.name person.test.name=lisi
-
启动类使用注解
@EnableConfigurationProperties
这个注解写在哪里不重要,只要能被 Spring 扫描到就行
@EnableConfigurationProperties(MyConfig.class) @SpringBootApplication public class App { public static void main(String[] args) { SpringApplication.run(Boot2021Application.class, args); } }
-
如果想要配置文件中配置属性的时候自动提示,要导入一个依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> </dependency> <!-- 打成的 jar 中再排除,因为只是开发更直观,其实并没有也不影响 --> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <excludes> <exclude> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> </exclude> </excludes> </configuration> </plugin> </plugins> </build>
7. @Conditional
条件装配,当指定的条件成立才导入某个 bean,衍生了很多注解
这是 springboot 的,springboot 的按需加载就是很大部分都是根据这个注解完成的
@Import 使用示例
用法一:导入普通类
// 一个普通类
public class User {
private String name;
}
// 通过 @Import 把 User 注册到容器中
@Configuration
@Import(User.class)
public class SpringConfig {
public static void main(String[] args) {
AnnotationConfigApplicationContext annCtx = new AnnotationConfigApplicationContext(SpringConfig.class);
// 未定义 bean 的 id 和 name, 通过类型获取,也可以通过类的全限定名获取
User bean = annCtx.getBean(User.class);
System.out.println(bean);
}
}
用法二:通过 ImportSelector 导入
/**
* 一个实现了 ImportSelector 接口的类
*/
public class MyImportSelector implements ImportSelector {
// 参数 icm 可以获取标注了注解 @Import(MyImportSelector.class) 的类上的所有注解
@Override
public String[] selectImports(AnnotationMetadata icm) {
// 数组的每一个元素都会注册到 IOC 容器
return new String[]{User.class.getName(), Order.class.getName()};
}
}
/**
* 通过 @Import 把指定的类导入到容器
* 上面的 icm 参数能获取这个类上的所有注解信息
*/
@Configuration
@PropertySource("classpath:jdbc.properties")
@ComponentScan("com.study")
@Import(MyImportSelector.class)
@EnableAspectJAutoProxy
@EnableTransacTionManagement
public class SpringConfig {
}
用法三:通过 ImportBeanDefinitionRegistrar 导入
/**
* 一个实现了 ImportBeanDefinitionRegistrar 接口的类
*/
public class MyBeanDefinitionRegister implements ImportBeanDefinitionRegistrar {
// 参数 icm 可以获取标注了注解 @Import(MyBeanDefinitionRegister.class) 的类上的所有注解
// 参数 registry 是 BeanFactory 里面的 bean 定义注册器,可以注入 BeanDefinition
@Override
public void registerBeanDefinitions(AnnotationMetadata icm, BeanDefinitionRegistry registry) {
// 创建一个 User 的 BeanDefinition
AbstractBeanDefinition userBeanDefinition = BeanDefinitionBuilder.rootBeanDefinition(User.class).getBeanDefinition();
// userBeanDefinition 注册到 ioc 容器(bean 的生命周期是根据 bean 定义来创建 bean)
registry.registerBeanDefinition("user", beanDefinition);
}
}
/**
* 通过 @Import 把指定的类导入到容器
* 上面的 icm 参数能获取这个类上的所有注解信息
*/
@Configuration
@Import(User.class)
@Import(MyBeanDefinitionRegister.class)
public class SpringConfig {
}
条件注册使用示例
Spring 批处理的条件配置
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({JobLauncher.class, DataSource.class})
@AutoConfigureAfter(HibernateJpaAutoConfiguration.class)
@ConditionalOnBean(JobLauncher.class)
@EnableConfigurationProperties(BatchProperties.class) // 自定义配置类
@Import(BatchConfigurerConfiguration.class)
public class BatchAutoConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(prefix = "spring.batch.job", name = "enabled", havingValue = "true", matchIfMissing = true)
public JobLauncherApplicationRunner jobLauncherApplicationRunner(JobLauncher jobLauncher, JobExplorer jobExplorer,
JobRepository jobRepository, BatchProperties properties) {
...
}
...
}
-
@Configuration(proxyBeanMethods = false)
spring 5.2 引入的,如果是 true(默认就是true),spring 会使用 CGLIB 动态代理来增强 bean 方法,在同一配置类中调用其他 bean 方法,Spring 将始终返回同一个实例
如果这个 bean 的创建频率很低,低到直接创建 bean 对象的开销小于创建代理的开销,这时可以设置为 false,不创建代理
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration(proxyBeanMethods = false) public class AppConfig { // 注册一个 myService bean @Bean public MyService myService bean() { return new MyService(); } // 注册一个 myController bean,依赖 myService // 因为 proxyBeanMethods 是 false,myService 不是 IOC 容器中的,而是直接 new 出来的 @Bean public MyController myController() { return new MyController(myService()); // 直接调用 myService() 方法 } }
-
@ConditionalOnClass({JobLauncher.class, DataSource.class})
当项目中存在 JobLauncher、DataSource 类时才注册当前 bean(BatchAutoConfiguration)到 IOC 容器
如果不存在,BatchAutoConfiguration 这个类就不注册了,类下的所有使用了 @Bean 的组件也就都不会注册了
如果用在 @Bean 上就只是控制这一个 bean,用在类上可以控制一批 bean
-
@AutoConfigureAfter(HibernateJpaAutoConfiguration.class)
当 HibernateJpaAutoConfiguration 类型的 bean 注册后,再注册当前 bean(BatchAutoConfiguration)
和 DependsOn 有异曲同工之妙
-
@ConditionalOnBean(JobLauncher.class)
当存在 JobLauncher 这种类型的 bean 时才配置当前 bean(BatchAutoConfiguration)
-
@EnableConfigurationProperties(BatchProperties.class)
启用 BatchProperties 配置类,如果 BatchProperties 使用 Component 标注了,可以不使用 EnableConfigurationProperties 注解
BatchProperties 类上面配置了 ConfigurationProperties 注解,表示把配置文件的值映射到 BatchProperties 上
// 因为当前类没有使用 @Component,不是一个 spring bean,所以要使用 EnableConfigurationProperties 注解来开启配置 // 表示配置文件中 spring.batch 打头的配置信息映射到当前类上 @ConfigurationProperties(prefix = "spring.batch") public class BatchProperties { // 省略属性 }
-
@Import(BatchConfigurerConfiguration.class)
注册一个 BatchConfigurerConfiguration 类型的 bean
-
@ConditionalOnMissingBean
JobLauncherApplicationRunner 类型的 bean 不存在时才注册
-
@ConditionalOnProperty(prefix = "spring.batch.job", name = "enabled", havingValue = "true", matchIfMissing = true)
- prefix+name 表示读取配置文件的值,组合起来就是要读取 spring.batch.job.enabled 这个属性
- havingValue = "true":当配置项 spring.batch.job.enabled 的值是 true 时,条件成立
- matchIfMissing = true:如果配置项缺失也视为条件满足(如果 spring.batch.job.enabled = false 就不会创建)
自定义条件注册
实现 Condition 接口,复写 matches 方法,当条件返回真才注册 bean
// 不用使用 @Component 注解
public class EvenWeekDayCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// 返回当前天是星期几(返回的是 1234567,星期天是 7)
int dayOfWeek = LocalDate.now().getDayOfWeek().getValue();
// 如果是偶数就返回 true
return dayOfWeek % 2 == 0;
}
}
根据条件注册 bean
@Component
public class MyBean3 {
// 如果是今天是 周二、周四、周六 就注册 User
@Conditional(EvenWeekDayCondition.class)
@Bean
public User user(){
return new User();
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具