SpringBoot——注册Bean的方式及适用场景

前言

在Spring Boot应用中,Bean是构成应用的核心组件。Spring容器负责管理这些Bean,包括它们的创建、配置、组装、管理和销毁。在Spring Boot中,有多种方式可以注册Bean,让Spring容器能够管理它们。

一、@Component及其派生注解

实现方式

  • @Component是一个泛化的注解,用于标记一个类作为Spring容器管理的Bean。
  • 在类上添加@Component、@Service、@Controller、@Repository等注解,配合@ComponentScan扫描包路径。

适用场景

  • 常规业务组件(如Service层、Controller层等)
  • 需要Spring自动扫描并管理的类
@Service
public class MyService {
    // 服务逻辑...
}

二、@Bean注解

实现方式

  • 在配置类(@Configuration标记的类)中,通过@Bean标注方法,返回对象实例。方法名默认为Bean的唯一标识。

适用场景

  • 引入第三方库的类(如数据库连接池、工具类)
  • 需要自定义初始化逻辑的Bean
@Configuration
public class MyConfig {
    @Bean
    public User user() {
        return new User("Marry", 12345L);
    }
}

三、@Import注解导入

3.1、导入其他配置类或普通类

实现方式

  • 在配置类中使用@Import导入其他配置类或普通类,使其成为Bean。

适用场景

  • 模块化配置(如将多个配置类分散管理,根据需要组合它们)
  • 快速注册单个Bean,,等同于在类上标注@Component
// 安全模块配置
@Configuration
public class SecurityConfig {
    @Bean
    public SecurityFilter securityFilter() {
        return new SecurityFilter();
    }
}
// 数据访问模块配置
@Configuration
public class DataAccessConfig {
    @Bean
    public Repository repository() {
        return new JpaRepository();
    }
}

// 无特殊注解
public class BeanD {
}

// 主配置类
@Configuration
@Import({SecurityConfig.class, DataAccessConfig.class, BeanD.class})
public class ApplicationConfig {
    @Bean
    public MyService myService() {
        // 可以使用DataAccessConfig中的bean
        return new MyService(repository()); 
    }
}

3.2、使用ImportSelector进行动态导入

实现方式

  • 需创建实现ImportSelector接口的类,并重写selectImports()方法返回需注册的类全限定名数组。

适用场景

  • 结合@Conditional等注解,可在selectImports()中根据条件(如配置属性、环境变量)动态返回不同的类名数组,实现按需加载。
  • 如SpringBoot的@EnableAutoConfiguration通过AutoConfigurationImportSelector加载spring.factories中的自动配置类
  • AnnotationMetadata参数可获取导入类的注解、接口、父类等信息(如@EnableXXX的配置),用于更复杂的逻辑判断
  • 如下则是判断MyCofig类上有@Configuration注解则加载Dog类,否则加载Cat类
public class MyImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata metadata) {
        System.out.println("元数据Class名称:" + metadata.getClassName());
        //各种条件的判定,判定完毕后,决定是否装载指定的bean
        boolean flag = metadata.hasAnnotation("org.springframework.context.annotation.Configuration");
        if(flag){
            return new String[]{"com.xc.springboot.bean.Dog"};
        }
        return new String[]{"com.xc.springboot.bean.Cat"};
    }
}

// 使用方式
@Configuration
@Import(MyImportSelector.class)
public class MyConfig {
}

3.3、使用ImportBeanDefinitionRegistrar进行更灵活的控制

实现方式

  • 需创建实现ImportBeanDefinitionRegistrar接口的类,并重写registerBeanDefinitions()方法通过BeanDefinitionRegistry动态注册Bean。

适用场景

  • 根据环境变量、类路径是否存在类等条件动态注册Bean
  • 如MyBatisMapper扫描等,利用该机制动态生成代理类
  • 通过@EnableMyFeature注解触发ImportBeanDefinitionRegistrar实现类,根据环境变量条件动态注册MyServiceImpl的Bean到Spring容器,实现灵活的编程式配置。
// 1.自定义注解@EnableMyFeature
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Import(MyFeatureRegistrar.class) // 关联注册实现类
public @interface EnableMyFeature {
    String value() default "";
}

// 2.实现ImportBeanDefinitionRegistrar
public class MyFeatureRegistrar implements ImportBeanDefinitionRegistrar, EnvironmentAware {

    private Environment environment;

    @Override
    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }

    @Override
    public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        // 读取@EnableMyFeature注解的属性
        Map<String, Object> attrs = metadata.getAnnotationAttributes(EnableMyFeature.class.getName());
        String value = (String) attrs.get("value");

        // 示例:根据环境变量决定是否注册Bean
        boolean isEnabled = environment.getProperty("my.feature.enabled", Boolean.class, false);
        if (isEnabled) {
            BeanDefinition beanDef = BeanDefinitionBuilder
                .genericBeanDefinition(MyServiceImpl.class)
                .addPropertyReference("dependency", "myDependency")
                .getBeanDefinition();
            registry.registerBeanDefinition("myService", beanDef);
        }
    }
}


// 3.在配置类中使用@EnableMyFeature
@Configuration
@EnableMyFeature("customValue") // 触发MyFeatureRegistrar的执行
public class AppConfig {
    // 其他Bean定义...
}

3.4、ImportSelector 与 ImportBeanDefinitionRegistrar区别

image.png

四、FactoryBean接口实现

实现方式

  • 实现FactoryBean接口,重写getObject()方法定义Bean实例,并通过@Component或@Bean注册接口实现类。

适用场景

  • 复杂对象的创建(如MyBatis的SqlSessionFactoryBean)
  • 需要延迟初始化或条件创建的Bean
  • 容器中实际注册的是getObject()返回的对象,而非FactoryBean本身
@Component
public class DogFactoryBean implements FactoryBean<Dog> {
    //创建bean的复杂过程
    @Override
    public Dog getObject() throws Exception {
        Dog d = new Dog();
        //.........
        return d;
    }
    //bean的类型
    @Override
    public Class<?> getObjectType() {
        return Dog.class;
    }
    //bean是否单例
    @Override
    public boolean isSingleton() {
        return true;
    }
}

五、编程式注册(ApplicationContext)

实现方式

  • 通过ApplicationContext或BeanFactory直接注册Bean。

适用场景

  • 在运行时动态添加Bean
public class App {
    public static void main(String[] args) {
        SpringApplication.run(App.class, args)
            .getBeanFactory()
            .registerSingleton("customBean", new CustomBean());
    }
}

六、@ComponentScan注解

实现方式

  • @ComponentScan注解用于指定Spring Boot启动时扫描的包路径。Spring容器会扫描这些包路径下的类,并将标记了@Component、@Service、@Repository、@Controller等注解的类注册为Bean。

适用场景

  • 当你需要让Spring Boot在启动时扫描特定的包路径,并注册其中的Bean时,可以使用@ComponentScan注解。
// 定义一个Service类,并使用@Service注解标记  
@Service  
public class MyService {  
    // 服务逻辑...  
}  
  
// 在主配置类中使用@ComponentScan注解指定扫描的包路径  
@Configuration  
@ComponentScan(basePackages = "com.example.myapp")  
public class AppConfig {  
    // 其他Bean定义...  
}

七、自动配置(Spring Boot Starter)

实现方式

  • 通过/META-INF/spring.factories声明自动配置类,结合@Conditional实现按需装配。
  • 通过/META-INF/spring/AutoConfiguration.imports 声明自动配置类,结合@Conditional实现按需装配。

注意:

  • springboot2.7版本以前,自动配置使用的配置文件为:META-INF/spring.factories,会从这个文件中读取自动配置类的全类名
  • springboot2.7到3.0以前,同时兼容了AutoConfiguration.imports和spring.factories文件
  • springboot3.0以后,自动配置只支持AutoConfiguration.imports文件

@AutoConfiguration

  • @AutoConfiguration 是 Spring Boot 3 中引入的新注解,是 @Configuration 的增强版,同时支持 @AutoConfigureBefore、@AutoConfigureAfter。

注册自动配置类的文件

  • 路径必须是:
src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
  • 内容示例(每行一个类全限定名)
  • 这是 Spring Boot 3 的关键注册方式,相当于新版本的 spring.factories。

适用场景

  • 开发自定义Starter
  • 封装通用模块供多个项目复用
# META-INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.xc.MyAutoConfiguration
@Configuration
public class MyAutoConfiguration {
    @Bean
    public MyBean myBean() { ... }
}

八、@ImportResource注解导入XML配置

实现方式

  • 在XML文件中通过<bean>标签定义Bean,并在配置类中使用@ImportResource导入XML文件。

适用场景

  • 旧系统迁移或集成遗留XML配置
  • 需要与Spring Boot注解配置混合使用
<!-- beans.xml -->
<bean id="xmlUser" class="com.example.User" />
@ImportResource("classpath:beans.xml")
@Configuration
public class XmlConfig { ... }

九、动态注册:BeanDefinitionRegistryPostProcessor

实现方式

  • 实现BeanDefinitionRegistryPostProcessor接口,在postProcessBeanDefinitionRegistry()方法中手动注册BeanDefinition。

适用场景

  • 框架开发中动态注册Bean(如Spring Boot Starter自动配置)
  • 根据运行时条件(如配置文件、环境变量)决定是否注册Bean
  • 以在Bean实例化前修改容器元信息
@Component
public class DynamicRegistrar implements BeanDefinitionRegistryPostProcessor {
    // 注册Bean定义(先执行)
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
        BeanDefinition definition = BeanDefinitionBuilder
                .genericBeanDefinition(User.class)
                .addPropertyValue("name", "DynamicUser")
                .getBeanDefinition();
        registry.registerBeanDefinition("dynamicUser", definition);
    }

    // 修改Bean定义(后执行-加载所有Bean定义之后、Bean实例化之前)
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        // 在这里可以对 BeanFactory 进行进一步的配置
        System.out.println("Bean factory post-processing");
    }
}

十、使用@EnableXxx注解

  • Spring Boot提供了许多@Enable*注解,如@EnableWebMvc、@EnableCaching等。这些注解通常会通过导入一个或多个配置类来启用特定的功能,并注册相关的Bean。
// 在配置类上使用@EnableWebMvc注解启用Spring MVC  
@Configuration  
@EnableWebMvc  
public class WebMvcConfig implements WebMvcConfigurer {  
    // 配置Spring MVC...  
  
    @Override  
    public void addViewControllers(ViewControllerRegistry registry) {  
        // 注册视图控制器...  
    }  
}

适用场景

  • 当你需要使用Spring Boot提供的特定功能,并且这些功能是通过@Enable*注解来启用的时,可以使用这些注解来注册相关的Bean。

自定义一个@EnableMyFeature

  • 自定义一个@EnableMyFeature 类似的功能要创建一个注解,并使用@Import注解来导入一个配置类或选择器。这样,当你在应用程序中使用这个自定义的@Enable注解时,Spring会自动导入并注册相关的配置或组件
// 自定义注解  
@Target(ElementType.TYPE)  
@Retention(RetentionPolicy.RUNTIME)  
@Import(MyEnableConfiguration.class)  
public @interface EnableMyFeature {  
    // 可以添加属性来配置功能  
}  
  
// 配置类  
@Configuration  
public class MyEnableConfiguration {  
    @Bean  
    public MyFeatureBean myFeatureBean() {  
        return new MyFeatureBean();  
    }  
}  
  
// 使用自定义注解  
@SpringBootApplication  
@EnableMyFeature  
public class MyApplication {  
    public static void main(String[] args) {  
        SpringApplication.run(MyApplication.class, args);  
    }  
}

十一、编程式地注册Bean(使用ApplicationContext)

某些情况下需要在运行时编程式地注册Bean。可以通过获取ApplicationContext的引用,并使用其提供的API来注册Bean。

@Component  
public class MyBeanRegistrar implements ApplicationContextAware {  
    private ApplicationContext applicationContext;  
  
    @Override  
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {  
        this.applicationContext = applicationContext;  
        registerBean();  
    }  
  
    private void registerBean() {  
        RootBeanDefinition beanDefinition = new RootBeanDefinition(MyBean.class);  
        applicationContext.getBeanFactory().registerSingleton("myBean", beanDefinition);  
    }  
}

适用场景

  • 当你需要在运行时动态地注册Bean时,可以使用编程式地注册Bean的方式。这种方式比较罕见,通常只在特定的场景下使用。

参考:
https://cloud.tencent.com/developer/article/2443495

https://cloud.tencent.com/developer/article/2531220

https://developer.aliyun.com/article/1416453

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容