Spring原理篇(9)--我们所说的Spring的推断方法是什么? 我们每天都在用;而且用得极多;

@TOC# Spring系列
记录在程序走的每一步___auth:huf


推断方法: 什么是推断方法? 我们每天都在用?怎么用的? 你知道当中的内容是什么?该问题是Spring的核心问题;在之前的篇章我们挖了一个坑: 在第一篇文章 AOP 的那一丢丢内容里面 就有一句话 <通过 UserService类推断构造方法实例化出来的对象> 有兴趣的可以翻到第一篇文章 去find一下这句话; 这个章节我们就把这个方法讲明白;

Spring 推断方法;

所谓Spring的推断方法;推断的就是 Bean的构造方法; 我们每天启动Spring Spring根据什么来创建Bean的呢? 就是通过其构造方法; 如果有多个构造方法 那么该使用哪一个构造方法? 我们每天都在用吧? 其重要性无可置疑吧?

在我们的Java中 我们可以定义

/**
 * StudentService 之 依赖注入;
 * auth:huf
 */
@Service(value="studentService")
public class StudentServiceImpl implements StudentService{
    @Autowired 
    private TeacharServiceImpl teacharService;
    public StudentServiceImpl() {
        System.out.println(0);
    }
    public StudentServiceImpl(TeacharServiceImpl teacharService) {
        System.out.println(1);
    }
    public StudentServiceImpl(TeacharServiceImpl teacharService,StudentMapper studentMapper, Student student) {
        System.out.println(2);
    }
}

在我们启动的时候 我们使用了无参构造方法 假设我们屏蔽了无参构造方法;在这里插入图片描述
这里就会提示 不可以没有 默认构造器 为什么呢? 难道没有默认构造器 就不行吗? 我这边Idea健康检查的问题; 如果健康检查去掉; 这边就不会出现红线; 这里就需要我们指定一个构造器;
在这里插入图片描述
这种情况下 用@Autowired指定一个构造器; 就不会有问题; 那么 可以同时指定2个吗?
在这里插入图片描述
健康检查是不允许的; 我们都知道 在@Autowired里面 有一个required默认为true 那么设置了这个东西为false 我们的构造方法 就不会再出问题; 那么问题又来了 我们这时候启动 执行会不会报错?
在这里插入图片描述
答案是不会;

这里来一波小总结:
一般情况下,一个类只有一个构造方法: 1. 要么是无参的构造方法 2. 要么是有参的构造方法

我们在该类为Spring Bean的时候. 我们必须要有构造方法; 如果没有构造方法 则使用默认的;
我们来画图理解这个逻辑 可能会更加直接;

推断方法不同的情况
1没有加Autowired情况
1.1多个构造方法
1.2只有一个有参的构造方法
1.3只有一个无参的构造方法
2加Autowired的情况
2.1一个required=true方法
2.2多个required=true方法.
2.3一个required=true一个为false
2.4没有required=true方法

如果没有使用@Autowired 我们会默认使用无参构造方法.如果没有不写 class会自动给我们补全;但是我们注释掉了 那么就会报错;一定需要指定一个构造方法;就可以在其他构造方法上加上@Autowired
在多个构造方法上写了@Autowired,那么此时Spring会报错。 但 是,因为@Autowired还有一个属性required,默认为ture,所以一个类中,只有能一个构造方法标 注了@Autowired或@Autowired(required=true),有多个会报错。但是可以有多个 @Autowired(required=false),这种情况下,需要Spring从这些构造方法中去自动选择一个构造方法(首先进行排序 按照参数多少进行排序 一定是用参数最多的;因为有一个集合;排序完.参数就是从多到少; 要是参数一样呢 会怎么样?留一个坑 一会填)。

假设我们使用这个 推断构造器:
在这里插入图片描述

开始回到我们的源码 doCreateBean 在第六章 的创建Bean对象的时候.把这段源码贴出来了只是我上次没有深挖这个地方 一笔带过 我们拿当时的那段源码过来 然后在其上面加工; 把忽略的知识点全部填充上;

doCreateBean

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
			throws BeanCreationException {
		开始实例化Bean 得到Warpper
		BeanWrapper instanceWrapper = null;
		判断Bean是不是单例的 如果是
		if (mbd.isSingleton()) {
			先从Factory 删除Bean; 这里 就是个ConcurrentMap 也就是删除正在创建的Bean的包装对象;
			instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
		}
		删除了之后  == null;  然后开始createBeanInstance
		if (instanceWrapper == null) {
			开始进入createBeanInstance
			instanceWrapper = createBeanInstance(beanName, mbd, args);
		}

createBeanInstance

	protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
		确保bean class 这里过;  
		Class<?> beanClass = resolveBeanClass(mbd, beanName);
		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());
		}
		Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
		if (instanceSupplier != null) {
			return obtainFromSupplier(instanceSupplier, beanName);
		}
		if (mbd.getFactoryMethodName() != null) {
			return instantiateUsingFactoryMethod(beanName, mbd, args);
		}
		重新创建同一bean时的快捷方式 
		一个类可能有多个构造器,所以Spring得根据参数个数、类型确定需要调用的构造器
        在使用构造器创建实例后,Spring会将解析过后确定下来的构造器或工厂方法保存在缓存中
		boolean resolved = false;  开关
		boolean autowireNecessary = false; 开关2
		if (args == null) {
			加锁
			synchronized (mbd.constructorArgumentLock) {
				查看缓存中是否有构造器 或者构造方法 如果有 改变上面的两个开关
				if (mbd.resolvedConstructorOrFactoryMethod != null) {
					resolved = true;
					autowireNecessary = mbd.constructorArgumentsResolved;
				}
			}
		}
		if (resolved) { 开关1 如果有构造器
			if (autowireNecessary) { 开关2
					该方法重要 autowireConstructor
				return autowireConstructor(beanName, mbd, null, null);
			}
			else {
				直接实例化Bean;
				return instantiateBean(beanName, mbd);
			}
		}
		获取目前传入的Bean的构造器 确定Bean使用的是什么构造器; 如果没有指定构造器 那么就是无参构造器;
		目前我们是三个构造器;全部为Autowird(required=false) 的情况  
		Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
		构造器!=null 
		mbd.getResolvedAutowireMode  
		等于这句话:
		--BeanDefinitionBuilder.genericBeanDefinition().setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
		
		mdb.hasConstructorArgumentValues 意思是询问:
		--BeanDefinitionBuilder.genericBeanDefinition().addConstructorArgValue()是否给了构造方法的值
		最后一个args 实际上问是否传入args
		
		if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
				mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
			四个方案中只要满足一个方案 就会进入autowireConstructor 
			return autowireConstructor(beanName, mbd, ctors, args);
		}

在经过determineConstructorsFromBeanPostProcessors 我们的构造器排序好了在这里插入图片描述

determineConstructorsFromBeanPostProcessors

@Nullable
	protected Constructor<?>[] determineConstructorsFromBeanPostProcessors(@Nullable Class<?> beanClass, String beanName)
			throws BeansException {
		这里拿出我们的BeanPostprocessors 进行排序执行; 很让人意外;这里有一个我们见过的PostProcessor
		AutowiredAnnotationBeanPostProcessor.determineCandidateConstructors
		我们可以进里面看一眼;
		if (beanClass != null && hasInstantiationAwareBeanPostProcessors()) {
			for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {
				Constructor<?>[] ctors = bp.determineCandidateConstructors(beanClass, beanName);
				if (ctors != null) {
					return ctors;
				}
			}
		}
		return null;
	}

determineCandidateConstructors

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

		省无数代码;之后再说 这里是有关lookup的;
		去缓存拿candidateConstructors  这时候肯定是没有的
		Constructor<?>[] candidateConstructors = this.candidateConstructorsCache.get(beanClass);
		if (candidateConstructors == null) {
			加锁
			synchronized (this.candidateConstructorsCache) {
				去缓存拿一次 . 没有
				candidateConstructors = this.candidateConstructorsCache.get(beanClass);
				if (candidateConstructors == null) {
					Constructor<?>[] rawCandidates;
					try {
						通过class得到了所有构造器. 这时候有三个构造器;
						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
					List<Constructor<?>> candidates = new ArrayList<>(rawCandidates.length);
					Constructor<?> requiredConstructor = null; 是否有required构造器;
					Constructor<?> defaultConstructor = null; 是否有默认构造器
					Constructor<?> primaryConstructor = BeanUtils.findPrimaryConstructor(beanClass);
					int nonSyntheticConstructors = 0;
					开始循环
					for (Constructor<?> candidate : rawCandidates) {
						if (!candidate.isSynthetic()) {
							nonSyntheticConstructors++;
						}
						else if (primaryConstructor != null) {
							continue;
						}
						查找头上是否带有autowired注解 然后封装成为MergedAnnotation
						MergedAnnotation<?> ann = findAutowiredAnnotation(candidate);
						
					......省掉部分代码
							判断required 是什么; 我们全部都是false;
							boolean required = determineRequiredStatus(ann);
							if (required) {..
							}
							把构造器添加进去candidates. 这时候candidates就有3个构造器
							candidates.add(candidate);
						}
						else if (candidate.getParameterCount() == 0) {
							defaultConstructor = candidate;
						}
					}.... 
					将构造器 放入缓存 下次使用;
					this.candidateConstructorsCache.put(beanClass, candidateConstructors);
				}
			}
		}
		返回
		return (candidateConstructors.length > 0 ? candidateConstructors : null);
	}

autowireConstructor

在这里插入图片描述
这里不做解释; 继续进来

public BeanWrapper autowireConstructor(String beanName, RootBeanDefinition mbd,
			@Nullable Constructor<?>[] chosenCtors, @Nullable Object[] explicitArgs) {....
			通过这句话 进行构造器的排序 参数越多 越靠前
			AutowireUtils.sortConstructors(candidates);
			int minTypeDiffWeight = Integer.MAX_VALUE;
			Set<Constructor<?>> ambiguousConstructors = null;
			Deque<UnsatisfiedDependencyException> causes = null;
			下面开始算得分; 得分越低的 越匹配;
			for (Constructor<?> candidate : candidates) {
			....算得分中
			}
			
		Assert.state(argsToUse != null, "Unresolved constructor arguments");
		得到实例 并返回;
		bw.setBeanInstance(instantiate(beanName, mbd, constructorToUse, argsToUse));
		return bw;
	}

总结:

  1. AbstractAutowireCapableBeanFactory类中的createBeanInstance()方法会去创建一个Bean 实例
  2. 根据BeanDefinition加载类得到Class对象
  3. 如果BeanDefinition绑定了一个Supplier,那就调用Supplier的get方法得到一个对象并直接返 回
  4. 如果BeanDefinition中存在factoryMethodName,那么就调用该工厂方法得到一个bean对象 并返回
  5. 如果BeanDefinition已经自动构造过了,那就调用autowireConstructor()自动构造一个对象
  6. 调用SmartInstantiationAwareBeanPostProcessor的determineCandidateConstructors()方 法得到哪些构造方法是可以用的
  7. 如果存在可用得构造方法,或者当前BeanDefinition的autowired是 AUTOWIRE_CONSTRUCTOR,或者BeanDefinition中指定了构造方法参数值,或者创建Bean 的时候指定了构造方法参数值,那么就调用**autowireConstructor()**方法自动构造一个对象
  8. 最后,如果不是上述情况,就根据无参的构造方法实例化一个对象

autowireConstructor

1 先检查是否指定了具体的构造方法和构造方法参数值,或者在BeanDefinition中缓存了具体的构 造方法或构造方法参数值,如果存在那么则直接使用该构造方法进行实例化
2 如果没有确定的构造方法或构造方法参数值,那么
1)如果没有确定的构造方法,那么则找出类中所有的构造方法
2)如果只有一个无参的构造方法,那么直接使用无参的构造方法进行实例化
3)如果有多个可用的构造方法或者当前Bean需要自动通过构造方法注入
4)根据所指定的构造方法参数值,确定所需要的最少的构造方法参数值的个数
5) 对所有的构造方法进行排序,参数个数多的在前面
6) 遍历每个构造方法
7)如果不是调用getBean方法时所指定的构造方法参数值,那么则根据构造方法参数类型找值
8)如果时调用getBean方法时所指定的构造方法参数值,就直接利用这些值
9)如果根据当前构造方法找到了对应的构造方法参数值,那么这个构造方法就是可用的,但是 不一定这个构造方法就是最佳的,所以这里会涉及到是否有多个构造方法匹配了同样的值, 这个时候就会用值和构造方法类型进行匹配程度的打分,找到一个最匹配的

为什么分越少优先级越高?
主要是计算找到的bean和构造方法参数类型匹配程度有多高。 假设bean的类型为A,A的父类是B,B的父类是C,同时A实现了接口D 如果构造方法的参数类型为 A,那么完全匹配,得分为0 如果构造方法的参数类型为B,那么得分为2 如果构造方法的参数类型为 C,那么得分为4 如果构造方法的参数类型为D,那么得分为1
总结就是: 父类 是得2分 接口是得1分; 分越少 越好;

这个情况我就不做源码了;

@Bean的情况

首先,Spring会把@Bean修饰的方法解析成BeanDefinition:
1如果方法是static的,那么解析出来的BeanDefinition中:
1). factoryBeanName为AppConfig所对应的beanName,比如"appConfig"
2). factoryMethodName为对应的方法名,比如"aService"
3). factoryClass为AppConfig.class

2如果方法不是static的,那么解析出来的BeanDefinition中:

  1. factoryBeanName为null
  2. factoryMethodName为对应的方法名,比如"aService"
  3. factoryClass也为AppConfig.class在由@Bean生成的BeanDefinition中,有一个重要的属性isFactoryMethodUnique,表示 factoryMethod是不是唯一的,在普通情况下@Bean生成的BeanDefinition的 isFactoryMethodUnique为true,但是如果出现了方法重载,那么就是特殊的情况

虽然有两个@Bean,但是肯定只会生成一个aService的Bean,那么Spring在处理@Bean时,也只会 生成一个aService的BeanDefinition,比如Spring先解析到第一个@Bean,会生成一个 BeanDefinition,此时isFactoryMethodUnique为true,但是解析到第二个@Bean时,会判断出来 beanDefinitionMap中已经存在一个aService的BeanDefinition了,那么会把之前的这个 BeanDefinition的isFactoryMethodUnique修改为false,并且不会生成新的BeanDefinition了。 并且后续在根据BeanDefinition创建Bean时,会根据isFactoryMethodUnique来操作,如果为 true,那就表示当前BeanDefinition只对应了一个方法,那也就是只能用这个方法来创建Bean了, 但是如果isFactoryMethodUnique为false,那就表示当前BeanDefition对应了多个方法,需要和推 断构造方法的逻辑一样,去选择用哪个方法来创建Bean。

总结 利用isFactoryMethodUnique 是否再次去生成BeanDefinition

seeyou

  • 5
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Like Java Long Time

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值