使用Spring框架时,我们最直观的感受就是“不用自己new对象”——只需一个注解,Spring就会自动帮我们创建好所需的对象,然后通过getBean()就能直接获取使用。但你有没有好奇过:Spring到底是怎么找到要创建的类?又是怎么一步步把对象造出来的?尤其是当一个类有多个构造方法时,Spring会选哪一个?今天就来拆解Spring Bean的创建全过程,把这些底层逻辑讲明白。

一、先搞懂:Spring怎么“找到”要创建的类?

在开始创建对象之前,Spring首先要知道:我要创建哪些类的对象?这就涉及到Spring的扫描机制,我们从最常见的代码示例入手:

// 初始化Spring容器
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
// 获取Bean对象
UserService userService = (UserService) context.getBean("userService");
userService.test();

当我们执行第一行代码,初始化AnnotationConfigApplicationContext(Spring容器)时,Spring就已经开始“工作”了,核心就是找到所有需要创建Bean的类,具体分为3步:

1. 解析配置类,确定扫描路径

Spring会先解析我们传入的配置类(这里是AppConfig.class),不管配置类是用注解(@Configuration)还是XML配置,核心目的都是拿到包扫描路径——比如我们在AppConfig上加上@ComponentScan("com.vvhz.service"),Spring就知道要去这个包下找需要创建的类。

2. 扫描路径,筛选目标类

Spring会遍历扫描路径下的所有Java类,逐个判断:这个类是否需要被Spring管理?判断的标准很简单——只要类上有这些注解:@Component、@Service、@Controller、@Repository,就会被Spring“记下来”。

这里可以简单理解为:Spring内部有一个“注册表”(源码中叫BeanDefinitionMap,本质是一个Map),会把筛选出来的类存进去,key是后续要用到的beanName,value就是这个类本身(Class对象)。

3. 生成beanName,存入注册表(BeanDefinitionMap)

筛选出需要管理的类后,Spring会为每个类生成唯一的beanName(Bean的唯一标识),再将其与类的相关信息一起存入“注册表”——也就是Spring源码中的BeanDefinitionMap(本质是ConcurrentHashMap,保证线程安全)。

这里要明确:存入BeanDefinitionMap的并非Class对象本身,而是封装了类信息的BeanDefinition对象(包含类的全路径、构造方法、属性、作用域等),key是生成的beanName,value是对应的BeanDefinition。

这也就解释了我们调用getBean("userService")时,Spring是如何快速找到UserService类:Spring会先根据传入的beanName,去BeanDefinitionMap中查询对应的BeanDefinition,从BeanDefinition中获取到目标类(UserService)的信息,进而基于这些信息去创建Bean对象。

二、核心环节:Spring Bean的完整创建生命周期

找到目标类之后,Spring就会开始正式创建Bean对象,这个过程就像“流水线”,从实例化到最终可用,一共分为6个关键步骤,每一步都有明确的作用,我们逐个拆解:

步骤1:实例化——通过构造方法创建对象(核心:推断构造方法)

创建对象的第一步,和我们自己写Java代码一样:通过类的构造方法new出一个对象。但问题来了:如果一个类有多个构造方法(无参、有参),Spring会选哪一个?这就是构造方法推断,也是很多人容易困惑的点。

Spring的推断规则其实很简单,优先级从高到低:

  • 如果有且只有一个构造方法(不管是无参还是有参),Spring直接用这个构造方法实例化;

  • 如果有多个构造方法,且其中一个被@Autowired注解标记,Spring就用这个被注解的构造方法;

  • 如果有多个构造方法,且没有任何一个被@Autowired注解,Spring会优先选择无参构造方法;如果没有无参构造方法,就会报错。

这里要注意:实例化只是创建了一个“空对象”(类似new UserService()),此时对象中的属性还都是默认值(null、0等),还不能直接使用。

步骤2:依赖注入——给对象的属性赋值

实例化得到空对象后,Spring会检查这个对象的属性:有没有被@Autowired、@Resource等注解标记的属性?如果有,Spring会从容器中找到对应的Bean,给这些属性赋值。

比如UserService中有一个UserDao属性,并且加了@Autowired注解:

@Service
public class UserService {
    @Autowired
    private UserDao userDao;
    
    // 构造方法、test方法...
}

Spring会在这一步,找到UserDao对应的Bean,自动给userDao赋值,不用我们手动new UserDao(),这就是Spring依赖注入的核心作用——解除类之间的依赖关系。

步骤3:Aware回调——让对象“知道”自己的环境

依赖注入完成后,Spring会判断:当前对象是否实现了Aware系列接口?比如BeanNameAware、BeanFactoryAware、BeanClassLoaderAware等。

这些接口的作用很简单:让Bean能获取到Spring容器的相关信息。比如实现了BeanNameAware接口,Spring就会调用该接口的setBeanName()方法,把当前Bean的beanName传入;实现了BeanFactoryAware,就能获取到Spring容器本身(BeanFactory)。

注意:这一步是Spring主动回调,我们只需要让Bean实现对应的接口,重写方法即可,不用手动调用。

步骤4:初始化前——执行@PostConstruct注解的方法

Aware回调之后,Spring会检查当前对象的方法:有没有被@PostConstruct注解标记的方法?如果有,Spring会立即调用这个方法。

这个方法是我们自己定义的,用来做一些初始化前的准备工作,比如加载资源、初始化参数等,它的执行时机是“依赖注入完成后,正式初始化前”。

@PostConstruct
public void initBefore() {
    // 初始化前的操作,比如加载配置文件
    System.out.println("初始化前:加载资源");
}

步骤5:初始化——执行InitializingBean接口的方法

初始化前的方法执行完后,Spring会判断:当前对象是否实现了InitializingBean接口?如果实现了,就会调用该接口的afterPropertiesSet()方法。

这个方法和@PostConstruct的作用类似,也是用来做初始化操作,但它是Spring提供的接口方法,优先级比@PostConstruct低(先执行@PostConstruct,再执行afterPropertiesSet())。

步骤6:初始化后——AOP动态代理(可选)

这是Bean创建的最后一步,也是最关键的一步之一:Spring会判断当前Bean是否需要进行AOP(面向切面编程)。

什么情况下需要AOP?比如Bean上有@Transactional(事务)、@Aspect(切面)等注解,或者配置了AOP相关的规则,Spring就会对这个Bean进行动态代理,生成一个代理对象。

这里要重点区分:

  • 如果不需要AOP:最终的Bean就是步骤1中通过构造方法实例化的对象(比如UserService本身);

  • 如果需要AOP:最终的Bean不是原对象,而是Spring生成的代理对象(比如UserService的代理类实例),后续我们通过getBean()拿到的,也是这个代理对象。

到这里,一个完整的Spring Bean就创建完成了。

三、补充:单例Bean和原型Bean的区别

Bean创建完成后,Spring会根据Bean的作用域(scope),做不同的后续处理,最常见的就是单例(singleton)和原型(prototype):

1. 单例Bean(默认)

如果Bean是单例(默认作用域),Spring会把创建好的Bean对象存入一个“单例池”(源码中是一个Map,key是beanName,value是Bean对象)。

后续再调用getBean()获取这个Bean时,Spring不会再重新创建对象,而是直接从单例池中取出已有的对象,这也是单例Bean的核心特点——全局只有一个实例。

2. 原型Bean

如果Bean是原型作用域(通过@Scope("prototype")指定),Spring创建完Bean后,不会存入单例池,后续每次调用getBean(),都会重新执行上述完整的创建流程,生成一个新的Bean对象。

简单说:单例Bean“一次创建,多次复用”,原型Bean“每次获取,都是新的”。

四、总结:一张图看懂Bean创建全流程

最后用一张流程图,梳理一下整个Bean的创建过程,方便大家记忆:

Spring容器初始化 → 解析配置类 → 扫描路径筛选目标类 → 生成beanName存入注册表 → 推断构造方法实例化对象 → 依赖注入赋值 → Aware回调 → @PostConstruct(初始化前) → InitializingBean(初始化) → AOP动态代理(可选) → 存入单例池(单例Bean)/ 直接返回(原型Bean)

其实Spring创建Bean的过程,本质就是“从找到类,到造好对象,再到让对象可用”的过程,其中构造方法推断、依赖注入、AOP是最核心的三个环节。理解了这个过程,你就能更清晰地明白Spring的“自动装配”到底是怎么回事,后续看Spring源码也会更轻松。

文章作者: Z
本文链接:
版权声明: 本站所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 微博客
Java spring framework spring java
喜欢就支持一下吧