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)


49781b05b0204833807175570a0b8c11.png

② 根据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


1b1e645dc07742d68ccf8cb49e0322a8.png

在前面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从beanDefinitionNames和manualSingletonNames集合中检测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中。

目录
相关文章
|
6天前
|
Java uml Spring
手写spring第四章-完善bean实例化,自动填充成员属性
手写spring第四章-完善bean实例化,自动填充成员属性
15 0
|
23天前
|
Java 应用服务中间件 Spring
Spring系列文章:Bean的作⽤域
Spring系列文章:Bean的作⽤域
|
2天前
|
消息中间件 安全 Java
在Spring Bean中,如何通过Java配置类定义Bean?
【4月更文挑战第30天】在Spring Bean中,如何通过Java配置类定义Bean?
9 1
|
5天前
|
前端开发 Java 数据格式
【Spring系列笔记】定义Bean的方式
在Spring Boot应用程序中,定义Bean是非常常见的操作,它是构建应用程序的基础。Spring Boot提供了多种方式来定义Bean,每种方式都有其适用的场景和优势。
19 2
|
6天前
|
XML Java 数据格式
手写spring第七章-完成便捷实现bean对象初始化和销毁方法
手写spring第七章-完成便捷实现bean对象初始化和销毁方法
6 0
|
6天前
|
XML Java 数据格式
手写spring第六章-实现应用上下文,完成bean的扩展机制
手写spring第六章-实现应用上下文,完成bean的扩展机制
12 0
|
6天前
|
设计模式 搜索推荐 Java
手写spring第三章-重构,使用依赖关系完善实例化bean操作
手写spring第三章-重构,使用依赖关系完善实例化bean操作
13 0
|
7天前
|
XML 人工智能 Java
Spring Bean名称生成规则(含源码解析、自定义Spring Bean名称方式)
Spring Bean名称生成规则(含源码解析、自定义Spring Bean名称方式)
|
8天前
|
安全 Java Maven
[AIGC] Spring Boot中的切面编程和实例演示
[AIGC] Spring Boot中的切面编程和实例演示
|
16天前
|
Java 数据库连接 开发者
浅谈Spring的Bean生命周期
浅谈Spring的Bean生命周期
20 1