likes
comments
collection
share

Spring源码(九)-确定构造方法-determineConstructorsFromBeanPostProcessors

作者站长头像
站长
· 阅读数 36

日积月累,水滴石穿 😄

前言

本篇是createBeanInstance 方法的最后一篇文章。第一篇分析了 obtainFromSupplierinstantiateUsingFactoryMethod方法。第二篇分析了autowireConstructorinstantiateBean方法。前两篇已经将 Spring 如何实例化 Bean 的方式都分析完了。

源码

这篇就分析一下 determineConstructorsFromBeanPostProcessors方法。该方法的作用是:确定构造方法,在类中寻找被@Autowired标注的构造方法。

平常我们可以这么使用,告诉 Spring我要使用这个构造方法实例化对象。

@Autowired
public TestController(ABServiceImpl str){
	System.out.println("一个");
}

Spring 是怎么去寻找我们所指定的构造方法呢!GO ~ ~ ~。直接打断点飙到 AbstractAutowireCapableBeanFactory类的createBeanInstance方法。

// 上面代码省略
// 代码执行到这,说明是第一次创建该bean
// 根据SmartInstantiationAwareBeanPostProcessor确定构造函数,
// 有三个子类,除了AutowiredAnnotationBeanPostProcessor有实现逻辑,其余两个是空实现
Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);

// 通过BeanPostProcessor找出了构造方法
// 或者BeanDefinition的autowire属性为AUTOWIRE_CONSTRUCTOR xml中使用了 autowire="constructor"
// 或者BeanDefinition中指定了构造方法参数值 使用了 <constructor-arg>标签
// 或者在getBean()时指定了args
if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
	// 就进行有参构造方法实例化Bean
	return autowireConstructor(beanName, mbd, ctors, args);
}

// Preferred constructors for default construction?
// 没啥用
ctors = mbd.getPreferredConstructors();
if (ctors != null) {
	return autowireConstructor(beanName, mbd, ctors, null);
}

// 用无参的构造方法来实例化bean
return instantiateBean(beanName, mbd);

上述逻辑挺简单的,需要创建的 Bean 如果是第一次创建,就会走上述逻辑。首先确定构造方法。然后再根据四个条件来判断是使用有参构造方法实例化Bean,还是使用无参的构造方法来实例化Bean

现在,我们来看本文的重点,调试进入 determineConstructorsFromBeanPostProcessors方法,该方法位于AbstractAutowireCapableBeanFactory类。

该方法有两个入参,分别为:

  • beanClass:当前创建Bean的Class对象
  • beanName:Bean名称
@Nullable
protected Constructor<?>[] determineConstructorsFromBeanPostProcessors(@Nullable Class<?> beanClass, String beanName)
throws BeansException {

if (beanClass != null && hasInstantiationAwareBeanPostProcessors()) {
    for (BeanPostProcessor bp : getBeanPostProcessors()) {
        if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
            SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
            //调用determineCandidateConstructors方法
            Constructor<?>[] ctors = ibp.determineCandidateConstructors(beanClass, beanName);
            if (ctors != null) {
                return ctors;
            }
        }
    }
}
return null;
}

首先有个判断条件,满足判断条件才走逻辑,否则返回 null。hasInstantiationAwareBeanPostProcessors()方法也就是看 hasInstantiationAwareBeanPostProcessors属性是否为 true。

protected boolean hasInstantiationAwareBeanPostProcessors() {
	return this.hasInstantiationAwareBeanPostProcessors;
}

hasInstantiationAwareBeanPostProcessors属性什么时候为 true 呢?当添加的 BeanPostProcessorInstantiationAwareBeanPostProcessor就为 true。addBeanPostProcessor具体在什么时候被调用后续的博文会讲到。

@Override
public void addBeanPostProcessor(BeanPostProcessor beanPostProcessor) {
        Assert.notNull(beanPostProcessor, "BeanPostProcessor must not be null");
        this.beanPostProcessors.remove(beanPostProcessor);

        if (beanPostProcessor instanceof InstantiationAwareBeanPostProcessor) {
                // 是否有InstantiationAwareBeanPostProcessor
                this.hasInstantiationAwareBeanPostProcessors = true;
        }
        if (beanPostProcessor instanceof DestructionAwareBeanPostProcessor) {
                // 是否有DestructionAwareBeanPostProcessor
                this.hasDestructionAwareBeanPostProcessors = true;
        }
        // 添加到 beanPostProcessors 中,到时候会按添加的顺序执行
        this.beanPostProcessors.add(beanPostProcessor);
}

通过了判断逻辑,再获得所有BeanPostProcessors,如果拿到的 BeanPostProcessorSmartInstantiationAwareBeanPostProcessor,就执行 determineCandidateConstructors()方法。跳转到SmartInstantiationAwareBeanPostProcessor接口。

@Nullable
default Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, String beanName)
throws BeansException {  
    return null;
}

SmartInstantiationAwareBeanPostProcessor接口默认没有对该方法进行实现。具体实现过程交给实现类实现。SmartInstantiationAwareBeanPostProcessor 接口下有三个实现类,分别是 AbstractAutoProxyCreatorInstantiationAwareBeanPostProcessorAdapterAutowiredAnnotationBeanPostProcessor。前两个类对determineCandidateConstructors方法是空实现,只有在 AutowiredAnnotationBeanPostProcessor 实现类中才有具体的实现过程。

@Override
@Nullable
public Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, final String beanName) throws BeanCreationException {

// Let's check for lookup methods here... 检查查找方法 lookup-methods
// @Lookup就是在这边通过反射发现的
// (xml形式的replace-method和lookup-method在BeanDefinitionParserDelegate中进行解析)
// 如果当前 beanName 在 lookupMethodsChecked 中存在,就不需要再次解析了
if (!this.lookupMethodsChecked.contains(beanName)) {
    if (AnnotationUtils.isCandidateClass(beanClass, Lookup.class)) {
        // 类中是否包含@Lookup注解
        try {
            Class<?> targetClass = beanClass;
            do {
            // 遍历当前beanClass中所有的方法
            // 并且把这些方法的信息封装为LookupOverride对象加载到mbd中
            ReflectionUtils.doWithLocalMethods(targetClass, method -> {
// 获得方法上的 Lookup 注解
            Lookup lookup = method.getAnnotation(Lookup.class);
            if (lookup != null) {
                Assert.state(this.beanFactory != null, "No BeanFactory available");
                LookupOverride override = new LookupOverride(method, lookup.value());
                try {
                    RootBeanDefinition mbd = (RootBeanDefinition)
                       this.beanFactory.getMergedBeanDefinition(beanName);
// 将 LookupOverride对象 放入 overrides Set 集合中
                    mbd.getMethodOverrides().addOverride(override);
                }
                catch (NoSuchBeanDefinitionException ex) {
                    throw new BeanCreationException(beanName,
       "Cannot apply @Lookup to beans without corresponding bean definition");
                }
            }
//if结束
        });
        // 获得当前类的父类
        targetClass = targetClass.getSuperclass();
    }
        while (targetClass != null && targetClass != Object.class);

    }
    catch (IllegalStateException ex) {
            throw new BeanCreationException(beanName, "Lookup method resolution failed", ex);
    }
}
    // lookupMethodsChecked是一个set,用来记录哪些bean的@Lookup注解被解析了,
    //下一次就不用解析了
    this.lookupMethodsChecked.add(beanName);
}
// 上述 if 结束 === 


// 先检查 candidateConstructorsCache 中是否缓存了当前bean中可用的构造方法
Constructor<?>[] candidateConstructors = this.candidateConstructorsCache.get(beanClass);
if (candidateConstructors == null) {
// Fully synchronized resolution now...
// 双重检测
synchronized (this.candidateConstructorsCache) {
candidateConstructors = this.candidateConstructorsCache.get(beanClass);

// 如果没有筛选过构造方法,就开始筛选
if (candidateConstructors == null) {
Constructor<?>[] rawCandidates;
try {
    // 拿到当前类所有的构造方法(包括public的和非public的),
    //如果类中没有写任何构造方法,这里会返回一个无参的构造方法
    rawCandidates = beanClass.getDeclaredConstructors();
}
catch (Throwable ex) {
    throw new BeanCreationException(beanName,
       "Resolution of declared constructors on bean Class [" + beanClass.getName() +
        "] from ClassLoader [" + beanClass.getClassLoader() + "] failed", ex);
}

// candidates:用来存储所有被筛选出来的构造方法,其实可以认为,
// 就是把所有@Autowired注解标注的方法放到里面, 但是还多放了一个默认构造方法
List<Constructor<?>> candidates = new ArrayList<>(rawCandidates.length);
// requiredConstructor:表示被@Autowired标注并且required为true的构造方法
// 因为只允许出现一个这样的构造方法, 所以当这个变量存在值后
// 又出现了一个相同情况的构造方法的话, Spring就会抛出异常
Constructor<?> requiredConstructor = null;
// defaultConstructor:用来保存默认构造方法
Constructor<?> defaultConstructor = null;

// 如果是kotlin的类才起效,如果是java中的类则直接返回null
Constructor<?> primaryConstructor = BeanUtils.findPrimaryConstructor(beanClass);
int nonSyntheticConstructors = 0;

// 遍历所有的构造方法
for (Constructor<?> candidate : rawCandidates) {
// nonSyntheticConstructors这个变量是和primaryConstructor != null一起使用的,所以也不用管
if (!candidate.isSynthetic()) {
    nonSyntheticConstructors++;
}
else if (primaryConstructor != null) {
    continue;
}

// 查看该构造方法上是否存在@Autowired,@Value,@Inject注解,
// 或者看代理类的父类中对应的构造方法上是否存在@Autowired,@Value,@Inject注解
MergedAnnotation<?> ann = findAutowiredAnnotation(candidate);
if (ann == null) {
// 如果当前类是cglib生成的代理类,则获取其父类,否则返回class本身
Class<?> userClass = ClassUtils.getUserClass(beanClass);
if (userClass != beanClass) {
    try {
    // 获取带有参数的构造函数
    Constructor<?> superCtor =
                    userClass.getDeclaredConstructor(candidate.getParameterTypes());
    //继续寻找@Autowired,@Value,@Inject注解
    ann = findAutowiredAnnotation(superCtor);
    }
    catch (NoSuchMethodException ex) {
    // Simply proceed, no equivalent superclass constructor found...
    }
}
}

// ann != null说明构造方法上存在注解
if (ann != null) {
// requiredConstructor:表示程序员手动指明一定要使用的构造方法
// 所以如果有多个构造方法上都写了@Autowired注解,如果其中有一个@Autowired注解的required属性为true的情况下,就会报错。

if (requiredConstructor != null) {
throw new BeanCreationException(beanName,
        "Invalid autowire-marked constructor: " + candidate +
        ". Found constructor with 'required' Autowired annotation already: " +
        requiredConstructor);
}
// 查看@Autowired的required属性值,默认为true
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);
        }
        requiredConstructor = candidate;
    }
    // candidates中存的是加了@Autowired注解的构造方法
    candidates.add(candidate);
}
// 如果当前构造方法上不存在@Autowired,并且是无参构造方法,则记录该无参构造方法
// 所以我们可以方法,在遍历构造方法时,其实只关心无参构造方法和加了@Autowired注解的构造方法
else if (candidate.getParameterCount() == 0) {
    defaultConstructor = candidate;
}
}

// 如果存在添加了@Autowired的构造方法
// candidates分为两种情况,要么candidates中包含一个required等于true的构造方法
// 要么candidates中包含一个或多个required等于false的构造方法
if (!candidates.isEmpty()) {
// 如果没有指定required为true的构造方法,那么就把无参构造方法添加到candidates中去,后续一起进行推断
if (requiredConstructor == null) {
    if (defaultConstructor != null) {
            // 那么就把无参的构造方法添加进去
            candidates.add(defaultConstructor);
    }
    // 如果没有指定required为true的构造方法,并且也没有无参的构造方法,并且只有一个构造方法
    else if (candidates.size() == 1 && logger.isInfoEnabled()) {
            // 给一个提示,没有无参的构造方法,然后又只有一个@Autowired(required=false)的构造方法
            // 所以其实一定会用这个构造方法,所以打印一个日志,告诉程序员,你其实可以把required改为true
            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));
    }
}

// candidateConstructors就是当前方法的返回值
// candidates到了这里存在两种情况,要么只有一个@Autowired(required=true)的构造方法,
// 要么多个@Autowired(required=false)+一个无参构造方法
candidateConstructors = candidates.toArray(new Constructor<?>[0]);
}
// 不存在加@Autowired注解的构造方法,那么则判断是不是只有一个有参的构造方法,如果是则返回
else if (rawCandidates.length == 1 && rawCandidates[0].getParameterCount() > 0) {
        candidateConstructors = new Constructor<?>[] {rawCandidates[0]};
}
// 不管
else if (nonSyntheticConstructors == 2 && primaryConstructor != null &&
                defaultConstructor != null && !primaryConstructor.equals(defaultConstructor)) {
        candidateConstructors = new Constructor<?>[] {primaryConstructor, defaultConstructor};
}
// 不管
else if (nonSyntheticConstructors == 1 && primaryConstructor != null) {
        candidateConstructors = new Constructor<?>[] {primaryConstructor};
}
// 如果没有构造方法上添加了@Autowired注解,并且有多个构造方法,并且没有primaryConstructor
else {
        // 返回一个空的Constructor数组,表示没有推断出来构造方法
        candidateConstructors = new Constructor<?>[0];
}
this.candidateConstructorsCache.put(beanClass, candidateConstructors);
}
}
}

// 要么返回一个required=true的构造方法
// 要么返回多个requreid=false+无参的构造方法
// 要么返回唯一的一个有参的构造方法
// 如果只有一个无参的构造方法,这里会返回null,外层逻辑会默认使用无参构造方法进行实例化
return (candidateConstructors.length > 0 ? candidateConstructors : null);
}

总结

这段代码的逻辑比较绕,我们来慢慢理一下:

  • 1、首先从缓存中判断当前 beanName 是否已经在 lookupMethodsChecked 中存在,如果存在就不需要再次解析了。否则,判断当前 Class 类中方法上是否包含@Lookup注解,存在则将方法的信息封装为LookupOverride对象加载到mbd中。
  • 2、从缓存中获取当前 bean 中可用的构造方法,如果存在直接返回。
  • 3、获取当前类所有的构造方法,并将值赋予 rawCandidates变量。如果当前类是 cglib 生成的代理类,则获取其父类。
  • 4、遍历每个构造方法,判断构造方法上是否包含@Autowired注解。
    • A:存在:判断 required 属性值是否为 true,为 true 时,将当前构造方法赋值给 requiredConstructor 属性。然后将当前构造方法添加到candidates属性中。
    • B:不存在:判断当前构造方法的参数是否为 0 个;将当前构造方法赋值给 defaultConstructor 属性。
  • 5、循环结束
  • 6、判断 candidates属性是否为空,表示是否存在@Autowired注解标注的构造方法。
    • A:不为空,当前 candidates属性中存着带有注解的构造方法。然后判断是否存在带有 required为true的构造方法。
      • A1:不存在,判断defaultConstructor属性不为 null,将 defaultConstructor属性的值添加到 candidatesdefaultConstructor属性存着无参构造方法。
      • A2:存在,将 candidates 属性赋值给 candidateConstructorscandidateConstructors最终返回值。
    • B:为空,也就是不存在加@Autowired注解的构造方法。则判断rawCandidates的大小是否等于1,并且参数个数大于是否0,这表示只有一个有参的构造方法,如果是则将值赋给 candidateConstructors属性。否则将空数组赋值给 candidateConstructors属性。
  • 7、判断 candidateConstructors属性的长度是否大于 0 ,大于则返回 candidateConstructors,否则返回 null。

Spring源码(九)-确定构造方法-determineConstructorsFromBeanPostProcessors 图来源于图灵学院

实例

既然上述逻辑比较麻烦。这里就举几个例子,便于更好更快的理解它。小玩意,我还能不懂你了~。

准备工作

  • 创建两个类,一个 A 类,一个 B 类。
//A 类
public class A {
	String name;

	private B b;

	public A() {
		System.out.println("使用无参构造方法实例化");

	}
	
	public A(String name) {
		System.out.println("使用 name 有参构造方法实例化");
		this.name = name;
	}

	public A(B b) {
		System.out.println("使用 B 有参构造方法实例化=" + b);
		this.b = b;
	}

	public A(String name, B b) {
		System.out.println("使用 name + B 有参构造方法实例化 ="+ b);

		this.name = name;
		this.b = b;
	}
}
//B 类
public class B {
}
  • 配置文件
<bean class="com.gongj.determineConstructors.A" id="aa"></bean>

<bean class="com.gongj.determineConstructors.B" id="bb"></bean>

<context:component-scan base-package="com.gongj.create"></context:component-scan>
  • 启动类
public static void main(String[] args) {
    ClassPathXmlApplicationContext context =
        new ClassPathXmlApplicationContext("spring-config5.xml");
    A a = (A)context.getBean("aa");
    System.out.println(a);
}

实例一

类中有两个@Autowired标注的方法,其required属性一个为true,一个为false

在 A 类的A(B b)构造方法A(String name)构造方法上加上 @Autowired注解,其required属性一个为true,一个为false

@Autowired(required = false)
public A(String name) {
    System.out.println("使用 name 有参构造方法实例化");
    this.name = name;
}

@Autowired
public A(B b) {
    System.out.println("使用 B 有参构造方法实例化=" + b);
    this.b = b;
}

各位小伙伴们可以猜猜会发生什么!猜不出来的可以再上去看看源码逻辑。小杰就先将答案说了,会抛出异常

直接启动Main,控制台会打印如下日志:

Spring源码(九)-确定构造方法-determineConstructorsFromBeanPostProcessors 一个类中如果有构造方法上写了@Autowired注解,并且required属性为 true的情况下,类中的其他构造方法上不能再写@Autowired注解了。如果写了启动时会报错。

实例二

类中有多个@Autowired标注的方法并且required属性都为 false

A(B b)构造方法上的 required属性修改为 false。也就是说类中有两个被@Autowired标注的方法并且required属性都为 false

@Autowired(required = false)
public A(B b) {
	System.out.println("使用 B 有参构造方法实例化=" + b);
    this.b = b;
}

各位小伙伴们可以再猜猜结果是什么! Spring源码(九)-确定构造方法-determineConstructorsFromBeanPostProcessors 可以看到一共找出来了三个构造方法。两个被@Autowired标注的方法加上无参构造方法。那 Spring 会使用谁呢?启动Main方法,控制台打印如下:

使用 B 有参构造方法实例化=com.gongj.determineConstructors.B@2a556333
com.gongj.determineConstructors.A@32d2fa64

可以看使用的是 A(B b)构造方法。为什么呢?这里简单的讲一下,如果要理解,那需要各位小伙伴去打DeBug实践了。

  • 既然我们从这一步找到了构造方法。那就会调用有参构造方法进行实例化Bean(autowireConstructor())。

  • 然后对构造方法进行排序。按构造方法的参数个数降序排序,先排序public构造函数,参数降序排列;然后排序非public 的构造函数,参数降序排列。

  • 然后循环遍历构造方法。

  • 再拿到每个构造方法的参数列表,循环参数列表。根据参数类型、参数名称首先与 bean 标签里 constructor-arg指定的值进行匹配、转换,转换不了抛出异常。如果constructor-arg没有指定值,则进行构造方法自动推断,说的直白点,就是去创建Bean或者获取Bean。

    就拿实例二,我们拿到了三个构造方法,然后进行排序,顺序为 A(B b) ->A(String name)->A()。然后循环每个构造方法;拿到每个构造方法的参数列表,循环参数列表。根据参数类型、参数名称首先与 bean 标签里 constructor-arg指定的值进行匹配;上述例子肯定不会与 constructor-arg匹配成功,因为都没有。那就会走构造方法自动推断。

    • A(B b)构造方法,就会去创建Bean或者获取Bean,并且能创建成功或者获取到Bean。也就是代表该构造方法匹配成功,但可能不是最适合的,先将信息存起来,继续循环其他构造方法。

      // 将当前构造方法和所找到参数值作为待使用的,遍历下一个构造方法
      if (typeDiffWeight < minTypeDiffWeight) {
          constructorToUse = candidate;
          argsHolderToUse = argsHolder;
          argsToUse = argsHolder.arguments;
          minTypeDiffWeight = typeDiffWeight;
          ambiguousConstructors = null;
      }
      
    • A(String name)构造方法,这推断不了,结束本次循环。

    • A():无参构造方法,都不会被推断,直接结束循环。

      if (constructorToUse != null && argsToUse != null && argsToUse.length > parameterCount) {
          // 进入这里说明已经找到选用的构造函数 且找到的构造函数参数个数大于当前遍历的,则不用继续遍历了
          // PS:上面已经按照参数个数降序排列了,首先遍历的肯定是参数个数多的
          break;
      }
      

我们修改一下代码,在 xml 文件增加如下代码,给 A Bean 增加constructor-arg标签。

<bean class="com.gongj.determineConstructors.A" id="aa">
    <constructor-arg index="0" value="gggg"></constructor-arg>
</bean>

再次猜测,控制台会打印什么?

使用 name 有参构造方法实例化
com.gongj.determineConstructors.A@38bc8ab5

A(B b)构造方法首先会与constructor-arg指定的值进行匹配、转换,但这根本就转换不了,String 转换为 B对象,抛出异常,结束本次循环。然后再与A(String name)构造方法比较。

实例三

类中只有一个构造方法的时候。

实例三需要修改代码,首先增加一个 C类并配置相关配置。

public class C {
}

//相关配置
<bean class="com.gongj.determineConstructors.C" id="cc"></bean>

修改 A类中代码如下,可以看到 A 类中增加了一个 C 属性,并类中只有一个构造方法A(C c, B b) ,该构造方法上并没有标注@Autowired注解。

public class A {
	String name;

	private C c;
	private B b;

//	public A() {
//		System.out.println("使用无参构造方法实例化");
//
//	}
	//@Autowired(required = false)
//	public A(String name) {
//		System.out.println("使用 name 有参构造方法实例化");
//		this.name = name;
//	}

	//@Autowired(required = false)
//	public A(B b) {
//		System.out.println("使用 B 有参构造方法实例化=" + b);
//		this.b = b;
//	}
	//@Autowired(required = false)
//	public A(String name, B b) {
//		System.out.println("使用 name + B 有参构造方法实例化 ="+ b);
//
//		this.name = name;
//		this.b = b;
//	}

	public A(C c, B b) {
		System.out.println("使用 C + B 有参构造方法实例化 ="+ b);
		this.c = c;
		this.b = b;
	}
}

启动控制台打印如下:

使用 C + B 有参构造方法实例化 =com.gongj.determineConstructors.B@17d677df
com.gongj.determineConstructors.A@42f93a98
  • 如你对本文有疑问或本文有错误之处,欢迎评论留言指出。如觉得本文对你有所帮助,欢迎点赞和关注。
转载自:https://juejin.cn/post/7081553925190467614
评论
请登录