Skip to content

IoC容器与依赖注入

IoC概念与原理

什么是IoC

IoC(Inversion of Control,控制反转)是一种设计思想,将对象的创建和管理权交给容器。

传统方式 vs IoC方式

传统方式:

java
public class UserService {
    // 自己创建依赖对象
    private UserDao userDao = new UserDaoImpl();
    
    public User getUser(Long id) {
        return userDao.findById(id);
    }
}

IoC方式:

java
public class UserService {
    private final UserDao userDao;
    
    // 通过构造方法注入依赖
    public UserService(UserDao userDao) {
        this.userDao = userDao;
    }
    
    public User getUser(Long id) {
        return userDao.findById(id);
    }
}

IoC的优势

优势说明
解耦降低组件之间的耦合度
可测试方便单元测试和Mock
可维护易于修改和扩展
可复用组件可独立使用

IoC容器

BeanFactory

BeanFactory是最基本的IoC容器,提供基础的依赖注入功能。

java
// 创建BeanFactory
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();

// 注册Bean定义
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(UserService.class);
beanFactory.registerBeanDefinition("userService", beanDefinition);

// 获取Bean
UserService userService = beanFactory.getBean(UserService.class);

ApplicationContext

ApplicationContext是BeanFactory的子接口,提供更多企业级功能。

java
// 基于XML配置
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");

// 基于Java配置
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

// 基于注解扫描
ApplicationContext context = new AnnotationConfigApplicationContext("com.example");

// 获取Bean
UserService userService = context.getBean(UserService.class);

ApplicationContext vs BeanFactory

特性ApplicationContextBeanFactory
Bean实例化时机容器启动时首次获取时
国际化支持支持不支持
事件发布支持不支持
AOP支持自动支持需手动配置
性能启动较慢启动较快

依赖注入方式

1. 构造器注入(推荐)

java
@Service
public class UserService {
    
    private final UserRepository userRepository;
    private final EmailService emailService;
    
    // 构造器注入
    public UserService(UserRepository userRepository, EmailService emailService) {
        this.userRepository = userRepository;
        this.emailService = emailService;
    }
}

优点:

  • 保证依赖不可变(final)
  • 保证依赖不为空
  • 易于单元测试
  • 清晰表达依赖关系

2. Setter注入

java
@Service
public class UserService {
    
    private UserRepository userRepository;
    private EmailService emailService;
    
    // Setter注入
    @Autowired
    public void setUserRepository(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
    
    @Autowired
    public void setEmailService(EmailService emailService) {
        this.emailService = emailService;
    }
}

优点:

  • 可选依赖
  • 灵活性高
  • 支持循环依赖

3. 字段注入(不推荐)

java
@Service
public class UserService {
    
    @Autowired
    private UserRepository userRepository;
    
    @Autowired
    private EmailService emailService;
}

缺点:

  • 无法使用final
  • 难以单元测试
  • 隐藏依赖关系
  • 容易空指针

注入方式选择

场景推荐方式
必需依赖构造器注入
可选依赖Setter注入
多个构造器构造器 + @Autowired

自动装配

@Autowired

java
@Service
public class UserService {
    
    private final UserRepository userRepository;
    
    // 构造器注入可省略@Autowired(Spring 4.3+)
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
}

@Autowired规则

  1. 按类型匹配:默认按类型查找Bean
  2. 按名称匹配:配合@Qualifier使用
  3. 可选注入:配合required=false
java
@Service
public class UserService {
    
    // 按类型匹配
    @Autowired
    private UserRepository userRepository;
    
    // 按名称匹配
    @Autowired
    @Qualifier("mysqlUserRepository")
    private UserRepository mysqlRepository;
    
    // 可选注入
    @Autowired(required = false)
    private OptionalService optionalService;
}

@Primary

当有多个同类型Bean时,使用@Primary指定默认Bean:

java
@Repository
@Primary
public class MysqlUserRepository implements UserRepository {
    // MySQL实现
}

@Repository
public class MongoUserRepository implements UserRepository {
    // MongoDB实现
}

@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;  // 注入MysqlUserRepository
}

@Qualifier

明确指定要注入的Bean:

java
@Service
public class UserService {
    
    @Autowired
    @Qualifier("mongoUserRepository")
    private UserRepository userRepository;  // 注入MongoUserRepository
}

@Autowired、@Resource、@Inject对比

@Autowired

java
// Spring提供的注解
@Autowired
private UserRepository userRepository;

// 可配合@Qualifier使用
@Autowired
@Qualifier("mysqlUserRepository")
private UserRepository userRepository;

// 可选注入
@Autowired(required = false)
private OptionalService optionalService;

@Resource

java
// Jakarta EE标准注解
@Resource
private UserRepository userRepository;

// 按名称注入
@Resource(name = "mysqlUserRepository")
private UserRepository userRepository;

@Inject

java
// Jakarta Inject标准注解
@Inject
private UserRepository userRepository;

// 可配合@Named使用
@Inject
@Named("mysqlUserRepository")
private UserRepository userRepository;

对比表

特性@Autowired@Resource@Inject
来源SpringJakarta EEJakarta Inject
默认匹配类型名称类型
可选注入支持不支持不支持
指定名称@Qualifiername属性@Named

Java配置

@Configuration

java
@Configuration
public class AppConfig {
    
    @Bean
    public UserRepository userRepository() {
        return new MysqlUserRepository();
    }
    
    @Bean
    public UserService userService() {
        return new UserService(userRepository());
    }
}

@Bean

java
@Configuration
public class AppConfig {
    
    @Bean
    @Primary
    public UserRepository mysqlUserRepository() {
        return new MysqlUserRepository();
    }
    
    @Bean
    public UserRepository mongoUserRepository() {
        return new MongoUserRepository();
    }
    
    @Bean
    public UserService userService(UserRepository userRepository) {
        return new UserService(userRepository);
    }
}

@ComponentScan

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

// 等价于
@Configuration
@ComponentScan({
    "com.example.service",
    "com.example.repository",
    "com.example.controller"
})
public class AppConfig {
}

@Import

java
@Configuration
@Import({DatabaseConfig.class, WebConfig.class})
public class AppConfig {
}

注解扫描

组件注解

注解说明使用场景
@Component通用组件通用Bean
@Service服务层业务逻辑
@Repository数据访问层数据访问
@Controller控制器Web层
@RestControllerREST控制器REST API
@Configuration配置类Java配置

自定义组件注解

java
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Service
public @interface BusinessService {
    String value() default "";
}

// 使用
@BusinessService
public class OrderService {
}

条件化注入

@Conditional

java
@Configuration
public class AppConfig {
    
    @Bean
    @Conditional(OnLinuxCondition.class)
    public UserService linuxUserService() {
        return new LinuxUserService();
    }
    
    @Bean
    @Conditional(OnWindowsCondition.class)
    public UserService windowsUserService() {
        return new WindowsUserService();
    }
}

public class OnLinuxCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        return context.getEnvironment().getProperty("os.name").toLowerCase().contains("linux");
    }
}

常用条件注解

java
@Configuration
public class AppConfig {
    
    // 条件属性
    @Bean
    @ConditionalOnProperty(name = "cache.enabled", havingValue = "true")
    public CacheService cacheService() {
        return new RedisCacheService();
    }
    
    // 条件类存在
    @Bean
    @ConditionalOnClass(RedisTemplate.class)
    public RedisService redisService() {
        return new RedisService();
    }
    
    // 条件Bean存在
    @Bean
    @ConditionalOnBean(DataSource.class)
    public JdbcTemplate jdbcTemplate(DataSource dataSource) {
        return new JdbcTemplate(dataSource);
    }
    
    // 条件Bean不存在
    @Bean
    @ConditionalOnMissingBean(UserService.class)
    public UserService defaultUserService() {
        return new DefaultUserService();
    }
}

Profile环境切换

定义Profile

java
@Configuration
public class DatabaseConfig {
    
    @Bean
    @Profile("dev")
    public DataSource devDataSource() {
        return new H2DataSource();
    }
    
    @Bean
    @Profile("prod")
    public DataSource prodDataSource() {
        return new MySQLDataSource();
    }
}

激活Profile

yaml
# application.yml
spring:
  profiles:
    active: dev
bash
# 命令行参数
java -jar app.jar --spring.profiles.active=prod

# 环境变量
export SPRING_PROFILES_ACTIVE=prod

Profile注解

java
@Service
@Profile("dev")
public class MockEmailService implements EmailService {
}

@Service
@Profile("prod")
public class SmtpEmailService implements EmailService {
}

下一步

继续学习 Bean的配置与管理,了解Bean的详细配置。