本文内容如有错误、不足之处,欢迎技术爱好者们一同探讨,在本文下面讨论区留言,感谢。
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
延迟查找示例:
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
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()
public interface HierarchicalBeanFactory extends BeanFactory {
/**
* 获取双亲的 BeanFactory
*/
@Nullable
BeanFactory getParentBeanFactory();
/**
* 判断是否存在 Bean
*/
boolean containsLocalBean(String name);
}
public interface ConfigurableListableBeanFactory
extends ListableBeanFactory, AutowireCapableBeanFactory, ConfigurableBeanFactory {
...
}
ConfigurableListableBeanFactory 可配置可罗列可列表的 BeanFactory 。
可以通过 applicationContext 获取 BeanFactory 的 parent 的 BeanFactory:
// 创建 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());
递归依赖查找是否存在指定 beanName 的 BeanFactory
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。
ObjectProvider 在 Spring 中会经常被使用,例如:有些类或者 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 | 是 |
关于单一类型查找,BeanFactory 和 ObjectFactory 是非安全的,只有 ObjectProvider是安全的。
集合类型查找,ListableBeanFactroy 和 ObjectProvider 都是安全的。
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 ,这个抽象类是所有应用上下文的一个基类,基本上所有应用上下文的实现,比如说包括注解的 Annoation,Configuration,以及 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(依赖注入)
浅谈 IOC 什么是 IOC?