Spring推断构造方法(下)

一、前言

Spring推断构造方法的前面两篇文章已经对查找候选构造方法以及推断使用的构造方法做了详细的介绍,在最后,这边文章主要简单介绍一下@Bean的情况

首先Spring在启动的过程中,会把@Bean修饰的方法解析成BeanDefinition

如果方法不是static的,BeanDefinition的参数如下:

  • factoryBeanName为@Bean方法所在类的beanName,比如下面的例子中factoryBeanName就是"appConfig"

    @ComponentScan("com.lizhi")
    public class AppConfig {
    
    	@Bean
    	public UserService userService(){
    		return new UserService();
    	}
    }
    
  • factoryMethodName为对应的方法名,比如"userService"

  • beanClass为AppConfig.class

如果方法是static的,BeanDefinition的参数如下:

  • factoryBeanName为null
  • factoryMethodName为对应的方法名,比如"userService"
  • beanClass也为AppConfig.class

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

@Bean
public static AService aService(){
    return new AService();
}

@Bean
public AService aService(BService bService){
    return new AService();
}
@ComponentScan("com.lizhi")
public class AppConfig {

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

@Configuration
public class GlobalConfig {

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

上面的这两种情况,都只会生成一个BeanDefinition,但是BeanDefinition的属性isFactoryMethodUnique为false

下面将介绍@Bean如果存在多个方法时,如何进行推断构造的

二、推断@Bean的实例方法

在createBeanInstance()方法中,前面会去判断当前创建的Bean实例是否是通过@Bean的方法创建的,如果是通过这种方式,将推断出factoryMethod来创建实例

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
   // Make sure bean class is actually resolved at this point.
   Class<?> beanClass = resolveBeanClass(mbd, beanName);
   ……
   // @Bean对应的BeanDefinition
   if (mbd.getFactoryMethodName() != null) {
      return instantiateUsingFactoryMethod(beanName, mbd, args);
   }
}
2.1 判断方法是否是static并获取beanClass

通过BeanDefinition的factoryBeanName是否为空来区分@Bean的方法是否是Static的

如果@Bean的实例已经创建了,则直接抛异常

否则缓存@Bean实例的beanName与所在类实例的beanName的依赖关系,并获取beanClass

isStatic变量记录了方法的类型是否为static,后面筛选@Bean方法时会用到

public BeanWrapper instantiateUsingFactoryMethod(
      String beanName, RootBeanDefinition mbd, @Nullable Object[] explicitArgs) {

   BeanWrapperImpl bw = new BeanWrapperImpl();
   this.beanFactory.initBeanWrapper(bw);

   Object factoryBean;
   Class<?> factoryClass;
   boolean isStatic;

   // 注意,这里拿到的是factoryBeanName,而不是factoryMethodName,比如AppConfig对象
   String factoryBeanName = mbd.getFactoryBeanName();
   if (factoryBeanName != null) {
      if (factoryBeanName.equals(beanName)) {
         throw new BeanDefinitionStoreException(mbd.getResourceDescription(), beanName,
               "factory-bean reference points back to the same bean definition");
      }
      factoryBean = this.beanFactory.getBean(factoryBeanName);

      // 该单例已经创建好了?
      if (mbd.isSingleton() && this.beanFactory.containsSingleton(beanName)) {
         throw new ImplicitlyAppearedSingletonException();
      }
      this.beanFactory.registerDependentBean(factoryBeanName, beanName);
      factoryClass = factoryBean.getClass();
      isStatic = false;
   }
   else {
      // It's a static factory method on the bean class.
      // static的@Bean方法
      if (!mbd.hasBeanClass()) {
         throw new BeanDefinitionStoreException(mbd.getResourceDescription(), beanName,
               "bean definition declares neither a bean class nor a factory-bean reference");
      }
      factoryBean = null;
      factoryClass = mbd.getBeanClass();
      isStatic = true;
   }
    ……
}

后面的操作同普通的推断构造的代码一致,会判断getBean()是否指定了参数值以及是否缓存了factoryMethod和方法参数值

2.2 寻找@Bean方法

isFactoryMethodUnique为true,如果只有一个方法,前面从缓存中可能获取了,如果缓存中没有,则直接从BeanDefinition中得到可用factoryMethod

其中candidates变量记录了缓存中的factoryMethod,如果缓存中没有,则去遍历factoryClass(@Beans所在类)的所有方法,然后筛选出与前面isStatic类型相同且与BeanDefinition中factoryMethod相同的方法,然后加入到候选的方法数组candidates中

List<Method> candidates = null;
if (mbd.isFactoryMethodUnique) {
   if (factoryMethodToUse == null) {
      factoryMethodToUse = mbd.getResolvedFactoryMethod();
   }
   if (factoryMethodToUse != null) {
      candidates = Collections.singletonList(factoryMethodToUse);
   }
}

// 找到对应的@Bean方法,由于可能参数重载,所以有可能会有多个
if (candidates == null) {
   candidates = new ArrayList<>();
   Method[] rawCandidates = getCandidateMethods(factoryClass, mbd);
   for (Method candidate : rawCandidates) {
      if (Modifier.isStatic(candidate.getModifiers()) == isStatic && mbd.isFactoryMethod(candidate)) {
         candidates.add(candidate);
      }
   }
}
2.3 使用默认方法

如果只有一个候选方法,并且getBean()调用没有指定参数值,BeanDefinition也没有指定参数值,且方法参数个数为0,那么就使用这个方法进行实例化,并将这个方法进行缓存

if (candidates.size() == 1 && explicitArgs == null && !mbd.hasConstructorArgumentValues()) {
   Method uniqueCandidate = candidates.get(0);
   if (uniqueCandidate.getParameterCount() == 0) {
      mbd.factoryMethodToIntrospect = uniqueCandidate;
      synchronized (mbd.constructorArgumentLock) {
         mbd.resolvedConstructorOrFactoryMethod = uniqueCandidate;
         mbd.constructorArgumentsResolved = true;
         mbd.resolvedConstructorArguments = EMPTY_ARGS;
      }
      bw.setBeanInstance(instantiate(beanName, mbd, factoryBean, uniqueCandidate, EMPTY_ARGS));
      return bw;
   }
}

如果不满足上面的条件,将对候选的方法进行排序,排序规则与构造推断一样,按public、参数个数进行排序

2.4 记录最小参数个数

该逻辑与构造推断的逻辑基本一致

int minNrOfArgs;
if (explicitArgs != null) {
   minNrOfArgs = explicitArgs.length;
}
else {
   // We don't have arguments passed in programmatically, so we need to resolve the
   // arguments specified in the constructor arguments held in the bean definition.
   if (mbd.hasConstructorArgumentValues()) {
      ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues();
      resolvedValues = new ConstructorArgumentValues();
      minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues);
   }
   else {
      minNrOfArgs = 0;
   }
}
2.5 遍历候选方法,推断合适的候选方法

这段的逻辑与构造方法推断也是一样的,先为每个方法寻找参数值,如果getBean()调用或者BeanDefinition指定了参数值,就用指定的,如果没有指定,并且autowiring指定可以通过Spring对其进行注入时,Spring会根据类型、参数名称去查找可用的Bean实例

boolean autowiring = (mbd.getResolvedAutowireMode() == AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR);
int parameterCount = candidate.getParameterCount();

if (parameterCount >= minNrOfArgs) {
    ArgumentsHolder argsHolder;

    Class<?>[] paramTypes = candidate.getParameterTypes();
    if (explicitArgs != null) {
        // Explicit arguments given -> arguments length must match exactly.
        if (paramTypes.length != explicitArgs.length) {
            continue;
        }
        argsHolder = new ArgumentsHolder(explicitArgs);
    }
    else {
        // Resolved constructor arguments: type conversion and/or autowiring necessary.
        // 根据参数类型和参数名找Bean
        String[] paramNames = null;
        ParameterNameDiscoverer pnd = this.beanFactory.getParameterNameDiscoverer();
        if (pnd != null) {
            paramNames = pnd.getParameterNames(candidate);
        }
        argsHolder = createArgumentArray(beanName, mbd, resolvedValues, bw,
                                         paramTypes, paramNames, candidate, autowiring, candidates.size() == 1);
    }

为方法找完参数之后,将会方法进行打分,逻辑与构造方法的打分是一样的,对于方法的分数相同的、参数个数相同、类型相同的方法放入到ambiguousFactoryMethods中缓存

int typeDiffWeight = (mbd.isLenientConstructorResolution() ?
      argsHolder.getTypeDifferenceWeight(paramTypes) : argsHolder.getAssignabilityWeight(paramTypes));
// Choose this factory method if it represents the closest match.
if (typeDiffWeight < minTypeDiffWeight) {
   factoryMethodToUse = candidate;
   argsHolderToUse = argsHolder;
   argsToUse = argsHolder.arguments;
   minTypeDiffWeight = typeDiffWeight;
   ambiguousFactoryMethods = null;
}
// Find out about ambiguity: In case of the same type difference weight
// for methods with the same number of parameters, collect such candidates
// and eventually raise an ambiguity exception.
// However, only perform that check in non-lenient constructor resolution mode,
// and explicitly ignore overridden methods (with the same parameter signature).
else if (factoryMethodToUse != null && typeDiffWeight == minTypeDiffWeight &&
      !mbd.isLenientConstructorResolution() &&
      paramTypes.length == factoryMethodToUse.getParameterCount() &&
      !Arrays.equals(paramTypes, factoryMethodToUse.getParameterTypes())) {
   if (ambiguousFactoryMethods == null) {
      ambiguousFactoryMethods = new LinkedHashSet<>();
      ambiguousFactoryMethods.add(factoryMethodToUse);
   }
   ambiguousFactoryMethods.add(candidate);
}

如果找到多个可用的,或者找的一个方法的返回值类型是Void类型,都会抛出异常

如果只找到一个可用的方法,并且不是通过getBean()调用传入的参数,且对方法和参数进行缓存,然后利用该方法和参数生成@Bean实例

// 方法返回值类型为Void
if (void.class == factoryMethodToUse.getReturnType()) {
    throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                    "Invalid factory method '" + mbd.getFactoryMethodName() + "' on class [" +
                                    factoryClass.getName() + "]: needs to have a non-void return type!");
}
// 找到多个可用的方法
else if (ambiguousFactoryMethods != null) {
    throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                    "Ambiguous factory method matches found on class [" + factoryClass.getName() + "] " +
                                    "(hint: specify index/type/name arguments for simple parameters to avoid type ambiguities): " +
                                    ambiguousFactoryMethods);
}

if (explicitArgs == null && argsHolderToUse != null) {
    mbd.factoryMethodToIntrospect = factoryMethodToUse;
    argsHolderToUse.storeCache(mbd, factoryMethodToUse);
}
}

bw.setBeanInstance(instantiate(beanName, mbd, factoryBean, factoryMethodToUse, argsToUse));
return bw;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值