Spring中如何获取到一个Bean实例(二)?

本文作为Spring中如何获取到一个Bean实例(一)?的姊妹篇,我们 对获取bean实例的流程做一些补充说明。

【1】主动获取方式

除了在项目启动过程中,refresh方法过程中spring会自动实例化单例bean并解析bean依赖之外,我们通常可能使用如下两种方式主动获取bean,触发bean实例化过程。

如下所示,分别是根据bean class type以及beanName来获取一个bean实例。

SysUser bean = applicationContext.getBean(SysUser.class);
SysUser bean1 = (SysUser) applicationContext.getBean("myuser");

//根据bean类型
public <T> T getBean(Class<T> requiredType) throws BeansException {}

//根据beanName
public Object getBean(String name) throws BeansException {}

① 根据bean类型

我们分析这个流程:

// AbstractApplicationContext#getBean(java.lang.Class<T>)
@Override
public <T> T getBean(Class<T> requiredType) throws BeansException {
	assertBeanFactoryActive();
	return getBeanFactory().getBean(requiredType);
}
getBean:1126, AbstractApplicationContext (org.springframework.context.support)
getBean:342, DefaultListableBeanFactory (org.springframework.beans.factory.support)
getBean:349, DefaultListableBeanFactory (org.springframework.beans.factory.support)
resolveBean:416, DefaultListableBeanFactory (org.springframework.beans.factory.support)
resolveNamedBean:1155, DefaultListableBeanFactory (org.springframework.beans.factory.support)
getBean:227, AbstractBeanFactory (org.springframework.beans.factory.support)
doGetBean:245, AbstractBeanFactory (org.springframework.beans.factory.support)

在这里插入图片描述

② 根据beanName

// AbstractApplicationContext#getBean(java.lang.String)
@Override
public Object getBean(String name) throws BeansException {
	assertBeanFactoryActive();
	return getBeanFactory().getBean(name);
}

@Override
public Object getBean(String name) throws BeansException {
	return doGetBean(name, null, null, false);
}
// 然后走到了这里
org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean

在这里插入图片描述
在前面Spring中如何获取到一个Bean实例(一)?我们提到过,这个AbstractBeanFactory的doGetBean方法就是触发bean实例初始化流程的入口方法。综上可以看出,根据beanName获取bean实例相对很简洁,根据Bean type则要麻烦得多。最终二者都是由doGetBean方法触发了bean的实例化。

【2】根据Bean Type获取bean时如何确定beanName?

这个过程发生在DefaultListableBeanFactory的resolveNamedBean方法中。

private <T> NamedBeanHolder<T> resolveNamedBean(
		ResolvableType requiredType, @Nullable Object[] args, boolean nonUniqueAsNull) throws BeansException {

	Assert.notNull(requiredType, "Required type must not be null");
	
	// 从beanDefinitionNames 和 manualSingletonNames 集合中检测beanName
	String[] candidateNames = getBeanNamesForType(requiredType);

// 第一次判断,如果length > 1 ,也就是有多个同样类型的bean,判断是否为候选bean
	if (candidateNames.length > 1) {
		List<String> autowireCandidates = new ArrayList<>(candidateNames.length);
		for (String beanName : candidateNames) {
		// 判断beanDefinitionMap是否不包含beanName  或者当前beanName的BeanDefinition中isAutowireCandidate是否为true
			if (!containsBeanDefinition(beanName) || getBeanDefinition(beanName).isAutowireCandidate()) {
				autowireCandidates.add(beanName);
			}
		}
		if (!autowireCandidates.isEmpty()) {
			candidateNames = StringUtils.toStringArray(autowireCandidates);
		}
	}

//如果此时候选beanName 只有一个,则直接触发getBean流程
	if (candidateNames.length == 1) {
		String beanName = candidateNames[0];
		return new NamedBeanHolder<>(beanName, (T) getBean(beanName, requiredType.toClass(), args));
	}
	else if (candidateNames.length > 1) {
		Map<String, Object> candidates = new LinkedHashMap<>(candidateNames.length);
		for (String beanName : candidateNames) {
			//	判断一级缓存singletonObjects里面是否有当前beanName
			if (containsSingleton(beanName) && args == null) {
				Object beanInstance = getBean(beanName);
				candidates.put(beanName, (beanInstance instanceof NullBean ? null : beanInstance));
			}
			else {
				candidates.put(beanName, getType(beanName));
			}
		}
		// 获取标注了primary的bean,如果有多个均标注了@Primary注解则抛出异常
		String candidateName = determinePrimaryCandidate(candidates, requiredType.toClass());
		if (candidateName == null) {
		// 如果候选name为空,则尝试判读其@Priority值,如果有多个一样的,则抛出异常
			candidateName = determineHighestPriorityCandidate(candidates, requiredType.toClass());
		}
		// 如果判断Primary Priority后,candidateName 不为null,则获取当前candidateName对应的实例返回
		if (candidateName != null) {
			Object beanInstance = candidates.get(candidateName);
			if (beanInstance == null || beanInstance instanceof Class) {
				beanInstance = getBean(candidateName, requiredType.toClass(), args);
			}
			return new NamedBeanHolder<>(candidateName, (T) beanInstance);
		}
		// 如果上面没有返回,则抛出异常NoUniqueBeanDefinitionException
		if (!nonUniqueAsNull) {
			throw new NoUniqueBeanDefinitionException(requiredType, candidates.keySet());
		}
	}

	return null;
}

方法流程梳理如下 :

  • 根据beanType从beanDefinitionNamesmanualSingletonNames集合中检测beanName
  • 第一次判断,如果length > 1 ,也就是有多个同样类型的bean,判断是否为候选bean
  • 如果此时候选beanName 只有一个,则直接触发getBean流程
  • 如果candidateNames length > 1,则触发判断逻辑
    • 获取标注了@Primary的bean,如果有多个均标注了@Primary注解则抛出异常,尝试得到唯一一个
    • 如果candidateName为空,则尝试判读其@Priority值,如果有多个一样的,则抛出异常
    • 如果判断Primary、 Priority后,candidateName 不为null,则获取当前candidateName对应的实例返回
    • 如果上面没有返回,则抛出异常NoUniqueBeanDefinitionException

【3】获取RootBeanDefinition

这里我们分析的是AbstractBeanFactory的doGetBean方法中实例化的前置步骤,获取bean定义。

在这里插入图片描述

AbstractBeanFactorygetMergedLocalBeanDefinition方法如下所示,直接从mergedBeanDefinitions这个ConcurrentHashMap中获取beanName对应的RootBeanDefinition 。

// AbstractBeanFactory
private final Map<String, RootBeanDefinition> mergedBeanDefinitions = new ConcurrentHashMap<>(256);


// AbstractBeanFactory
protected RootBeanDefinition getMergedLocalBeanDefinition(String beanName) throws BeansException {
	// Quick check on the concurrent map first, with minimal locking.
	// 本文从这里直接返回
	RootBeanDefinition mbd = this.mergedBeanDefinitions.get(beanName);
	if (mbd != null && !mbd.stale) {
		return mbd;
	}
	return getMergedBeanDefinition(beanName, getBeanDefinition(beanName));
}

如果mergedBeanDefinitions中没有呢?那么将会从beanDefinitionMap中获取,如果beanDefinitionMap也没有将抛出异常。

// DefaultListableBeanFactory
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);

// DefaultListableBeanFactory#getBeanDefinition
@Override
public BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException {
	BeanDefinition bd = this.beanDefinitionMap.get(beanName);
	if (bd == null) {
		if (logger.isTraceEnabled()) {
			logger.trace("No bean named '" + beanName + "' found in " + this);
		}
		throw new NoSuchBeanDefinitionException(beanName);
	}
	return bd;
}

那么getMergedBeanDefinition方法是做什么呢?顾名思义,其将尝试获取得到一个“合并”后的BeanDefinition。也就是说如果当前BeanDefinition有parent,则将二者BeanDefinition合并,拥有同样属性的以child BeanDefinition为主。

综上也可以说明在使用spring的getBean进行bean实例化时,这里不再触发bean定义的加载,其默认在上下文refresh中已经完成了资源的扫描、定位和加载,BeanDefinition存放在DefaultListableBeanFactory中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

流烟默

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

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

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

打赏作者

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

抵扣说明:

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

余额充值