单一类型查找

在ioc概述演示过demo…
image.png

集合类型依赖查找

注: 在spring中的bean可以被二次定义覆盖

ListableBeanFactory可以通过某个类型去查找一个集合的列表,集合列表可能有两种情况,一种是查询Bean的名称、还有一种是查询Bean的实例。推荐使用Bean的名称来判断Bean是否存在,这种方式可以避免提早初始化Bean,产生一些不确定的因素。

image.png

层次性依赖查找

image.png

如下图的层次结构, 可以看到ConfigurableListableBeanFactory具有层次性的以及可罗列的依赖查找的能力
image.png
层次性的依赖查找的实现与下面代码类似, 是通过递归查找实现的

  1. public class HierarchicalDependencyLookupDemo {
  2. public static void main(String[] args) {
  3. AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
  4. // 1. 通过 @Bean方式定义, 注意, 注解尽管多种方式, 但是不会重复注册
  5. applicationContext.register(ObjectProviderDemo.class);
  6. applicationContext.refresh();
  7. ConfigurableListableBeanFactory beanFactory = applicationContext.getBeanFactory();
  8. // System.out.println("当前的beanFactory的parent: " + beanFactory.getParentBeanFactory());
  9. HierarchicalBeanFactory parentBeanFactory = createParentBeanFactory();
  10. beanFactory.setParentBeanFactory(parentBeanFactory);
  11. // System.out.println("当前的beanFactory的parent: " + beanFactory.getParentBeanFactory());
  12. displayContainsLocalBean(beanFactory, "user");
  13. displayContainsLocalBean(parentBeanFactory, "user");
  14. displayContainsBean(beanFactory,"user");
  15. displayContainsBean(parentBeanFactory,"user");
  16. applicationContext.close();
  17. }
  18. private static HierarchicalBeanFactory createParentBeanFactory() {
  19. // 创建BeanFactory容器
  20. DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
  21. // 加载配置
  22. XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
  23. reader.loadBeanDefinitions("classpath:META-INF/dependency-lookup-context.xml");
  24. return beanFactory;
  25. }
  26. private static boolean containsBean(HierarchicalBeanFactory beanFactory, String beanName) {
  27. BeanFactory parentBeanFactory = beanFactory.getParentBeanFactory();
  28. if (parentBeanFactory instanceof HierarchicalBeanFactory) {
  29. HierarchicalBeanFactory hierarchicalBeanFactory = (HierarchicalBeanFactory) parentBeanFactory;
  30. return containsBean(hierarchicalBeanFactory, beanName);
  31. }
  32. return beanFactory.containsLocalBean(beanName);
  33. }
  34. private static void displayContainsBean(HierarchicalBeanFactory beanFactory, String beanName) {
  35. System.out.printf("当前BeanFactory[%s]是否包含Bean[%s] : %s\n", beanFactory, beanName,
  36. containsBean(beanFactory, beanName));
  37. }
  38. private static void displayContainsLocalBean(HierarchicalBeanFactory beanFactory, String beanName) {
  39. System.out.printf("当前BeanFactory[%s]是否包含localBean[%s] : %s\n", beanFactory, beanName,
  40. beanFactory.containsLocalBean(beanName));
  41. }
  42. }
  1. "C:\Program Files\Java\jdk1.8.0_221\bin\java.exe" "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2019.3.2\lib\idea_rt.jar=13889:C:\Program Files\JetBrains\IntelliJ IDEA 2019.3.2\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_221\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\rt.jar;D:\geekbang-spring-learning\dependency-lookup\target\classes;D:\geekbang-spring-learning\ioc-container-overview\target\classes;C:\XZrepository\org\springframework\spring-context\5.2.2.RELEASE\spring-context-5.2.2.RELEASE.jar;C:\XZrepository\org\springframework\spring-aop\5.2.2.RELEASE\spring-aop-5.2.2.RELEASE.jar;C:\XZrepository\org\springframework\spring-beans\5.2.2.RELEASE\spring-beans-5.2.2.RELEASE.jar;C:\XZrepository\org\springframework\spring-core\5.2.2.RELEASE\spring-core-5.2.2.RELEASE.jar;C:\XZrepository\org\springframework\spring-jcl\5.2.2.RELEASE\spring-jcl-5.2.2.RELEASE.jar;C:\XZrepository\org\springframework\spring-expression\5.2.2.RELEASE\spring-expression-5.2.2.RELEASE.jar;C:\XZrepository\org\springframework\spring-jdbc\5.2.2.RELEASE\spring-jdbc-5.2.2.RELEASE.jar;C:\XZrepository\org\springframework\spring-tx\5.2.2.RELEASE\spring-tx-5.2.2.RELEASE.jar;C:\XZrepository\org\springframework\spring-webmvc\5.2.2.RELEASE\spring-webmvc-5.2.2.RELEASE.jar;C:\XZrepository\org\springframework\spring-web\5.2.2.RELEASE\spring-web-5.2.2.RELEASE.jar;C:\XZrepository\org\springframework\spring-webflux\5.2.2.RELEASE\spring-webflux-5.2.2.RELEASE.jar;C:\XZrepository\io\projectreactor\reactor-core\3.3.1.RELEASE\reactor-core-3.3.1.RELEASE.jar;C:\XZrepository\org\reactivestreams\reactive-streams\1.0.3\reactive-streams-1.0.3.jar" top.xinzhang0618.dependency.lookup.HierarchicalDependencyLookupDemo
  2. 当前BeanFactory[org.springframework.beans.factory.support.DefaultListableBeanFactory@51565ec2: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.context.event.internalEventListenerProcessor,org.springframework.context.event.internalEventListenerFactory,objectProviderDemo,helloWorld]; parent: org.springframework.beans.factory.support.DefaultListableBeanFactory@7e0e6aa2]是否包含localBean[user] : false
  3. 当前BeanFactory[org.springframework.beans.factory.support.DefaultListableBeanFactory@7e0e6aa2: defining beans [user,objectFactory,superUser]; root of factory hierarchy]是否包含localBean[user] : true
  4. 当前BeanFactory[org.springframework.beans.factory.support.DefaultListableBeanFactory@51565ec2: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.context.event.internalEventListenerProcessor,org.springframework.context.event.internalEventListenerFactory,objectProviderDemo,helloWorld]; parent: org.springframework.beans.factory.support.DefaultListableBeanFactory@7e0e6aa2]是否包含Bean[user] : true
  5. 当前BeanFactory[org.springframework.beans.factory.support.DefaultListableBeanFactory@7e0e6aa2: defining beans [user,objectFactory,superUser]; root of factory hierarchy]是否包含Bean[user] : true

基本都是单一查找,依赖查找可以在LB这种场景应用,但这个分层查找有啥用?

比如 Spring MVC 中,Biz 组件放在 Root ApplicationContext,而 Web 组件放在 DispatcherServlet 的 ApplicationContext,后者是前者的子 ApplicationContext,所以,子 ApplicationContext 可以读取父 ApplicationContext

请问小马哥,个人还是不太明白这个容器的层次关系的具体应用场景,在什么情况下才有必要构建这个树结构?

作者回复: 容器的层次关系主要的目的是实现 Bean 复用,假设一个应用存在一个 Root ApplicationContext,内部的 Bean 来自于一个 jar 包,那么,这个jar 包可能被两个不同的 Servlet 应用使用,这时,ServletContext A 和 ServletContext B 同时复用了这个 parent ApplicationContext,而它自己也有 ApplicationContext,这也是 Spring Web MVC 所涉及的应用上下文架构。

延迟依赖查找

image.png

【学】getIfAvailable写法

如下ObjectProvider接口, 提供了getIfAvailable()的方法, 并提供了default实现, 可以传入一个Supplier做保底

  1. public interface ObjectProvider<T> extends ObjectFactory<T>, Iterable<T> {
  2. @Nullable
  3. T getIfAvailable() throws BeansException;
  4. default T getIfAvailable(Supplier<T> defaultSupplier) throws BeansException {
  5. T dependency = getIfAvailable();
  6. return (dependency != null ? dependency : defaultSupplier.get());
  7. }
  8. default void ifAvailable(Consumer<T> dependencyConsumer) throws BeansException {
  9. T dependency = getIfAvailable();
  10. if (dependency != null) {
  11. dependencyConsumer.accept(dependency);
  12. }
  13. }
  14. }
  15. -------------------------使用示例
  16. ObjectProvider<String> beanProvider = applicationContext.getBeanProvider(String.class);
  17. String ifAvailable = beanProvider.getIfAvailable(String::new);

安全依赖查找

其实这里讲的就是bean没有或者初始化不成功等场景时, 下列获取bean的方法安不安全, 会不会抛异常
很显然, ObjectProvider#getIfAvailable()就不会抛异常嘛
image.png

【学】异常统一捕获处理

  1. public static void displayBeanFactoryGetBean(BeanFactory beanFactory) {
  2. printBeansException("displayBeanFactoryGetBean", () -> beanFactory.getBean(User.class));
  3. }
  4. // 这里其实利用了Runnable这种函数式编程接口, 调用的时候直接写lambda表达式
  5. private static void printBeansException(String source, Runnable runnable) {
  6. System.err.println("==========================================");
  7. System.err.println("Source from :" + source);
  8. try {
  9. runnable.run();
  10. } catch (BeansException exception) {
  11. exception.printStackTrace();
  12. }
  13. }

DefaultListableBeanFactory

DefaultListableBeanFactory是spring/springboot的BeanFactory接口的兜底实现, 它实现了ConfigurableListableBeanFactory

  • ConfigurableListableBeanFactory继承了ListableBeanFactory, 因此具有集合类型依赖查找的功能
  • ListableBeanFactory继承了BeanFactory, 因此具有了单一类型的依赖查找的功能
  • ConfigurableListableBeanFactory继承了ConfigurableBeanFactory, ConfigurableBeanFactory又继承了HierarchicalBeanFactory, 因此具有了层次性依赖查找的功能

spring内建可查找的依赖

image.png

image.png

image.png

依赖查找中的典型异常

image.png

面试题

ObjectFactory与BeanFactory的区别?

均提供依赖查找的能力

  • ObjectFactory仅关注一个或一种类型的Bean依赖查找, 并且本身不具备依赖查找的能力, 能力则由BeanFactory输出
  • BeanFactory提供了单一类型, 集合类型以及层次性等多种依赖查找的方式

BeanFactory.getBean操作是否线程安全?

是的, 操作过程会加互斥锁