写点什么

Spring 之依赖注入底层原理

  • 2023-04-11
    湖南
  • 本文字数:3420 字

    阅读完需:约 11 分钟

Spring 框架作为 Java 开发中最流行的框架之一,其核心特性之一就是依赖注入(Dependency Injection,DI)。在 Spring 中,依赖注入是通过 IOC 容器(Inversion of Control,控制反转)来实现的。本文将详细介绍 Spring 的依赖注入底层原理,并提供源码示例。

什么是依赖注入

依赖注入是一种设计模式,它将对象之间的依赖关系从代码中移除,并由容器来管理这些依赖关系。依赖注入的主要目的是降低代码的耦合度,使代码更加灵活和可维护。


在 Java 中,依赖通常是通过构造函数或者 Setter 方法来注入的。使用依赖注入,我们可以将对象的创建和依赖关系的管理分离开来,从而使得代码更加容易测试和维护。

实现原理

Spring 的依赖注入是通过 IOC 容器来实现的。在 Spring 中,IOC 容器负责创建和管理对象,以及管理对象之间的依赖关系。

IOC 容器

IOC 容器是指用于管理对象和依赖关系的容器。Spring 提供了多种 IOC 容器实现,包括 BeanFactory 和 ApplicationContext 等。

  • BeanFactory 是 Spring 中最基本的 IOC 容器,它提供了基本的 IOC 功能。

  • ApplicationContext 则是 BeanFactory 的扩展,它提供了更多的功能,如事件发布、国际化支持、AOP 等。


在 Spring 中,BeanFactory 和 ApplicationContext 都是通过反射来实例化对象,并通过依赖注入来管理对象之间的依赖关系。

Bean 定义

在 Spring 中,每个被管理的对象都需要有一个对应的 Bean 定义。Bean 定义是一个元数据,它描述了一个 Bean 的类型、属性、依赖关系等信息。


Bean 定义通常是通过 XML 配置文件、 Java 配置类或者注解来定义的。下面是一个使用 XML 配置文件定义 Bean 的示例:

<bean id="userService" class="com.example.UserService">    <property name="userRepository" ref="userRepository"/></bean>
<bean id="userRepository" class="com.example.UserRepositoryImpl"/>
复制代码

在上面的示例中,我们定义了一个名为 userService 的 Bean,它的类型是 com.example.UserService。它依赖于另一个名为 userRepository 的 Bean,类型是 com.example.UserRepositoryImpl。

依赖注入

在 Spring 中,依赖注入是通过反射来实现的。当 IOC 容器创建 Bean 时,它会检查 Bean 定义中所声明的依赖关系,并尝试通过反射来注入这些依赖关系。


依赖注入通常分为三种方式:构造函数注入、Setter 方法注入和字段注入。

构造函数注入

构造函数注入是最常见的依赖注入方式。在 Spring 中,我们可以通过构造函数来注入 Bean 的依赖关系。当 IOC 容器创建 Bean 时,它会检查 Bean 定义中所声明的构造函数,并尝试通过反射来调用这个构造函数,并将依赖关系作为参数传递进去。


下面是一个使用构造函数注入的示例:

public class UserService {    private final UserRepository userRepository;
public UserService(UserRepository userRepository) { this.userRepository = userRepository; }
// ...}
复制代码

在上面的示例中,我们定义了一个 UserService 类,并将 UserRepository 依赖关系通过构造函数注入进去。

Setter 方法注入

Setter 方法注入是另一种常见的依赖注入方式。我们可以通过 Setter 方法来注入 Bean 的依赖关系。当 IOC 容器创建 Bean 时,它会检查 Bean 定义中所声明的 Setter 方法,并尝试通过反射来调用这个 Setter 方法,并将依赖关系作为参数传递进去。


下面是一个使用 Setter 方法注入的示例:

public class UserService {    private UserRepository userRepository;
public void setUserRepository(UserRepository userRepository) { this.userRepository = userRepository; }
// ...}
复制代码

在上面的示例中,我们定义了一个 UserService 类,并将 UserRepository 依赖关系通过 Setter 方法注入进去。

字段注入

字段注入是一种不太常见的依赖注入方式。我们可以通过字段来注入 Bean 的依赖关系。当 IOC 容器创建 Bean 时,它会尝试通过反射来注入这些字段。


下面是一个使用字段注入的示例:

public class UserService {    @Autowired    private UserRepository userRepository;
// ...}
复制代码

在上面的示例中,我们使用了 @Autowired 注解来将 UserRepository 依赖关系注入到 userService 对象中的 userRepository 字段中。

生命周期回调

在 Spring 中,Bean 生命周期包括四个阶段:实例化、属性赋值、初始化、销毁。在这些阶段中,Spring 提供了多个回调方法,以便我们在 Bean 的生命周期中进行一些自定义操作。


下面是一些常用的 Bean 生命周期回调方法:

  • 实例化:Bean 实例化之后,Spring 会调用 BeanPostProcessor 的 postProcessBeforeInitialization 方法。

  • 属性赋值:在 Bean 实例化之后,Spring 会将 Bean 定义中所声明的属性值赋值给 Bean 对象。

  • 初始化:在 Bean 属性赋值之后,Spring 会调用 InitializingBeanafterPropertiesSet 方法或者 @Bean 注解的 initMethod 方法。

  • 销毁:在 IOC 容器关闭时,Spring 会调用 DisposableBean 的 destroy 方法或者 @Bean 注解的 destroyMethod 方 法。


下面是一个实现 InitializingBean 和 DisposableBean 接口的示例:

public class UserService implements InitializingBean, DisposableBean {    private UserRepository userRepository;
public void setUserRepository(UserRepository userRepository) { this.userRepository = userRepository; }
public void afterPropertiesSet() throws Exception { // 在Bean属性赋值之后,执行一些初始化操作 }
public void destroy() throws Exception { // 在 IOC 容器关闭时,执行一些销毁操作 }
// ...}
复制代码

注解

在 Spring 中,我们可以使用注解来简化 Bean 定义和依赖注入的过程。Spring 提供了多个注解来实现依赖注入和生命周期回调等功能。


下面是一些常用的 Spring 注解:

  • @Component:用于标记一个类为 Bean。

  • @Autowired:用于标记一个字段、构造函数或者 Setter 方法需要进行依赖注入。

  • @Qualifier:当存在多个相同类型的 Bean 时,用于指定依赖注入的具体实现。

  • @Value:用于注入一个常量值。

  • @PostConstruct:用于标记一个方法为 Bean 初始化方法。

  • @PreDestroy:用于标记一个方法为 Bean 销毁方法。


下面是一个使用注解的示例:

@Componentpublic class UserService {    @Autowired    private UserRepository userRepository;
@PostConstruct public void init() { // 在Bean属性赋值之后,执行一些初始化操作 }
@PreDestroy public void destroy() { // 在 IOC 容器关闭时,执行一些销毁操作 }
// ...}
复制代码

源码示例


下面是一个使用 Spring 的依赖注入功能的示例:

@Componentpublic class UserService {    @Autowired    private UserRepository userRepository;
public void save(User user) { userRepository.save(user); }}
@Componentpublic class UserRepositoryImpl implements UserRepository { public void save(User user) { // 保存用户信息 }}
@Configurationpublic class AppConfig { @Bean public UserService userService() { return new UserService(); }
@Bean public UserRepository userRepository() { return new UserRepositoryImpl(); }}
public class Main { public static void main(String[] args) { ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); UserService userService = context.getBean(UserService.class); User user = new User(); userService.save(user); }}
复制代码

在上面的示例中,我们使用了 @Component 注解标记了 UserService 和 UserRepositoryImpl 两个类为 Bean。在 AppConfig 类中,我们使用 @Bean 注解来定义了 UserRepository 和 UserService 两个 Bean。在 Main 类中,我们使用 AnnotationConfigApplicationContext 来创建 IOC 容器,并通过依赖注入来获取 UserService 对象,并调用它的 save 方法。

总结

本文详细介绍了 Spring 的依赖注入底层原理,并提供了源码示例。通过本文的学习,我们可以更好地理解 Spring 的依赖注入机制,以及如何在实际开发中使用它来降低代码的耦合度,使代码更加灵活和可维护。


作者:这堆干货有点猛

链接:https://juejin.cn/post/7220227713130315835

来源:稀土掘金

用户头像

还未添加个人签名 2021-07-28 加入

公众号:该用户快成仙了

评论

发布
暂无评论
Spring 之依赖注入底层原理_Java_做梦都在改BUG_InfoQ写作社区