Spring推断构造方法(上)

梦想不抛弃苦心追求的人,只要不停止追求,你们会沐浴在梦想的光辉之中。再美好的梦想与目标,再完美的计划和方案,如果不能尽快在行动中落实,最终只能是纸上谈兵,空想一番。只要瞄准了大方向,坚持不懈地做下去,才能够扫除挡在梦想前面的障碍,实现美好的人生蓝图。Spring推断构造方法(上),希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

在Bean实例的生命周期中,会调用doCreateBean()方法来创建Bean实例,其中最主要的是实例化得到一个原始Bean,后续的属性填充、循环依赖以及AOP等等操作,都是依赖于原始的实例化bean对象,而实例化的时候,需要调用合适的构造方法来创建实例,Spring提供了推断构造方法的机制来获取最合适的构造方法

核心的源码位于AbstractAutowireCapableBeanFactory的createBeanInstance()中

一、其他方式创建实例

1.1 通过instanceSupplier获取实例

我们在创建Bean的BeanDefinition的时候,可以设置BeanDefinition的instanceSupplier属性,这是一个lambda表达式

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition();
AbstractBeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();
beanDefinition.setBeanClass(UserService.class);
//设置beanDefintion的该属性
beanDefinition.setInstanceSupplier(() -> new User());
context.registerBeanDefinition("userService",beanDefinition);

当创建Bean实例的时候,首先会去判断当前bean的BeanDefinition中是否设置了instanceSupplier属性,如果设置了,将调用obtainFromSupplier()生成一个实例

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);

   // BeanDefinition中添加了Supplier,则调用Supplier来得到对象
   Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
   if (instanceSupplier != null) {
      return obtainFromSupplier(instanceSupplier, beanName);
   }
    ……
}

在该方法中,会调用instanceSupplier的get()方法,就是执行lambda表达式来生成一个实例对象,如果得到的实例为null,则会去创建一个空实例

protected BeanWrapper obtainFromSupplier(Supplier<?> instanceSupplier, String beanName) {
    Object instance;
    try {
        instance = instanceSupplier.get();
    }

    if (instance == null) {
        instance = new NullBean();
    }
    BeanWrapper bw = new BeanWrapperImpl(instance);
    initBeanWrapper(bw);
    return bw;
}

1.2 通过缓存的构造方法和参数

在通过缓存的构造方法和参数创建实例前,会判断当前的bean是否是通过@Bean来创建,如果是这种方式,将会执行@Bean的构造推断以及生成bean实例

// @Bean对应的BeanDefinition
if (mbd.getFactoryMethodName() != null) {
   return instantiateUsingFactoryMethod(beanName, mbd, args);
}

Spring只有在getBean()的时候没有指定参数才会用到缓存的构造方法和参数

resolvedConstructorOrFactoryMethod用于缓存构造方法,其中constructorArgumentsResolved记录是否需要构造注入

如果缓存了构造方法,则判断是否需要进行依赖注入,如果需要则调用autowireConstructor()方法来生成实例,该方法是推断构造方法最核心的方法;如果不需要进行依赖注入,则会直接去调用无参的构造方法直接实例化

// Shortcut when re-creating the same bean...
// 一个原型BeanDefinition,会多次来创建Bean,那么就可以把该BeanDefinition所要使用的构造方法缓存起来,避免每次都进行构造方法推断
boolean resolved = false;
boolean autowireNecessary = false;
if (args == null) {
   synchronized (mbd.constructorArgumentLock) {
      if (mbd.resolvedConstructorOrFactoryMethod != null) {
         resolved = true;
         // autowireNecessary表示有没有必要要进行注入,比如当前BeanDefinition用的是无参构造方法,那么autowireNecessary为false,否则为true,表示需要给构造方法参数注入值
         autowireNecessary = mbd.constructorArgumentsResolved;
      }
   }
}
if (resolved) {
   // 如果确定了当前BeanDefinition的构造方法,那么看是否需要进行对构造方法进行参数的依赖注入(构造方法注入)
   if (autowireNecessary) {
      // 方法内会拿到缓存好的构造方法的入参
      return autowireConstructor(beanName, mbd, null, null);
   }
   else {
      // 构造方法已经找到了,但是没有参数,那就表示是无参,直接进行实例化
      return instantiateBean(beanName, mbd);
   }
}

二、寻找构造方法

如果前面的方式都没法创建一个实例bean,第二步就要去获取所有可用的构造方法

// 如果没有找过构造方法,那么就开始找了

// Candidate constructors for autowiring?
// 提供一个扩展点,可以利用SmartInstantiationAwareBeanPostProcessor来控制用beanClass中的哪些构造方法
// 比如AutowiredAnnotationBeanPostProcessor会把加了@Autowired注解的构造方法找出来
Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);

determineConstructorsFromBeanPostProcessors()的方法名就可以看出,也是调用BeanPostProcessor的方法,这一步调用的是SmartInstantiationAwareBeanPostProcessor实现类的determineCandidateConstructors()方法,只要找到了构造方法就返回

protected Constructor<?>[] determineConstructorsFromBeanPostProcessors(@Nullable Class<?> beanClass, String beanName)
      throws BeansException {

   if (beanClass != null && hasInstantiationAwareBeanPostProcessors()) {
      for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {
         Constructor<?>[] ctors = bp.determineCandidateConstructors(beanClass, beanName);
         if (ctors != null) {
            return ctors;
         }
      }
   }
   return null;
}

SmartInstantiationAwareBeanPostProcessor的实现类中,只有AutowiredAnnotationBeanPostProcessor对determineCandidateConstructors()进行了实现

2.1 缓存@Lookup注解方法

如果当前beanName对应的beanClass没有经过Lookup的检查,则会遍历该类以及父类的所有方法,将有@Lookup注解的方法封装成LookupOverride,并缓存至beanDefinition的methodOverrides中,后续讲解@Lookup的时候会用到

if (!this.lookupMethodsChecked.contains(beanName)) {
   // 判断beanClass是不是java.开头的类,比如String
   if (AnnotationUtils.isCandidateClass(beanClass, Lookup.class)) {
      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并设置到BeanDefinition的methodOverrides中
                   LookupOverride override = new LookupOverride(method, lookup.value());
                   RootBeanDefinition mbd = (RootBeanDefinition)
                       this.beanFactory.getMergedBeanDefinition(beanName);
                   mbd.getMethodOverrides().addOverride(override);
               }
           });
           targetClass = targetClass.getSuperclass();
       }
       while (targetClass != null && targetClass != Object.class);
   }
   this.lookupMethodsChecked.add(beanName);
}

2.2 获取构造方法并判断是否满足规则

2.2.1 获取构造方法

首先会从AutowiredAnnotationBeanPostProcessor的缓存candidateConstructorsCache中根据beanClass来获取构造方法,如果缓存中没有,才会去调用beanClasss的getDeclaredConstructors()获取所有的构造方法(包括非public)

rawCandidates变量存放的是所有未经过处理的构造方法

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 {
            // 拿到所有的构造方法
            rawCandidates = beanClass.getDeclaredConstructors();
         }
2.2.2 解析@Autowired的构造方法

用requiredConstructor记录@Autowired的required为true的构造方法,用defaultConstructor记录默认无参的构造方法

candidates变量记录所有加了@Autowired的构造方法

调用findAutowiredAnnotation()判断构造方法是否有@Autowired,如果没有,再判断是否为代理类,如果是代理类再去获取被代理类的构造方法,判断是否有该注解

如果requiredConstructor已经记录了一个@Autowired的required为true的构造方法,如果此时遍历的构造方法也有@Autowired,就直接抛异常

如果当前遍历的构造方法上@Autowired的required为true时,需要判断candidates是否为空,candidates记录的是加了@Autowired的构造方法,如果不为空,则抛异常,与上一步的功能一样,都是为了保证只要有一个@Autowired的required为true构造方法,就不允许再有其他加了@Autowired的构造方法,即使required为false也不行

如果当前遍历的构造方法没有加@Autowired,且参数个数为0,表明这是一个无参的构造方法,用defaultConstructor记录这个构造方法

List<Constructor<?>> candidates = new ArrayList<>(rawCandidates.length);

// 用来记录required为true的构造方法,一个类中只能有一个required为true的构造方法
Constructor<?> requiredConstructor = null;
// 用来记录默认无参的构造方法
Constructor<?> defaultConstructor = null;
// 遍历每个构造方法
for (Constructor<?> candidate : rawCandidates) {
    // 当前遍历的构造方法是否写了@Autowired
    MergedAnnotation<?> ann = findAutowiredAnnotation(candidate);
    if (ann == null) {
        // 如果beanClass是代理类,则得到被代理的类的类型
        Class<?> userClass = ClassUtils.getUserClass(beanClass);
        if (userClass != beanClass) {
            try {
                Constructor<?> superCtor =
                    userClass.getDeclaredConstructor(candidate.getParameterTypes());
                ann = findAutowiredAnnotation(superCtor);
            }
            catch (NoSuchMethodException ex) {
                // Simply proceed, no equivalent superclass constructor found...
            }
        }
    }

    // 当前构造方法上加了@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
        // 如果默认无参的构造方法上也加了@Autowired,那么也会加到candidates中
        candidates.add(candidate);

        // 从上面代码可以得到一个结论,在一个类中,要么只能有一个required为true的构造方法,要么只能有一个或多个required为false的方法
    }
    else if (candidate.getParameterCount() == 0) {
        // 记录唯一一个无参的构造方法
        defaultConstructor = candidate;
    }
    // 有可能存在有参、并且没有添加@Autowired的构造方法
}
2.2.3 返回候选的构造方法

candidates存放的是所有加了@Autowired的构造方法,如果candidates不为空,判断是否有required为true的构造方法,如果都是required为false的构造方法,则判断是否有默认无参构造方法,如果有,也将其加入到candidates中,作为候选的构造方法

如果没有加@Autowired的构造方法,只有一个有参的构造方法,则将该构造方法加入到候选构造方法;如果有多个有参的构造方法,则会返回null;如果只有一个默认的无参构造方法也返回null

所有候选的构造方法都存在candidateConstructors变量中,最后会将这些候选的构造方法进行缓存

对于多个有参的构造方法,会在后面推断使用哪一个构造方法时作进一步推断

if (!candidates.isEmpty()) {
   // Add default constructor to list of optional constructors, as fallback.
   // 如果不存在一个required为true的构造方法,则所有required为false的构造方法和无参构造方法都是合格的
   if (requiredConstructor == null) {
      if (defaultConstructor != null) {
         candidates.add(defaultConstructor);
      }
   }
   // 如果只存在一个required为true的构造方法,那就只有这一个是合格的
   candidateConstructors = candidates.toArray(new Constructor<?>[0]);
}
// 没有添加了@Autowired注解的构造方法,并且类中只有一个构造方法,并且是有参的
else if (rawCandidates.length == 1 && rawCandidates[0].getParameterCount() > 0) {
   candidateConstructors = new Constructor<?>[] {rawCandidates[0]};
}
……
else {
   // 如果有多个有参、并且没有添加@Autowired的构造方法,是会返回空的
   candidateConstructors = new Constructor<?>[0];
}
this.candidateConstructorsCache.put(beanClass, candidateConstructors);

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/153663.html

(0)
飞熊的头像飞熊bm

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!