hexon
发布于 2026-03-16 / 1 阅读
0

12、推断@Bean方法底层源码解析

上一篇文章中,我们详细阐述了构造方法推断的含义,并对其源码进行了具体分析。然而,除了构造方法推断外,我们还可以通过@Bean注解的方式来注册Bean。在分析org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBeanInstance方法时,我们也曾提到其中存在一个if判断分支,专门用于处理通过@Bean定义的Bean的实例化过程,只是当时并未深入探讨其内部实现。因此,本文将重点围绕@Bean方法的底层实现机制展开详细分析。

本文内容:

  1. 什么是Spring中的推断@Bean方法?

  2. 推断@Bean方法底层源码解析

  3. isFactoryMethodUnique的作用源码解析

  4. @Bean注解Bean创建过程底层源码解析

  5. @Bean注解解析过程底层源码解析

Spring中的@Bean方法推断机制

让我们通过一个具体的代码示例来理解Spring中@Bean方法的推断机制:

public class UserService {
​
    private OrderService orderService;
​
​
    public UserService() {
    }
​
    public UserService(OrderService orderService) {
        this.orderService = orderService;
    }
​
    public void test() {
        System.out.println(orderService);
    }
​
}
@Component
public class OrderService {
​
}
@ComponentScan
public class MyConfig {
​
    @Bean
    public UserService userService() {
        return new UserService();
    }
​
    @Bean
    public UserService userService(OrderService orderService) {
        return new UserService(orderService);
    }
​
}

观察上述代码,你能确定最终会创建几个UserService的Bean吗?

答案是只会存在一个。这是因为两个@Bean方法的方法名相同(都是userService),在Spring容器中,Bean的名称默认由方法名决定,因此只会有一个名为"userService"的Bean被注册,对应的BeanDefinition也只有一个。

既然只有一个BeanDefinition,那么当存在多个重载的@Bean方法时,Spring就需要从中选择一个来执行实例化。这个过程类似于我们之前讨论的"构造方法推断"机制——Spring需要从多个候选者中决定到底该调用哪个方法。

理解@Bean的前置知识:factory-bean与factory-method

在深入分析Spring推断@Bean方法的源码之前,我们需要先理解两个基础概念:factory-beanfactory-method。这两个属性在XML配置中用于定义工厂方法创建Bean的方式,它们也是理解@Bean注解底层实现的关键前置知识。

两种工厂方法配置方式

方式一:实例工厂方法(非静态)

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
​
    <bean name="orderService" class="com.hexon.service.OrderService"/>
​
    <!--factory-bean和factory-method是BeanDefinition里面的属性,factory-bean和前面学的FactoryBean接口不是同一个东西-->
    <bean name="userService" factory-bean="orderService" factory-method="create"/>
​
</beans>
@Component
public class OrderService {
    // 注意:这是一个实例方法(非静态)
    public UserService create() {
        return new UserService();
    }
}

关键点

  • factory-bean指向一个已存在的Bean实例

  • factory-method实例方法,需要先有工厂Bean的实例才能调用

  • Spring会先从容器获取orderService实例,然后调用它的create()方法创建userService

方式二:静态工厂方法

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
       http://www.springframework.org/schema/beans/spring-beans.xsd">
​
    <bean name="orderService" class="com.hexon.service.OrderService"/>
    
    <!-- 只使用factory-method,不指定factory-bean -->
    <!-- class: 指定包含静态工厂方法的类 -->
    <!-- factory-method: 指定静态工厂方法名 -->
    <bean name="userService" class="com.hexon.service.OrderService" factory-method="create"/>
​
</beans>
@Component
public class OrderService {
    // 注意:这是一个静态方法
    public static UserService create() {
        return new UserService();
    }
}

关键点

  • 只使用factory-method,不需要factory-bean

  • 通过class属性指定包含静态工厂方法的类

  • factory-method静态方法,可以直接通过类名调用

重要说明

注意:这里的factory-beanFactoryBean接口是两个完全不同的概念:

  • factory-bean是BeanDefinition中的一个属性,用于指定实例工厂方法的来源Bean

  • FactoryBean是一个接口,实现该接口的类可以自定义创建Bean的逻辑

虽然名字相似,但不要混淆它们。

与@Bean注解的关系

理解这两种工厂方法配置方式对分析@Bean源码至关重要,因为:

  1. @Bean注解本质上就是一种工厂方法声明,标注了@Bean的方法就是工厂方法

  2. @Bean方法可以类比为XML配置中的factory-method

  3. 配置类(@Configuration)本身相当于factory-bean的角色

  4. 静态@Bean方法则对应静态工厂方法的场景

这两种配置方式在Spring底层都被抽象为统一的Bean创建模型,最终都会转化为BeanDefinition中的factoryBeanNamefactoryMethodName属性,这也是Spring处理@Bean注解时的核心数据结构。

在后续的源码分析中,我们将看到Spring是如何解析@Bean方法,并将其转换为类似的工厂模型来创建Bean实例的。

推断@Bean方法底层源码解析

我们首先通过断点的方式来验证下上面的前置知识中的描述。

示例代码:

@Component
public class OrderService {
}
public class UserService {
​
    private OrderService orderService;
​
​
    public UserService() {
    }
​
    public UserService(OrderService orderService) {
        this.orderService = orderService;
    }
​
    public void test() {
        System.out.println(orderService);
    }
​
}
public class Main {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
​
        UserService userService = (UserService) applicationContext.getBean("userService");
        userService.test();
​
        }
}
@ComponentScan
public class MyConfig {
​
    @Bean
    public UserService userService() {
        return new UserService();
    }
​
}

源码入口:

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBeanInstance
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#instantiateUsingFactoryMethod
org.springframework.beans.factory.support.ConstructorResolver#instantiateUsingFactoryMethod

断点设置:

首先我们测试的是非静态方法,此时按照前面说的那么factoryBeanName其实就是配置类myConfig。

再将userService方法改成静态方法:

@ComponentScan
public class MyConfig {
​
    @Bean
    public static UserService userService() {
        return new UserService();
    }
​
}

静态方法可以通过类直接调用,所以factoryBeanName此时是null:

无论是哪种方法最终都会调用org.springframework.beans.factory.support.SimpleInstantiationStrategy#instantiate(org.springframework.beans.factory.support.RootBeanDefinition, java.lang.String, org.springframework.beans.factory.BeanFactory, java.lang.Object, java.lang.reflect.Method, java.lang.Object...)方法进行实例化:

上面的代码只有一个userServcie方法,因此还不涉及到推断使用哪个方法,但是如果代码是下面这样情况就不一样了:

@ComponentScan
public class MyConfig {
​
    @Bean
    public UserService userService() {
        System.out.println(0);
        return new UserService();
    }
​
    @Bean
    public UserService userService(OrderService orderService) {
        System.out.println(1);
        return new UserService(orderService);
    }
​
}

结果

1
com.hexon.service.OrderService@68999068

结果打印的是一个参数中的,根据我们上节课学习推断构造方法其实也容易猜出这个结果,因为方法的参数多而且可以找到对应的依赖。

到底是怎么推断的,我们就要详细阅读一下:

org.springframework.beans.factory.support.ConstructorResolver#instantiateUsingFactoryMethod方法的源码。

这个方法我们只大概走读一下,因为大部分的逻辑和构造方法推断类似,而且这个方法也在ConstructorResolver类中。

前面的源码我们阅读过了,主要是在提取factoryBeanName ,我们分析后面的代码。

再往后面有一个BeanDefnition中重要的属性:

在由@Bean生成的BeanDefinition中,有一个重要的属性isFactoryMethodUnique,表示factoryMethod是不是唯一的,在普通情况下@Bean生成的BeanDefinition的isFactoryMethodUnique为true,但是如果出现了方法重载,那么就是特殊的情况,比如:

@Bean
public UserService userService() {
    return new UserService();
}
​
@Bean
public UserService userService(OrderService orderService) {
    return new UserService(orderService);
}

虽然有两个@Bean,但是肯定只会生成一个userService的Bean,那么Spring在处理@Bean时,也只会生成一个userService的BeanDefinition,比如Spring先解析到第一个@Bean,会生成一个BeanDefinition,此时isFactoryMethodUnique为true,但是解析到第二个@Bean时,会判断出来beanDefinitionMap中已经存在一个userService的BeanDefinition了,那么会把之前的这个BeanDefinition的isFactoryMethodUnique修改为false,并且不会生成新的BeanDefinition了

并且后续在根据BeanDefinition创建Bean时,会根据isFactoryMethodUnique来操作,如果为true,那就表示当前BeanDefinition只对应了一个方法,那也就是只能用这个方法来创建Bean了,但是如果isFactoryMethodUnique为false,那就表示当前BeanDefition对应了多个方法,需要和推断构造方法的逻辑一样,去选择用哪个方法来创建Bean。

isFactoryMethodUnique属性是BeanDefinition中的,BeanDefinition是在容器启动过程中解析的,下面我们来看下在容器启动过程中大致是怎么处理解析@Bean注解的。

@Bean注解解析过程底层源码解析

这里我们通过isFactoryMethodUnique属性定位到源码处理的位置:

断点:

loadBeanDefinitionsForBeanMethod方法会在处理每个@Bean方法过程中进入一次。

首先,Spring会把@Bean修饰的方法解析成BeanDefinition:

  1. 如果方法不是static的,那么解析出来的BeanDefinition中:

    1. factoryBeanName为MyConfig所对应的beanName,比如"myConfig"

    2. factoryMethodName为对应的方法名,比如"userService"

    3. factoryClass为MyConfig.class

  2. 如果方法是static的,那么解析出来的BeanDefinition中:

    1. factoryBeanName为null

    2. factoryMethodName为对应的方法名,比如"userService"

    3. factoryClass也为MyConfig.class

具体源码大概是这里:

再后面是在解析@Bean注解本身的属性,到这里分析的是第一个@Bean方法进入的情况。如果是第二次进入,那么会进入org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#isOverriddenByExistingDefinition方法,从而将isFactoryMethodUnique设置成false:

另外,从这个代码可以看出,如果@Bean方法的名称一样就只会生成一个BeanDefinition,也就是说默认情况是根据方法名来决定是否是同一个BeanDefinition的:

@ComponentScan
public class MyConfig {
    // 方法名: userService1
    @Bean
    public UserService userService1() {
        System.out.println(0);
        return new UserService();
    }
​
    // 方法名: userService2
    @Bean
    public UserService userService2(OrderService orderService) {
        System.out.println(1);
        return new UserService(orderService);
    }
}

在Spring中,@Bean方法的方法名默认就是Bean的名称(除非通过@Bean("customName")指定别名)。因此:

  • userService1()方法 → 生成名为"userService1"的Bean

  • userService2()方法 → 生成名为"userService2"的Bean

这是两个完全不同的名称,所以会生成两个独立的BeanDefinition。

扩展思考

思考下面代码的运行情况:

@Component
public class OrderService {
​
}
public class UserService {
​
    private OrderService orderService;
​
​
    public UserService() {
    }
​
    public UserService(OrderService orderService) {
        this.orderService = orderService;
    }
​
    public void test() {
        System.out.println(orderService);
    }
​
}
@ComponentScan
public class MyConfig {
​
    // factory-method
​
    @Bean
    public UserService userService() {
        System.out.println(0);
        return new UserService();
    }
​
    @Bean
    public UserService userService(OrderService orderService) {
        System.out.println(1);
        return new UserService(orderService);
    }
​
    @Bean
    public UserService userService(OrderService orderService, OrderService orderService1) {
        System.out.println(2);
        return new UserService(orderService);
    }
}
public class Main {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
​
        UserService userService = (UserService) applicationContext.getBean("userService");
        userService.test();
​
        }
}

输出:

2
com.hexon.service.OrderService@50eac852

如果指定参数:

public class Main {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
​
        UserService userService = (UserService) applicationContext.getBean("userService", new OrderService());
        userService.test();
​
        }
}

结果:

2
com.hexon.service.OrderService@50eac852

这是因为UserService是非懒加载的单例Bean,在容器启动过程中就创建了

@ComponentScan
public class MyConfig {
​
    @Bean
    @Lazy
    public UserService userService() {
        System.out.println(0);
        return new UserService();
    }
​
    @Bean
    @Lazy
    public UserService userService(OrderService orderService) {
        System.out.println(1);
        return new UserService(orderService);
    }
​
    @Bean
    @Lazy
    public UserService userService(OrderService orderService, OrderService orderService1) {
        System.out.println(2);
        return new UserService(orderService);
    }
}

给同名的所有方法加@Lazy,即可打印:

1
com.hexon.service.OrderService@3427b02d

源码中可能还有细节处理我没有看到,刚开始可能认为给第一个@Bean方法加@Lazy就可以,但是这里可能有重载的逻辑影响