再次理解 Spring 中的 IOC、DI、AOP 与多态

目录

引言

[1. IOC(控制反转)](#1. IOC(控制反转))

[1.1 什么是 IOC?](#1.1 什么是 IOC?)

[1.2 IOC 的核心思想](#1.2 IOC 的核心思想)

[1.3 IOC 的实现](#1.3 IOC 的实现)

[2. DI(依赖注入)](#2. DI(依赖注入))

[2.1 什么是 DI?](#2.1 什么是 DI?)

[2.2 DI 的实现方式](#2.2 DI 的实现方式)

[2.3 DI 的核心作用](#2.3 DI 的核心作用)

[3. AOP(面向切面编程)](#3. AOP(面向切面编程))

[3.1 什么是 AOP?](#3.1 什么是 AOP?)

[3.2 AOP 的核心概念](#3.2 AOP 的核心概念)

[3.3 AOP 的实现方式](#3.3 AOP 的实现方式)

[4. 多态与 Spring 的关系](#4. 多态与 Spring 的关系)

[4.1 什么是多态?](#4.1 什么是多态?)

[4.2 多态在 Spring 中的应用](#4.2 多态在 Spring 中的应用)

接口和实现类的依赖注入

具体分析

[4.3 多态的优势](#4.3 多态的优势)

[5. IOC、DI、AOP 与多态的关系](#5. IOC、DI、AOP 与多态的关系)

[5.1 IOC 和 DI](#5.1 IOC 和 DI)

[5.2 DI 和多态](#5.2 DI 和多态)

[5.3 AOP 和多态](#5.3 AOP 和多态)

[5.4 综合关系](#5.4 综合关系)

[6. 代码示例](#6. 代码示例)

[6.1 未使用 AOP 的示例](#6.1 未使用 AOP 的示例)

[6.2 使用 AOP 的示例](#6.2 使用 AOP 的示例)

[6.3 测试类](#6.3 测试类)

[6.4 运行结果](#6.4 运行结果)

[7.Spring 容器的选择](#7.Spring 容器的选择)

常用实现类

[8. Spring Bean 的概念](#8. Spring Bean 的概念)

[8.1 什么是 Bean?](#8.1 什么是 Bean?)

[8.2 Bean 的特点](#8.2 Bean 的特点)

[8.3 Bean 的配置方式](#8.3 Bean 的配置方式)

[8.4 Bean 的创建流程](#8.4 Bean 的创建流程)

[1 Bean 的定义](#1 Bean 的定义)

[2 Bean 的实例化](#2 Bean 的实例化)

[3 属性赋值](#3 属性赋值)

[4 初始化](#4 初始化)

[5 使用](#5 使用)

[6 销毁](#6 销毁)

[8.5 Bean 的生命周期](#8.5 Bean 的生命周期)

[1 Bean 的创建](#1 Bean 的创建)

[2 Bean 的初始化](#2 Bean 的初始化)

[3 Bean 的使用](#3 Bean 的使用)

[4 Bean 的销毁](#4 Bean 的销毁)

[8.6 Bean 的作用域](#8.6 Bean 的作用域)

[9. @Autowired 注解](#9. @Autowired 注解)

[9.1 什么是 @Autowired?](#9.1 什么是 @Autowired?)

[9.2 @Autowired 的使用场景](#9.2 @Autowired 的使用场景)

[9.3 @Autowired 的工作原理](#9.3 @Autowired 的工作原理)

[@Autowired 的匹配规则](#@Autowired 的匹配规则)

10.一些常用注解的联系和区别

[1. @Component、@Service、@Repository 的作用](#1. @Component、@Service、@Repository 的作用)

[1.1 @Component](#1.1 @Component)

[1.2 @Service](#1.2 @Service)

[1.3 @Repository](#1.3 @Repository)

总结

[2. @Autowired 的作用](#2. @Autowired 的作用)

[2.1 使用场景](#2.1 使用场景)

[2.2 工作原理](#2.2 工作原理)

[2.3 总结](#2.3 总结)

[3. @Component、@Service、@Repository 和 @Autowired 的区别](#3. @Component、@Service、@Repository 和 @Autowired 的区别)

结合以上我们做一个底层分析:

[2. Spring 容器的运行流程](#2. Spring 容器的运行流程)

[2.1 Spring 容器启动](#2.1 Spring 容器启动)

[2.2 Bean 的创建和依赖注入](#2.2 Bean 的创建和依赖注入)

[2.3 Bean 的初始化](#2.3 Bean 的初始化)

[2.4 Bean 的使用](#2.4 Bean 的使用)

[3. 底层原理](#3. 底层原理)

[3.1 反射机制](#3.1 反射机制)

[3.2 动态代理](#3.2 动态代理)

[3.3 依赖查找](#3.3 依赖查找)

[4. 代码示例](#4. 代码示例)

[4.1 定义 Bean](#4.1 定义 Bean)

[4.2 测试类](#4.2 测试类)

[4.3 运行结果](#4.3 运行结果)

[5. 总结](#5. 总结)


引言

Spring 框架是 Java 开发中最流行的框架之一,其核心特性包括 IOC(控制反转)DI(依赖注入)AOP(面向切面编程) 。这些特性不仅简化了开发流程,还提高了代码的可维护性和扩展性。与此同时,Spring 的实现也深度依赖于 Java 的 多态 特性。本文将详细探讨 Spring IOC、DI、AOP 与多态之间的关系,并通过代码示例帮助读者深入理解。

首先要知道Spring的实现是基于以下原理:

  • IOC 是 Spring 的核心设计原则,DI 是其实现方式。

  • AOP 通过动态代理增强目标方法,依赖于多态实现代理对象。

  • Bean 的生命周期 包括实例化、属性赋值、初始化、使用和销毁。

  • Spring 的底层实现依赖于 反射机制动态代理


1. IOC(控制反转)

1.1 什么是 IOC?

IOC(Inversion of Control)是一种设计原则,它将对象的创建和依赖关系的管理从应用程序代码中转移到框架或容器中。在 Spring 中,IOC 容器负责创建和管理 Bean 的生命周期。
将对象的创建权力反转给Spring框架!

1.2 IOC 的核心思想

  • 传统方式:开发者手动创建对象并管理依赖关系。

  • IOC 方式:Spring 容器负责创建对象并注入依赖关系,开发者只需关注业务逻辑。

1.3 IOC 的实现

Spring 通过 BeanFactoryApplicationContext 实现 IOC 容器。开发者只需通过配置文件(XML 或注解)定义 Bean,Spring 容器会自动创建和管理这些 Bean。


2. DI(依赖注入)

2.1 什么是 DI?

DI(Dependency Injection)是 IOC 的一种实现方式,它通过将依赖关系注入到对象中,而不是由对象自己创建依赖。

2.2 DI 的实现方式

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

字段注入

java 复制代码
@Autowired
private UserRepository userRepository;

构造器注入

java 复制代码
@Autowired
public UserService(UserRepository userRepository) {
    this.userRepository = userRepository;
}

Setter 方法注入

java 复制代码
@Autowired
public void setUserRepository(UserRepository userRepository) {
    this.userRepository = userRepository;
}

2.3 DI 的核心作用

  • 解耦:将对象的创建和依赖关系分离,降低代码的耦合度。

  • 灵活性:通过配置文件或注解动态注入依赖,便于扩展和维护。


3. AOP(面向切面编程)

3.1 什么是 AOP?

AOP(Aspect-Oriented Programming)是一种编程范式,它通过将横切关注点(如日志、事务、安全等)与核心业务逻辑分离,提高代码的模块化。

3.2 AOP 的核心概念

  • 切面(Aspect):横切关注点的模块化,如日志切面、事务切面。

  • 通知(Advice):切面在目标方法执行前后执行的动作。

  • 切入点(Pointcut):定义哪些方法需要被增强。

  • 目标对象(Target):被增强的对象。

  • 代理对象(Proxy):Spring 通过动态代理生成的对象,用于拦截目标方法的调用。

3.3 AOP 的实现方式

Spring AOP 支持两种动态代理方式:

  1. JDK 动态代理:适用于目标类实现了接口的情况。

  2. CGLIB 动态代理:适用于目标类没有实现接口的情况。


4. 多态与 Spring 的关系

4.1 什么是多态?

多态是面向对象编程的三大特性之一,它允许父类或接口引用指向子类或实现类的对象,并在运行时调用实际对象的方法。

4.2 多态在 Spring 中的应用

  • 依赖注入:Spring 通过多态将接口与实现类关联起来。例如:
java 复制代码
@Autowired
private UserRepository userRepository; // 实际注入的是 UserRepositoryImpl 的实例
  • 虽然 userRepository 的类型是 UserRepository(接口),但实际调用的是 UserRepositoryImplsave 方法。

  • AOP 代理:Spring AOP 通过动态代理生成代理对象,代理对象实现了目标接口或继承了目标类,并在运行时拦截方法调用。

举个例子:

接口和实现类的依赖注入

最常见的情况是,通过接口注入实现类。例如,你有一个UserService接口和它的实现类UserServiceImpl。你可以在Spring配置中声明一个UserService类型的bean,但实际上注入的是UserServiceImpl的实例。

java 复制代码
@Service
public class UserServiceImpl implements UserService {
    // 实现方法
}

在另一个需要UserService的类中,你可以这样注入:

java 复制代码
@Service
public class UserController {
    @Autowired
    private UserService userService; // 这里注入的是UserServiceImpl的实例
}

这种方式利用了多态:UserService接口可以被任何实现了该接口的类实例化,而具体的实现(如UserServiceImpl)则在运行时确定。

具体分析

(1)Spring 容器管理 Bean

  • 在 Spring 容器中,所有的 Bean 都是通过类型(接口或类)来管理的。

  • 当你使用 @Autowired 注解时,Spring 会根据字段的类型(UserRepository)从容器中查找匹配的 Bean。

(2)注入实现类的实例

  • 在你的代码中,UserRepositoryImplUserRepository 接口的实现类,并且被标记为 @Repository

  • Spring 容器会创建 UserRepositoryImpl 的实例,并将其注册为一个 Bean。

  • 当你使用 @Autowired 注入 UserRepository 时,Spring 会找到 UserRepositoryImpl 的实例,并将其注入到 userRepository 字段中。

(3)多态的体现

  • 虽然 userRepository 字段的类型是 UserRepository(接口),但实际注入的是 UserRepositoryImpl 的实例。

  • 当你调用 userRepository.save() 时,实际调用的是 UserRepositoryImplsave 方法。

为什么没有显式的多态代码

  • Spring 的依赖注入机制 :Spring 自动完成了 Bean 的创建和注入,你不需要手动编写类似 UserRepository userRepository = new UserRepositoryImpl(); 的代码。

  • 多态的隐式使用:虽然你没有显式地写出多态的代码,但 Spring 在底层通过多态机制将接口和实现类关联起来。

4.3 多态的优势

  • 解耦:通过接口调用方法,降低代码的耦合度。

  • 扩展性:可以轻松替换实现类,而无需修改调用代码。


5. IOC、DI、AOP 与多态的关系

5.1 IOC 和 DI

  • IOC 是设计原则,DI 是其实现方式。

  • Spring 通过 IOC 容器管理 Bean 的生命周期,并通过 DI 将依赖注入到目标对象中。

5.2 DI 和多态

  • DI 依赖于多态,通过接口注入实现类的实例。

  • 例如:

java 复制代码
@Autowired
private UserRepository userRepository; // 实际注入的是 UserRepositoryImpl 的实例

5.3 AOP 和多态

  • AOP 通过动态代理生成代理对象,代理对象实现了目标接口或继承了目标类。

  • 代理对象在运行时拦截方法调用,并执行增强逻辑。

5.4 综合关系

  • IOC 容器 管理 Bean 的生命周期。

  • DI 通过多态将接口与实现类关联起来。

  • AOP 通过动态代理增强目标方法,依赖于多态实现代理对象。


6. 代码示例

6.1 未使用 AOP 的示例

java 复制代码
// 用户仓库接口
public interface UserRepository {
    void save();
}

// 用户仓库实现类
@Repository
public class UserRepositoryImpl implements UserRepository {
    @Override
    public void save() {
        System.out.println("保存用户到数据库...");
    }
}

// 用户服务类
@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;

    public void saveUser() {
        userRepository.save();
    }
}

6.2 使用 AOP 的示例

java 复制代码
// 切面类
@Component
@Aspect
public class LoggingAspect {

    @Pointcut("execution(* com.example.UserService.saveUser(..))")
    public void saveUserPointcut() {}

    @Before("saveUserPointcut()")
    public void beforeSaveUser() {
        System.out.println("前置通知:准备保存用户...");
    }

    @After("saveUserPointcut()")
    public void afterSaveUser() {
        System.out.println("后置通知:用户保存完成。");
    }
}

6.3 测试类

java 复制代码
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = AppConfig.class)
public class UserServiceTest {

    @Autowired
    private UserService userService;

    @Test
    public void testSaveUser() {
        userService.saveUser();
    }
}

6.4 运行结果

java 复制代码
前置通知:准备保存用户...
保存用户到数据库...
后置通知:用户保存完成。

7.Spring 容器的选择

Spring 提供了两种主要的 IOC 容器:

  1. BeanFactory

    • 是 Spring 最基础的容器接口。

    • 提供了 Bean 的创建、配置和管理功能。

    • 适合资源受限的环境,延迟加载 Bean。

  2. ApplicationContext

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

    • 支持国际化、事件传播、AOP 等功能。

    • 默认立即加载 Bean。

常用实现类

  • ClassPathXmlApplicationContext:从类路径加载 XML 配置文件。

  • AnnotationConfigApplicationContext:基于注解配置的容器。

  • FileSystemXmlApplicationContext:从文件系统加载 XML 配置文件。

8. Spring Bean 的概念

8.1 什么是 Bean?

‌**Spring Bean** ‌是Spring框架中的核心概念之一,代表由Spring容器管理的对象。Spring Bean是指在Spring容器中实例化、组装和管理的对象,通常是应用程序中业务逻辑、数据访问、服务等功能的具体实现。通过定义Bean,开发者可以利用Spring提供的依赖注入(Dependency Injection)和面向切面编程(Aspect-Oriented Programming)等特性,简化应用程序的开发和维护‌12。

8.2 Bean 的特点

  • 生命周期管理:Spring 容器负责 Bean 的创建、初始化和销毁。

  • 依赖注入:Spring 容器自动将 Bean 的依赖注入到目标对象中。

  • 作用域:Bean 可以有不同的作用域,例如单例(Singleton)、原型(Prototype)等。

8.3 Bean 的配置方式

Spring 支持以下几种方式配置 Bean:

1.基于XML的配置‌:通过在XML配置文件中声明Bean。例如:

XML 复制代码
<bean id="myBean" class="com.example.MyClass">
    <property name="propertyName" value="propertyValue"/>
</bean>

2.基于注解的配置‌:使用注解如@Component、@Service、@Repository等来标识Bean,并在配置类中启用注解扫描。例如:

java 复制代码
@Component
public class MyBean {
    // ...
}
@Configuration
@ComponentScan(basePackages = "com.example")
public class AppConfig {
    // ...
}
java 复制代码
@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;
}

@Repository
public class UserRepositoryImpl implements UserRepository {
    // 实现代码
}

3.基于Java配置‌:使用@Configuration和@Bean注解在配置类中显式声明Bean。例如:

java 复制代码
@Configuration
public class AppConfig {
    @Bean
    public MyBean myBean() {
        return new MyBean();
    }
}

8.4 Bean 的创建流程

Spring Bean 的创建过程涉及多个接口和回调方法。以下是详细的流程:

1 Bean 的定义

Spring 容器通过 BeanDefinition 对象来描述 Bean 的元数据,包括:

  • Bean 的类名。

  • Bean 的作用域(Singleton、Prototype 等)。

  • Bean 的属性值。

  • Bean 的初始化方法和销毁方法。

BeanDefinition 的来源可以是 XML 配置文件、注解或 Java 配置类。

2 Bean 的实例化

Spring 容器通过反射机制创建 Bean 的实例。具体步骤如下:

  1. 加载 Bean 的类

    • 根据 BeanDefinition 中的类名,使用 ClassLoader 加载类。
  2. 创建实例

    • 使用反射调用类的默认构造器创建实例。

    • 如果 Bean 实现了 FactoryBean 接口,则调用 getObject() 方法创建实例。

3 属性赋值

Spring 容器通过依赖注入将 Bean 的属性值设置到实例中。具体步骤如下:

  1. 解析依赖

    • 根据 @Autowired 或 XML 配置,找到需要注入的 Bean。
  2. 注入依赖

    • 通过反射或 Setter 方法将依赖注入到目标 Bean 中。
4 初始化

在 Bean 的属性赋值完成后,Spring 容器会调用初始化方法。具体步骤如下:

  1. 调用 BeanNameAwareBeanFactoryAware

    • 如果 Bean 实现了 BeanNameAware 接口,Spring 会调用 setBeanName() 方法。

    • 如果 Bean 实现了 BeanFactoryAware 接口,Spring 会调用 setBeanFactory() 方法。

  2. 调用 ApplicationContextAware

    • 如果 Bean 实现了 ApplicationContextAware 接口,Spring 会调用 setApplicationContext() 方法。
  3. 调用 BeanPostProcessor 的前置方法

    • Spring 会调用所有 BeanPostProcessorpostProcessBeforeInitialization() 方法。
  4. 调用初始化方法

    • 如果 Bean 实现了 InitializingBean 接口,Spring 会调用 afterPropertiesSet() 方法。

    • 如果 Bean 配置了 init-method,Spring 会调用指定的初始化方法。

  5. 调用 BeanPostProcessor 的后置方法

    • Spring 会调用所有 BeanPostProcessorpostProcessAfterInitialization() 方法。
5 使用

初始化完成后,Bean 可以被应用程序使用。例如,通过 @Autowired 注入到其他 Bean 中。

6 销毁

当 Spring 容器关闭时,会调用 Bean 的销毁方法。具体步骤如下:

  1. 调用 DisposableBean 接口

    • 如果 Bean 实现了 DisposableBean 接口,Spring 会调用 destroy() 方法。
  2. 调用销毁方法

    • 如果 Bean 配置了 destroy-method,Spring 会调用指定的销毁方法。

8.5 Bean 的生命周期

1 Bean 的创建

Spring 容器根据配置(XML 或注解)创建 Bean 的实例。

2 Bean 的初始化
  • 如果 Bean 实现了 InitializingBean 接口,Spring 会调用 afterPropertiesSet() 方法。

  • 如果 Bean 配置了 init-method,Spring 会调用指定的初始化方法。

3 Bean 的使用
  • Bean 可以被应用程序使用,例如通过 @Autowired 注入到其他 Bean 中。
4 Bean 的销毁
  • 如果 Bean 实现了 DisposableBean 接口,Spring 会调用 destroy() 方法。

  • 如果 Bean 配置了 destroy-method,Spring 会调用指定的销毁方法。

8.6 Bean 的作用域

Spring 支持以下 Bean 作用域:

  1. Singleton

    • 默认作用域,每个 Spring 容器中只有一个 Bean 实例。
  2. Prototype

    • 每次请求时都会创建一个新的 Bean 实例。
  3. Request

    • 每个 HTTP 请求都会创建一个新的 Bean 实例。
  4. Session

    • 每个 HTTP 会话都会创建一个新的 Bean 实例。
  5. Global Session

    • 用于 Portlet 应用,每个全局 HTTP 会话都会创建一个新的 Bean 实例。

9. @Autowired 注解

9.1 什么是 @Autowired

@Autowired 是 Spring 提供的注解,用于实现 依赖注入(Dependency Injection, DI)。它的作用是将 Spring 容器中的 Bean 自动注入到目标字段、构造器或方法中。

9.2 @Autowired 的使用场景

@Autowired 可以用在以下位置:

字段注入

java 复制代码
@Autowired
private UserRepository userRepository;

构造器注入

java 复制代码
@Autowired
public UserService(UserRepository userRepository) {
    this.userRepository = userRepository;
}

Setter 方法注入

java 复制代码
@Autowired
public void setUserRepository(UserRepository userRepository) {
    this.userRepository = userRepository;
}

9.3 @Autowired 的工作原理

  1. 扫描 Bean

    • Spring 容器启动时,会扫描所有被 @Component@Service@Repository 等注解标记的类,并将它们注册为 Bean。
  2. 解析依赖

    • 当 Spring 容器遇到 @Autowired 注解时,会根据类型(如 UserRepository)查找匹配的 Bean。

    • 如果找到多个匹配的 Bean,Spring 会根据 @Qualifier 注解或 Bean 的名称进一步筛选。

  3. 注入依赖

    • Spring 会将找到的 Bean 注入到目标字段、构造器参数或 Setter 方法中。

@Autowired 的匹配规则

  • 按类型匹配

    • 如果容器中只有一个匹配类型的 Bean,则直接注入。
java 复制代码
@Autowired
private UserRepository userRepository;
  • 按名称匹配
  • 如果容器中有多个匹配类型的 Bean,可以通过 @Qualifier 注解指定 Bean 的名称。
java 复制代码
@Autowired
@Qualifier("userRepositoryImpl")
private UserRepository userRepository;

强制注入

  • 如果找不到匹配的 Bean,Spring 会抛出 NoSuchBeanDefinitionException

  • 可以通过 required = false 设置非强制注入:

java 复制代码
@Autowired(required = false)
private UserRepository userRepository;

10.一些常用注解的联系和区别

1. @Component@Service@Repository 的作用

这些注解的作用是 标识一个类为 Spring Bean,并将它们注册到 Spring 容器中。它们的区别主要在于语义和用途。

1.1 @Component
  • 作用:通用的注解,用于标识一个类为 Spring Bean。

  • 使用场景:适用于任何需要被 Spring 管理的组件。

  • 示例

java 复制代码
@Component
public class MyComponent {
    // 业务逻辑
}
1.2 @Service
  • 作用:标识一个类为服务层(Service Layer)的 Bean。

  • 使用场景:用于业务逻辑层,通常包含复杂的业务规则和事务管理。

  • 示例

java 复制代码
@Service
public class UserService {
    // 业务逻辑
}
1.3 @Repository
  • 作用:标识一个类为数据访问层(DAO Layer)的 Bean。

  • 使用场景:用于数据访问层,通常与数据库交互。

  • 额外功能@Repository 注解会自动将数据访问层的异常转换为 Spring 的统一异常体系(如 DataAccessException)。

  • 示例

java 复制代码
@Repository
public class UserRepositoryImpl implements UserRepository {
    // 数据访问逻辑
}

总结

  • @Component 是通用的注解,适用于任何组件。

  • @Service@Repository@Component 的特化形式,分别用于服务层和数据访问层。

  • 它们的本质都是将类注册为 Spring Bean,区别在于语义和用途。


2. @Autowired 的作用

@Autowired 是 Spring 提供的注解,用于实现 依赖注入(Dependency Injection, DI)。它的作用是将 Spring 容器中的 Bean 自动注入到目标字段、构造器或方法中。

2.1 使用场景
  • 字段注入
java 复制代码
@Autowired
private UserRepository userRepository;
  • 构造器注入
java 复制代码
@Autowired
public UserService(UserRepository userRepository) {
    this.userRepository = userRepository;
}
  • Setter 方法注入
java 复制代码
@Autowired
public void setUserRepository(UserRepository userRepository) {
    this.userRepository = userRepository;
}
2.2 工作原理
  1. Spring 容器启动时,会扫描所有被 @Component@Service@Repository 等注解标记的类,并将它们注册为 Bean。

  2. 当 Spring 容器遇到 @Autowired 注解时,会根据类型(如 UserRepository)查找匹配的 Bean。

  3. 如果找到匹配的 Bean,Spring 会将其注入到目标字段、构造器参数或 Setter 方法中。

2.3 总结
  • @Autowired 用于将 Spring 容器中的 Bean 注入到目标对象中。

  • 它依赖于 @Component@Service@Repository 等注解注册的 Bean。


3. @Component@Service@Repository@Autowired 的区别

特性 @Component@Service@Repository @Autowired
作用 标识一个类为 Spring Bean,并注册到容器中。 将 Spring 容器中的 Bean 注入到目标对象中。
使用场景 定义 Bean 的类别(通用、服务层、数据访问层)。 实现依赖注入。
依赖关系 @Autowired 依赖,用于注入其他 Bean。 依赖于 @Component@Service@Repository 注册的 Bean。
示例 @Component@Service@Repository @Autowired

  • @Component@Service@Repository:用于标识一个类为 Spring Bean,并注册到容器中。它们的区别在于语义和用途。

  • @Autowired:用于将 Spring 容器中的 Bean 注入到目标对象中,实现依赖注入。

  • 关系

    • @Component@Service@Repository 注册的 Bean 可以被 @Autowired 注入。

    • @Autowired 依赖于 @Component@Service@Repository 注册的 Bean。

结合以上我们做一个底层分析:

java 复制代码
@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;

    public void saveUser() {
        userRepository.save();
    }
}
  • @Service:标识 UserService 类为一个 Spring Bean,并将其注册到 Spring 容器中。

  • @Autowired:告诉 Spring 容器将 UserRepository 类型的 Bean 注入到 userRepository 字段中。

  • userRepository.save():调用 UserRepositorysave 方法。


2. Spring 容器的运行流程

以下是 Spring 容器在运行时的详细流程:

2.1 Spring 容器启动

  1. 扫描 Bean

    • Spring 容器启动时,会扫描所有被 @Component@Service@Repository 等注解标记的类。

    • 在你的代码中,UserService 类被 @Service 注解标记,因此 Spring 会将其注册为一个 Bean。

  2. 创建 BeanDefinition

    • Spring 会为每个 Bean 创建一个 BeanDefinition 对象,描述 Bean 的元数据(如类名、作用域、依赖关系等)。
  3. 注册 Bean

    • BeanDefinition 注册到 Spring 容器的 BeanFactory 中。

2.2 Bean 的创建和依赖注入

  1. 实例化 Bean

    • 当 Spring 容器需要创建 UserService 的实例时,会调用 UserService 的默认构造器(如果没有显式定义构造器,Java 会提供一个默认的无参构造器)。
  2. 依赖注入

    • Spring 容器会检查 UserService 类中是否有需要注入的依赖(通过 @Autowired 注解标记的字段、构造器或方法)。

    • 在你的代码中,userRepository 字段被 @Autowired 注解标记,因此 Spring 会查找 UserRepository 类型的 Bean。

  3. 查找依赖的 Bean

    • Spring 容器会查找所有实现了 UserRepository 接口的 Bean。

    • 如果找到多个匹配的 Bean,Spring 会根据 @Qualifier 注解或 Bean 的名称进一步筛选。

    • 如果找不到匹配的 Bean,Spring 会抛出 NoSuchBeanDefinitionException

  4. 注入依赖

    • Spring 会将找到的 UserRepository Bean 注入到 userRepository 字段中。

    • 注入的过程是通过反射实现的,Spring 会直接设置 userRepository 字段的值。


2.3 Bean 的初始化

  1. 调用初始化方法

    • 如果 UserService 类实现了 InitializingBean 接口,Spring 会调用 afterPropertiesSet() 方法。

    • 如果 UserService 类配置了 init-method,Spring 会调用指定的初始化方法。

  2. Bean 准备就绪

    • 初始化完成后,UserService Bean 就可以被应用程序使用了。

2.4 Bean 的使用

  1. 获取 Bean

    • 当应用程序需要 UserService Bean 时,可以通过 @Autowired 注入或 ApplicationContext.getBean() 方法获取。

    • 例如:

java 复制代码
@Autowired
private UserService userService;
  1. 调用方法

    • 当调用 userService.saveUser() 时,Spring 会先找到 userRepository 字段的值(即 UserRepository 的实例),然后调用其 save() 方法。

3. 底层原理

3.1 反射机制

  • Spring 通过反射机制创建 Bean 的实例,并设置字段的值。

  • 例如,userRepository 字段的注入是通过反射调用 Field.set() 方法实现的。

3.2 动态代理

  • 如果 UserRepository 被 AOP 增强(如事务管理或日志记录),Spring 会为其创建代理对象。

  • 代理对象会拦截方法调用,并执行增强逻辑(如事务管理)。

3.3 依赖查找

  • Spring 容器通过 BeanFactoryApplicationContext 查找和获取 Bean。

  • 例如,@Autowired 注解的背后是 Spring 调用 BeanFactory.getBean() 方法查找依赖的 Bean。


4. 代码示例

4.1 定义 Bean

java 复制代码
// 用户仓库接口
public interface UserRepository {
    void save();
}

// 用户仓库实现类
@Repository
public class UserRepositoryImpl implements UserRepository {
    @Override
    public void save() {
        System.out.println("保存用户到数据库...");
    }
}

// 用户服务类
@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;

    public void saveUser() {
        userRepository.save();
    }
}

4.2 测试类

java 复制代码
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = AppConfig.class)
public class UserServiceTest {

    @Autowired
    private UserService userService;

    @Test
    public void testSaveUser() {
        userService.saveUser();
    }
}

4.3 运行结果

复制代码
保存用户到数据库...

5. 总结

  • @Service :标识 UserService 类为 Spring Bean,并将其注册到容器中。

  • @Autowired :将 UserRepository 类型的 Bean 注入到 userRepository 字段中。

  • Spring 容器的运行流程

    1. 扫描并注册 Bean。

    2. 创建 Bean 的实例。

    3. 解析并注入依赖。

    4. 调用初始化方法。

    5. Bean 准备就绪,可以被应用程序使用。

相关推荐
九转苍翎29 分钟前
Java SE(10)——抽象类&接口
java
明月与玄武29 分钟前
Spring Boot中的拦截器!
java·spring boot·后端
矢鱼29 分钟前
单调栈模版型题目(3)
java·开发语言
n33(NK)36 分钟前
Java中的内部类详解
java·开发语言
为美好的生活献上中指38 分钟前
java每日精进 5.07【框架之数据权限】
java·开发语言·mysql·spring·spring cloud·数据权限
菲兹园长1 小时前
SpringBoot统一功能处理
java·spring boot·后端
muxue1781 小时前
go语言封装、继承与多态:
开发语言·后端·golang
一刀到底2111 小时前
spring ai alibaba 使用 SystemPromptTemplate 很方便的集成 系统提示词
spring·ai alibaba
一刀到底2111 小时前
java 多核,多线程,分布式 并发编程的现状 :从本身的jdk ,到 spring ,到其它第三方。
java·分布式·高并发
开心码农1号1 小时前
Go语言中 源文件开头的 // +build 注释的用法
开发语言·后端·golang