注册 bean 的方式

注册 bean 的方式

  1. XML 方式,这个已经没人使用了
  2. 单注解 @Component@Service@Controller@RestController@Respository@Configuation
  3. 组合注解 @Configuation + @Bean@Configuation + @Import@Configuation + @ImportResource
  4. @ConfigurationProperties@Condition

@Configuation + @Bean

@Configuration
public class MyConfig { // MyConfig 也会注册

    @Bean
    public User user(){ // User 会注册到容器中
    		retu new User();
    }

}

@Configuation + @ImportResource

这种方式是想兼容老项目,把 xml 中配置的 bean 批量导入容器中

@Configuration
@ImportResource({"classpath:applicationContext.xml", "classpath:anotherContext.xml"}) // 把这两个 xml 文件中的 bean 注册到容器中
public class ImportResourceConfig {
    
}

@Configuation + @Import

通过指定类批量注册

@Configuration
@Import({User.class, Person.class}) // 把 User 和 Person 注册到容器中
public class SpringConfig {

}

通过 ImportSelector 批量注册

public class MyImportSelector implements ImportSelector { // 实现 ImportSelector 接口
    
    @Override
    public String[] selectImports(AnnotationMetadata icm) {
      	// 返回一个全限定名的类的数组
        return new String[]{User.class.getName(), "com.cyrus.dto.Person"};
    }
}


@Configuration  
@PropertySource("classpath:jdbc.properties") 
@ComponentScan("com.study")
@Import(MyImportSelector.class) // 把内部返回的数组全都注册到容器中
@EnableAspectJAutoProxy 
@EnableTransacTionManagement 
public class SpringConfig {

}

selectImports(AnnotationMetadata icm) 的参数可以获取到标注了 @Import 注解的类上的注解,标注的类是 SpringConfig,SpringConfig 上的所有注解

通过 ImportBeanDefinitionRegistrar 批量注册

public class MyBeanDefinitionRegister implements ImportBeanDefinitionRegistrar { // 实现 ImportBeanDefinitionRegistrar 接口

  @Override
	public void registerBeanDefinitions(AnnotationMetadata icm, BeanDefinitionRegistry registry) {
    
    		// 注册 User 类型的 BeanDefinition
        AbstractBeanDefinition userBeanDefinition = BeanDefinitionBuilder.rootBeanDefinition(User.class).getBeanDefinition();
				registry.registerBeanDefinition("user", beanDefinition);
    
    		// 注册 Person 类型的 BeanDefinition
    		RootBeanDefinition personBeanDefinition = new RootBeanDefinition();
    		personBeanDefinition.setBeanClassName("com.cyrus.dto.Person");
    		registry.registerBeanDefinition("person", personBeanDefinition);
    
    }
}


@Configuration
@Import(User.class) // 注册 User
@Import(MyBeanDefinitionRegister.class) // 内部会把 BeanDefinition 都注册到容器中
public class SpringConfig {
 
}

Spring 内部会先注册 BeanDefinition,然后根据 BeanDefinition 的信息(比如 lazy、calss、name 等)构建出 Bean 放入到容器中

registerBeanDefinitions(AnnotationMetadata icm, BeanDefinitionRegistry registry) 参数:

  • 第一个参数和 ImportSelector 的 selectImports(AnnotationMetadata icm) 方法相同,也是获取标注了 @Import 的类上的所有注解
  • 第二个参数是 BeanDefinition 注册器,用来注册 BeanDefinition 的(不是直接注册 bean,根据 Spring 机制间接注册 Bean)

@ConfigurationProperties

这是用来绑定配置文件的,把配置文件的属性绑定到 java 对象的字段上。这个 java 对象会被注册到容器中

有多种搭配方式:@EnableConfigurationProperties@Configuration@Component@Bean

虽然也是注册bean的方式,意义上主要是读取配置文件。SpringBoot 读取配置文件

@Condition

自定义条件注册

@Configuration
public class MyBean3 {
    
    @Conditional(EvenWeekDayCondition.class) // 这个类决定 User 是否注册(内部 matches 方法返回 true 就注册,false 不注册)
    @Bean
    public User user(){
        return new User();
    }
}

// 不用标注注解,如果当前星期是偶数就返回 true
public class EvenWeekDayCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        int dayOfWeek = LocalDate.now().getDayOfWeek().getValue(); // 返回星期,从1开始
        return dayOfWeek % 2 == 0; // 是否是偶数
    }
}

Spring 批处理条件注册分析

@Configuration(proxyBeanMethods = false) // 是否对当前类下的 @Bean 使用代理拦截
@ConditionalOnClass({JobLauncher.class, DataSource.class}) // 如果 JobLauncher 和 DataSource 类存在,才进行注册
@AutoConfigureAfter(HibernateJpaAutoConfiguration.class) // 等 HibernateJpaAutoConfiguration 类型的 bean 注册了,才进行注册
@ConditionalOnBean(JobLauncher.class) // 如果 JobLauncher 类型的 bean 存在,才进行注册
@EnableConfigurationProperties(BatchProperties.class) // 启用自定义配置类
@Import(BatchConfigurerConfiguration.class) // 注册 BatchConfigurerConfiguration 的 bean
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) {
		...
	}
	...
 
}
 
  • @ConfigurationproxyBeanMethods 属性规定 当前类 下的 @Bean 如果有依赖关系,是否使用代理拦截创建

    @Configuration(proxyBeanMethods = false)
    public class ConfigA {
        @Bean
        public ServiceA serviceA() {
            return new ServiceA();
        }
        
        @Bean 
        public ServiceB serviceB() {
            // 如果 proxyBeanMethods 是 true,serviceA 会到容器中获取
          	// 这里是 false,会直接调用方法,会产生新的实例(如果要保证单例需要自行控制)
            return new ServiceB(serviceA()); 
        }
    }
    
  • @ConditionalOnClass 当指定的类如果不存在,就不进行注册(当前类下的各个 @Bean 配置的 bean 也不会注册了)

  • @ConditionalOnBean 同上,当指定的 bean 如果不存在,就不进行注册

  • @AutoConfigureAfter 等指定的 bean 先配置完成,再注册当前类。指定 bean 的创建顺序,和 @DependsOn 有类似效果,但两者也有区别

    1. AutoConfigureAfter 依赖的 bean 不一定存在;DependsOn 依赖的 bean 必须存在
    2. AutoConfigureAfter 语义上属于自动配置的基础支持层面;DependsOn 属于应用层面,程序员自定义 bean 时
    @Configuration
    public class AppConfig {
        @Bean
        @DependsOn("redis") // 确保 redis 先创建(如果没有会报错)
        public DataSource dataSource() {
    
        }
    }
    
  • @ConditionalOnMissingBean 当指定的 bean 如果已经存在,不会进行注册

  • @ConditionalOnProperty 根据配置文件的配置项决定当前 bean 是否注册,如果为真进行注册,为假不注册

    • prefix = "spring.batch.job", name = "enabled" 确定配置项,这里的配置项是 spring.bath.job.enabled
    • havingValue = "true" 如果 spring.bath.job.enabledtrue 条件为真
    • matchIfMissing = true 如果没有 spring.bath.job.enabled 配置项,也判定条件为真(如果 spring.batch.job.enabledfalse 条件为假)
      要么不要配置,默认是真;如果你配置了为 false 那就是假
posted @ 2024-06-29 16:42  CyrusHuang  阅读(50)  评论(0)    收藏  举报