你真的了解Spring的依赖查找吗?

1.写在前面

前面的博客我们介绍了Spring的总览,今天我们来了解下Spring的依赖查找的相关的内容,我们会从它的前世今生来带你了解下,以及各种类型的查找的方式,同时介绍对应的相对比较安全的查找的方式。以及会介绍一些比较常见的面试题,供大家参考。

2.依赖查找的今世前生

那么提到依赖查找,很多人第一个想到的就是Spring中的依赖查找,但是在Spring出来之前,Java中其实已经就有了依赖查找的API了,具体的如下,同时Spring也参考其中的一部分。

单一类型依赖查找

  • JNDI - javax.naming.Context#lookup(javax.naming.Name)
  • JavaBeans - java.beans.beancontext.BeanContext

集合类型依赖查找

  • java.beans.beancontext.BeanContext

层次性依赖查找

  • java.beans.beancontext.BeanContext

单一类型的依赖查找

那么我这儿先带大家看下对应的源码,后面再带着大家写几个例子,首先我们先看对应的Java中的Context类,具体的如下:

在这里插入图片描述

上面的两个方法都是javax.naming.Context提供的Bean的查找的方法。

接下来我们继续看一下BeanContext,这个BeanContextSpring参考对应的实现的方式。具体的内容如下:

在这里插入图片描述

可以看到这接口继承的了Collection接口,我们可以知道这个接口下面所有的Bean都是存在这个集合下面的,对这个集合进行增删改查。

集合类型依赖查找

集合类型查找是根据一个key去查找出来一个集合的类型。

在这里插入图片描述

对应的方法如上图所示,返回的是一个object对象,也就是集合的对象也是可以返回的

层次性依赖查找BeanContext的源码写的比较混乱,这儿我们不做介绍了。

3.单一类型依赖查找

单一类型依赖查找接口 - 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)
  • 根据 Bean 名称 + 类型查找: getBean(String,Class)

上面的有些的查找的方式我们前面的博客已经介绍过了,上面的覆盖默认参数的方式,我们只要知道有这个方法就行了,不需要做过多的了解,因为这个方法比较危险,它会覆盖默认的参数。

在这里插入图片描述

在这里插入图片描述

我们看一下对应的5.1的延迟查找的API,具体的如下:

在这里插入图片描述

这儿我们可以看到返回的是ObjectProvider,我们点开这个实现类的内容,具体的如下:

在这里插入图片描述

可以发现这个类是继承了ObjectFactory类的,那么一切都那么好理解了。

我们写了一个简单的例子就行,具体的代码如下:

package org.learn.spring.dependency.lookup;

import org.springframework.beans.factory.ObjectProvider;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;

// 通过 ObjectProvider 进行依赖查找
public class ObjectProviderDemo {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();

        // 将当前类ObjectProviderDemo 作为配置类(configuration class)
        applicationContext.register(ObjectProviderDemo.class);
        // 启动应用上下文
        applicationContext.refresh();

        lookupByObjectProvider(applicationContext);
        applicationContext.close();
    }

    private static void lookupByObjectProvider(AnnotationConfigApplicationContext applicationContext) {
        ObjectProvider<String> objectProvider = applicationContext.getBeanProvider(String.class);
        System.out.println(objectProvider.getObject());
    }

    // 方法的名称就是Bean的名称, Bean的名称helloWorld
    @Bean
    public String helloWorld(){
        return "Hello World";
    }
}

还有一个getBeanProvider(ResolvableType)这个方法我们后面再说,ResolvableType这个类型主要是用来处理泛型的。等到后面我们介绍泛型的时候,我们再来详细的介绍。

4.集合类型依赖查找

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

  • 根据 Bean 类型查找
  • 获取同类型 Bean 名称列表
    • getBeanNamesForType(Class)
    • Spring 4.2 getBeanNamesForType(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>)

上面的API我前面都有或多或少的介绍过,我这儿不做赘述了,从上面的API我们可以得到一个结论就是ListableBeanFactory主要用来查找集合的。而方式有两种一种是通过Bean的类型查找,一种是通过注解的类型去查找。集合的列表主要有两种情况,一种查询Bean的名称的集合,一种是查询Bean的实例的集合。

5.层次性依赖查找

层次性依赖查找接口 - HierarchicalBeanFactory 将单一类型的查找和集合类型查找合并到一起去

  • 双亲 BeanFactory: getParentBeanFactory()
  • 层次性查找
    • 根据 Bean 名称查找
      • 基于 containsLocalBean 方法实现
    • 根据 Bean 类型查找实例列表
      • 单一类型: BeanFactoryUtils#beanOfType
      • 集合类型: BeanFactoryUtils#beansOfTypeIncludingAncestors
    • 根据 Java 注解查找名称列表
      • BeanFactoryUtils#beanNamesForTypeIncludingAncestors

具体的代码如下:

package org.learn.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;

// HierarchicalBeanFactory 层次查找依赖实例
public class HierarchicalDependencyLookup {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();

        // 将当前类ObjectProviderDemo 作为配置类(configuration class)
        applicationContext.register(ObjectProviderDemo.class);

        // 1.获取HierarchicalBeanFactory <- ConfigurableBeanFactory <- ConfigurableListableBeanFactory
        ConfigurableListableBeanFactory beanFactory = applicationContext.getBeanFactory();
        System.out.println("当前BeanFactory 的 Parent BeanFactory:" + beanFactory.getParentBeanFactory());

        // 2.设置ParentBeanFactory
        HierarchicalBeanFactory parentBeanFactory = createParentBeanFactory();
        beanFactory.setParentBeanFactory(parentBeanFactory);
        System.out.println("当前BeanFactory 的 Parent BeanFactory:" + beanFactory.getParentBeanFactory());

        // 启动应用上下文
        applicationContext.refresh();
        displayContainsLocalBean(beanFactory, "user");
        displayContainsLocalBean(parentBeanFactory, "user");

        displayContainsBean(beanFactory, "user");
        displayContainsBean(parentBeanFactory, "user");

        applicationContext.close();
    }

    private static ConfigurableListableBeanFactory createParentBeanFactory() {
        // 创建BeanFactory容器
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
        // XML 配置文件的路径
        String location = "classpath:META-INF/dependency-lookup-context.xml";
        // 加载配置
        reader.loadBeanDefinitions(location);
        return beanFactory;
    }

    private static void displayContainsLocalBean(HierarchicalBeanFactory beanFactory, String beanName) {
        System.out.printf("当前 BeanFactory[%s] 是否包含 Local Bean [name : %s] : %s\n", beanFactory, beanName, beanFactory.containsLocalBean(beanName));
    }

    private static void displayContainsBean(HierarchicalBeanFactory beanFactory, String beanName) {
        System.out.printf("当前 BeanFactory[%s] 是否包含 Bean [name : %s] : %s\n", beanFactory, beanName, containsBean(beanFactory, beanName));
    }

    private static boolean containsBean(HierarchicalBeanFactory beanFactory, String beanName) {
        BeanFactory parentBeanFactory = beanFactory.getParentBeanFactory();
        if (parentBeanFactory instanceof HierarchicalBeanFactory) {
            HierarchicalBeanFactory parentHierarchicalBeanFactory = HierarchicalBeanFactory.class.cast(parentBeanFactory);
            if (containsBean(parentHierarchicalBeanFactory, beanName)) {
                return true;
            }
        }
        return beanFactory.containsLocalBean(beanName);
    }
}

上面的代码就是实现了层次的查找,这个时候是有两个工厂,一个是父工厂,一个子工厂,containsBean方法就是通过递归的方式来查找所有的工厂中包含的对应的Bean。

其实BeanFactoryUtils有相应的实现,具体的的代码如下:

在这里插入图片描述

6.延迟依赖查找

Bean 延迟依赖查找接口

  • org.springframework.beans.factory.ObjectFactory
  • org.springframework.beans.factory.ObjectProvider
    • Spring 5 对 Java 8 特性扩展
    • 函数式接口
      • getIfAvailable(Supplier)
      • ifAvailable(Consumer)
    • Stream 扩展 - stream()

具体的代码如下:

package org.learn.spring.dependency.lookup;

import org.learn.spring.ioc.overview.domain.User;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;

// 通过 ObjectProvider 进行依赖查找
public class ObjectProviderDemo {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();

        // 将当前类ObjectProviderDemo 作为配置类(configuration class)
        applicationContext.register(ObjectProviderDemo.class);
        // 启动应用上下文
        applicationContext.refresh();

        lookupByAvailable(applicationContext);
        lookupByStreamOps(applicationContext);
        applicationContext.close();
    }

    private static void lookupByStreamOps(AnnotationConfigApplicationContext applicationContext) {
        ObjectProvider<String> objectProvider = applicationContext.getBeanProvider(String.class);
        Iterable<String> stringIterable = objectProvider;
        for (String s : stringIterable) {
            System.out.println(s);
        }
        objectProvider.stream().forEach(System.out::println);
    }

    private static void lookupByAvailable(AnnotationConfigApplicationContext applicationContext) {
        ObjectProvider<User> userObjectProvider = applicationContext.getBeanProvider(User.class);
        User user = userObjectProvider.getIfAvailable(User::createUser);
        System.out.println("当前 User 对象:" + user);
    }

  
    @Bean
    @Primary
    public String helloWorld() {
        return "Hello World";
    }

    @Bean
    public String message() {
        return "Message";
    }
}

7.安全依赖查找

依赖查找安全性对比

在这里插入图片描述

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

具体的代码如下:

package org.learn.spring.dependency.lookup;

import org.learn.spring.ioc.overview.domain.User;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

// 类型安全查找的示例
public class TypeSafetyDependencyLookupDemo {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();

        // 将当前类ObjectProviderDemo 作为配置类(configuration class)
        applicationContext.register(ObjectProviderDemo.class);
        // 启动应用上下文
        applicationContext.refresh();

        // 演示BeanFactory#getBean方法的安全性 不安全的
        displayBeanFactoryGetBean(applicationContext);

        // 演示ObjectFactory#getObject方法的安全性 不安全的
        displayObjectFactoryGetObject(applicationContext);

        // 演示ObjectProvider#getIfAvailable方法的安全性 安全的
        displayObjectProviderGetIfAvailable(applicationContext);

        // 演示 ListableBeanFactory#getBeansOfType方法的安全性 安全的 也是会抛出BeansException 但是这个只有在Bean创建失败的时候抛出
        displayListableBeanFactoryGetBeansOfType(applicationContext);

        //演示ObjectProvider#StreamOps方法的安全性 安全的
        displayObjectProviderStreamOps(applicationContext);

        // 关闭应用上下文
        applicationContext.close();
    }

    // 不安全的
    public static void displayBeanFactoryGetBean(BeanFactory beanFactory) {
        printBeansException("displayBeanFactoryGetBean", () -> beanFactory.getBean(User.class));
    }

    private static void displayObjectFactoryGetObject(AnnotationConfigApplicationContext applicationContext) {
        // ObjectProvider is ObjectFactory
        ObjectFactory<User> userObjectProvider = applicationContext.getBeanProvider(User.class);
        printBeansException("displayObjectFactoryGetObject", userObjectProvider::getObject);
    }


    private static void displayObjectProviderGetIfAvailable(AnnotationConfigApplicationContext applicationContext) {
        ObjectProvider<User> userObjectProvider = applicationContext.getBeanProvider(User.class);
        printBeansException("displayObjectProviderGetIfAvailable", userObjectProvider::getIfAvailable);
    }

    // 也是会抛出BeansException 但是这个只有在Bean创建失败的时候抛出
    private static void displayListableBeanFactoryGetBeansOfType(ListableBeanFactory beanFactory) {
        printBeansException("displayListableBeanFactoryGetBeansOfType", () -> beanFactory.getBeansOfType(User.class));
    }

    private static void displayObjectProviderStreamOps(AnnotationConfigApplicationContext applicationContext) {
        ObjectProvider<User> userObjectProvider = applicationContext.getBeanProvider(User.class);
        printBeansException("displayObjectProviderStreamOps", () -> userObjectProvider.stream().forEach(System.out::println));
    }

    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();
        }
    }

}

运行结果如下:

在这里插入图片描述

可以看出与上面的表格是一样的。

8.内建可查找的依赖

AbstractApplicationContext 内建可查找的依赖

在这里插入图片描述

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

在这里插入图片描述

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

在这里插入图片描述

那么这些类在什么地方注册的呢?具体的如下:

在这里插入图片描述

后面在初始化的流程中,我们会详细的分析,上面的代码是AnnotationConfigUtils类中的

9.依赖查找中的经典异常

BeansException 子类型

在这里插入图片描述

具体的代码如下:

演示NoUniqueBeanDefinitionException异常

package org.learn.spring.dependency.lookup;

import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;

// NoUniqueBeanDefinitionException 的异常举例
public class NoUniqueBeanDefinitionExceptionDemo {

    public static void main(String[] args) {
        // 创建BeanFactory 容器
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();

        // NoUniqueBeanDefinitionExceptionDemo 作为配置类(configuration class)
        applicationContext.register(NoUniqueBeanDefinitionExceptionDemo.class);

        // 启动应用上下文
        applicationContext.refresh();

        try {
            // 由于应用上下文存在两个String类型的Bean,通过单一查找会抛出异常
            applicationContext.getBean(String.class);
        } catch (NoUniqueBeanDefinitionException e) {
            System.err.printf("Spring 应用上下文存在 %d 个 %s 类型的Bean,具体的原因:%s", e.getNumberOfBeansFound(), e.getBeanType(), e.getMessage());
        }

        // 关闭应用上下文
        applicationContext.close();
    }

    @Bean
    public String bean1() {
        return "bean1";
    }

    @Bean
    public String bean2() {
        return "bean2";
    }
}

演示BeanInstantiationException异常

package org.learn.spring.dependency.lookup;

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;

// BeanInstantiationException 异常的示例
public class BeanInstantiationExceptionDemo {

    public static void main(String[] args) {
        // 创建BeanFactory 容器
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();

        // 注册BeanDefinition Bean Class 是CharSequence的接口
        BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(CharSequence.class);
        applicationContext.registerBeanDefinition("errorBean", beanDefinitionBuilder.getBeanDefinition());

        // 启动应用上下文
        applicationContext.refresh();

        // 关闭应用上下文
        applicationContext.close();
    }
}

演示BeanCreationException异常

package org.learn.spring.dependency.lookup;

import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

// BeanCreationException 异常示例
public class BeanCreationExceptionDemo {
    public static void main(String[] args) {
        // 创建BeanFactory 容器
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();

        // 注册BeanDefinition Bean Class 是POJO的普通类,不过在初始化方法回调时抛出异常
        BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(POJO.class);
        applicationContext.registerBeanDefinition("errorBean", beanDefinitionBuilder.getBeanDefinition());

        // 启动应用上下文
        applicationContext.refresh();

        // 关闭应用上下文
        applicationContext.close();
    }

    static class POJO implements InitializingBean {

        @Override
        public void afterPropertiesSet() throws Exception {
            throw new Exception("For purposes....");
        }
    }
}

10.面试题

10.1 ObjectFactory 与 BeanFactory 的区别?

ObjectFactory 与 BeanFactory 均提供依赖查找的能力。

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

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

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

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值