day08 - Spring 之推断构造方法

1、createBeanInstance方法讲解

摘要:

本文主要分析了Spring框架中createBeanInstance和determineCandidateConstructors两个核心方法的实现逻辑。createBeanInstance负责Bean实例化,处理了Supplier、工厂方法、构造方法注入等多种实例化方式,并采用缓存机制优化性能。determineCandidateConstructors方法通过注解处理和缓存检查确定候选构造方法,支持@Autowired和@Lookup等注解,实现了方法级别依赖注入。两个方法共同构成了Spring容器创建Bean实例的核心流程,体现了Spring在依赖注入和性能优化方面的设计思想。

1、全路径是

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

流程图:

2、源码方法讲解

c 复制代码
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
    //1.解析BeanClass
    Class<?> beanClass = resolveBeanClass(mbd, beanName);
    //2.检查访问权限
    if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {
       throw new BeanCreationException(mbd.getResourceDescription(), beanName,
             "Bean class isn't public, and non-public access not allowed: " + beanClass.getName());
    }
 }  

resolveBeanClass():从BeanDefiniton中解析出实际的class对象

Supplier机制:通过编程式API提供的Bean实例生成器,优先级最高。

c 复制代码
// Supplier检查(最高优先级)
Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
if (instanceSupplier != null) {
    return obtainFromSupplier(instanceSupplier, beanName);
}


// 工厂检查方法
if (mbd.getFactoryMethodName() != null) {
    return instantiateUsingFactoryMethod(beanName, mbd, args);
}

工厂方法:对应@Bean注解的方法或XML配置的factory-method

c 复制代码
// 构造方法缓存检查(性能优化)
boolean resolved = false;
boolean autowireNecessary = false;
if (args == null) {
    synchronized (mbd.constructorArgumentLock) {
        // resolvedConstructorOrFactoryMethod 缓存的构造方法或工厂方法
       if (mbd.resolvedConstructorOrFactoryMethod != null) {
          resolved = true;
          // constructorArgumentsResolved 标识构造方法参数是否已解析  
          autowireNecessary = mbd.constructorArgumentsResolved;
       }
    }
}
c 复制代码
if (resolved) {
    // autowireNecessary=true 需要构造方法注入
    if (autowireNecessary) {
       // 方法内会拿到缓存好的构造方法的入参
       return autowireConstructor(beanName, mbd, null, null);
    }
    // 需要无参构造,直接进行实例化
    else {
       return instantiateBean(beanName, mbd);
    }
}

构造方法注入的判断条件:

1、后置处理器返回了候选构造方法(如@Autowired标注的构造方法)

2、BeanDefinition的自动装配模式是

3、BeanDefinition中制定了构造方法参数值AUTOWIRE_CONSTRUCTOR

4、getBean()时传入了构造参数

c 复制代码
ctors = mbd.getPreferredConstructors();
if (ctors != null) {
    return autowireConstructor(beanName, mbd, ctors, null);
}

// No special handling: simply use no-arg constructor.
// 不匹配以上情况,则直接使用无参构造方法
return instantiateBean(beanName, mbd);

2、determineCandidateConstructors方法讲解

1、方法流程图:

2、相关源码讲解

c 复制代码
if (!this.lookupMethodsChecked.contains(beanName)) {

    if (AnnotationUtils.isCandidateClass(beanClass, Lookup.class)) {
       try {
          Class<?> targetClass = beanClass;
          do {
             // 遍历targetClass中的method,查看是否写了@Lookup方法
             ReflectionUtils.doWithLocalMethods(targetClass, method -> {
                Lookup lookup = method.getAnnotation(Lookup.class);
                if (lookup != null) {
                   Assert.state(this.beanFactory != null, "No BeanFactory available");
                   // 将当前method封装成LookupOverride并设置到RootBeanDefinition的methodOverrides中
                   LookupOverride override = new LookupOverride(method, lookup.value());
                   try {
                      RootBeanDefinition mbd = (RootBeanDefinition)
                            this.beanFactory.getMergedBeanDefinition(beanName);
                      mbd.getMethodOverrides().addOverride(override);
                   }
                   catch (NoSuchBeanDefinitionException ex) {
                      throw new BeanCreationException(beanName,
                            "Cannot apply @Lookup to beans without corresponding bean definition");
                   }
                }
             });
             targetClass = targetClass.getSuperclass();
          }
          while (targetClass != null && targetClass != Object.class);
       }
       catch (IllegalStateException ex) {
          throw new BeanCreationException(beanName, "Lookup method resolution failed", ex);
       }
    }
    this.lookupMethodsChecked.add(beanName);
}

@Lookup 注解作用:实现方法级别的依赖注入 ,主要用于原型bean注入到单例bean里面

缓存机制:lookupMethodsChecked集合记录已处理过的bean,避免重复处理

1)缓存检查双重锁定

c 复制代码
// 1.第一次检查无锁,减少同步开销
Constructor<?>[] candidateConstructors = this.candidateConstructorsCache.get(beanClass);
if (candidateConstructors == null) {
    // 2.确保只有一个线程执行解析逻辑
    synchronized (this.candidateConstructorsCache) {
       // 3.有锁,再次检查防止多个线程通过第一次检查  
       candidateConstructors = this.candidateConstructorsCache.get(beanClass);
       if (candidateConstructors == null) {
            // 构造方法解析逻辑...
        }
    }
}
2) 变量初始化
c 复制代码
// 用来记录required为true的构造方法,一个类中只能有一个required为true的构造方法,多个会报错
Constructor<?> requiredConstructor = null;
// 记录默认无参的构造方法
Constructor<?> defaultConstructor = null;
// kotlin主构造器
Constructor<?> primaryConstructor = BeanUtils.findPrimaryConstructor(beanClass);
int nonSyntheticConstructors = 0;

3) @Autowired构造方法处理

c 复制代码
// 构造方法加了 @Autowired注解
if (ann != null) {
    // 整个类中如果有一个required为true的构造方法,不能有其他的加了@Autowired的构造方法,否则报错
    if (requiredConstructor != null) {
       throw new BeanCreationException(beanName,
             "Invalid autowire-marked constructor: " + candidate +
             ". Found constructor with 'required' Autowired annotation already: " +
             requiredConstructor);
    }

    boolean required = determineRequiredStatus(ann);
    if (required) {
       if (!candidates.isEmpty()) {
          throw new BeanCreationException(beanName,
                "Invalid autowire-marked constructors: " + candidates +
                ". Found constructor with 'required' Autowired annotation: " +
                candidate);
       }
       // 记录唯一一个required为true的构造方法
       requiredConstructor = candidate;
    }
    // 记录所有加了@Autowired注解的构造方法,包括required为true或者false的
    candidates.add(candidate);
}

@Autowired注解规则:

required = true:一个类中最多只有一个

required = false:可以有多个

候选构造方法决策逻辑

c 复制代码
if (!candidates.isEmpty()) {
    // 如果不存在一个required为true的构造方法,
    // 则所有required为false的构造方法和无参构造方法都是合格的
    if (requiredConstructor == null) {
       if (defaultConstructor != null) {
          candidates.add(defaultConstructor);
       }
       else if (candidates.size() == 1 && logger.isInfoEnabled()) {
          logger.info("Inconsistent constructor declaration on bean with name '" + beanName +
                "': single autowire-marked constructor flagged as optional - " +
                "this constructor is effectively required since there is no " +
                "default constructor to fall back to: " + candidates.get(0));
       }
    }
    // 如果只存在一个required为true的构造方法,那就只有这一个是合格的
    candidateConstructors = candidates.toArray(new Constructor<?>[0]);
}

决策逻辑:

  • 情况1:存在@Autowired构造方法
  • 有requiredConstructor:只使用该构造方法
  • 无requiredConstructor:添加无参构造方法作为备选
  • 情况2:无@Autowired注解,只有一个有参构造方法 → 使用该构造方法
  • 情况3:无@Autowired注解,多个构造方法 → 返回空数组

喜欢我的文章记得点个在看,或者点赞,持续更新中ing...

相关推荐
道亦无名5 小时前
aiPbMgrSendAck
java·网络·数据库
发现你走远了6 小时前
Windows 下手动安装java JDK 21 并配置环境变量(详细记录)
java·开发语言·windows
心 -6 小时前
java八股文DI
java
NEXT066 小时前
后端跑路了怎么办?前端工程师用 Mock.js 自救实录
前端·后端·程序员
泯泷6 小时前
提示工程的悖论:为什么与 AI 对话比你想象的更难
人工智能·后端·openai
黎雁·泠崖6 小时前
Java常用类核心详解(一):Math 类超细讲解
java·开发语言
大尚来也6 小时前
跨平台全局键盘监听实战:基于 JNativeHook 在 Java 中捕获 Linux 键盘事件
java·linux
追随者永远是胜利者6 小时前
(LeetCode-Hot100)15. 三数之和
java·算法·leetcode·职场和发展·go
懒惰成性的7 小时前
12.Java的异常
java·开发语言
装不满的克莱因瓶7 小时前
Java7新特性:try-with-resources写法
java·前端·javascript·jdk·新特性·jdk7