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 配置文件

  1. 一个 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>
    
  2. 导入这个 xml 中的 bean

    @Configuration
    @ImportResource({"classpath:applicationContext.xml", "classpath:anotherContext.xml"})
    public class ImportResourceConfig {
        
    }
    

5. FactoryBean + @Component

这是一个接口,实现 getObject 方法通过工厂方式来创建 bean,语义上是用来创建复杂的 bean

  1. 一个普通的类

    public class MyBean {
        private String message;
    }
    
  2. 导入这个 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 对象

  1. 创建一个 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;
    }
    
  2. 配置文件

    # 会与绑定 MyConfig.id
    person.test.id=101
    
    # 会与绑定 MyConfig.name
    person.test.name=lisi
    
  3. 启动类使用注解 @EnableConfigurationProperties

    这个注解写在哪里不重要,只要能被 Spring 扫描到就行

    @EnableConfigurationProperties(MyConfig.class)
    @SpringBootApplication
    public class App {
    
        public static void main(String[] args) {
    
            SpringApplication.run(Boot2021Application.class, args);
        }
    
    }
    
  4. 如果想要配置文件中配置属性的时候自动提示,要导入一个依赖

    <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) {
		...
	}
	...

}

  1. @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() 方法
        }
    }
    
  2. @ConditionalOnClass({JobLauncher.class, DataSource.class})

    当项目中存在 JobLauncher、DataSource 类时才注册当前 bean(BatchAutoConfiguration)到 IOC 容器

    如果不存在,BatchAutoConfiguration 这个类就不注册了,类下的所有使用了 @Bean 的组件也就都不会注册了

    如果用在 @Bean 上就只是控制这一个 bean,用在类上可以控制一批 bean

  3. @AutoConfigureAfter(HibernateJpaAutoConfiguration.class)

    当 HibernateJpaAutoConfiguration 类型的 bean 注册后,再注册当前 bean(BatchAutoConfiguration)

    和 DependsOn 有异曲同工之妙

  4. @ConditionalOnBean(JobLauncher.class)

    当存在 JobLauncher 这种类型的 bean 时才配置当前 bean(BatchAutoConfiguration)

  5. @EnableConfigurationProperties(BatchProperties.class)

    启用 BatchProperties 配置类,如果 BatchProperties 使用 Component 标注了,可以不使用 EnableConfigurationProperties 注解

    BatchProperties 类上面配置了 ConfigurationProperties 注解,表示把配置文件的值映射到 BatchProperties 上

    // 因为当前类没有使用 @Component,不是一个 spring bean,所以要使用 EnableConfigurationProperties 注解来开启配置
    // 表示配置文件中 spring.batch 打头的配置信息映射到当前类上
    @ConfigurationProperties(prefix = "spring.batch")
    public class BatchProperties {
    	// 省略属性
    }
    
  6. @Import(BatchConfigurerConfiguration.class)

    注册一个 BatchConfigurerConfiguration 类型的 bean

  7. @ConditionalOnMissingBean

    JobLauncherApplicationRunner 类型的 bean 不存在时才注册

  8. @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();
    }
}

posted @   CyrusHuang  阅读(6)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示