依赖查找的前生

单一类型依赖查找

JNDI- javax.naming.Context#lookup(javax.naming.Name);
JNDI- javax.naming.Context#lookup(String);

JavaBeans - java.beans.beancontext.BeanContext
BeanContext 继承了 Collection 接口,BeanContext 中的所有成员都是 bean

集合类型依赖查找

通过一个 key,找到对应的多个实例

java.beans.beancontext.BeanContextServices 的 Iterator getCurrentServiceSelectors(Class serviceClass);

层级性依赖查找

image.png

单一类型依赖查找

用到了 BeanFactory接口

根据 Bean 名称查找

getBean(String)
Spring 2.5 覆盖默认参数:getBean(String,Object…)

根据 Bean 类型查找

Bean 实时查找

Spring 3.0 getBean(Class)
Spring 4.1 覆盖默认参数:getBean(Class,Object…)

Spring 5.1 Bean 延时查找

getBeanProvider(Class)
getBeanProvider(ResolvableType)

相关代码:

  1. /**
  2. * 通过 ObjectProvider 依赖查找
  3. *
  4. * @author mindartisan.blog.csdn.net
  5. * @date
  6. *
  7. * @commet @Bean 定义的类在当前类,当前类默认就是配置类,不用写 「@Configuration」注解,
  8. *
  9. * @Configuration 非必需注解:https://docs.spring.io/spring-framework/docs/5.2.2.RELEASE/javadoc-api/org/springframework/context/annotation/Bean.html
  10. */
  11. public class ObjectProviderDemo {
  12. public static void main(String[] args) {
  13. // 创建 BeanFactory 容器
  14. AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
  15. // 将当前类作为配置类
  16. applicationContext.register(ObjectProviderDemo.class);
  17. // 启动应用上下文
  18. applicationContext.refresh();
  19. lookupByObjectProvider(applicationContext);
  20. applicationContext.close();
  21. }
  22. @Bean
  23. // 此时的 Bean 名称就是方法名称
  24. public String helloWorld() {
  25. return "Hello,World";
  26. }
  27. private static void lookupByObjectProvider(AnnotationConfigApplicationContext applicationContext) {
  28. ObjectProvider<String> objectProvider = applicationContext.getBeanProvider(String.class);
  29. System.out.println(objectProvider.getObject());
  30. }
  31. }

根据 Bean 名称+类型查找

getBean(String,Class)

集合类型依赖查找

用到了ListableBeanFactory 接口,推荐使用 bean 名称判断 bean 是否存在。

根据 Bean 类型查找

Bean 名称可以先定义,但是实例必须得初始化

获取同类型 Bean 名称列表

getBeanNamesForType(Class)
Spring 4.2 getBeanNameForType(ResolvableType)

获取同类型的 Bean 实例列表

getBeansOfType(Class) 及其重载方法

根据注解类型查找

Spring 3.0 获取标注类型 Bean 名称列表

getBeanNamesForAnnotation(Class<?extends Annotation>)

Spring 3.0 获取标注类型 Bean 实例列表

getBeansWithAnnotation(Class<?extends Annotation>)

Spring 3.0 获取指定名称+标注类型 Bean 实例

findAnnotationOnBean(String,Class<?extends Annotation>)

层次性依赖查找

用到了HierarchicalBeanFactory接口,方法为 getParentBeanFactory()

注意下图中 ConfigurableListBeanFactory 继承 ConfigurableBeanFactory 以及 ListableBeanFactory 接口。
image.png

根据 Bean 名称查找

基于 containsLocalBean()实现

根据 Bean 类型查找实例列表

单一类型:使用BeanFactoryUtils#beanOfTypeIncludingAncestors
集合类型:使用BeanFactoryUtils#beansOfTypelncludingAncestors

相关代码:
HierarchicalDependencyLookupDemo.class

  1. /**
  2. * 层次性依赖查找示例
  3. *
  4. * @author mindartisan.blog.csdn.net
  5. * @date
  6. */
  7. public class HierarchicalDependencyLookupDemo {
  8. public static void main(String[] args) {
  9. // 创建 BeanFactory 容器
  10. AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
  11. // 将当前类作为配置类,此处不用也行,因为当前类里没有 @Bean
  12. applicationContext.register(HierarchicalDependencyLookupDemo.class);
  13. // 1. 获取 HierarchicalBeanFactory <- ConfigurableBeanFactory <- ConfigurableListBeanFactory
  14. ConfigurableListableBeanFactory beanFactory = applicationContext.getBeanFactory();
  15. System.out.println("当前 BeanFactory 的 parent BeanFactory:" + beanFactory.getParentBeanFactory());
  16. // 2. 设置 parent BeanFactory
  17. ConfigurableListableBeanFactory parentBeanFactory = createParentBeanFactory();
  18. beanFactory.setParentBeanFactory(parentBeanFactory);
  19. System.out.println("当前 BeanFactory 的 parent BeanFactory:" + beanFactory.getParentBeanFactory());
  20. System.out.println("------------------------");
  21. displayContainsLocalBean(beanFactory,"user");
  22. displayContainsLocalBean(parentBeanFactory,"user");
  23. displayContainsBean(beanFactory,"user");
  24. displayContainsBean(parentBeanFactory,"user");
  25. // 启动应用上下文
  26. applicationContext.refresh();
  27. applicationContext.close();
  28. }
  29. private static void displayContainsBean(HierarchicalBeanFactory beanFactory,String beanName) {
  30. System.out.printf("当前 BeanFactory[%s] 是否包含 Bean[name: %s] : %s\n", beanFactory, beanName,
  31. containsBean(beanFactory, beanName));
  32. }
  33. private static boolean containsBean(HierarchicalBeanFactory beanFactory,String beanName) {
  34. BeanFactory parentBeanFactory = beanFactory.getParentBeanFactory();
  35. if (parentBeanFactory instanceof HierarchicalBeanFactory) {
  36. HierarchicalBeanFactory hierarchicalBeanFactory = HierarchicalBeanFactory.class.cast(parentBeanFactory);
  37. if (containsBean(hierarchicalBeanFactory, beanName)) {
  38. return true;
  39. }
  40. }
  41. return beanFactory.containsLocalBean(beanName);
  42. }
  43. private static void displayContainsLocalBean(HierarchicalBeanFactory beanFactory, String beanName) {
  44. System.out.printf("当前 BeanFactory[%s] 是否包含 LocalBean[name: %s] : %s\n", beanFactory, beanName,
  45. beanFactory.containsLocalBean(beanName));
  46. }
  47. private static ConfigurableListableBeanFactory createParentBeanFactory() {
  48. // 创建 BeanFactory 容器
  49. DefaultListableBeanFactory defaultListableBeanFactory = new DefaultListableBeanFactory();
  50. XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(defaultListableBeanFactory);
  51. String location = "classpath:/META-INF/dependency-lookup-context.xml";
  52. // 加载配置
  53. reader.loadBeanDefinitions(location);
  54. return defaultListableBeanFactory;
  55. }
  56. }

注意 pom.xml 的依赖要「 复用 ioc-container-overview 」

根据 Java 注解查找名称列表

使用 BeanFactoryUtils#beanNamesForTypeIncludingAncestors

延迟依赖查找

通过 org.springframework.beans.factory.ObjectFactory接口以及org.springframework.beans.factory.ObjectProvider接口实现,后者继承了前者,可以调用前者的getObject()获取当前关联的 bean。
image.png
其中使用到了 Java 8 的新特性:

  1. 函数式接口
  2. Stream

相关代码:
ObjectProviderDemo.class

  1. /**
  2. * 通过 ObjectProvider 依赖查找
  3. *
  4. * @author mindartisan.blog.csdn.net
  5. * @date
  6. *
  7. * @commet @Bean 定义的类在当前类,当前类默认就是配置类,不用写 「@Configuration」注解,
  8. *
  9. * @Configuration 非必需注解:https://docs.spring.io/spring-framework/docs/5.2.2.RELEASE/javadoc-api/org/springframework/context/annotation/Bean.html
  10. */
  11. public class ObjectProviderDemo {
  12. public static void main(String[] args) {
  13. // 创建 BeanFactory 容器
  14. AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
  15. // 将当前类作为配置类
  16. applicationContext.register(ObjectProviderDemo.class);
  17. // 启动应用上下文
  18. applicationContext.refresh();
  19. lookupByObjectProvider(applicationContext);
  20. lookupIfAvaiable(applicationContext);
  21. lookupByStreamOps(applicationContext);
  22. applicationContext.close();
  23. }
  24. private static void lookupByStreamOps(AnnotationConfigApplicationContext applicationContext) {
  25. ObjectProvider<String> objectProvider = applicationContext.getBeanProvider(String.class);
  26. // Iterable<String> stringIterable = objectProvider;
  27. // for (String string : stringIterable) {
  28. // System.out.println(string);
  29. // }
  30. objectProvider.stream().forEach(System.out::println);
  31. }
  32. private static void lookupIfAvaiable(AnnotationConfigApplicationContext applicationContext) {
  33. ObjectProvider<User> userObjectProvider = applicationContext.getBeanProvider(User.class);
  34. // 这里演示的是如果 User 不存在的话,进行兜底:
  35. // 此时「getIfAvailable(User::createUser)」会创建一个新的 User,如果是 getIfAvailable(),则会输出 null
  36. User user = userObjectProvider.getIfAvailable(User::createUser);
  37. System.out.println("当前 User 对象:" + user);
  38. }
  39. private static void lookupByObjectProvider(AnnotationConfigApplicationContext applicationContext) {
  40. ObjectProvider<String> objectProvider = applicationContext.getBeanProvider(String.class);
  41. System.out.println(objectProvider.getObject());
  42. }
  43. @Bean
  44. @Primary
  45. // 此时的 Bean 名称就是方法名称
  46. public String helloWorld() {
  47. return "Hello,World";
  48. }
  49. @Bean
  50. public String message() {
  51. return "Message";
  52. }
  53. }

安全依赖查找

此处的安全是指是否抛出异常

单一类型查找

BeanFactory#getBean -> 不安全
ObjectFactory#getObject -> 不安全
ObjectProvider#getIfAvailable -> 安全

集合类型查找

ListableBeanFactory#getBeansOfType -> 安全
ObjectProvider#stream -> 安全

层级类型依赖

依赖于其扩展的单一或者集合类型的接口。

相关代码

TypeSafetyDependencyLookupDemo.class:

  1. **
  2. * 类型安全依赖查找示例
  3. *
  4. * @author mindartisan.blog.csdn.net
  5. * @date
  6. */
  7. public class TypeSafetyDependencyLookupDemo {
  8. public static void main(String[] args) {
  9. // 创建 BeanFactory 容器
  10. AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
  11. // 将当前类作为配置类
  12. applicationContext.register(TypeSafetyDependencyLookupDemo.class);
  13. // 启动应用上下文
  14. applicationContext.refresh();
  15. // 演示 beanFactory#getBean 安全性
  16. displayBeanFactoryGetBean(applicationContext);
  17. // 演示 ObjectFactory#getObject 安全性
  18. displayObjectFactoryGetBean(applicationContext);
  19. // 演示 ObjectProvider#getIfAvaiable 方法的安全性
  20. displayObjectProviderIfAvailable(applicationContext);
  21. // 演示 ListableBeanFactory#getBeansOfType 方法的安全性
  22. displayListableBeanFactoryGetBeansOfType(applicationContext);
  23. // 演示 ObjectProvider Stream 操作的安全性
  24. displayObjectProviderStreamOps(applicationContext);
  25. // 关闭应用上下文
  26. applicationContext.close();
  27. }
  28. /**
  29. * 演示 ObjectProvider Stream 操作的安全性
  30. *
  31. * @param beanFactory bean工厂
  32. */
  33. private static void displayObjectProviderStreamOps(BeanFactory beanFactory) {
  34. // 不抛异常
  35. ObjectProvider<User> userObjectProvider = beanFactory.getBeanProvider(User.class);
  36. printBeansException("displayObjectProviderStreamOps", () -> userObjectProvider.forEach(System.out::println));
  37. }
  38. /**
  39. * 演示 ListableBeanFactory#getBeansOfType 方法的安全性
  40. *
  41. * @param beanFactory bean工厂
  42. */
  43. private static void displayListableBeanFactoryGetBeansOfType(ListableBeanFactory beanFactory) {
  44. // 不抛异常
  45. printBeansException("displayListableBeanFactoryGetBeansOfType", () -> beanFactory.getBeansOfType(User.class));
  46. }
  47. /**
  48. * 演示 ObjectProvider#getIfAvaiable 方法的安全性
  49. *
  50. * @param beanFactory bean工厂
  51. */
  52. private static void displayObjectProviderIfAvailable(BeanFactory beanFactory) {
  53. // 不抛异常
  54. ObjectProvider<User> userObjectProvider = beanFactory.getBeanProvider(User.class);
  55. printBeansException("displayObjectProviderIfAvailable", () -> userObjectProvider.getIfAvailable());
  56. }
  57. /**
  58. * 演示 ObjectFactory#getObject 安全性
  59. *
  60. * @param beanFactory bean工厂
  61. */
  62. private static void displayObjectFactoryGetBean(BeanFactory beanFactory) {
  63. // 抛出「org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com
  64. // .mindartisan.spring.geek.ioc.overview.domain.User' 」异常
  65. ObjectProvider<User> userObjectProvider = beanFactory.getBeanProvider(User.class);
  66. printBeansException("displayObjectFactoryGetBean", userObjectProvider::getObject);
  67. }
  68. /**
  69. * 演示 beanFactory#getBean 安全性
  70. *
  71. * @param beanFactory bean工厂
  72. */
  73. public static void displayBeanFactoryGetBean(BeanFactory beanFactory) {
  74. // 此时并无 User 对应的 bean,抛出「org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.mindartisan.spring.geek.ioc.overview.domain.User' available」异常
  75. printBeansException("displayBeanFactoryGetBean", () -> beanFactory.getBean(User.class));
  76. }
  77. public static void printBeansException(String source, Runnable runnable) {
  78. System.err.println("==========================================");
  79. System.err.println("Source from :" + source);
  80. try {
  81. runnable.run();
  82. } catch (BeansException exception) {
  83. exception.printStackTrace();
  84. }
  85. }
  86. }

内建可查找的依赖

基本上所有的上下文的实现都是基于AbstractApplicationContext此抽象类实现,会在上下文启动的过程中会初始化一些内部的依赖,称为内建的可查询依赖。

AbstractApplicationContext 内建可查找的依赖

Bean 名称 Bean 实例 使用场景
environment Environment 对象 外部化配置:Java 系统的属性,比如 -D 参数
Profiles:比如 dev、test
systemProperties java.util.Properties 对象 Java 系统属性、系统路径或目录
systemEnvironment java.util.Map 对象 操作系统的环境变量(主要指当前用户环境变量)
messageSource MessageSource 对象 国际化文案
lifecycleProcessor LifecycleProcessor 对象 Lifecycle Bean 处理器(Bean 生命周期)
applicationEventMulticaster ApplicationEventMulticaster 对象 事件广播

注解驱动 Spring 应用上下文内建可查找的依赖

AnnotationConfigUtils 里的常量

Bean 名称 Bean 实例 使用场景
org.springframework.context.
annotation.internalConfigur
ationAnnotationProcessor
ConfigurationClassPostProcessor 对象 用来处理 Spring 配置类
org.springframework.context.
annotation.internalAutowire
dAnnotationProcessor
AutowiredAnnotationBeanPostProcessor 对象 处理 @Autowired 以及 @Value
注解(构造器代码里 add 了上述两个注解)
org.springframework.context.
annotation.internalCommonAn
notationProcessor
CommonAnnotationBeanPostProcessor 对象 (条件激活)处理 JSR-250 注解,
如 @PostConstruct 等
org.springframework.context.
event.internalEventListener
Processor
EventListenerMethodProcessor 对象 处理标注 @EventListener 的
Spring 事件监听方法(直接标注在方法上就行,无需实现 ApplicationListener 接口)
org.springframework.context.
event.internalEventListener
Factory
DefaultEventListenerFactory 对
@EventListener 事件监听方法适
配为 ApplicationListener
org.springframework.context.
annotation.internalPersiste
nceAnnotationProcessor
PersistenceAnnotationBeanPostProcessor 对象 (条件激活)处理 JPA 注解场景

依赖查找中的经典异常

异常类型 触发条件 场景举例
NoSuchBeanDefinitionException 当查找 Bean 不存在于 IoC 容器时 BeanFactory#getBean
ObjectFactory#getObject
NoUniqueBeanDefinitionException 类型依赖查找时,IoC 容器存在多
个 Bean 实例
BeanFactory#getBean(Clas
s
BeanInstantiationException 当 Bean 所对应的类型非具体类时 BeanFactory#getBean
BeanCreationException 当 Bean 初始化过程中 Bean 初始化方法执行异常
BeanDefinitionStoreException 当 BeanDefinition 配置元信息非
法时
XML 配置资源无法打开时

相关代码:
NoUniqueBeanDefinitionExceptionDemo.class

  1. /**
  2. * 没有具体的 bean 定义异常示例
  3. *
  4. * @author mindartisan.blog.csdn.net
  5. * @date
  6. */
  7. public class NoUniqueBeanDefinitionExceptionDemo {
  8. public static void main(String[] args) {
  9. // 创建 BeanFactory 容器
  10. AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
  11. // 将当前类作为配置类
  12. applicationContext.register(NoUniqueBeanDefinitionExceptionDemo.class);
  13. // 启动应用上下文
  14. applicationContext.refresh();
  15. try {
  16. applicationContext.getBean(String.class);
  17. } catch (NoUniqueBeanDefinitionException e) {
  18. System.err.printf("Spring 应用上下文当前存在 %d 个 %s 类型的 Bean,具体原因:%s",
  19. e.getNumberOfBeansFound(),
  20. String.class.getName(),
  21. e.getMessage());
  22. }
  23. // 关闭应用上下文
  24. applicationContext.close();
  25. }
  26. @Bean
  27. public String bean1() {
  28. return "bean1";
  29. }
  30. @Bean
  31. public String bean2() {
  32. return "bean2";
  33. }
  34. }

BeanInstantiationException.class

  1. /**
  2. * bean 实例化异常示例
  3. *
  4. * @author mindartisan.blog.csdn.net
  5. * @date
  6. */
  7. public class BeanInstantiationExceptionDemo {
  8. public static void main(String[] args) {
  9. // 创建 BeanFactory 容器
  10. AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
  11. BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(Character.class);
  12. // 下面的不会有异常
  13. // BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(String.class);
  14. applicationContext.registerBeanDefinition("errorBean",beanDefinitionBuilder.getBeanDefinition());
  15. // 启动应用上下文
  16. applicationContext.refresh();
  17. // 关闭应用上下文
  18. applicationContext.close();
  19. }
  20. }

BeanCreationExceptionDemo.class

  1. **
  2. * bean 创建异常示例
  3. *
  4. * @author mindartisan.blog.csdn.net
  5. * @date
  6. */
  7. public class BeanCreationExceptionDemo {
  8. public static void main(String[] args) {
  9. // 创建 BeanFactory 容器
  10. AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
  11. // 注册 BeanDefinition Bean Class 是一个 POJO 普通类,不过初始化方法回调时抛出异常
  12. BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(POJO.class);
  13. applicationContext.registerBeanDefinition("errorBean", beanDefinitionBuilder.getBeanDefinition());
  14. // 启动应用上下文
  15. applicationContext.refresh();
  16. // 关闭应用上下文
  17. applicationContext.close();
  18. }
  19. static class POJO implements InitializingBean {
  20. @PostConstruct // CommonAnnotationBeanPostProcessor
  21. public void init() throws Throwable {
  22. throw new Throwable("init() : For purposes...");
  23. }
  24. @Override
  25. public void afterPropertiesSet() throws Exception {
  26. throw new Exception("afterPropertiesSet() : For purposes...");
  27. }
  28. }
  29. }