红茶的个人站点

  • 首页
  • 专栏
  • 开发工具
  • 其它
  • 隐私政策
Awalon
Talk is cheap,show me the code.
  1. 首页
  2. 专栏
  3. Spring Boot 学习笔记
  4. 正文

Spring 源码学习 18:FactoryBean

2025年7月14日 12点热度 0人点赞 0条评论

FactoryBean

有一个实体类:

@Data
@AllArgsConstructor
@NoArgsConstructor
static class Student{
    private String name;
    private int age;
}

它的工厂类:

@Component("student")
static class StudentFactoryBean implements FactoryBean<Student> {
​
    private final Random random = new Random();
    private List<String> names = List.of("Tom", "Jack", "Jane");
​
    @Override
    public Student getObject() throws Exception {
        int age = random.nextInt(100);
        int nameIndex = random.nextInt(names.size());
        return new Student(names.get(nameIndex), age);
    }
​
    @Override
    public Class<?> getObjectType() {
        return Student.class;
    }
​
    @Override
    public boolean isSingleton() {
        return false;
    }
}

Spring 中的工厂类需要实现FactoryBean接口,该接口有三个方法:

  • getObject,返回工厂生产的产品

  • getObjectType,返回产品的类型(Class 对象)

  • isSingleton,生产的产品是否单例

注意,这里的工厂类用@Component("student")标记,说明它是被 Spring 容器管理的 Bean,且名称为student。

用主配置类导入工厂定义:

@Configuration
@Import(StudentFactoryBean.class)
static class Config{}

测试:

AnnotationConfigApplicationContext context =  new AnnotationConfigApplicationContext(Config.class);
Student student1 = (Student)context.getBean("student");
Student student2 = (Student)context.getBean("student");
System.out.println(student1);
System.out.println(student2);
context.close();

对于 Spring 中的工厂类,比较特殊,通过它们的 Bean 名称获取到的并不是工厂对象,而是工厂生产的产品。

如果你需要获取容器中的工厂对象本身,需要:

StudentFactoryBean studentFactoryBean = (StudentFactoryBean)context.getBean("&student");

当然也可以通过类型获取:

StudentFactoryBean studentFactoryBean = context.getBean(StudentFactoryBean.class);

此外,FactoryBean接口的getObjectType方法并不一定要返回类型:

@Override
public Class<?> getObjectType() {
    return null;
}

此时通过名称获取产品是没有问题的,但如果通过类型获取产品:

Student student1 = context.getBean(Student.class);

会报错。

特殊性

工厂生产的产品并不完全由 Spring 容器管理,不具备完整的 Spring Bean 生命周期。

修改 Student:

@Data
@NoArgsConstructor
@Slf4j
static class Student implements ApplicationContextAware {
    private String name;
    private int age;

    @Autowired
    public void setClassRoom(ClassRoom classRoom) {
        this.classRoom = classRoom;
    }

    private ClassRoom classRoom;

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
        log.info("Student created");
    }

    @PostConstruct
    public void init() {
        log.info("Student init");
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        log.info("Student setApplicationContext");
    }
}

为产品类添加了 Setter 注入、@PostConstruct 方法、ApplicationContextAware 接口。实际测试会发现,这些都不会生效。

但 Bean 的后处理器会部分生效。

添加一个自定义的 Bean 后处理器:

@Slf4j
@Component
static class MyBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof Student) {
            Student student = (Student) bean;
            log.info("{} before init", student);
        }
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof Student) {
            Student student = (Student) bean;
            log.info("{} after init", student);
        }
        return bean;
    }
}

实际测试会发现,对于工厂的产品,postProcessBeforeInitialization不会生效,但postProcessAfterInitialization方法会被调用。

总结

Spring 中的 FactoryBean 比较特殊,其产品的创建、依赖注入、初始化等工作都应该由 FactoryBean 的 getObject 方法完成,Spring 容器只会调用 Bean 后处理器的 postProcessAfterInitialization 方法处理产品对象。

使用 FactoryBean 的产品对象很不方便,无法使用相应的 Spring Bean 生命周期钩子,因此能使用 Bean 方法创建 Bean 的情况下尽量不要使用 FactoryBean。

@Indexed

Spring Boot 会扫描入口类所在的包(及其子包),将组建(Compoent)类的 Bean 定义添加到 Bean 工厂。可以用下边的代码模拟这个过程:

DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(beanFactory);
scanner.scan("com.example.demo.scan");
String[] beanNames = beanFactory.getBeanDefinitionNames();
for (String beanName : beanNames) {
    System.out.println(beanName);
}

中有三个类定义:

@Component
public class Bean1 {
}
@Configuration
public class Bean2 {
}
public class Bean3 {
}

指定包下有很多类,就可能导致读取类定义的时间过长。因此从 Spring 5.0 开始,使用一种索引技术加速这种包扫描的过程。

要使用扫描索引,需要为项目添加注解处理器:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <configuration>
        <annotationProcessorPaths>
            <path>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>${lombok.version}</version>
            </path>
            <path>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context-indexer</artifactId>
            </path>
        </annotationProcessorPaths>
    </configuration>
</plugin>

Maven 编译插件maven-compiler-plugin 原本只需要处理 lombok 相关注解,现在需要处理@Indexed注解,因此添加额外的注解处理器spring-context-indexer。

对项目重新编译,就能看到:

image-20250712114913894

这个就是注解处理器生成的扫描索引文件:

com.example.demo.DemoApplication=org.springframework.stereotype.Component,org.springframework.boot.SpringBootConfiguration
com.example.demo.scan.Bean1=org.springframework.stereotype.Component
com.example.demo.scan.Bean2=org.springframework.stereotype.Component

key 是需要被添加为 Bean 的完整类名,值为使用的组件注解。

实际上所有的组件注解都以直接或间接的方式包含@Indexed注解:

@Indexed
public @interface Component {
@Component
public @interface Configuration {

因此这些类都会被添加到扫描索引中。

现在运行的时候包扫描会检测是否存在扫描索引文件,如果存在,就不再需要通过目录读取字节码分析是否需要添加,只需要从索引文件中读取对应的类添加即可。

代理

Bean 的依赖注入和初始化阶段只会使用原始对象,不牵扯代理对象。

比如下面这个示例:

@Component
@Slf4j
@Getter
public static class MyBean {
    private MyBean2 myBean2;
    private boolean initialized = false;

    @Autowired
    public void setMyBean2(MyBean2 myBean2) {
        log.info("MyBean.setMyBean2() is called.");
        this.myBean2 = myBean2;
    }

    public void hello() {
        log.info("MyBean.hello() is called.");
    }

    @PostConstruct
    public void init() {
        this.initialized = true;
        log.info("MyBean.init() is called.");
    }
}

MyBean 的依赖注入阶段对应setMyBean2方法调用,初始化阶段对应init方法调用。

为其创建一个切面:

@Aspect
@Slf4j
@Component
public static class MyAspect {
    @Before("execution(* com.example..MyBean.*(..))")
    public void before() {
        log.info("MyAspect.before() is called.");
    }
}

execution 表达式将为MyBean创建一个代理,并拦截所有的方法调用。

开启 AOP 功能(加入相应的 Bean 后处理器):

@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)
@Import({MyAspect.class, MyBean.class, MyBean2.class})
public static class Config {
}

测试:

AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class);
MyBean beanProxy = ctx.getBean(MyBean.class);

从输出可以看到,通过 Spring 容器获取 Bean 实例的过程中,依赖注入和初始化阶段都不会触发 AOP 中的增强方法,也就是说这两个阶段 AOP 代理对象都不参与。

当然,如果在 Bean 创建后手动调用,会触发 AOP 的方法增强:

beanProxy.init();
beanProxy.setMyBean2(new MyBean2());

属性

代理对象中的属性与原始对象中的属性是不同的。比如:

public static class MyBean {
    protected MyBean2 myBean2;
    protected boolean initialized = false;

这里将原始对象的属性改为protected,CGLIB 创建的代理对象就会继承这两个属性。

编写一个工具方法打印代理对象和原始对象中的属性:

public static void showProxyAndTarget(MyBean proxy) throws Exception {
    System.out.println(">>>>> 代理中的成员变量");
    System.out.println("\tinitialized=" + proxy.initialized);
    System.out.println("\tmyBean2=" + proxy.myBean2);

    if (proxy instanceof Advised advised) {
        System.out.println(">>>>> 目标中的成员变量");
        MyBean target = (MyBean) advised.getTargetSource().getTarget();
        System.out.println("\tinitialized=" + target.initialized);
        System.out.println("\tmyBean2=" + target.myBean2);
    }
}

结果:

>>>>> 代理中的成员变量
	initialized=false
	myBean2=null
>>>>> 目标中的成员变量
	initialized=true
	myBean2=com.example.demo.ProxyTests2$MyBean2@47404bea

可以看到,代理对象中的成员变量都是声明时候的值。不过这并不会影响代理对象的使用,因为通常情况下我们都是通过 Setter 访问属性,而代理对象的 Setter 实际会调用原始对象的 Setter。

局限性

我们知道,CGLIB 实现代理是通过对原始类型的继承以及方法重写实现的,因此通过 CGLIB 实现的 AOP 代码增强,只能作用于可以被重写的方法。

为原始类型添加四种不同的方法:

public static class MyBean {
    public void function1(){
        log.info("function1");
    }

    public static void function2(){
        log.info("function2");
    }

    final public void function3(){
        log.info("function3");
    }

    private void function4(){
        log.info("function4");
    }
    // ...
}

这四种方法分别是:

  • 普通的 public 方法

  • 静态方法

  • final 标记的 public 方法

  • 私有的方法

测试:

AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class);
MyBean beanProxy = ctx.getBean(MyBean.class);
beanProxy.function1();
beanProxy.function2();
beanProxy.function3();
beanProxy.function4();

结果:

16:01:17.133 [main] INFO com.example.demo.ProxyTests3$MyAspect -- MyAspect.before() is called.
16:01:17.134 [main] INFO com.example.demo.ProxyTests3$MyBean -- function1
16:01:17.134 [main] INFO com.example.demo.ProxyTests3$MyBean -- function2
16:01:17.134 [main] INFO com.example.demo.ProxyTests3$MyBean -- function3
16:01:17.134 [main] INFO com.example.demo.ProxyTests3$MyBean -- function4

可以看到,静态方法、final 方法、私有方法都没有被增强,本质上它们都不能被重写,所以不能被 CGLIB 代理实现的 AOP 进行代码增强。

可以通过编译时织入(CTW)或运行时织入(RTW)技术实现对这些方法的 AOP 增强。

@Value

在 Spring 中,可以使用 @Value 注解为 Bean 属性实现一些特殊注入,这里用一个示例探讨 @Value 注解的实现原理。

创建自定义的 @Value 注解:

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface MyValue{
    String value();
}

作为示例的配置类:

@Configuration(proxyBeanMethods = false)
@ToString
@Getter
static class MyProperties {
    @MyValue("${JAVA_HOME}")
    private String javaHome;
}

注意,这里使用了@Configuration(proxyBeanMethods = false),这是为了让配置类的 Bean 不要使用代理,以方便后续利用反射读取配置类的注解时产生代理类没有相应注解的 bug。当然,你可以可以添加判断,如果是代理类就读取原始类型,但这里采取简化的方式。

创建一个 Bean 后处理器用于解析自定义注解:

@Component
@Slf4j
static class MyConfigPropertiesBeanPostProcessor implements BeanPostProcessor {
    @Autowired
    private ApplicationContext applicationContext;

    @SneakyThrows
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        Optional<Component> annotation = AnnotationUtils.findAnnotation(bean.getClass(), Component.class);
        if (annotation.isPresent()) {
            // 存在自定义 @MyConfigurationProperties 注解
            // 处理使用了 @Value 的属性
            Field[] declaredFields = bean.getClass().getDeclaredFields();
            for (Field field : declaredFields) {
                MyValue valueAnnotation = field.getAnnotation(MyValue.class);
                if (valueAnnotation != null) {
                    // 获取 @Value 注解的值
                    String value = valueAnnotation.value();
                    log.info("获取到的原始 Value 值:{}", value);
                    // 尝试作为 ${...} 表达式进行解析
                    value = applicationContext.getEnvironment().resolvePlaceholders(value);
                    log.info("尝试作为 ${...} 表达式解析后的值:{}", value);
                    field.setAccessible(true);
                    field.set(bean, value);
                }
            }
        }
        return bean;
    }
}

通过反射调用获取到使用了 @MyValue 注解的字段,然后利用环境(applicationContext.getEnvironment())对象的resolvePlaceholders方法解析${...}表达式中的属性值。

除了解析环境属性占位符,还可能需要处理类型转换:

@MyValue("18")
private int age;

处理类型转换:

// 尝试类型转换
ConfigurableBeanFactory autowireCapableBeanFactory = (ConfigurableBeanFactory) applicationContext.getAutowireCapableBeanFactory();
Object convertedValue = autowireCapableBeanFactory.getTypeConverter().convertIfNecessary(value, field.getType());
log.info("value 的值从 {} 转换为 {}", value.getClass(), convertedValue.getClass());
field.set(bean, convertedValue);

@Value 注解还可以使用 SpEL 表达式:

@MyValue("#{@student}")
private Student student;

这里利用 SpEL 表达式注入了一个 student 依赖。

解析 SpEL 表达式:

// 尝试当做 SpEL 表达式 #{...} 进行解析
ConfigurableBeanFactory autowireCapableBeanFactory = (ConfigurableBeanFactory) applicationContext.getAutowireCapableBeanFactory();
Object evaluatedValue = autowireCapableBeanFactory.getBeanExpressionResolver().evaluate(value, new BeanExpressionContext(autowireCapableBeanFactory, null));
log.info("尝试作为 SpEL 表达式进行解析后的值:{}", evaluatedValue);

还可以将 SpEL 表达式和 ${...} 占位符混合使用:

@MyValue("#{'hello, ' + '${JAVA_HOME}'}")
private String hello;

代码不需要修改,可以看到处理过程:

image-20250712172647320

先替换了${...}的值,然后作为 SpEL 表达式进行了解析,最终拼接出了字符串。

完整示例可以看这里。

@Autowired

看一个简单示例:

@Component
@Data
static class Student {
    @Autowired
    private Teacher teacher;
}

@Data
@AllArgsConstructor
static class Teacher {
    private String name;
    private int age;
}

@Configuration
@Import({Student.class})
static class Config{
    @Bean
    public Teacher teacher(){
        return new Teacher("Tom", 20);
    }
}

Student 的 teacher 字段使用@Autowired 注入Teacher Bean。用容器加载配置类:

GenericApplicationContext context = new GenericApplicationContext();
context.registerBean(Config.class);
context.registerBean(ConfigurationClassPostProcessor.class);
context.refresh();
Student bean = context.getBean(Student.class);
System.out.println(bean);
context.close();

正如前面说过的,GenericApplicationContext容器缺少必要的工厂和 Bean 后处理器,这里也仅添加了一个处理配置类的 ConfigurationClassPostProcessor,缺少处理@Autowired 注解的 Bean 后处理器AutowiredAnnotationBeanPostProcessor,因此@Autowired注解的依赖注入不会生效:

AutowiredTests.Student(teacher=null)

字段注入

可以实现一个自定义 Bean 后处理器,来处理这个依赖注入:

static class AutowiredBeanPostProcessor implements BeanPostProcessor, ApplicationContextAware {
    private ApplicationContext applicationContext;
    @SneakyThrows
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        for (Field declaredField : bean.getClass().getDeclaredFields()) {
            // 检查是否有 @Autowired 注解
            if (declaredField.isAnnotationPresent(Autowired.class)) {
                // 作为依赖注入进行解析
                AutowireCapableBeanFactory beanFactory = applicationContext.getAutowireCapableBeanFactory();
                Object dependency = beanFactory.resolveDependency(new DependencyDescriptor(declaredField, true), null);
                declaredField.setAccessible(true);
                declaredField.set(bean, dependency);
            }
        }
        return bean;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}

利用 Bean 工厂的resolveDependency方法可以处理依赖注入:

Object dependency = beanFactory.resolveDependency(new DependencyDescriptor(declaredField, true), null);

DependencyDescriptor对象用于描述一个要处理的依赖注入位置,可以是类字段,也可以是 Setter 方法等。

此外,为了获取必要信息,还需要在 Bean 后处理器中使用容器句柄,但这个 Bean 后处理器本身就是用于处理 @Autowired 注解的,因此显然不能在这里使用 @Autowired 注解进行注入,所以这里使用 ApplicationContextAware 接口。

最后,在容器中注册自定义 Bean 后处理器:

context.registerBean(AutowiredBeanPostProcessor.class);

现在就可以看到依赖注入被正确处理了。

Setter 注入

除了字段注入,还有可能是 Setter 注入:

private ClassRoom classRoom;

@Autowired
public void setClassRoom(ClassRoom classRoom) {
    this.classRoom = classRoom;
}

同样的,Bean 后处理器中要考虑这种情况:

for (Method method : bean.getClass().getDeclaredMethods()) {
    // 检查方法是否有 @Autowired 注解
    if (method.isAnnotationPresent(Autowired.class)) {
        int parameterCount = method.getParameterCount();
        Object[] args = new Object[parameterCount];
        for (int i = 0; i < parameterCount; i++) {
            args[i] = beanFactory.resolveDependency(new DependencyDescriptor(new MethodParameter(method, i), true), null);
        }
        method.setAccessible(true);
        method.invoke(bean, args);
    }
}

这里通过new DependencyDescriptor(new MethodParameter(method, i)指定处理method方法的第i个参数的依赖注入。

参数索引从 0 开始。

包装类型

注入的类型还可以是包装类型:

@Autowired
private Optional<Teacher> optionalTeacher;

@Autowired
@ToString.Exclude
private ObjectFactory<Teacher> objectFactory;

幸运的是新版本的 Spring 的beanFactory.resolveDependency(...)方法可以正确处理这些包装类型以完成注入。

在旧的版本中,需要调用DependencyDescriptor.increaseNestingLevel(...)才能正确处理,详情可以看这个视频。

注入代理对象

依赖注入时可以使用@Lazy,此时应当注入代理对象而不是原始对象:

@Lazy
@Autowired
private Teacher teacher;

注入代理对象:

// 有 @Lazy 注解,注入代理对象
ContextAnnotationAutowireCandidateResolver resolver = new ContextAnnotationAutowireCandidateResolver();
resolver.setBeanFactory(beanFactory);
dependency = resolver.getLazyResolutionProxyIfNecessary(dependencyDescriptor, null);

注入数组

还可以注入数组:

@Autowired
@ToString.Exclude
private List<Service> serviceList;

处理数组注入:

DependencyDescriptor dependencyDescriptor = new DependencyDescriptor(declaredField, true);
Object dependency;
if (dependencyDescriptor.getDependencyType().isArray()){
    // 处理数组依赖注入
    // 获取数组元素类型
    Class<?> componentType = dependencyDescriptor.getDependencyType().getComponentType();
    String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, componentType);
    List<Object> beans = new ArrayList<>();
    for (String elementBeanName : beanNames) {
        beans.add(beanFactory.getBean(elementBeanName));
    }
    // 类型转换
    dependency = beanFactory.getTypeConverter().convertIfNecessary(beans, dependencyDescriptor.getDependencyType());
}

实际上beanFactory.resolveDependency方法可以正确处理数组注入,这里只是为了演示如何实现数组注入。

注入 List

与注入数组类似,还可以注入 List:

@Autowired
@ToString.Exclude
private List<Service> serviceList;

区别是处理 List 注入的时候,需要获取到 List 泛型的类型,然后根据泛型类型去 Bean 工厂中查找所需的 Bean:

else if(dependencyDescriptor.getDependencyType() == List.class){
    // 处理 List 依赖注入
    // 获取 List 中的泛型
    Class<?> resolve = dependencyDescriptor.getResolvableType().getGeneric().resolve();
    log.info("List 泛型的类型是 {}", resolve.getName());
    String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, resolve);
    log.info("获取到的 List 元素的 bean 名称:{}", Arrays.toString(beanNames));
    List<Object> beans = new ArrayList<>();
    for (String name : beanNames) {
        beans.add(beanFactory.getBean(name));
    }
    log.info("生成要注入的 List:{}", beans);
    dependency = beans;
}

通过dependencyDescriptor.getResolvableType().getGeneric().resolve()可以获取到 List 的泛型类型。

与数组注入相同的是,beanFactory.resolveDependency同样可以正确处理 List 注入,这里手动编写处理过程同样是出于演示处理过程的目的。

特殊 Bean

如前面所说的,Spring 容器中单例 Bean 都保存在:

image-20250713181411560

除此之外,还有一些特殊类型的 Bean 保存在:

image-20250713181845991

会在容器调用 refresh 方法时添加这些 Bean:

image-20250713182104964

查找特殊 Bean:

// 检查是不是特殊类型
Object specialBean = findSpecialType(dependencyType, beanFactory);
if (specialBean != null) {
    dependency = specialBean;
} else if (dependencyType.isArray()) {

匹配逻辑:

private Object findSpecialType(Class<?> dependencyType, DefaultListableBeanFactory beanFactory) throws NoSuchFieldException, IllegalAccessException {
    Field resolvableDependencies = DefaultListableBeanFactory.class.getDeclaredField("resolvableDependencies");
    resolvableDependencies.setAccessible(true);
    Map<Class<?>, Object> specialBeans = (Map<Class<?>, Object>) resolvableDependencies.get(beanFactory);
    for (Map.Entry<Class<?>, Object> entry : specialBeans.entrySet()) {
        Class<?> clazz = entry.getKey();
        Object bean = entry.getValue();
        if (clazz.isAssignableFrom(dependencyType) && dependencyType.isAssignableFrom(bean.getClass())) {
            log.info("匹配到特殊 Bean: {}", bean);
            return bean;
        }
    }
    return null;
}

泛型接口

有时候定义的 Bean 的类型是泛型接口:

@Bean
public Dao<User> userDao() {
    return new Dao<User>() {
    };
}

@Bean
public Dao<Employee> employeeDao() {
    return new Dao<Employee>() {
    };
}

@Bean
public Dao<Police> policeDao() {
    return new Dao<Police>() {
    };
}

依赖注入:

@Autowired
private Dao<User> userDao;

处理依赖注入的时候就需要考虑泛型信息来进行匹配:

// 依赖类型是泛型接口
String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, dependencyType);
ContextAnnotationAutowireCandidateResolver resolver = new ContextAnnotationAutowireCandidateResolver();
for (String name : beanNames) {
    // 获取实现了接口的 Bean 定义
    BeanDefinition beanDefinition = beanFactory.getBeanDefinition(name);
    // 判断 Bean 定义与依赖类型是否匹配
    boolean autowireCandidate = resolver.isAutowireCandidate(new BeanDefinitionHolder(beanDefinition, name), dependencyDescriptor);
    if (autowireCandidate) {
        dependency = beanFactory.getBean(name);
        log.info("泛型接口匹配到 Bean:{}", dependency);
        break;
    }
}

可以使用resolver.isAutowireCandidate方法判断 Bean 定义是否与依赖注入类型相匹配,BeanDefinitionHolder类型包含 Bean 定义与 Bean 名称。

其实beanFactory.resolveDependency方法本身就支持对泛型接口的解析,这里仅用于演示如何实现。

@Qualifier

如果依赖注入的类型有多个 Bean 作为候选,可以使用 @Qualifier 注解进一步限定:

@Autowired
@Qualifier("service1")
private Service service1;

处理逻辑和泛型接口是类似的:

// 使用了 @Qualifier 注解限定的字段
// 获取所有依赖注入类型的 Bean
String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, dependencyType);
ContextAnnotationAutowireCandidateResolver candidateResolver = new ContextAnnotationAutowireCandidateResolver();
for (String name : beanNames) {
    BeanDefinition beanDefinition = beanFactory.getBeanDefinition(name);
    boolean autowireCandidate = candidateResolver.isAutowireCandidate(new BeanDefinitionHolder(beanDefinition, name), dependencyDescriptor);
    if (autowireCandidate) {
        dependency = beanFactory.getBean(name);
        log.info("@Qualifier 注解进行的依赖注入,匹配到:{}", dependency);
        break;
    }
}

按照名称匹配

我们知道,@Autowired 优先按照类型匹配,当匹配到多个类型时,且没有设置 @Primary 注解,会按照 Bean 名称进行匹配:

@Autowired
private Service service2;

此时需要匹配名称是service2且类型是Service的 Bean。

匹配逻辑:

Object dependency = null;
// 存在多个候选项时,按照 Bean 名称进行匹配
for (String name : BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, dependencyType)) {
    if(name.equals(dependencyDescriptor.getDependencyName())) {
        dependency = beanFactory.getBean(name);
        log.info("按照名称进行匹配:{}", dependency);
        return dependency;
    }
}
return dependency;

完整示例见这里。

本文的完整示例可以从这里获取。

参考资料

  • 黑马程序员Spring视频教程,深度讲解spring5底层原理

本作品采用 知识共享署名 4.0 国际许可协议 进行许可
标签: autowried FactoryBean indexed Proxy value 代理
最后更新:2025年7月14日

魔芋红茶

加一点PHP,加一点Go,加一点Python......

点赞
< 上一篇

文章评论

razz evil exclaim smile redface biggrin eek confused idea lol mad twisted rolleyes wink cool arrow neutral cry mrgreen drooling persevering
取消回复

COPYRIGHT © 2021 icexmoon.cn. ALL RIGHTS RESERVED.
本网站由提供CDN加速/云存储服务

Theme Kratos Made By Seaton Jiang

宁ICP备2021001508号

宁公网安备64040202000141号