夯实Spring系列|第七章:IoC 依赖查找(专题)

夯实Spring系列|第七章:IoC 依赖查找(专题)

本章说明

通过 第二章:IoC 依赖查找 ,我们已经简单了解到 Spring 中依赖查找的基本使用;本章会更加全面和具体的讨论 Spring IoC 依赖查找,并结合少量的源码分析。

1.项目环境

2.依赖查找的前世今生

在 Spring 之前,其实 JavaBeans 和 JNDI 也有相关的实现

单一类型依赖查找

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

集合类型依赖查找

  • java.beans.beancontext.BeanContext

层次性依赖查找

  • java.beans.beancontext.BeanContext

具体的源码我们就不在此进行展开,Spring 中实现也是借鉴的他们的设计,而且比他们做的更好,我们将学习重点放在下面 Spring 中即可。

3.单一类型依赖查找

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

  • 根据 Bean 名称查找
    • getBean(String)
    • Spring 2.5- getBean(String name, Object… args) 覆盖默认参数
  • 根据 Bean 类型查找
    • Bean 实时查找
      • Spring 3.0 - getBean(Class requiredType)
      • Spring 4.1 - getBean(Class requiredType, Object… args) 覆盖默认参数
    • Spring 5.1 Bean 延迟查找
      • getBeanProvider(Class requiredType)
      • getBeanProvider(ResolvableType requiredType)
  • 根据 Bean 名称 + 类型 查找:getBean(String name, Class requiredType)

覆盖参数是什么意思呢?

比如 getBean() 获取这个 Bean 对象,通过覆盖参数的形式可以在获取对象的同时覆盖掉对象的参数,建议大家不要去使用这种方式,比较危险。

如果 getBean() 返回的对象是一个单例对象,然后每调用一次,都会去覆盖,这种方式非常不可取,获得的对象的状态不可控。

3.1 根据 Bean 名称查找

第二章:IoC 依赖查找 4.3~4.4 小结

3.2 根据 Bean 类型查找

第二章:IoC 依赖查找 4.5~4.6 小结

3.3 根据 Bean 名称 + 类型 查找

第二章:IoC 依赖查找 4.7 小结

3.4 Spring 5.1 Bean 延迟查找

在第二章里面演示了 ObjectFactory 来进行延迟查找,我们这里用 Spring 5.1 里面的提供新 API

ObjectProvider来演示。

注:示例中注释掉代码中是另一种写法

/**
 * 通过{@link org.springframework.beans.factory.ObjectProvider} 进行依赖查找
 */
public class ObjectProviderDemo {
    public static void main(String[] args) {
        // 创建 BeanFactory 容器
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        // 将当前类作为配置类{Configuration.class}
        applicationContext.register(ObjectProviderDemo.class);
        //启动应用上下文
        applicationContext.refresh();
        lookupByObjectProvider(applicationContext);
        applicationContext.close();
    }

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

    @Bean
    //如果没有定义name 那么方法名就是 Bean 的名称
    public String helloWorld() {
        return "Hello,World";
    }

}

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

相关示例

第二章:IoC 依赖查找 4.6、4.8 小结

5.层次性依赖查找

层次性依赖查找接口 - HierarchicalBeanFactory

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

5.1 源码分析

我们先看一个相关的类图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fvxnoZvW-1587437109660)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200420211007120.png)]

结合部分源码

HierarchicalBeanFactory#getParentBeanFactory() 方法表明了他具有层次性,其实是一种双亲委派模式的实现。

ConfigurableBeanFactory 继承了 HierarchicalBeanFactory,从而也具有了层次性

public interface ConfigurableBeanFactory extends HierarchicalBeanFactory, SingletonBeanRegistry {...

ConfigurableListableBeanFactory 又继承了 ConfigurableBeanFactory,通过这种组合的方式使得 ConfigurableListableBeanFactory 具有了可配置性(Configurable)、集合遍历性(Listable)、层次性(Hierarchical)等等,下面我们用这个层次性实现类来演示相关代码。

public interface ConfigurableListableBeanFactory
		extends ListableBeanFactory, AutowireCapableBeanFactory, ConfigurableBeanFactory {...

5.2 ParentBeanFactory 示例

设置当前 BeanFactory 的 Parent BeanFactory

/**
 * 层次性的依赖查找示例
 */
public class HierarchicalDependencyLookupDemo {

    public static void main(String[] args) {
        // 创建 BeanFactory 容器
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        // 将当前类作为配置类{Configuration.class}
        applicationContext.register(HierarchicalDependencyLookupDemo.class);

        //1.获取 HierarchicalBeanFactory <- ConfigurableBeanFatory <- ConfigurableListableBeanFactory
        ConfigurableListableBeanFactory beanFactory = applicationContext.getBeanFactory();
        System.out.println("当前 BeanFactory 的 Parent BeanFactory" + beanFactory.getParentBeanFactory());
        //设置 Parent BeanFactory
        beanFactory.setParentBeanFactory(createParentBeanFactory());
        System.out.println("当前 BeanFactory 的 Parent BeanFactory" + beanFactory.getParentBeanFactory());
        
        //启动应用上下文
        applicationContext.refresh();
        //关闭
        applicationContext.close();
    }

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

执行结果

当前 BeanFactory 的 Parent BeanFactory:null
当前 BeanFactory 的 Parent BeanFactory:org.springframework.beans.factory.support.DefaultListableBeanFactory@3427b02d: defining beans [user,superUser,objectFactory,userRepository]; root of factory hierarchy

第一次输出的结果是 null ,因为我们还没有设置过当前 BeanFactory 的 Parent BeanFactory。

我们创建一个新的 BeanFactory,通过 beanFactory.setParentBeanFactory()方法进行设置。

第二次输出的就是我们设置的 BeanFactory。

5.3 LocalBean 示例

判断当前 BeanFactory 是否包含这个 Bean

在上述例子中增加这个方法

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

main()方法中调用displayLocalBean(beanFactory, "user");

执行结果

当前 BeanFactory [org.springframework.beans.factory.support.DefaultListableBeanFactory@32eff876: 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@3427b02d] 是否包含 bean[name:org.springframework.beans.factory.support.DefaultListableBeanFactory@32eff876: 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@3427b02d] 
 结果: false

当然不存在 user 这个 Bean。因为这个 Bean 存在父 BeanFactory 中

main()方法中调用displayLocalBean((HierarchicalBeanFactory) beanFactory.getParentBeanFactory(), "user");

结果为 true;为了更优雅的调用,我们可以采用递归的方式进行查询。

    private static void displayContainsBean(HierarchicalBeanFactory beanFactory, String beanName) {
        System.out.printf("当前 BeanFactory [%s] 是否包含 bean[name:%s] \n 结果: %s\n", beanFactory, beanFactory,
                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);
    }

5.4 BeanFactoryUtils

Spring 中也有类似的实现,也是通过递归的方式。

org.springframework.beans.factory.BeanFactoryUtils#beanNamesForTypeIncludingAncestors()

	public static String[] beanNamesForTypeIncludingAncestors(ListableBeanFactory lbf, ResolvableType type) {
		Assert.notNull(lbf, "ListableBeanFactory must not be null");
		String[] result = lbf.getBeanNamesForType(type);
		if (lbf instanceof HierarchicalBeanFactory) {
			HierarchicalBeanFactory hbf = (HierarchicalBeanFactory) lbf;
			if (hbf.getParentBeanFactory() instanceof ListableBeanFactory) {
				String[] parentResult = beanNamesForTypeIncludingAncestors(
						(ListableBeanFactory) hbf.getParentBeanFactory(), type);
				result = mergeNamesWithParent(result, parentResult, hbf);
			}
		}
		return result;
	}

6.延迟依赖查找

Bean 延迟依赖查找接口

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

6.1 getIfAvailable

我们先在代码中通过注解定义一个 User

    @Bean
    public User user() {
        return User.createUser("ifAvailable-user");
    }

下面方法中User::createUser 只是提供兜底实现(当获取的对象为空时),也可以不写。

    private static void lookupGetIfAvailable(AnnotationConfigApplicationContext applicationContext) {
        ObjectProvider<User> beanProvider = applicationContext.getBeanProvider(User.class);
//        User ifAvailable = beanProvider.getIfAvailable(()->User.createUser());
        User ifAvailable = beanProvider.getIfAvailable(User::createUser);
        System.out.println(ifAvailable);
    }

6.2 ifAvailable

通过 Consumer 的方式消费掉,我们这里直接打印

    private static void lookupIfAvailable(AnnotationConfigApplicationContext applicationContext) {
        ObjectProvider<User> beanProvider = applicationContext.getBeanProvider(User.class);
        beanProvider.ifAvailable(System.out::println);
    }

6.3 Stream 扩展

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

7.安全依赖查找

依赖查找安全性对比

依赖查找类型代表实现是否安全
单一类型查找BeanFactory#getBean
ObjectFactroy#getObject
ObjectProvider#getIfAvailable
集合类型查找ListableBeanFactory#getBeansOfType
ObjectProvider#stream

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

完整示例 TypeSafetyDependencyLookupDemo

/**
 * 类型安全 依赖查找示例
 */
public class TypeSafetyDependencyLookupDemo {
    public static void main(String[] args) {
        // 创建 BeanFactory 容器
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        // 将当前类作为配置类{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();
    }

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

    private static void displayListableBeanFactoryGetBeansOfType(ListableBeanFactory applicationContext) {
        printBeansException("displayListableBeanFactoryGetBeansOfType",()->{
            applicationContext.getBeansOfType(User.class);
        });
    }

    private static void displayObjectProviderIfAvailable(AnnotationConfigApplicationContext applicationContext) {
        printBeansException("displayObjectProviderIfAvailable", () -> {
            ObjectProvider<User> beanProvider = applicationContext.getBeanProvider(User.class);
            System.out.println(beanProvider.getIfAvailable(User::createUser));
        });
    }

    private static void displayObjectFactoryGetObject(AnnotationConfigApplicationContext applicationContext) {
        printBeansException("displayObjectFactoryGetObject", () -> {
            ObjectFactory<User> beanProvider = applicationContext.getBeanProvider(User.class);
            beanProvider.getObject();
        });
    }

    private static void displayBeanFactoryGetBean(BeanFactory beanFactory) {
        printBeansException("displayBeanFactoryGetBean", () -> beanFactory.getBean(User.class));
    }

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

7.1 BeanFactory#getBean(不安全)

如果上下文中没有 User 这个Bean类型的定义,会抛出NoSuchBeanDefinitionException

输出结果:

Source from : displayBeanFactoryGetBean
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.huajie.thinking.in.spring.ioc.overview.domain.User' available
	...

7.2 ObjectFactroy#getObject(不安全)

如果上下文中没有 User 这个Bean类型的定义,会抛出NoSuchBeanDefinitionException

输出结果:

Source from : displayObjectFactoryGetObject
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.huajie.thinking.in.spring.ioc.overview.domain.User' available
    ...

7.3 ObjectProvider#getIfAvailable(安全)

即使如果上下文中没有 User 这个Bean类型的定义,也不会报错

输出结果:

Source from : displayObjectProviderIfAvailable

7.4 ListableBeanFactory#getBeansOfType(安全)

即使如果上下文中没有 User 这个Bean类型的定义,也不会报错

输出结果:

Source from : displayListableBeanFactoryGetBeansOfType

7.5 ObjectProvider#stream(安全)

即使如果上下文中没有 User 这个Bean类型的定义,也不会报错

输出结果:

Source from : displayObjectProviderStreamOps

8.内建可查找的依赖

本章只是做介绍,后续的章节会做详细的讨论。

AbstractApplicationContext 内建可查找的依赖

Bean 名称Bean 实例使用场景
environmentEnvironment 对象外部化配置以及 Profiles
systemPropertiesjava.util.Properties 对象Java 系统属性
systemEnvironmentjava.util.Map 对象操作系统环境变量
messageSourceMessageSource 对象国际化文案
lifecycleProcessorLifecycleProcessor 对象Lifecycle Bean 处理器
applicationEventMulticasterApplicationEventMulticaster 对象Spring 事件广播

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

相关细节:

org.springframework.context.annotation.AnnotationConfigUtils

  • registerAnnotationConfigProcessors()
Bean 名称Bean 实例使用场景
org.springframework.context.annotation. internalConfigurationAnnotationProcessorConfigurationClassPostProcessor 对象处理Spring配置类
org.springframework.context.annotation. internalAutowiredAnnotationProcessorAutowiredAnnotationBeanPostProcessor对象处理 @Autowired 以及 @Value 注解
org.springframework.context.annotation. internalCommonAnnotationProcessorCommonAnnotationBeanPostProcessor对象(条件激活) 处理 JSR - 250 注解,比如 @PostConstruct
org.springframework.context.event. internalEventListenerProcessorEventListenerMethodProcessor对象处理标准 @EventListener 的 Spring 事件监听方法
org.springframework.context.event. internalEventListenerFactoryDefaultEventlistenerFactory 对象@EvenListener 事件监听方法适配为 ApplicationListener
org.springframework.context.annotation. internalPersistenceAnnotationProcessorPersistenceAnnotationBeanPostProcessor 对象(条件激活)处理 JPA 注解场景

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

BeansException 子类型

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

9.1 NoUniqueBeanDefinitionException

当前上下文中存在多个相同类型 Bean 的定义

/**
 * {@link NoUniqueBeanDefinitionException}示例
 */
public class NoUniqueBeanDefinitionExceptionDemo {
    public static void main(String[] args) {
        // 创建 BeanFactory 容器
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        // 将当前类作为配置类{Configuration.class}
        applicationContext.register(NoUniqueBeanDefinitionExceptionDemo.class);
        //启动应用上下文
        applicationContext.refresh();
        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
    private String bean1() {
        return "1";
    }

    @Bean
    private String bean2() {
        return "2";
    }

    @Bean
    private String bean3() {
        return "3";
    }
}

9.2 BeanInstantiationException

当 Bean 所对应的类型非具体类时,比如是一个接口CharSequence

/**
 * {@link BeanInstantiationException}示例
 */
public class BeanInstantiationExceptionDemo {
    public static void main(String[] args) {
        // 创建 BeanFactory 容器
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();

        BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(CharSequence.class);

        applicationContext.registerBeanDefinition("exception-bean", beanDefinitionBuilder.getBeanDefinition());

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

        applicationContext.close();
    }

}

9.4 BeanCreationException

Bean 创建过程中发生异常

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

        //在初始化的时候,抛出异常
        BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(POJO.class);

        applicationContext.registerBeanDefinition("exception-bean", beanDefinitionBuilder.getBeanDefinition());

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

        applicationContext.close();
    }

    static class POJO implements InitializingBean{

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

}

10.面试题

1.ObjectFactory 与 BeanFactory 区别?

两者都是提供依赖查找的能力,ObjectFactory 是 Spring 早期的接口。

ObjectFactory 仅关注一个或者一种类型的 Bean 依赖查找,并且自身不具备依赖查找的能力,能力则由 BeanFactory 输出,例如 ObjectFactoryCreatingFactoryBean 类 通过 setTargetBeanName 方法设置 beanName 再通过 getObject 方法,实际上还是通过 BeanFactory 来查找,通过这种方式实现一个延迟查找、间接查找。

public Object getObject() throws BeansException {
   return this.beanFactory.getBean(this.targetBeanName);
}

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

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

是线程安全,操作过程中会增加互斥锁。

3.Spring 依赖查找与注入在来源上的区别?

这个问题后续章节会做解答,下一章将会讨论依赖注入相关的内容。

11.参考

  • 极客时间-小马哥《小马哥讲Spring核心编程思想》
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值