Spring容器提供了五种获取Bean的方式,可以根据beanName获取Bean,也可以根据classType来获取Bean,所有根据beanName来获取Bean的方式,底层都会通过调用下面的doGetBean()来获取Bean对象

protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)

一、根据beanName获取Bean

Spring容器提供了三种根据beanName获取Bean的方式:

// 启动Spring容器
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
// 直接根据beanName来获取
UserService userService = (UserService) context.getBean("userService");
// 根据beanName获取Bean后,判断该Bean是否属于UserService类型
UserService userService1 = (UserService) context.getBean("userService",UserService.class);
// 指定构造方法参数,使用指定的构造方法的生成Bean,如果UserService是单例的,则容器启动的过程中就会去自行推断构造方法然后创建单例Bean;如果UserService是原型的,则每次都会getBean()时,会根据后面提供的构造方法参数使用指定的构造方法创建Bean
UserService userService2 = (UserService) context.getBean("userService",new OrderService());

上面三种方式都会调用doGetBean()来获取Bean对象,下面将详细介绍doGetBean()的实现

protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)throws BeansException {// name有可能是 &xxx 或者 xxx,如果name是&xxx,那么beanName就是xxx// name有可能传入进来的是别名,那么beanName就是idString beanName = transformedBeanName(name);Object beanInstance;// Eagerly check singleton cache for manually registered singletons.Object sharedInstance = getSingleton(beanName);if (sharedInstance != null && args == null) {// 如果sharedInstance是FactoryBean,那么就调用getObject()返回对象beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);}

上述代码在《实例化单例Bean》的FactoryBean创建部分有大概介绍过,下面将详细介绍核心的代码方法

1.1 name转换,获取真正的beanName

transformedBeanName()主要将传进来的name转换成真正的beanName,具体实现如下:

protected String transformedBeanName(String name) {return canonicalName(BeanFactoryUtils.transformedBeanName(name));
}

先看里面的BeanFactoryUtils.transformedBeanName()实现,下面代码在FactoryBean的创建中已经讲过,想要获取一个FactoryBean的对象,那么传进来的name是以“&”前缀开头,可以只有一个“&”,也可以有多个“&”,下面的代码只是将前缀的“&”去掉而已

public static String transformedBeanName(String name) {Assert.notNull(name, "'name' must not be null");if (!name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {return name;}return transformedBeanNameCache.computeIfAbsent(name, beanName -> {do {beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());}while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));return beanName;});
}

接下来看canonicalName()实现了怎样的功能,调用BeanFactoryUtils.transformedBeanName()返回的可能还不是最终的beanName,可能是Bean的一个别名,使用@Bean注解创建Bean的时候,可以为Bean指定多个名称,如下所示:

Spring会把第一个名字即“userService”作为真正的beanName,而后面的名字作为Bean的别名进行存储

@Bean({"userService","userService1"})
public UserService userService(){return new UserService();
}AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
//根据别名也能获取Bean
UserService userService = (UserService) context.getBean("userService1");

Bean的别名存放在一个aliasMap中,其中KEY=别名,VALUE=beanName/别名,根据别名从aliasMap中拿到的可能是真正的beanName,也可能还是一个别名,所以用do-while循环,直到拿出来的名字从aliasMap再找不到对应的值,那么该名字就是真正的beanName了

public String canonicalName(String name) {String canonicalName = name;// Handle aliasing...String resolvedName;do {resolvedName = this.aliasMap.get(canonicalName);if (resolvedName != null) {canonicalName = resolvedName;}}while (resolvedName != null);return canonicalName;
}

1.2 父BeanFactory创建Bean对象

获取到真正的beanName之后,不论是单例Bean还是原型Bean,都会调用getSingleton(beanName)去单例池中查找是否有单例Bean。如果从单例池中取出对象了,接下来的getObjectForBeanInstance()主要根据原始name判断当前Bean是否是FactoryBean,以及获取FactoryBean对象。如果单例池中不存在,则需要创建Bean实例

如果当前BeanFactory的beanDefinitionMap中不存在该beanName,且存在parentBeanFactory,则让parentBeanFactory去创建Bean

BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {// Not found -> check parent.// &&&&xxx---->&xxxString nameToLookup = originalBeanName(name);if (parentBeanFactory instanceof AbstractBeanFactory) {return ((AbstractBeanFactory) parentBeanFactory).doGetBean(nameToLookup, requiredType, args, typeCheckOnly);}else if (args != null) {// Delegation to parent with explicit args.return (T) parentBeanFactory.getBean(nameToLookup, args);}else if (requiredType != null) {// No args -> delegate to standard getBean method.return parentBeanFactory.getBean(nameToLookup, requiredType);}else {return (T) parentBeanFactory.getBean(nameToLookup);}
}

originalBeanName(name)方法用于将别名和"&&&"格式的名字进行转换真正的beanName或FactoryBean的beanName

protected String originalBeanName(String name) {String beanName = transformedBeanName(name);if (name.startsWith(FACTORY_BEAN_PREFIX)) {beanName = FACTORY_BEAN_PREFIX + beanName;}return beanName;
}

1.3 当前BeanFactory来创建Bean

从mergedBeanDefinitions中根据beanName获取RootBeanDefinition,《实例化单例Bean》中提到过真正创建Bean的时候,用的是合并后的RootBeanDefinition而不是普通的BeanDefinition

然后判断当前RootBeanDefinition是否是抽象的,如果是抽象的将会抛出异常

接着获取该Bean的@DependsOn注解依赖的beanName

  • 创建@DependsOn注解依赖Bean对象

    RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
    // 检查BeanDefinition是不是Abstract的
    checkMergedBeanDefinition(mbd, beanName, args);
    // Guarantee initialization of beans that the current bean depends on.
    String[] dependsOn = mbd.getDependsOn();
    

    DependsOn注解的使用如下:

    DependsOn注解指明在创建UserServicce的Bean之前,需要先创建orderService和user,在Spring生成BeanDefinition的时候,就回去解析@DependsOn,然后将依赖的beanName存放在BeanDefinition的字符串数组dependsOn中

    @Component
    @DependsOn({"orderService","user"})
    public class UserService {}
    

    如果DependsOn数组不为空,将遍历数组,首先判断是否存在@DependsOn的循环依赖(这个地方的循环依赖不同于依赖注入的循环依赖),即UserService依赖于user,而User的@DependsOn又依赖于userService,对于这种情况,将直接抛出异常

    如果不存在循环依赖,则把这种依赖关系存放在dependentBeanMap中,它的key表示被依赖Bean的beanName,而value是一个Set,存放依赖该Bean的beanName集合,然后调用getBean(dep)来创建依赖的Bean对象

    if (dependsOn != null) {// dependsOn表示当前beanName所依赖的,当前Bean创建之前dependsOn所依赖的Bean必须已经创建好了for (String dep : dependsOn) {// beanName是不是被dep依赖了,如果是则出现了循环依赖if (isDependent(beanName, dep)) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");}// dep被beanName依赖了,存入dependentBeanMap中,dep为key,beanName为valueregisterDependentBean(dep, beanName);// 创建所依赖的beantry {getBean(dep);}catch (NoSuchBeanDefinitionException ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"'" + beanName + "' depends on missing bean '" + dep + "'", ex);}}
    }
    
  • 创建单例Bean

    如果RootBeanDefinition的scope属性是单例的,则调用getSingleton创建单例Bean

    if (mbd.isSingleton()) {sharedInstance = getSingleton(beanName, () -> {try {return createBean(beanName, mbd, args);}catch (BeansException ex) {destroySingleton(beanName);throw ex;}});// 前面讲过,主要用于获取FactoryBeanbeanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
    }
    

    getSingleton(beanName, ObjectFactory<?> singletonFactory)先从单例池中根据beanName来获取单例Bean,如果单例池中没有,则再通过createBean来创建单例Bean

    public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {synchronized (this.singletonObjects) {Object singletonObject = this.singletonObjects.get(beanName);if (singletonObject == null) {……try {// 通过lambda表达式调用createBean方法singletonObject = singletonFactory.getObject();newSingleton = true;}……// 添加到单例池if (newSingleton) {addSingleton(beanName, singletonObject);}}return singletonObject;}
    }
    
  • 创建原型Bean

    如果RootBeanDefinition的scope属性是原型的,则直接调用createBean方法创建原型Bean

    if (mbd.isPrototype()) {// It's a prototype -> create a new instance.Object prototypeInstance = null;try {beforePrototypeCreation(beanName);prototypeInstance = createBean(beanName, mbd, args);}finally {afterPrototypeCreation(beanName);}beanInstance = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
    }
    
  • 创建其他作用域Bean

    除了单例和原型Bean,Spring还支持request、session和application作用域的Bean

    首先获取scopeName,然后根据scopeName获取对应的Scope对象,比如:RequestScope和SessionScope

    Scope对象的get方法都会去调用AbstractRequestAttributesScope类的get(),RequestScope和SessionScope都继承该类

    String scopeName = mbd.getScope();
    Scope scope = this.scopes.get(scopeName);
    if (scope == null) {throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
    }
    try {  // session.getAttriute(beaName)  setAttriObject scopedInstance = scope.get(beanName, () -> {beforePrototypeCreation(beanName);try {return createBean(beanName, mbd, args);}finally {afterPrototypeCreation(beanName);}});beanInstance = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
    }
    

    getScope()返回Scope对象对应的Scope值,然后调用getAttribute方法,如果request和session中都没有Bean实例,

    public Object get(String name, ObjectFactory<?> objectFactory) {RequestAttributes attributes = RequestContextHolder.currentRequestAttributes();Object scopedObject = attributes.getAttribute(name, getScope());if (scopedObject == null) {// lambda表达式调用createBean创建实例scopedObject = objectFactory.getObject();// 并将Bean设置到对应的属性中attributes.setAttribute(name, scopedObject, getScope());……}return scopedObject;
    }
    

    getAttribute根据传进来的Scope值,判断从request还是session去根据name获取对应属性的对象

    public Object getAttribute(String name, int scope) {if (scope == SCOPE_REQUEST) {if (!isRequestActive()) {throw new IllegalStateException("Cannot ask for request attribute - request is not active anymore!");}return this.request.getAttribute(name);}else {HttpSession session = getSession(false);if (session != null) {Object value = session.getAttribute(name);if (value != null) {this.sessionAttributesToUpdate.put(name, value);}return value;}return null;}
    }
    

详解Spring的GetBean方法相关推荐

  1. 详解Spring中getBean()方法

    我们日常会经常使用getBean()方法从spring容器中获取我们需要的bean.那么,getBean()方法是怎么一步步完成从spring容器中获取bean的呢? 下面我们就通过源码分析一下. 一 ...

  2. 多图详解Spring框架的设计理念与设计模式

    Spring作为现在最优秀的框架之一,已被广泛的使用,51CTO也曾经针对Spring框架中的JDBC应用做过报道.本文将从另外一个视角试图剖析出Spring框架的作者设计Spring框架的骨骼架构的 ...

  3. 五篇教你掌握spring之三:详解Spring的bean以及注解开发

    详解Spring的bean以及注解开发 各种复杂类型的依赖注入 我们采用一个类的大杂烩的形式,新建一个Student package com.lwh.pojo;import java.util.*;p ...

  4. 用IDEA详解Spring中的IoC和DI(挺透彻的,点进来看看吧)

    用IDEA详解Spring中的IoC和DI 一.Spring IoC的基本概念 控制反转(IoC)是一个比较抽象的概念,它主要用来消减计算机程序的耦合问题,是Spring框架的核心. 依赖注入(DI) ...

  5. java中batch基础_详解Spring batch 入门学习教程(附源码)

    详解Spring batch 入门学习教程(附源码) 发布时间:2020-09-08 00:28:40 来源:脚本之家 阅读:99 作者:achuo Spring batch 是一个开源的批处理框架. ...

  6. java按钮权限控制_详解Spring Security 中的四种权限控制方式

    Spring Security 中对于权限控制默认已经提供了很多了,但是,一个优秀的框架必须具备良好的扩展性,恰好,Spring Security 的扩展性就非常棒,我们既可以使用 Spring Se ...

  7. java webmethod 参数_java详解Spring接收web请求参数的方式

    本篇文章给大家带来的内容是java详解Spring接收web请求参数的方式 .有一定的参考价值,有需要的朋友可以参考一下,希望对你们有所帮助. 1 查询参数 请求格式:url?参数1=值1&参 ...

  8. java切面不需要接口了吗_详解Spring AOP 实现“切面式”valid校验

    why: 为什么要用aop实现校验? answer: spring mvc 默认自带的校验机制 @Valid + BindingResult, 但这种默认实现都得在Controller方法的中去接收B ...

  9. CORS跨域资源共享(二):详解Spring MVC对CORS支持的相关类和API【享学Spring MVC】

    每篇一句 重构一时爽,一直重构一直爽.但出了问题火葬场 前言 上篇文章通过我模拟的跨域请求实例和结果分析,相信小伙伴们都已经80%的掌握了CORS到底是怎么一回事以及如何使用它.由于Java语言中的w ...

最新文章

  1. PCL点云变换与移除NaN
  2. java 9999 符号_java 9999(示例代码)
  3. Idea debugger 无法启动-unable to open debugger port , java.net.SocketException socket closed
  4. java——对象学习笔记
  5. php全局cors,PHP开启CORS - slagga的个人页面 - OSCHINA - 中文开源技术交流社区
  6. JZOJ5857 【NOIP提高组模拟A组2018.9.8】没有上司的舞会
  7. java if经典程序_java经典程序(11-20)
  8. 解释Linux进程的父子关系,Linux进程管理
  9. codeforce303C-Minimum Modular-剪枝,暴力
  10. 如何遍历或枚举JavaScript对象?
  11. pacman吃豆人_“植物河豚”狗爪豆,你吃过吗?
  12. Kotlin 我的第一个kotlin程序【Android Studio】
  13. 各行各业有对象系列之三:对象存储与银行
  14. 微信小程序图片转换成文字_怎么利用微信小程序把图片上的文字转到word文本中?...
  15. rpg存档修改网站_Switch上国宝级欧美版RPG游戏推荐
  16. android跳转app store,从微信跳转到appstore下载App
  17. MATLA 复制文件到指定文件夹
  18. beini奶瓶-无线网络蹭网破解-简明说明
  19. 数据分析应关注AARRR模型的哪些指标
  20. 高级Java程序员技术栈

热门文章

  1. spark开发环境搭建及部署
  2. 团队作业——系统设计(人月神教)
  3. 矿物绝缘电缆有哪些应用领域
  4. 《阿里如何实现秒级百万TPS?搜索离线大数据平台架构解读》阅读笔记
  5. windows无法识别U盘(即usb出现问题)
  6. 如果遇到Xshell键盘无法使用,你需要这样做!
  7. 【数据结构】TopK问题
  8. 怎么找回删除的文件?
  9. 大数据机器学习系统研究进展
  10. oracle里面的terminate,pg_cancel_backend() 与pg_terminate_backend()