Spring框架的依赖注入(Dependency Injection, DI)

依赖注入(DI) 是Spring框架的核心特性之一,它通过将对象的依赖关系交由容器管理,从而实现模块之间的解耦。这种方式使得代码更加灵活、可测试和易于维护。


什么是依赖注入?

在传统的编程中,对象通常自己负责创建和管理它的依赖项。例如:

public class UserService {
    private UserRepository userRepository = new JdbcUserRepository(); // 直接创建依赖

    public void addUser(String username) {
        userRepository.save(username);
    }
}

在这种模式下,UserService直接依赖于JdbcUserRepository的具体实现,导致代码耦合性高,难以替换或测试。

而在依赖注入模式下,依赖关系由外部容器(如Spring容器)提供,而不是由对象自身创建。例如:

@Service
public class UserService {
    private final UserRepository userRepository;

    @Autowired
    public UserService(UserRepository userRepository) { // 通过构造函数注入依赖
        this.userRepository = userRepository;
    }

    public void addUser(String username) {
        userRepository.save(username);
    }
}

在这里,UserService不再关心UserRepository的具体实现,而是通过构造函数接收一个UserRepository实例。Spring容器会自动将合适的实现(如JdbcUserRepositoryMongoUserRepository)注入到UserService中。


依赖注入的三种方式

Spring支持以下三种常见的依赖注入方式:

1. 构造函数注入(Constructor Injection)

通过构造函数传递依赖项。这是推荐的方式,尤其是对于强制依赖项。

@Service
public class UserService {
    private final UserRepository userRepository;

    @Autowired
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
}
  • 优点
    • 保证依赖项不可为空(final修饰符)。
    • 更适合单元测试(可以通过构造函数直接传入Mock对象)。
    • 明确声明了类的依赖关系。
  • 适用场景:
    强制依赖项(即对象必须拥有的依赖)。

2. Setter方法注入(Setter Injection)

通过Setter方法设置依赖项。适用于可选依赖项。

@Service
public class UserService {
    private UserRepository userRepository;

    @Autowired
    public void setUserRepository(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
}
  • 优点
    • 灵活性高,可以在运行时动态修改依赖。
    • 适用于可选依赖项。
  • 缺点
    • 依赖项可能为null,需要额外的检查。
    • 不适合强制依赖项。
  • 适用场景:
    可选依赖项(即对象可以不依赖某个组件正常工作)。

3. 字段注入(Field Injection)

通过注解直接注入依赖项。这是最简洁的方式,但不推荐作为主要注入方式。

@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;
}
  • 优点
    • 代码简洁,无需编写构造函数或Setter方法。
  • 缺点
    • 隐藏了依赖关系,降低了代码的可读性和可测试性。
    • 无法使用final修饰符,可能导致依赖项为null
  • 适用场景:
    简单项目或测试代码中。

Spring容器如何实现依赖注入?

Spring容器(如ApplicationContext)负责管理Bean的生命周期和依赖注入。以下是其工作流程:

  1. 扫描与注册:
    Spring通过扫描配置文件或注解(如@Component@Service等),发现并注册所有Bean。
  2. 解析依赖:
    Spring分析每个Bean的依赖关系,确定哪些Bean需要注入。
  3. 创建实例:
    Spring根据依赖关系创建Bean的实例,并通过构造函数、Setter方法或字段注入依赖。
  4. 注入依赖:
    Spring将依赖项注入到目标Bean中,完成装配。
  5. 管理生命周期:
    Spring管理Bean的初始化、使用和销毁过程。

示例:完整的依赖注入示例

1. 定义接口和实现类
// 数据访问接口
public interface UserRepository {
    void save(String username);
}

// JDBC实现
@Repository
public class JdbcUserRepository implements UserRepository {
    @Override
    public void save(String username) {
        System.out.println("Saving user '" + username + "' to relational database via JDBC.");
    }
}

// MongoDB实现
@Repository
public class MongoUserRepository implements UserRepository {
    @Override
    public void save(String username) {
        System.out.println("Saving user '" + username + "' to MongoDB.");
    }
}

2. 定义服务类
@Service
public class UserService {
    private final UserRepository userRepository;

    @Autowired
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public void addUser(String username) {
        userRepository.save(username);
    }
}

3. 配置Spring容器
方法一:基于Java配置
@Configuration
@ComponentScan(basePackages = "org.example")
public class AppConfig {
}
方法二:基于XML配置

4. 测试代码
public class MainApp {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        UserService userService = context.getBean(UserService.class);
        userService.addUser("Alice");
    }
}

你可能感兴趣的:(java,开发语言,spring,servlet,spring,cloud,mybatis,spring,boot)