SpringIoc依赖查找-5

1. 依赖查找的今世前生: Spring IoC容器从Java标准中学到了什么?

  • 单一类型依赖查找

    • JNDI - javax.naming.Context#lookup(javax.naming.Name)

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Vrzgmly7-1669257924074)(C:\Users\13301\AppData\Roaming\Typora\typora-user-images\image-20221122112306129.png)]

    • JavaBeans - java.beans.beancontext.BeanContext

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-g1ER47wg-1669257924076)(C:\Users\13301\AppData\Roaming\Typora\typora-user-images\image-20221122112822387.png)]

  • 集合类型依赖查找

    • java.beans.beancontext.BeanContext

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DOUakDJL-1669257924076)(C:\Users\13301\AppData\Roaming\Typora\typora-user-images\image-20221122112827221.png)]

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BbGHg86B-1669257924076)(C:\Users\13301\AppData\Roaming\Typora\typora-user-images\image-20221122112849605.png)]

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XlcSZ6MM-1669257924077)(C:\Users\13301\AppData\Roaming\Typora\typora-user-images\image-20221122112904560.png)]

      集合查找方法

  • 层次性依赖查找

    • java.beans.beancontext.BeanContext

      这里要集合 Java Beans Api

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CP5sDgqD-1669257924077)(C:\Users\13301\AppData\Roaming\Typora\typora-user-images\image-20221122113537918.png)]

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vc6gDUz5-1669257924077)(C:\Users\13301\AppData\Roaming\Typora\typora-user-images\image-20221122113631987.png)]

2. 单一类型依赖查找: 如何查找已知名称或类型的Bean对象?

  • 单一类型依赖查找接口- BeanFactory

    • 根据Bean名称查找

      • getBean(String)

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

    • 根据 Bean类型查找

      • Bean实时查找

        • Spring 3.0 getBean(Class)

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

          [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JlGW4QZa-1669257924078)(C:\Users\13301\AppData\Roaming\Typora\typora-user-images\image-20221123110444600.png)]

          返回一个实例, 这个实例可能是shared(单例)可能是independent(原生), 这里就会告诉你一个不好的特点, 如果是shared的话, 当你的整个一个BeanFactory实例里面, 这个容器里面包含了同样一个实例, 那么之后你每次调用一次就会覆盖它的方法, 这个方式实际上有点不可取的, 那么因此建议采用 BeanFactory 的方式只好是只读方法, 要不你就完全的覆盖相关定义, 比如说你吧以前的BeanDefinition把它进行删除, 后面做一个Override做一个覆盖

    • Spring 5.1 Bean延迟查找

      • pom.xml

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.2.RELEASE</version>
         </dependency>
        
      • getBeanProvider(Class)

        package org.xiaoge.thinking.in.spring.dependency.lookup;
        
        import org.springframework.beans.factory.ObjectProvider;
        import org.springframework.context.annotation.AnnotationConfigApplicationContext;
        import org.springframework.context.annotation.Bean;
        
        /**
         * 
         * 通过{@link ObjectProvider} 进行依赖查找
         * 
         * @PACKAGE_NAME: org.xiaoge.thinking.in.spring.dependency.lookup
         * @Classname ObjectProviderDemo
         * @Date 2022/11/23 11:23
         * @Author zhangxiao
         * @Description TODO
         */
        public class ObjectProviderDemo {
                  
                    // @Configuration 是非必须注解
        
            public static void main(String[] args) {
                  
                  
                // 创建 ApplicationContext 容器
                AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        
                // 将当前类 ObjectProviderDemo 作为配置类 (Configuration class)
                applicationContext.register(ObjectProviderDemo.class);
        
                // 启动应用上下文
                applicationContext.refresh();
        
                // 依赖查找集合对象
                lookupByObjectProvider(applicationContext);
        
                // 关闭应用上下文
                applicationContext.close();
            }
        
            @Bean
            public String helloWord() {
                  
                   // @Bean没有value的时候 方法名称就是 Bean 名称 == "helloWord"
                return "hello word";
            }
        
        
            private static void lookupByObjectProvider(AnnotationConfigApplicationContext applicationContext) {
                  
                  
                // 通常来说String类型的Bean Spring 上下文是不会定义的, 因此这个时候可以用 注意:只能查唯一Bean多个Bean会报错NoUniqueBeanDefinitionException
                ObjectProvider<String> beanProvider = applicationContext.getBeanProvider(String.class);
                System.out.println(beanProvider.getObject());
            }
        
        
        }
        
        
        
        // 运行结果
        hello word
        
      • getBeanProvider(ResolvableType)

        后面写

    • 根据Bean名称+类型查找: getBean(String,Class)

    • 建议: 不要用覆盖默认参数的形式去查找

3. 集合类型依赖查找: 如何查找已知类型多个Bean集合?

  • 集合类型依赖查找接口- ListableBeanFactory

    • 根据Bean类型查找

      • 获取同类型Bean名称列表

        • getBeanNamesForType(Class)

          • 注意: 这个方法比较BeanDefinition里面的getBeanClassName和FactoryBean里面的getObjectType类型的匹配情况, 所以这个时候还没有涉及到Bean的初始化

            [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nCOG5tTX-1669257924078)(C:\Users\13301\AppData\Roaming\Typora\typora-user-images\image-20221123133546661.png)]

        • Spring 4.2 getBeanNamesForType(ResolvableType)

      • 获取同类型Bean 实例列表

        • getBeansOfType(Class)以及重载方法
          • 注意: 它会涉及到Bean的初始化, 这个方法可能 会提前把一些你的类进行初始化, 初始化之后会导致一些Bean初始化并不完全, 这个时候会导致一些位置的错误, 所以在判断的时候首先去通过名称去判断, 然后再去通过类型判断, 这样的话比较好一点
    • 通过注解类型查找

      • 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>)

4. 层次性依赖查找: 依赖查找也有双亲委派?

  • 层次性依赖查找接口– HierarchicalBeanFactory

    • 双亲BeanFactory: getParentBeanFactory()

    • 层次性查找

      • 根据 Bean名称查找

        • 基于containsLocalBean方法实现

          package org.xiaoge.thinking.in.spring.dependency.lookup;
          
          import org.springframework.beans.factory.BeanFactory;
          import org.springframework.beans.factory.HierarchicalBeanFactory;
          import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
          import org.springframework.beans.factory.support.DefaultListableBeanFactory;
          import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
          import org.springframework.context.annotation.AnnotationConfigApplicationContext;
          import org.springframework.context.annotation.Bean;
          import org.springframework.context.annotation.Configuration;
          
          /**
           *
           * 层次性 依赖查找 示例
           *
           * @PACKAGE_NAME: org.xiaoge.thinking.in.spring.dependency.lookup
           * @Classname HierarchicalDependencyLookupDemo
           * @Date 2022/11/23 13:52
           * @Author zhangxiao
           * @Description TODO
           */
          public class HierarchicalDependencyLookupDemo {
                      
                      
          
              public static void main(String[] args) {
                      
                      
                  // 创建 ApplicationContext 容器
                  AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
                  // 讲当前类 HierarchicalDependencyLookupDemo 作为配置类 (Configuration Class)
                  applicationContext.register(HierarchicalDependencyLookupDemo.class);
          
                  // 1. 获取HierarchicalBeanFactory <- ConfigurableBeanFactory <- ConfigurableListableBeanFactory
                  ConfigurableListableBeanFactory beanFactory = applicationContext.getBeanFactory();
                  System.out.println("当前 BeanFactory 的 Parent BeanFactory : " + beanFactory.getParentBeanFactory());
          
                  ConfigurableListableBeanFactory parentBeanFactory = createParentBeanFactory();
          
                  // 2. 设置 Parent BeanFactory
                  beanFactory.setParentBeanFactory(parentBeanFactory);
                  System.out.println("当前 BeanFactory 的 Parent BeanFactory : " + beanFactory.getParentBeanFactory());
          
                  // 启动应用上下文
                  applicationContext.refresh();
          
                  // 当前 Bean 是否在当前容器中
                  displayContainsLocalBean(beanFactory, "user");
                  displayContainsLocalBean(parentBeanFactory, "user");
          
                  // 当前 Bean 是否在容器中 实现双亲委派
                  displayContainsBean(beanFactory, "user");
                  displayContainsBean(parentBeanFactory, "user");
          
                  // 关闭应用上下文
                  applicationContext.close();
          
              }
          
              /**
               * 查询 Bean 是否 在容器中 实现双亲委派
               * @param beanFactory
               * @param beanName
               */
              private static void displayContainsBean(HierarchicalBeanFactory beanFactory, String beanName) {
                      
                      
                  System.out.printf("当前 BeanFactory[%s] 是否包含 Bean[name : %s] : %s\n", beanFactory, beanName, containsBean(beanFactory, beanName));
              }
          
          
              /**
               * 递归实现, 查找 Bean 这是 BeanFactoryUtils 没有实现的
               * @param beanFactory
               * @param beanName
               * @return
               */
              private static Boolean containsBean(HierarchicalBeanFactory beanFactory, String beanName) {
                      
                      
                  if (beanFactory.containsLocalBean(beanName)) {
                      
                      
                      return true;
                  }
                  // 获取父容器
                  BeanFactory parentBeanFactory = beanFactory.getParentBeanFactory();
                  // 判断了类型也判断了它是否为空
                  if (parentBeanFactory instanceof HierarchicalBeanFactory) {
                      
                      
                      // 转换类型
                      HierarchicalBeanFactory parentHierarchicalBeanFactory = HierarchicalBeanFactory.class.cast(parentBeanFactory);
          
                      if (parentHierarchicalBeanFactory.containsLocalBean(beanName)) {
                      
                      
                          return true;
                      }
          
                      containsBean(parentHierarchicalBeanFactory, beanName);
                  }
          
                  return false;
              }
          
          
              /**
               * 当前 bean 是否在 当前备本地(它自己的容器)容器中
               * @param beanFactory
               * @param beanName
               */
              private static void displayContainsLocalBean(HierarchicalBeanFactory beanFactory, String beanName) {
                      
                      
                  System.out.printf("当前 BeanFactory[%s] 是否包含 Local Bean[name : %s] : %s\n", beanFactory, beanName, beanFactory.containsLocalBean(beanName));
              }
          
          
              /**
               *
               * @return
               */
              private static ConfigurableListableBeanFactory createParentBeanFactory() {
                      
                      
                  // 创建BeanFactory容器
                  DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
                  XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
          
                  // XML 配置文件 ClassPath 路径
                  String location = "classpath:META-INF/dependency-lookup-context.xml";
          
                  // 加载配置
                  reader.loadBeanDefinitions(location);
          
                  return beanFactory;
              }
          
          }
          
          
          // 运行结果
          当前 BeanFactoryParent BeanFactory : null
          当前 BeanFactoryParent BeanFactory : org.springframework.beans.factory.support.DefaultListableBeanFactory@3c09711b: defining beans [user,objectFactory,superUser]; root of factory hierarchy
          当前 BeanFactory[org.springframework.beans.factory.support.DefaultListableBeanFactory@1e397ed7: 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,hierarchicalDependencyLookupDemo]; parent: org.springframework.beans.factory.support.DefaultListableBeanFactory@3c09711b] 是否包含 Local Bean[name : user] : false
          当前 BeanFactory[org.springframework.beans.factory.support.DefaultListableBeanFactory@3c09711b: defining beans [user,objectFactory,superUser]; root of factory hierarchy] 是否包含 Local Bean[name : user] : true
          当前 BeanFactory[org.springframework.beans.factory.support.DefaultListableBeanFactory@1e397ed7: 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,hierarchicalDependencyLookupDemo]; parent: org.springframework.beans.factory.support.DefaultListableBeanFactory@3c09711b] 是否包含 Bean[name : user] : true
          当前 BeanFactory[org.springframework.beans.factory.support.DefaultListableBeanFactory@3c09711b: defining beans [user,objectFactory,superUser]; root of factory hierarchy] 是否包含 Bean[name : user] : true
          
          
      • 根据 Bean类型查找实例列表

        • 单一类型: BeanFactoryUtils#beanOfType

        • 集合类型:BeanFactoryUtils#beansOfTypeIncludingAncestors

          • 注意: 改方法实现了双亲委派的原则

            [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uhtppiEk-1669257924079)(C:\Users\13301\AppData\Roaming\Typora\typora-user-images\image-20221123151054825.png)]

      • 根据Java 注解查找名称列表

        • BeanFactoryUtils#beanNamesForTypeIncludingAncestors

5. 延迟依赖查找: 非延迟初始化Bean也能实现延迟查找?

  • Bean延迟依赖查找接口

    • org. springframework. beans.factory.ObjectFactory

    • org. springframework. beans.factory.ObjectProvider

      • Spring 5 对Java 8 特性扩展

        • 函数式接口

          • getIfAvailable (Supplier)
          • ifAvailable (Consumer)
        • Stream扩展- stream ()

          package org.xiaoge.thinking.in.spring.dependency.lookup;
          
          import com.sun.xml.internal.ws.api.model.wsdl.WSDLOutput;
          import org.springframework.beans.factory.ObjectProvider;
          import org.springframework.context.annotation.AnnotationConfigApplicationContext;
          import org.springframework.context.annotation.Bean;
          import org.springframework.context.annotation.Primary;
          import org.xiaoge.thinking.in.spring.ioc.overview.domain.User;
          
          /**
           *
           * 通过{@link ObjectProvider} 进行依赖查找
           *
           * @PACKAGE_NAME: org.xiaoge.thinking.in.spring.dependency.lookup
           * @Classname ObjectProviderDemo
           * @Date 2022/11/23 11:23
           * @Author zhangxiao
           * @Description TODO
           */
          public class ObjectProviderDemo {
                      
                        // @Configuration 是非必须注解
          
              public static void main(String[] args) {
                      
                      
                  // 创建 ApplicationContext 容器
                  AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
          
                  // 将当前类 ObjectProviderDemo 作为配置类 (Configuration class)
                  applicationContext.register(ObjectProviderDemo.class);
          
                  // 启动应用上下文
                  applicationContext.refresh();
          
                  // 延迟加载依赖查找集合对象
                  lookupByObjectProvider(applicationContext);
          
                  // 延迟加载
                  lookupIfAvailable(applicationContext);
          
                  // 延迟加载(查找的是集合)
                  lookupByStreamOps(applicationContext);
          
                  // 关闭应用上下文
                  applicationContext.close();
              }
          
              private static void lookupByStreamOps(AnnotationConfigApplicationContext applicationContext) {
                      
                      
                  ObjectProvider<String> beanProvider = applicationContext.getBeanProvider(String.class);
          //        Iterable<String> stringIterable = beanProvider;
          //        for (String s : stringIterable) {
                      
                      
          //            System.out.println(s);
          //        }
          
                  // Stream -> Method reference
                  beanProvider.stream().forEach(System.out::println);
              }
          
              /**
               * 这是个兜底方案, 当没有User Bean 不在容器中时  使用 自己的
               * @param applicationContext
               */
              private static void lookupIfAvailable(AnnotationConfigApplicationContext applicationContext) {
                      
                      
                  ObjectProvider<User> beanProvider = applicationContext.getBeanProvider(User.class);
                  User user = beanProvider.getIfAvailable(User::createUser);
                  System.out.println("当前 User 对象 : " + user);
              }
          
              @Bean
              @Primary
              public String helloWord() {
                      
                       // @Bean没有value的时候 方法名称就是 Bean 名称 == "helloWord"
                  return "hello word";
              }
          
              @Bean
              public String message() {
                      
                       // @Bean没有value的时候 方法名称就是 Bean 名称 == "helloWord"
                  return "message";
              }
          
              private static void lookupByObjectProvider(AnnotationConfigApplicationContext applicationContext) {
                      
                      
                  // 通常来说String类型的Bean Spring 上下文是不会定义的, 因此这个时候可以用 注意:只能查唯一Bean多个Bean会报错NoUniqueBeanDefinitionException
                  ObjectProvider<String> beanProvider = applicationContext.getBeanProvider(String.class);
                  System.out.println(beanProvider.getObject());
              }
          
          
          }
          
          
          // 运行结果
          hello word
          当前 User 对象 : User{
                      
                      id=1, name='xiaoge'}
          hello word
          message
          

6. 安全依赖查找

  • 依赖查找安全性对比

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qU0KbAWg-1669257924079)(C:\Users\13301\AppData\Roaming\Typora\typora-user-images\image-20221123154119989.png)]

    注意:层次性依赖查找的安全性取决于其扩展的单一或集合类型的 BeanFactory 接口

    package org.xiaoge.thinking.in.spring.dependency.lookup;
    
    import com.sun.xml.internal.ws.api.model.wsdl.WSDLOutput;
    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.ListableBeanFactory;
    import org.springframework.beans.factory.ObjectFactory;
    import org.springframework.beans.factory.ObjectProvider;
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    import org.xiaoge.thinking.in.spring.ioc.overview.domain.User;
    
    /**
     * 类型安全 依赖查找 示例
     *
     * @author <a href="mailto:[email protected]">Zhang Xiao</a>
     * @since
     */
    public class TypeSafetyDependencyLookupDemo {
          
          
    
        public static void main(String[] args) {
          
          
            // 创建 ApplicationContext 容器
            AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
    
            // 将当前类 TypeSafetyDependencyLookupDemo 作为配置类 (Configuration class)
            applicationContext.register(TypeSafetyDependencyLookupDemo.class);
    
            // 启动应用上下文
            applicationContext.refresh();
    
            // 演示 BeanFactory#getBean 方法的安全性
            displayBeanFactoryGetBean(applicationContext);
    
            // 演示 ObjectFactory#getObject 方法的安全性
            displayObjectFactoryGetObject(applicationContext);
    
            // 演示 ObjectProvider#ifAvailable 方法的安全性
            displayObjectProviderIfAvailable(applicationContext) ;
    
            // 演示 ListableBeanFactory#getBeansOfType 方法的安全性
            displayListableBeanFactoryGetBeansOfType(applicationContext);
    
            // 演示 ObjectProvider Stream 操作的安全性
            displayObjectProviderStreamOps(applicationContext);
    
            // 关闭应用上下文
            applicationContext.close();
        }
    
        /**
         * ObjectProvider
         * @param applicationContext
         */
        private static void displayObjectProviderStreamOps(AnnotationConfigApplicationContext applicationContext) {
          
          
            ObjectProvider<User> objectProvider = applicationContext.getBeanProvider(User.class);
            printBeansException("displayObjectProviderStreamOps", () -> objectProvider.forEach(System.out::println));
        }
    
    
        /**
         * ListableBeanFactory#getBeansOfType 是否安全
         * @param beanFactory
         */
        private static void displayListableBeanFactoryGetBeansOfType(ListableBeanFactory beanFactory) {
          
          
            printBeansException("displayListableBeanFactoryGetBeansOfType", () -> beanFactory.getBeansOfType(User.class));
        }
    
        /**
         * ObjectProvider#getIfAvailable 是否安全
         * @param applicationContext
         */
        private static void displayObjectProviderIfAvailable(AnnotationConfigApplicationContext applicationContext) {
          
          
            ObjectProvider<User> objectProvider = applicationContext.getBeanProvider(User.class);
            printBeansException("displayObjectProviderIfAvailable", () -> objectProvider.getIfAvailable());
        }
    
        /**
         * ObjectFactory#getObject 是否安全
         * @param applicationContext
         */
        private static void displayObjectFactoryGetObject(AnnotationConfigApplicationContext applicationContext) {
          
          
            // ObjectProvider is ObjectFactory
            ObjectFactory<User> objectFactory = applicationContext.getBeanProvider(User.class);
            printBeansException("displayObjectFactoryGetObject", () -> objectFactory.getObject());
        }
    
        /**
         * BeanFactory#getBean 是否安全
         * @param applicationContext
         */
        private static void displayBeanFactoryGetBean(AnnotationConfigApplicationContext applicationContext) {
          
          
            printBeansException("displayBeanFactoryGetBean", () -> applicationContext.getBean(User.class));
        }
    
    
        /**
         * 打印异常信息
         * @param source
         * @param runnable
         */
        private static void printBeansException(String source, Runnable runnable) {
          
          
            System.err.println("===========================================");
            System.err.println("Source from : " + source);
            try {
          
          
                runnable.run();
            } catch (BeansException e) {
          
          
                e.printStackTrace();
            }
    
        }
    
    }
    
    
    
    // 运行结果
    ===========================================
    Source from : displayBeanFactoryGetBean
    org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.xiaoge.thinking.in.spring.ioc.overview.domain.User' available
    	at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:351)
    	at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:342)
    	at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1126)
    	at org.xiaoge.thinking.in.spring.dependency.lookup.TypeSafetyDependencyLookupDemo.lambda$displayBeanFactoryGetBean$4(TypeSafetyDependencyLookupDemo.java:90)
    	at org.xiaoge.thinking.in.spring.dependency.lookup.TypeSafetyDependencyLookupDemo.printBeansException(TypeSafetyDependencyLookupDemo.java:103)
    	at org.xiaoge.thinking.in.spring.dependency.lookup.TypeSafetyDependencyLookupDemo.displayBeanFactoryGetBean(TypeSafetyDependencyLookupDemo.java:90)
    	at org.xiaoge.thinking.in.spring.dependency.lookup.TypeSafetyDependencyLookupDemo.main(TypeSafetyDependencyLookupDemo.java:30)
    ===========================================
    Source from : displayObjectFactoryGetObject
    org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.xiaoge.thinking.in.spring.ioc.overview.domain.User' available
    	at org.springframework.beans.factory.support.DefaultListableBeanFactory$1.getObject(DefaultListableBeanFactory.java:370)
    	at org.xiaoge.thinking.in.spring.dependency.lookup.TypeSafetyDependencyLookupDemo.lambda$displayObjectFactoryGetObject$3(TypeSafetyDependencyLookupDemo.java:82)
    	at org.xiaoge.thinking.in.spring.dependency.lookup.TypeSafetyDependencyLookupDemo.printBeansException(TypeSafetyDependencyLookupDemo.java:103)
    	at org.xiaoge.thinking.in.spring.dependency.lookup.TypeSafetyDependencyLookupDemo.displayObjectFactoryGetObject(TypeSafetyDependencyLookupDemo.java:82)
    	at org.xiaoge.thinking.in.spring.dependency.lookup.TypeSafetyDependencyLookupDemo.main(TypeSafetyDependencyLookupDemo.java:33)
    ===========================================
    Source from : displayObjectProviderIfAvailable
    ===========================================
    Source from : displayListableBeanFactoryGetBeansOfType
    ===========================================
    Source from : displayObjectProviderStreamOps
    

    由此可见建议使用ObjectProvider来进行依赖查找

7. 内建可查找的依赖: 那些Spring IoC容器内建依赖可供查找?

  • AbstractApplicationContext 内建可查找的依赖

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9kofvJzg-1669257924080)(C:\Users\13301\AppData\Roaming\Typora\typora-user-images\image-20221123164247234.png)]

  • 注解驱动Spring 应用上下文内建可查找的依赖(部分)

    C:\Users\13301\AppData\Roaming\Typora\typora-user-images\image-20221123164332343.png

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tiJRKs5K-1669257924081)(C:\Users\13301\AppData\Roaming\Typora\typora-user-images\image-20221123164505327.png)]

    其实也非常好记, 只需要关注AnnotationConfigUtils就知道了

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jktjKKOn-1669257924081)(C:\Users\13301\AppData\Roaming\Typora\typora-user-images\image-20221123170438893.png)]

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PidXZteP-1669257924082)(C:\Users\13301\AppData\Roaming\Typora\typora-user-images\image-20221123170557025.png)]

8. 依赖查找中的经典异常: Bean找不到?Bean不唯一的?Bean创建失败?

  • BeansException子类型

    C:\Users\13301\AppData\Roaming\Typora\typora-user-images\image-20221123170857785.png

  • NoSuchBeanDefinitionException不做演示, 上面案例有

  • NoUniqueBeanDefinitionException

    package org.xiaoge.thinking.in.spring.dependency.lookup;
    
    import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    import org.springframework.context.annotation.Bean;
    
    /**
     * {@link NoUniqueBeanDefinitionException} 示例代码
     *
     * @author <a href="mailto:[email protected]">Zhang Xiao</a>
     * @since
     */
    public class NoUniqueBeanDefinitionExceptionDemo {
          
          
    
        public static void main(String[] args) {
          
          
            // 创建 ApplicationContext 容器
            AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
    
            // 将当前类 NoUniqueBeanDefinitionExceptionDemo 作为配置类 (Configuration class)
            applicationContext.register(NoUniqueBeanDefinitionExceptionDemo.class);
    
            // 启动应用上下文
            applicationContext.refresh();
    
            // 由于 Spring 上下文存在两个String类型的 Bean, 通过单一类型查找会报错
            try {
          
          
                applicationContext.getBean(String.class);
            } catch (NoUniqueBeanDefinitionException e) {
          
          
                System.err.printf("Spring 应用上下文存在%d个 %s 类型的Bean, 具体原因: %s",
                        e.getNumberOfBeansFound(),
                        String.class.getName(),
                        e.getMessage()
                        );
            }
    
            // 关闭应用上下文
            applicationContext.close();
        }
    
        @Bean
        public String bean1() {
          
          
            return "bean1";
        }
    
        @Bean
        public String bean2() {
          
          
            return "bean2";
        }
    }
    
    
    // 运行结果
    Spring 应用上下文存在2java.lang.String 类型的Bean, 具体原因: No qualifying bean of type 'java.lang.String' available: expected single matching bean but found 2: bean1,bean2
    
    
  • BeanInstantiationException

    package org.xiaoge.thinking.in.spring.dependency.lookup;
    
    import org.springframework.beans.BeanInstantiationException;
    import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
    import org.springframework.beans.factory.config.BeanDefinition;
    import org.springframework.beans.factory.support.BeanDefinitionBuilder;
    import org.springframework.beans.factory.support.BeanDefinitionRegistry;
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    
    /**
     * {@link BeanInstantiationException} 示例
     *
     * @author <a href="mailto:[email protected]">Zhang Xiao</a>
     * @since
     */
    public class BeanInstantiationExceptionDemo {
          
          
    
        public static void main(String[] args) {
          
          
            // 创建 ApplicationContext 容器
            AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
    
            // 注册 BeanDefinition Bean Class 是一个 CharSequence 接口
            BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(CharSequence.class);
            // 注册Bean
            applicationContext.registerBeanDefinition("errorBean", beanDefinitionBuilder.getBeanDefinition());
    
            // 启动应用上下文
            applicationContext.refresh();
    
            // 关闭应用上下文
            applicationContext.close();
        }
    
    }
    
    
    // 运行结果
    十一月 23, 2022 5:56:42 下午 org.springframework.context.support.AbstractApplicationContext refresh
    警告: Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'errorBean': Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [java.lang.CharSequence]: Specified class is an interface
    Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'errorBean': Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [java.lang.CharSequence]: Specified class is an interface
    	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1320)
    	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1214)
    	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:557)
    	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:517)
    	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:323)
    	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
    	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:321)
    	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
    	at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:879)
    	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:878)
    	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:550)
    	at org.xiaoge.thinking.in.spring.dependency.lookup.BeanInstantiationExceptionDemo.main(BeanInstantiationExceptionDemo.java:28)
    Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [java.lang.CharSequence]: Specified class is an interface
    	at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:70)
    	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1312)
    	... 11 more
    
    Process finished with exit code 1
    
    
  • BeanCreationException

    package org.xiaoge.thinking.in.spring.dependency.lookup;
    
    import org.springframework.beans.factory.BeanCreationException;
    import org.springframework.beans.factory.InitializingBean;
    import org.springframework.beans.factory.support.BeanDefinitionBuilder;
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    
    import javax.annotation.PostConstruct;
    
    /**
     * {@link BeanCreationException} 示例
     *
     * @author <a href="mailto:[email protected]">Zhang Xiao</a>
     * @since
     */
    public class BeanCreationExceptionDemo {
          
          
    
        public static void main(String[] args) {
          
          
            // 创建 ApplicationContext 容器
            AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
    
            // 注册 BeanDefinition Bean Class 是一个 POJO 普通类,不过初始化方法回调时抛出异常
            BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(POJO.class);
            // 注册Bean
            applicationContext.registerBeanDefinition("errorBean", beanDefinitionBuilder.getBeanDefinition());
    
            // 启动应用上下文
            applicationContext.refresh();
    
            // 关闭应用上下文
            applicationContext.close();
        }
    
        static class POJO implements InitializingBean {
          
          
    
            @PostConstruct // CommonAnnotationBeanPostProcessor 实现的这个注解, 顺序很重要, 因为PostConstruct注解装饰的方法会先执行
            public void init() throws Exception {
          
          
                throw new Exception("init() : For purposes...");
            }
    
            @Override
            public void afterPropertiesSet() throws Exception {
          
          
                throw new Exception("afterPropertiesSet() : For purposes...");
            }
    
        }
    
    }
    
    
    // 运行结果
    十一月 23, 2022 5:57:12 下午 org.springframework.context.support.AbstractApplicationContext refresh
    警告: Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'errorBean': Invocation of init method failed; nested exception is java.lang.Exception: init() : For purposes...
    Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'errorBean': Invocation of init method failed; nested exception is java.lang.Exception: init() : For purposes...
    	at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization(InitDestroyAnnotationBeanPostProcessor.java:160)
    	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:416)
    	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1788)
    	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:595)
    	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:517)
    	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:323)
    	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
    	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:321)
    	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
    	at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:879)
    	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:878)
    	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:550)
    	at org.xiaoge.thinking.in.spring.dependency.lookup.BeanCreationExceptionDemo.main(BeanCreationExceptionDemo.java:28)
    Caused by: java.lang.Exception: init() : For purposes...
    	at org.xiaoge.thinking.in.spring.dependency.lookup.BeanCreationExceptionDemo$POJO.init(BeanCreationExceptionDemo.java:38)
    	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    	at java.lang.reflect.Method.invoke(Method.java:498)
    	at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleElement.invoke(InitDestroyAnnotationBeanPostProcessor.java:389)
    	at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleMetadata.invokeInitMethods(InitDestroyAnnotationBeanPostProcessor.java:333)
    	at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization(InitDestroyAnnotationBeanPostProcessor.java:157)
    	... 12 more
    
    Process finished with exit code 1
    
    

9. 面试题精选

  • ObjectFactory 与BeanFactory 的区别?

    答: 0bjectFactory 与 BeanFactory 均提供依赖查找的能力。

    不过 ObjectFactory 仅关注一个 或一种类型的 Bean 依赖查找, 并且自身不具备依赖查找的能力,能力则由 BeanFactory 输出。

    BeanFactory则提供了单一类型、集合类型以及层次性等多种依赖查找方式。

    根据名称查找

    C:\Users\13301\AppData\Roaming\Typora\typora-user-images\image-20221124100431367.png

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8nJA3QGQ-1669257924083)(C:\Users\13301\AppData\Roaming\Typora\typora-user-images\image-20221124100527299.png)]

    C:\Users\13301\AppData\Roaming\Typora\typora-user-images\image-20221124100618961.png

    根据类型查找

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-89L10AE8-1669257924084)(C:\Users\13301\AppData\Roaming\Typora\typora-user-images\image-20221124102640878.png)]

    C:\Users\13301\AppData\Roaming\Typora\typora-user-images\image-20221124102627020.png

    C:\Users\13301\AppData\Roaming\Typora\typora-user-images\image-20221124102750503.png

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-713k2XRX-1669257924085)(C:\Users\13301\AppData\Roaming\Typora\typora-user-images\image-20221124102926690.png)]

    最终还是用了BeanFactory

    可以通过改文章, 可以看出它是怎么通过ObjectFactoryCreatingFactoryBean创建ObjectFactory的

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

    答: BeanFactory. getBean方法的执行是线程安全的,操作过程中会增加互斥锁。

猜你喜欢

转载自blog.csdn.net/zsx1314lovezyf/article/details/128013766