查找Bean有哪些手段?

总体来说,查找Bean有两种手段,一种是单Bean查找(只会返回一个Bean),一种是多Bean查找(返回多个Bean),它们分别对应BeanFactory和ListableBeanFactory。ListableBeanFactoty继承自BeanFactory接口。

再往细分,BeanFactory接口定义的方法主要有两种,一种是根据名称或类型非安全查找(当未根据传入的参数查找到对应的Bean时会抛出异常),这系列的代表是getBean(String)、getBean(Class)…,另一种是安全查找,这一系列的代表是getBeanProvider方法。ListableBeanFactory中所有和Bean查找相关的方法都是安全的,因为其返回值不是Map就是数组,如果未找到,只会返回空Map或者空数组,不会抛出异常,但其findAnotationOnBean方法并不是安全地。

除了根据BeanName或者BeanType来查找单个或者多个Bean之外,ListableBeanFactory还定义了根据注解来查找Bean的方法-getBeansWithAnnotation以及根据指定BeanName和注解类型来查找对应Bean中的指定注解数据方法-findAnnotationOnBean。

另外需注意的是虽然BeanProvider的getIfAvailable方法是安全的,但是其getObject方法并不是安全地。下面会演示到。

依赖查找安全性对比

Talk is cheap. Show me the code

第一步:定义一个类,创建Spring 注解驱动应用上下文-AnnotationConfigApplicationContext,不需要向该上下文注册任何Bean,调用上下文的refresh方法,然后调用我们上面提到的方法来从应用上下文中查找DependencyLookupDemo。

package com.xxx.hyl.dependency.lookup;import org.springframework.beans.BeansException;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.stereotype.Component;import java.util.Arrays;
import java.util.Map;
/*
* @author 君战
*/
public class DependencyLookupDemo {public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();context.refresh();try {// 非安全查找,会抛出异常DependencyLookupDemo contextBean = context.getBean(DependencyLookupDemo.class);} catch (BeansException e) {System.err.println("查找 DependencyLookupDemo 失败,异常信息为:" + e.getMessage());}// 安全地查找Bean,不会抛出异常ObjectProvider<DependencyLookupDemo> beanProvider =context.getBeanProvider(DependencyLookupDemo.class);DependencyLookupDemo lookupDemo = beanProvider.getIfAvailable();System.out.println("根据 getBeanProvider.getIfAvailable 方法查找到的 DependencyLookupDemo 为 : " + lookupDemo);try{DependencyLookupDemo providerObject = beanProvider.getObject();} catch (BeansException e){System.err.println("根据 getBeanProvider.getObject 方法查询失败,异常信息为 : " + e.getMessage());}System.out.println("=======================以上为查找单个Bean==========================");Map<String, DependencyLookupDemo> lookupDemoMap =context.getBeansOfType(DependencyLookupDemo.class);System.out.println("根据 getBeansOfType 方法查找到的 DependencyLookupDemo 为:" + lookupDemoMap);String[] lookupBeanNames = context.getBeanNamesForType(DependencyLookupDemo.class);System.out.println("根据 getBeanNamesForType 方法查找到的 DependencyLookupDemo 为:" + Arrays.toString(lookupBeanNames));// 根据注解类型来查找BeanMap<String, Object> beansWithAnnotation = context.getBeansWithAnnotation(Component.class);System.out.println("根据 getBeansWithAnnotation 方法查找到的IoC容器添加了@Component注解的Bean有 :" + beansWithAnnotation);try {// 非安全查找,会抛出异常。随便指定一个BeanNameComponent unKnowBeanType = context.findAnnotationOnBean("unKnowBeanType", Component.class);} catch (BeansException e) {System.err.println("根据 findAnnotationOnBean 方法,指定beanName为 unKnowBeanType ,注解类型为 @Component ,异常信息为:" + e.getMessage());}}
}

第二步:查看控制台打印信息。可以看到红字的都是发生了异常,基本与我们描述的情况吻合。

底层行为分析

要分析getBeanProvider方法为什么是安全的,就要和getBean方法来对比着分析,因为这两者的底层实现其实都是一样的。我们都知道Spring提供的唯一一个可用的IoC容器产品就是DefaultListableBea-nFactory,在该类中实现了BeanFactory定义的这两个方法。

先看看DefaultListableBeanFactory实现的一个参数的getBean方法。可以看到其底层还是调用两个参数的getBean方法,并且在resolveBean方法返回值为null的时候,抛出NoSuchBeanDefinitionException异常。

// DefaultListableBeanFactory#getBean(java.lang.Class<T>)
@Override
public <T> T getBean(Class<T> requiredType) throws BeansException {return getBean(requiredType, (Object[]) null);
}@SuppressWarnings("unchecked")
@Override
public <T> T getBean(Class<T> requiredType, @Nullable Object... args) throws BeansException {Assert.notNull(requiredType, "Required type must not be null"); // getBean方法底层调用的是resolveBean方法,resolveBean方法并不会抛出异常,如果未找到,只会返回nullObject resolved = resolveBean(ResolvableType.forRawClass(requiredType), args, false);if (resolved == null) { // getBean自己在这里做了一层校验,如果resolveBean方法返回值为null,抛出NoSuchBeanDefinitionException异常throw new NoSuchBeanDefinitionException(requiredType);}return (T) resolved;
}

再看看参数类型为Class的getBeanProvider方法,其底层是调用参数类型为ResolvableType的getBeanProvider方法。在该方法中使用了匿名内部类的方式返回一个BeanObjectProvider实现类。BeanObjectProvider是ObjectProvider接口的一个子接口,定义在DefaultListableBeanFactory类中,在该接口中并未进行任何扩展。

在返回的BeanObjectProvider类中,对于getObject及其重载方法,底层实现依然是调用resolveBean方法,并且和getBean方法的实现一致,如果该方法的返回值为null,都会抛出NoSuchBeanDefinitionException。但是在其实现的getIfAvailable、getIfUnique方法中不会对resolveBean方法的返回值进行校验,这意味着即使该方法返回为null,也不会抛出异常。

// DefaultListableBeanFactory#getBeanProvider(java.lang.Class<T>)
@Override
public <T> ObjectProvider<T> getBeanProvider(Class<T> requiredType) {Assert.notNull(requiredType, "Required type must not be null");return getBeanProvider(ResolvableType.forRawClass(requiredType));
}@Override
public <T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType) {return new BeanObjectProvider<T>() {@Overridepublic T getObject() throws BeansException {T resolved = resolveBean(requiredType, null, false); // 底层实现依然是调用resolveBean方法if (resolved == null) { // 检查resolveBean方法返回值,如果resolveBean方法返回值为null,抛出NoSuchBeanDefinitionException异常throw new NoSuchBeanDefinitionException(requiredType);}return resolved;}@Overridepublic T getObject(Object... args) throws BeansException {T resolved = resolveBean(requiredType, args, false);if (resolved == null) { // 检查resolveBean方法返回值,如果resolveBean方法返回值为null,抛出NoSuchBeanDefinitionException异常throw new NoSuchBeanDefinitionException(requiredType);}return resolved;}@Override@Nullablepublic T getIfAvailable() throws BeansException {return resolveBean(requiredType, null, false); // 不检查resolveBean方法返回值}@Override@Nullablepublic T getIfUnique() throws BeansException {return resolveBean(requiredType, null, true);// 不检查resolveBean方法返回值}@SuppressWarnings("unchecked")@Overridepublic Stream<T> stream() {return Arrays.stream(getBeanNamesForTypedStream(requiredType)).map(name -> (T) getBean(name)).filter(bean -> !(bean instanceof NullBean));}@SuppressWarnings("unchecked")@Overridepublic Stream<T> orderedStream() {String[] beanNames = getBeanNamesForTypedStream(requiredType);if (beanNames.length == 0) {return Stream.empty();}Map<String, T> matchingBeans = new LinkedHashMap<>(beanNames.length);for (String beanName : beanNames) {Object beanInstance = getBean(beanName);if (!(beanInstance instanceof NullBean)) {matchingBeans.put(beanName, (T) beanInstance);}}Stream<T> stream = matchingBeans.values().stream();return stream.sorted(adaptOrderComparator(matchingBeans));}};
}private interface BeanObjectProvider<T> extends ObjectProvider<T>, Serializable {}

总结

getBean及其重载方法以及getBeanProvider和其重载方法,底层实现都是调用resolveBean方法,只不过在getBean和其重载方法以及getBeanProvider方法返回的ObjectProvider实现类中实现的getObject方法都会对resolveBean方法的返回值进行校验,如果为null则抛出NoSuchBeanDefinitionExcepti-on。而getIfAvailable方法则是直接返回resolveBean方法返回值,不会进行校验。

就相当于一个是框架内部自己进行了验证,确保返回给使用者的数据不为空,另一个不对返回值进行任何保证,需要使用者自己进行校验。

什么是安全查找Bean,什么是非安全查找Bean?如何安全地查找Bean?相关推荐

  1. no qualifying bean of type_就是要让你彻底学会 @Bean 注解

    点击上方"Java知音",选择"置顶公众号" 技术文章第一时间送达! 推荐阅读(点击即可跳转阅读) 1. SpringBoot内容聚合 2. 面试题内容聚合 3 ...

  2. Spring源码分析系列——bean创建过程分析(三)——工厂方法创建bean

    前言 spring创建bean的方式 测试代码准备 createBeanInstance()方法分析 instantiateUsingFactoryMethod()方法分析 总结 spring创建be ...

  3. R语言apropos函数查找包含特定字符的函数、find函数查找函数所在的位置实战

    R语言apropos函数查找包含特定字符的函数.find函数查找函数所在的位置实战 目录 R语言apropos函数实战 # 基本语法 #apropos函数 # find函数 apropos函数返回一个 ...

  4. spring bean xml 调用方法_Spring通过Xml方式注册Bean的几处关键实现点

    1.前言 我们用SpringMVC的时候一般会用到Xml配置文件,那么我们这篇文章就来谈下Spring读取Xml配置文件的一些关键实现点. 2.AbstractRefreshableApplicati ...

  5. path与classpath区别 path是Windows查找.exe文件的路径;classpath是jvm查找.class文件的路径

    CLASSPATH环境变量.作用是指定类搜索路径,要使用已经编写好的类,前提当然是能够找到它们了,JVM就是通过CLASSPATH来寻找类的.class文件 总而言之,path是Windows查找.e ...

  6. PHP二分法查找,MYSQL索引即为用了此查找

    算法:当数据量很大适宜采用该方法.采用二分法查找时,数据需是排好序的.主要思想是:(设查找的数组区间为array[low, high]) (1)确定该区间的中间位置K (2)将查找的值T与array[ ...

  7. php恶意代码,php快速查找数据库中恶意代码的方法,快速查找恶意代码_PHP教程...

    php快速查找数据库中恶意代码的方法,快速查找恶意代码 本文实例讲述了php快速查找数据库中恶意代码的方法.分享给大家供大家参考.具体如下: 数据库被输入恶意代码,为了保证你的数据库的安全,你必须得小 ...

  8. c语言折半查找输出坐标,数据结构(C语言版)——有序表查找(折半查找)(代码版)...

    数据结构(C语言版)--有序表查找(折半查找)(代码版) 数据结构(C语言版)--有序表查找(折半查找)(代码版) #include #include #define ERROR 0 #define ...

  9. C语言(CED)查找最接近的元素(分治法/二分查找):在一个非降序列中,查找与给定值最接近的元素。(递归实现)

    (请先看置顶博文)https://blog.csdn.net/GenuineMonster/article/details/104495419 一.题目大意 查找最接近的元素(分治法/二分查找):在一 ...

  10. 蛮力法在查找算法中的应用(JAVA)--顺序查找

    蛮力法在查找算法中的应用 对于查找算法来说,最简单的一个思路就是逐个匹配,直到找到目标元素 顺序查找: public class Main {public static void main(Strin ...

最新文章

  1. html页面转换成pdf
  2. 第15届全国大学生智能汽车竞赛 人工智能挑战赛(百度)
  3. 非常有用的css使用总结
  4. 开发完整J2EE解决方案的八个步骤
  5. java编程控制电脑硬件_如何快速学习AP计算机中的Java编程?
  6. 工作167:eachrt解决问题方法思路
  7. 【OpenCV 例程200篇】83. 频率域低通滤波:印刷文本字符修复
  8. April Fools Day Contest 2016 A. Da Vinci Powers
  9. iPhone 12来了!苹果官宣第二场新品发布会时间,10月14日见!
  10. 魅族16s Pro最新预热海报公布:将配备双扬声器
  11. ES6新特性_ES6模板字符串---JavaScript_ECMAScript_ES6-ES11新特性工作笔记007
  12. Jmeter并发压测
  13. 《编码规范和测试方法——C/C++版》作业 ·008——编写一个符合依赖倒置原则的简单学生管理系统
  14. 关于static继承的问题
  15. Smart3D基础理论
  16. 【macOS】Macbook修改键盘映射
  17. wordpress英文主题怎样汉化成中文网站模板
  18. 全球及中国DNA测序行业发展与竞争状况分析报告2022~2027年
  19. 数字电路实验 01 - | TTL门电路的逻辑功能测试
  20. TWS耳机供应链,看看背后都有谁?

热门文章

  1. linux由哪些部分组成,linux内核处于什么位置?,Linux由几部分组成?Linux系统结构介绍!...
  2. 翻译: Transfer learning 迁移学习指南
  3. android公交车代码,android实现查询公交车还有几站的功能
  4. mysql 流量带宽_CentOS中使用iftop命令监控网络带宽流量
  5. mysql 备份任务_设置mysql 定时备份任务
  6. 为什么我们要使用图嵌入?
  7. 剑指offer JZ02/05 替换空格 Python
  8. php 自动选择时间的代码,JavaScript_extjs 时间范围选择自动判断的实现代码,extjs中 有时需要选择一个日期 - phpStudy...
  9. python编程符号大全_2020 年最值得学习的 5 大 AI 编程语言
  10. python最常用的版本是_在下列选项中,( ) 是最常用的 Python版本,也称之为CIassicPython。_学小易找答案...