Spring容器核心:Bean配置与依赖注入详解

Spring框架的核心是它的容器,负责管理应用中各个组件(称为Bean)的创建、配置和组装。本文将深入讲解Spring容器中Bean的配置方式和依赖注入机制,这是掌握Spring框架的基础。

一、Bean配置方式

Spring提供了多种配置Bean的方式,适应不同场景和开发风格。

1. XML配置方式(传统方式)

XML是最早的Spring配置方式,虽然现在使用减少,但在一些遗留系统中仍可见。

<!-- applicationContext.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
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="userService" class="com.example.service.UserServiceImpl">
        <property name="userDao" ref="userDao"/>
    </bean>

    <bean id="userDao" class="com.example.dao.UserDaoImpl"/>
</beans>

特点

  • 集中式配置,与Java代码分离
  • 配置冗长,类型不安全
  • 适合简单应用或需要频繁修改配置的场景

2. 注解配置方式(现代主流)

使用注解可以简化配置,让代码更加简洁。

核心注解

  • @Component:通用组件注解
  • @Service:标识服务层组件
  • @Repository:标识数据访问组件
  • @Controller:标识控制器组件
@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private UserDao userDao;
    // ...
}

@Repository
public class UserDaoImpl implements UserDao {
    // ...
}

启用注解扫描

<context:component-scan base-package="com.example"/>

或使用Java配置:

@Configuration
@ComponentScan("com.example")
public class AppConfig {}

实践建议

  • 优先使用注解方式,代码更简洁
  • 合理使用各层注解(@Service/@Repository等)提高代码可读性
  • 基础包扫描路径要设置准确,避免扫描不必要的包

3. Java配置类(@Configuration)

Java配置类结合了XML的集中配置和注解的简洁性,是Spring Boot的默认方式。

@Configuration
public class AppConfig {
    
    @Bean
    public UserService userService(UserDao userDao) {
        return new UserServiceImpl(userDao);
    }
    
    @Bean
    public UserDao userDao() {
        return new UserDaoImpl();
    }
}

特点

  • 类型安全,IDE支持好
  • 可以结合编程式逻辑
  • 适合需要复杂初始化逻辑的Bean

4. 条件化Bean(@Conditional)

Spring 4.0引入的条件化Bean机制,可以根据特定条件决定是否创建Bean。

@Configuration
public class DataSourceConfig {
    
    @Bean
    @Conditional(ProdEnvironmentCondition.class)
    public DataSource prodDataSource() {
        // 生产环境数据源
    }
    
    @Bean
    @Conditional(DevEnvironmentCondition.class)
    public DataSource devDataSource() {
        // 开发环境数据源
    }
}

public class ProdEnvironmentCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        return "prod".equals(context.getEnvironment().getProperty("env"));
    }
}

常用条件注解

  • @Profile:基于环境profile的条件
  • @ConditionalOnClass:类路径存在指定类时生效
  • @ConditionalOnProperty:配置属性存在且有特定值时生效

实践建议

  • 使用条件化Bean实现环境相关的配置
  • Spring Boot提供了丰富的@Conditional派生注解,优先使用
  • 复杂条件判断可以实现Condition接口

二、依赖注入方式

依赖注入(DI)是Spring的核心特性,实现了控制反转(IoC)。

1. @Autowired自动装配

@Autowired是Spring提供的自动装配注解。

@Service
public class OrderService {
    
    @Autowired
    private UserService userService;
    
    // 构造器注入
    @Autowired
    public OrderService(InventoryService inventoryService) {
        this.inventoryService = inventoryService;
    }
    
    // Setter注入
    @Autowired
    public void setPaymentService(PaymentService paymentService) {
        this.paymentService = paymentService;
    }
}

注入方式

  1. 字段注入(不推荐,不利于测试)
  2. 构造器注入(推荐,不可变依赖)
  3. Setter注入(可选依赖)

实践建议

  • 优先使用构造器注入,保证依赖不可变
  • 避免字段注入,不利于单元测试
  • 对于可选依赖使用Setter注入

2. @Qualifier限定符

当同一类型有多个Bean时,使用@Qualifier指定具体Bean。

public interface MessageService {
    void send(String message);
}

@Service("smsService")
public class SmsService implements MessageService {
    // ...
}

@Service("emailService")
public class EmailService implements MessageService {
    // ...
}

@Service
public class NotificationService {
    @Autowired
    @Qualifier("smsService")
    private MessageService messageService;
}

3. @Resource与@Inject

除了@Autowired,Spring还支持JSR-250的@Resource和JSR-330的@Inject

@Service
public class BillingService {
    
    @Resource(name = "creditCardProcessor")
    private PaymentProcessor paymentProcessor;
    
    @Inject
    private InvoiceService invoiceService;
}

对比

特性@Autowired@Resource@Inject
标准SpringJSR-250JSR-330
按名称装配需要@Qualifiername属性需要@Named
必需性required属性默认必需默认必需
构造器注入支持不支持支持

实践建议

  • 新项目统一使用@Autowired
  • 需要与其他DI框架兼容时考虑@Inject
  • 遗留系统可能使用@Resource

三、Bean作用域

Spring Bean默认是单例(Singleton)的,但也支持其他作用域。

@Service
@Scope("prototype")  // 每次获取新实例
public class PrototypeService {
    // ...
}

@Controller
@Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class UserPreferences {
    // 每个会话一个实例
}

常用作用域

  • singleton:默认,每个容器一个实例
  • prototype:每次请求新实例
  • request:每个HTTP请求一个实例
  • session:每个HTTP会话一个实例
  • application:每个ServletContext一个实例
  • websocket:每个WebSocket会话一个实例

实践建议

  • 无状态服务使用singleton(默认)
  • 有状态组件根据需求选择合适作用域
  • 注意prototype作用域的性能影响
  • 对于request/session作用域,在非Web环境注入时需要代理

四、最佳实践总结

  1. 配置选择

    • 新项目优先使用Java配置类+注解方式
    • 简单项目可以使用纯注解方式
    • 复杂配置或需要动态逻辑时使用Java配置类
  2. 依赖注入

    • 强制依赖使用构造器注入
    • 可选依赖使用Setter注入
    • 避免字段注入
    • 同一类型多个Bean时使用@Qualifier
  3. 作用域

    • 默认使用singleton
    • 有状态组件选择合适作用域
    • 注意作用域代理的使用
  4. 条件化Bean

    • 使用@Profile管理环境特定配置
    • 复杂条件实现Condition接口
    • Spring Boot的条件注解更简洁

通过合理运用Spring容器的各种特性,可以构建出灵活、可维护的应用程序架构。理解这些核心概念是掌握Spring框架的基础。

添加新评论