欢迎关注微信公众号 「思客潘」

Spring 框架:将对象注册到 IOC 容器的 6 种方式详解与实践案例


一、概述

在 Spring 框架中,IOC(Inversion of Control,控制反转) 是其核心思想,而 Bean 是 Spring 管理的基本单元。将一个普通 Java 对象(POJO)交由 Spring 容器管理,意味着该对象的创建、初始化、依赖注入和销毁均由 Spring 容器负责,开发者无需手动 new 实例。

本文将系统性地介绍 6 种将对象注册到 Spring 容器的方式,从基础注解到高级编程式注册,结合具体代码案例,帮助开发者根据实际场景选择最合适的方案。


二、方式一:@ComponentScan + @Component(注解扫描方式)

1. 原理说明

  • 使用 @Component 注解标记类,表示该类是一个 Spring 管理的组件。
  • 使用 @ComponentScan 注解指定扫描的包路径,Spring 启动时会自动发现并注册所有被 @Component 及其衍生注解标记的类。

2. 注解说明

  • @Component:通用组件注解。
  • @Service@Repository@Controller:分别是 @Component 的语义化衍生注解,分别用于业务层、数据层和控制层。

3. 案例代码

// 1. 定义一个服务类,使用 @Service 标记
@Service
public class UserService {
    public void sayHello() {
        System.out.println("Hello from UserService!");
    }
}
// 2. 配置类启用组件扫描
@Configuration
@ComponentScan(basePackages = "com.example.service") // 指定扫描路径
public class AppConfig {
}
// 3. 测试
public class Application {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = 
            new AnnotationConfigApplicationContext(AppConfig.class);
        
        UserService userService = context.getBean(UserService.class);
        userService.sayHello(); // 输出:Hello from UserService!
    }
}

4. 适用场景

  • 普通业务类、工具类等项目内部组件。
  • Spring Boot 项目中,@SpringBootApplication 注解已包含 @ComponentScan,无需额外配置。

三、方式二:@Configuration + @Bean(配置类方式)

1. 原理说明

  • 使用 @Configuration 注解标记一个配置类。
  • 在该类中定义 @Bean 注解的方法,方法返回的对象将被注册为 Spring 容器中的 Bean。

2. 案例代码

// 定义一个第三方类(无法修改源码)
public class ThirdPartyService {
    public void doWork() {
        System.out.println("Third party service is working.");
    }
}
// 配置类中手动注册
@Configuration
public class AppConfig {
    
    @Bean
    public ThirdPartyService thirdPartyService() {
        return new ThirdPartyService();
    }
}
// 测试
public class Application {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = 
            new AnnotationConfigApplicationContext(AppConfig.class);
        
        ThirdPartyService service = context.getBean(ThirdPartyService.class);
        service.doWork();
    }
}

3. 适用场景

  • 引入第三方 JAR 包中的类(如 DataSourceRedisTemplate)。
  • 需要自定义初始化逻辑的 Bean(如设置属性、条件判断等)。

四、方式三:@Import 注解(快速导入)

1. 原理说明

  • @Import 可以直接将指定的类注册为 Bean,无需 @Component
  • 支持导入普通类、配置类(@Configuration)、或实现 ImportSelector / ImportBeanDefinitionRegistrar 的选择器。

2. 案例代码

方式 1:直接导入类
@Import(UserService.class) // 直接注册 UserService
public class AppConfig {
}
方式 2:导入配置类
@Configuration
public class DatabaseConfig {
    @Bean
    public DataSource dataSource() {
        return new HikariDataSource();
    }
}

@Import(DatabaseConfig.class) // 导入整个配置类
public class AppConfig {
}

3. 适用场景

  • 模块化开发中聚合多个配置。
  • Spring Boot 自动配置(spring.factories + @EnableAutoConfiguration)底层机制。

五、方式四:FactoryBean 接口(工厂方式)

1. 原理说明

  • 实现 FactoryBean<T> 接口,通过 getObject() 方法返回真正的 Bean 实例。
  • Spring 容器会自动调用该方法,并将返回对象注册为 Bean。

2. 案例代码

public class Person {
    private String name;
    // getter/setter
}

public class PersonFactoryBean implements FactoryBean<Person> {
    @Override
    public Person getObject() {
        Person person = new Person();
        person.setName("Factory Created Person");
        return person;
    }

    @Override
    public Class<?> getObjectType() {
        return Person.class;
    }
}
@Configuration
public class AppConfig {
    @Bean
    public PersonFactoryBean personFactoryBean() {
        return new PersonFactoryBean();
    }
}
// 测试:获取的是 Person 对象,而非 PersonFactoryBean
Person person = context.getBean(Person.class);
System.out.println(person.getName()); // 输出:Factory Created Person

3. 适用场景

  • MyBatis 的 SqlSessionFactoryBean
  • 需要复杂初始化逻辑或代理对象的场景。

六、方式五:编程式注册(运行时动态注册)

1. 原理说明

  • 通过 BeanDefinitionRegistryApplicationContext 在运行时动态注册 Bean。

2. 案例代码

public class DynamicBeanRegister {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        
        // 获取注册器
        BeanDefinitionRegistry registry = (BeanDefinitionRegistry) context.getBeanFactory();
        
        // 创建 Bean 定义
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(UserService.class);
        AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
        
        // 注册到容器
        registry.registerBeanDefinition("userService", beanDefinition);
        
        // 刷新容器
        context.refresh();
        
        // 获取 Bean
        UserService userService = context.getBean(UserService.class);
        userService.sayHello();
    }
}

3. 适用场景

  • 插件化系统、动态加载模块。
  • 单元测试中模拟 Bean 注入。

七、方式六:XML 配置方式(传统方式)

1. 案例代码

<!-- applicationContext.xml -->
<beans>
    <bean id="userService" class="com.example.UserService" />
</beans>
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) context.getBean("userService");

2. 说明

  • 已被注解方式取代,仅用于维护老项目。

八、总结与选型建议

方式 适用场景 推荐度
@Component + @ComponentScan 项目内部普通组件 ⭐⭐⭐⭐⭐
@Configuration + @Bean 第三方类、自定义初始化 ⭐⭐⭐⭐⭐
@Import 模块聚合、自动配置 ⭐⭐⭐⭐☆
FactoryBean 复杂对象、代理、框架开发 ⭐⭐⭐⭐☆
编程式注册 动态加载、插件系统 ⭐⭐⭐☆☆
XML 配置 老项目维护 ⭐☆☆☆☆

九、附录:Bean 作用域说明

作用域 说明
singleton 单例(默认),容器中唯一实例
prototype 多例,每次获取都创建新实例
request 每个 HTTP 请求创建一个实例
session 每个 HTTP 会话创建一个实例

提示:可通过 @Scope("prototype") 注解设置。


Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐