Spring IoC 是什么?

本文内容如有错误、不足之处,欢迎技术爱好者们一同探讨,在本文下面讨论区留言,感谢。

文章目录

        • 简介
        • 依赖查找
          • 单一类型依赖查找
          • 集合类型依赖查找
          • 层次性依赖查找
          • 延迟依赖查找
          • 安全依赖查找
          • 内建可查找的依赖
          • 依赖查找中的经典异常
        • 结论
        • 参考资料

简介

IoC 全称为 Inversion of Control,译为 “控制反转”。

Spring 指的是控制反转,IOC 容器负责实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。交由 Spring 容器统一进行管理,从而实现松耦合。

依赖查找

Java SE中的依赖查找:

JNDI Context 类:

public interface Context {
	public Object lookup(Name name) throws NamingException;

    public Object lookup(String name) throws NamingException;
...
}

Context 类通过 lookup 方法进行依赖查找。

Java Beans BeanContext类:

public interface BeanContext extends BeanContextChild, Collection, DesignMode, Visibility {
...
}

BeanContext 继承了 Collection 集合类,可以对集合进行查找。

下面介绍 Spring 中的依赖查找

单一类型依赖查找

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

  1. 根据 Bean 名称查找
    • getBean(String)
    • getBean(String,Object…)
  2. 根据 Bean 类型查找
    • getBean(Class)
    • getBean(Class,Object…)
  3. Bean 延迟查找
    • getBeanProvider(Class)
    • getBeanProvider(ResolvableType)
  4. 根据 Bean 名称 + 类型查找
    • getBean(String,Class)

延迟查找示例:
getBeanProvider(Class)

public class ObjectProviderDemo {

    public static void main(String[] args) {
        // 创建 ApplicationContext 容器
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        // 注册 ObjectProvider 配置类
        applicationContext.register(ObjectProviderDemo.class);

        // 启动 ApplicationContext 容器
        applicationContext.refresh();

        // 依赖查找
        lookupByObjectProvider(applicationContext);

        // 关闭 容器
        applicationContext.close();
    }

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

    private static void lookupByObjectProvider(AnnotationConfigApplicationContext applicationContext) {
        ObjectProvider<String> objectProvider = applicationContext.getBeanProvider(String.class);
        System.out.println(objectProvider.getObject());
    }
}
集合类型依赖查找

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

  1. 根据 Bean 类型查找
    • 获取同类型 Bean 名称列表
      • getBeanNamesForType(Class)
      • Spring 4.2 getBeanNamesForType(ResolvableType)
    • 获取同类型 Bean 实例列表
      • getBeansOfType(Class) 以及重载方法
  2. 通过注解类型查找
    • 获取标注类型 Bean 名称列表
      • getBeanNamesForAnnotation(Class)
    • 获取标注类型 Bean 实例列表
      • getBeansWithAnnotation(Class)
    • 获取指定名称 + 标注类型 Bean 实例
      • findAnnotationOnBean(String,Class)
        集合类型查找的核心类:ListableBeanFactory,通过单词的意思可以看出来,Listable 是有列表功能的,含义是在查找 Bean 的时候可以运用列表的功能。
        首先第一个能力是通过 Bean 类型查找,主要有两个,通过 Bean 名称列表和 Bean 实例列表,Bean 的名称由于你可以新定义,所以这个时候不需要我们把 Bean 进行初始化,Bean 的实例需要进行初始化,这里的实例是一个非延迟的实例,或者是单体。
        第二个能力是根据注解去查找,标注过这个类型的 Bean 名称以及 Bean 的实例,比根据 Bean 类型查找多了一个方法,根据注解类型和标注的 Bean 实例名称去查找。

ListableBeanFactory 源码,继承 BeanFactory,可以通过单一(BeanFactory)或者复杂获得复杂(ListableBeanFactory)或者复合类型,并对其(BeanFactory) 做一些扩展,

public interface ListableBeanFactory extends BeanFactory {

	/**
	* 是否包含指定的 beanName 的BeanDefinition
	**/
	boolean containsBeanDefinition(String beanName);

	/**
	* BeanDefinition 个数
	**/
	int getBeanDefinitionCount();
	
	/**
	* BeanDefinition 名称数组
	**/
	String[] getBeanDefinitionNames();
	
	/**
	* BeanDefinition 根据类型查找 名称数组
	**/
	String[] getBeanNamesForType(ResolvableType type);

	/**
	* BeanDefinition 根据类型查找 名称数组
	**/
	String[] getBeanNamesForType(@Nullable Class<?> type);
	
	String[] getBeanNamesForType(@Nullable Class<?> type, boolean includeNonSingletons, boolean allowEagerInit);

	<T> Map<String, T> getBeansOfType(@Nullable Class<T> type) throws BeansException;

	<T> Map<String, T> getBeansOfType(@Nullable Class<T> type, boolean includeNonSingletons, boolean allowEagerInit)
			throws BeansException;

	String[] getBeanNamesForAnnotation(Class<? extends Annotation> annotationType);

	Map<String, Object> getBeansWithAnnotation(Class<? extends Annotation> annotationType) throws BeansException;

	@Nullable
	<A extends Annotation> A findAnnotationOnBean(String beanName, Class<A> annotationType)
			throws NoSuchBeanDefinitionException;

}

ResolvableType 类主要是用于泛型上面的,相当于另外一种 Class 的实现。

package org.springframework.core;

public class ResolvableType implements Serializable {
...
}

ListableBeanFactory 类中有两种方式查找,通过 Bean 的名称类别和通过 Bean 的实例列表。

查找 Bean 的名称:

Return the names of beans matching the given type (including subclasses), judging from either bean definitions or the value of {@code getObjectType} in the case of FactoryBeans.

这里会判断 BeanDefinition 是否已经被定义,通过 BeanDefinition#getBeanClassName(),返回 Bean Class 的名称,因此在 ListableBeanFactory#getBeanNamesForType() 会根据 BeanDefinition#getBeanClassName() 返回的结果进行判断,会调用 Class#isAssignableFrom() 方法进行判断,同时还通过 FactoryBean#getObjectType() 进行类型匹配,这个时候比较是比较我们的一个定义,以及 BeanFactory 里面的 getType 类型的一个匹配情况,因此并没有涉及到 Bean 的初始化的过程。

查找 Bean 的实例:

Return the bean instances that match the given object type (including subclasses), judging from either bean definitions or the value of
{@code getObjectType} in the case of FactoryBeans.

ListableBeanFactory#getBeansForType() 将会触发 Bean 的初始化,可能会提前把一些你的类进行初始化,初始化以后,会导致一些 Bean 初始化不完全,这个时候会导致位置的错误。
因此在对 Bean 是否存在进行判断,首先通过名称进行判断,然后根据实例判断。可以避免提前初始化 Bean,造成错误。
这里有个基本的特点,依赖查找是等待容器启动完成后或者加载完成之后,进行的初始化操作,BeanDefinition 是在 Bean 定义的注册阶段,就是注册完成的阶段就完成。

层次性依赖查找

层次性依赖查找接口 : HierarchicalBeanFactory
- 双亲 BeanFactory:getParentBeanFactory()

  • 层次性查找
    • 根据 Bean 名称查找
    • 基于 containsLocalBean 方法实现
    • 根据 Bean 类型查找实例列表
    • 单一类型
      • BeanFactoryUtils#beanOfType
    • 集合类型
      • BeanFactoryUtils#beansOfTypeIncludingAncestors
    • 根据 Java 注解查找名称列表
      • BeanFactoryUtils#beanNamesForTypeIncludingAncestors
        将单一类型和集合类型合并查找,就是层次性查找,可以看出层次性查找,和 Java 里面的 ClassLoader 里面的双亲委派非常相似,这个 Bean 可以通过层层递进的方式进行查找,核心是 BeanFactoryUtils 这个工具类,有单一类型和集合类型分别进行查找,还有根据 Java 注解的方式返回 Bean 的名称列表。
public interface HierarchicalBeanFactory extends BeanFactory {

	/**
	 * 获取双亲的 BeanFactory
	 */
	@Nullable
	BeanFactory getParentBeanFactory();

	/**
	* 判断是否存在 Bean 
	*/
	boolean containsLocalBean(String name);

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

ConfigurableListableBeanFactory 可配置可罗列可列表的 BeanFactory

可以通过 applicationContext 获取 BeanFactoryparentBeanFactory:

// 创建 BeanFactory 容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
// 注册 Configuration 类
applicationContext.register(HierarchicalDependencyDemo.class);
// 启动 applicationContext
applicationContext.refresh();

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

递归依赖查找是否存在指定 beanNameBeanFactory

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

下面介绍 BeanFactoryUtils 工具类的使用:
重点介绍一下 beansOfTypeIncludingAncestors 方法的逻辑,这里的入参是 ListableBeanFactory ,通过 lbf instanceof HierarchicalBeanFactory 判断 lbf 是否是有层次性的 BeanFactory , 然后进行递归,一直到找到它为止。!hbf.containsLocalBean(beanName)这里做了一个判断,如果有相同的 BeanName ,将排除子层里面的 BeanFactory

public static <T> Map<String, T> beansOfTypeIncludingAncestors(ListableBeanFactory lbf, Class<T> type)
			throws BeansException {

Assert.notNull(lbf, "ListableBeanFactory must not be null");
	Map<String, T> result = new LinkedHashMap<>(4);
	result.putAll(lbf.getBeansOfType(type));
	if (lbf instanceof HierarchicalBeanFactory) {
		HierarchicalBeanFactory hbf = (HierarchicalBeanFactory) lbf;
		if (hbf.getParentBeanFactory() instanceof ListableBeanFactory) {
			Map<String, T> parentResult = beansOfTypeIncludingAncestors(
					(ListableBeanFactory) hbf.getParentBeanFactory(), type);
			parentResult.forEach((beanName, beanInstance) -> {
				if (!result.containsKey(beanName) && !hbf.containsLocalBean(beanName)) {
					result.put(beanName, beanInstance);
				}
			});
		}
	}
	return result;
}

HierarchicalBeanFactory 能够提供层次性的依赖查找,其中有个方法比较重要,就是 getParentBeanFactory(),与之对应的是其子接口提供的方法 ConfigurableBeanFactory#setParentBeanFactory(),一个读一个写。因此可以通过层次性的方面去找整个上下文里面是否存在相应的 Bean

延迟依赖查找
  1. Bean 延迟依赖查找接口
    • ObjectFactory
    • ObjectProvider
  2. Java 8 特性扩展
    • 函数式接口
      • getIfAvailable(Supplier)
      • ifAvailable(Consumer)
      • Stream 扩展 - stream()
        ObjectFactory 的延迟查找是通过 getObject() 方法来返回所关联的一个 BeanObjectProvider 继承于 ObjectFactory 这么一个接口来实现的,因此 ObjectProvider 里面可以调用或者说继承 getObject() 这个方法来获取当前我关联的这个 BeanSpring 5ObjectProvider 做了一些关于函数式接口的扩展,通过 Supplier 或者 Consumer 来进行消费或者提供相应的数据源,通过这个接口可以明显的感受到这种函数的应用,还有一个是关于 Stream 的扩展。

ObjectProviderSpring 中会经常被使用,例如:有些类或者 Bean 没有被完全加载完之后可以通过在某些阶段来进行加载,和 BeanFactory 延迟加载类似,这里不需要 Bean 去定义 LazyInitization 等于 True,相当于程序的方式来帮助我们去做这个事情,

ObjectProvider

public interface ObjectProvider<T> extends ObjectFactory<T>, Iterable<T> {

	/**
	 * 获取 Object  
	 * 入参可以复习 类似于 BeanFactory 的 getBean() 方法
	 */
	T getObject(Object... args) throws BeansException;

	/**
	 * 如果存在返回,不存在返回空
	 */
	@Nullable
	T getIfAvailable() throws BeansException;

	/**
	 * 提供的一个默认的方法,和上面方法不同在于,不存在不返回空,而是根据 defaultSupplier 中的计算方式返回
	 * 比如:你想初始化一个Bean ,但是这个Bean 不存在,可以提供新的方式进行初始化。
	 */
	default T getIfAvailable(Supplier<T> defaultSupplier) throws BeansException {
		T dependency = getIfAvailable();
		return (dependency != null ? dependency : defaultSupplier.get());
	}

	/**
	 * 通过 Consumer 进行消费
	 */
	default void ifAvailable(Consumer<T> dependencyConsumer) throws BeansException {
		T dependency = getIfAvailable();
		if (dependency != null) {
			dependencyConsumer.accept(dependency);
		}
	}
	@Nullable
	T getIfUnique() throws BeansException;
...
}

BeanFactory

public interface BeanFactory {
	Object getBean(String name, Object... args) throws BeansException;
...
}

ObjectProviderDemo 例子

public class ObjectProviderDemo {

    public static void main(String[] args) {
        // 创建 ApplicationContext 容器
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        // 注册 ObjectProvider 配置类
        applicationContext.register(ObjectProviderDemo.class);

        // 启动 ApplicationContext 容器
        applicationContext.refresh();

        // 依赖查找
        lookupIfAvaiable(applicationContext);

        // 关闭 容器
        applicationContext.close();
    }

    public static void lookupIfAvaiable(AnnotationConfigApplicationContext applicationContext){
        ObjectProvider<User> userObjectProvider = applicationContext.getBeanProvider(User.class);
        // 通过 lamba 进行方法的引用
        User u = userObjectProvider.getIfAvailable(User::createUser());
    }
}
安全依赖查找

依赖查找安全性对比

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

关于单一类型查找,BeanFactoryObjectFactory 是非安全的,只有 ObjectProvider是安全的。
集合类型查找,ListableBeanFactroyObjectProvider 都是安全的。

TypeSafetyDependencyLookupDemo 代码示例:

/**
 * 类型安全 依赖查找示例
 */
public class TypeSafetyDependencyLookupDemo {
    public static void main(String[] args) {
        // 创建 ApplicationContext 容器
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        // 注册 TypeSafetyDependencyLookupDemo 配置类
        applicationContext.register(TypeSafetyDependencyLookupDemo.class);

        // 启动 ApplicationContext 容器
        applicationContext.refresh();

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

        displayObjectFactoryGetObject(applicationContext);
        // 关闭 容器
        applicationContext.close();
    }

    private static void displayObjectFactoryGetBean(AnnotationConfigApplicationContext applicationContext) {
        ObjectFactory<User> objectFactory = applicationContext.getBeanProvider(User.class);
        printBeansException(()->objectProvider.getObject());
    }

    private static void displayBeanFactoryGetBean(AnnotationConfigApplicationContext applicationContext) {
        printBeansException(()->applicationContext.getBean(User.class));
    }

    private static void printBeansException(Runnable runnable){
        try {
            runnable.run();
            // 由于 BeansException 是 RuntimeException 因此不会被 Runnable.run() 捕获到
        } catch (BeansException exception) {
            // 线程安全的,生产上不要使用,容易发生阻塞
            exception.printStackTrace();
        }
    }
}

示例代码将会抛出 NoSuchBeanDefinitionException 异常。
因此可以确定 BeanFactory#getBean()ObjectProvider#getObject() 是不安全的。

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

内建可查找的依赖

Spring 应用中有一个抽象类 AbstractApplicationContext ,这个抽象类是所有应用上下文的一个基类,基本上所有应用上下文的实现,比如说包括注解的 AnnoationConfiguration,以及 Web 的实现都是基于这个抽象类来进行实现的,因此这个抽象类可以在构建的过程中,也就是在上下文启动的过程中,初始化一些相关的内部的一些依赖,这些内部的依赖我们称之为内建的可查询依赖。

依赖查找中的经典异常

BeansException 子类型

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

其实 IoC 对编程带来的最大改变不是从代码上,而是从思想上,发生了“主从换位”的变化。应用程序原本是老大,要获取什么资源都是主动出击,但是在 IoC/DI 思想中,应用程序就变成被动的了,被动的等待 IoC 容器来创建并注入它所需要的资源了。

IoC 很好的体现了面向对象设计法则之一—— 好莱坞法则:“别找我们,我们找你”;即由 IoC 容器帮对象找相应的依赖对象并注入,而不是由对象主动去找。

参考资料

什么是IOC(控制反转)、DI(依赖注入)

  • https://blog.csdn.net/qq_42709262/article/details/81951402

浅谈 IOC 什么是 IOC?

  • https://blog.csdn.net/qq_36537546/article/details/90599137

你可能感兴趣的:(Spring,spring,ioc,java)