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中。

目录
相关文章
|
11天前
|
XML 安全 Java
|
1月前
|
缓存 Java Spring
实战指南:四种调整 Spring Bean 初始化顺序的方案
本文探讨了如何调整 Spring Boot 中 Bean 的初始化顺序,以满足业务需求。文章通过四种方案进行了详细分析: 1. **方案一 (@Order)**:通过 `@Order` 注解设置 Bean 的初始化顺序,但发现 `@PostConstruct` 会影响顺序。 2. **方案二 (SmartInitializingSingleton)**:在所有单例 Bean 初始化后执行额外的初始化工作,但无法精确控制特定 Bean 的顺序。 3. **方案三 (@DependsOn)**:通过 `@DependsOn` 注解指定 Bean 之间的依赖关系,成功实现顺序控制,但耦合性较高。
实战指南:四种调整 Spring Bean 初始化顺序的方案
|
9天前
|
安全 Java 开发者
Spring容器中的bean是线程安全的吗?
Spring容器中的bean默认为单例模式,多线程环境下若操作共享成员变量,易引发线程安全问题。Spring未对单例bean做线程安全处理,需开发者自行解决。通常,Spring bean(如Controller、Service、Dao)无状态变化,故多为线程安全。若涉及线程安全问题,可通过编码或设置bean作用域为prototype解决。
21 1
|
2月前
|
XML Java 数据格式
Spring从入门到入土(bean的一些子标签及注解的使用)
本文详细介绍了Spring框架中Bean的创建和使用,包括使用XML配置文件中的标签和注解来创建和管理Bean,以及如何通过构造器、Setter方法和属性注入来配置Bean。
75 9
Spring从入门到入土(bean的一些子标签及注解的使用)
|
2月前
|
Java 测试技术 Windows
咦!Spring容器里为什么没有我需要的Bean?
【10月更文挑战第11天】项目经理给小菜分配了一个紧急需求,小菜迅速搭建了一个SpringBoot项目并完成了开发。然而,启动测试时发现接口404,原因是控制器包不在默认扫描路径下。通过配置`@ComponentScan`的`basePackages`字段,解决了问题。总结:`@SpringBootApplication`默认只扫描当前包下的组件,需要扫描其他包时需配置`@ComponentScan`。
|
3月前
|
XML Java 数据格式
spring复习02,xml配置管理bean
详细讲解了Spring框架中基于XML配置文件管理bean的各种方式,包括获取bean、依赖注入、特殊值处理、属性赋值、集合类型处理、p命名空间、bean作用域及生命周期和自动装配。
spring复习02,xml配置管理bean
|
2月前
|
Java 开发者 Spring
Spring bean的生命周期详解!
本文详细解析Spring Bean的生命周期及其核心概念,并深入源码分析。Spring Bean是Spring框架的核心,由容器管理其生命周期。从实例化到销毁,共经历十个阶段,包括属性赋值、接口回调、初始化及销毁等。通过剖析`BeanFactory`、`ApplicationContext`等关键接口与类,帮助你深入了解Spring Bean的管理机制。希望本文能助你更好地掌握Spring Bean生命周期。
121 1
|
2月前
|
Java Spring
获取spring工厂中bean对象的两种方式
获取spring工厂中bean对象的两种方式
46 1
|
2月前
|
Java 开发者 Spring
Spring bean的生命周期详解!
本文详细介绍了Spring框架中的核心概念——Spring Bean的生命周期,包括实例化、属性赋值、接口回调、初始化、使用及销毁等10个阶段,并深入剖析了相关源码,如`BeanFactory`、`DefaultListableBeanFactory`和`BeanPostProcessor`等关键类与接口。通过理解这些核心组件,读者可以更好地掌握Spring Bean的管理和控制机制。
97 1
|
3月前
|
XML Java 数据格式
spring复习03,注解配置管理bean
Spring框架中使用注解配置管理bean的方法,包括常用注解的标识组件、扫描组件、基于注解的自动装配以及使用注解后的注意事项,并提供了一个基于注解自动装配的完整示例。
spring复习03,注解配置管理bean