Spring技术内幕笔记之IOC的实现
IOC容器的实现
依赖反转:
依赖对象的获得被反转了,于是依赖反转更名为:依赖注入。许多应用都是由两个或者多个类通过彼此的合作来实现业务逻辑的,这使得每个对象都需要与其合作的对象的引用,如果这个获取过程需要自身实现,那么这将导致代码高度耦合并且难以测试。 ----维基百科
依赖控制反转有很多实现方式。在Spring中,IOC容器是实现这个模式的载体,它可以在对象生成或初始化时直接将数据注入到对象中,也可以通过对象引用注入到对象数据域中的方式来注入对方法调用的依赖。这种依赖注入是可以递归的,对象被逐层注入。?
?
IOC容器系列的设计与实现:BeanFactory 和 ApplicationContext
Spring IOC容器的设计中,主要两个主要的容器系列:
一个是实现BeanFactory接口的简单容器系列,这系列容器只实现了容器的最基本的功能;
另一个是ApplicationContext应用上下文,他作为容器的高级形态而存在。
具体什么是容器呢?它在Spring框架中到底长什么样?
其实对IOC容器的使用者来说,我们经常接触到的BeanFactory 和 ApplicationContext都可以看成是容器具体表现形式。ApplicationContext是BeanFactory的子接口,BeanFactory提供了配置和基本功能,ApplicationContext添加了更多企业特定的功能,ApplicationContext是BeanFactory的完整超集。在Spring中,Spring有各式各样的IOC容器的实现供用户选择和使用。
ApplicationContext接口表示Spring IOC容器,负责实例化、配置和组装bean。容器通过读取配置元数据来获取实例化、配置和组装对象的指令。配置元数据用XML、Java注解或Java代码表示。它允许组成应用程序的对象以及这些对象之间丰富的相互依赖关系。
Spring提供了几个ApplicationContext接口的实现。在独立应用程序中,通常创建ClassPathXmlApplicationContext或FileSystemXmlApplicationContext的实例。虽然XML一直是定义配置元数据的传统格式,但可以通过提供少量XML配置以声明式地启用对这些附加元数据形式的支持,来指示容器使用Java注解或代码作为元数据格式
BeanDefinition
Spring通过BeanDefinition来管理基于Spring的应用中的各种对象以及它们之间的相互依赖关系。BeanDefinition抽象了我们对Bean的定义,是让容器起作用的主要数据类型。IOC容器是用来管理对象依赖关系的,对IOC容器来说,BeanDefinition就是对依赖反转模式中管理的对象依赖关系的数据抽象,也是容器实现依赖反转功能的核心数据结构,依赖反转功能都是围绕对这个BeanDefinition的处理来完成的。
Spring IOC容器的设计
BeanFactory的应用场景
BeanFactory接口定义了IOC容器最基本的形式,并且提供了IOC容器所应该遵守的最基本的服务契约。
BeanFactory主要接口:
public interface BeanFactory {
String FACTORY_BEAN_PREFIX = "&";
Object getBean(String name) throws BeansException;
<T> T getBean(String name, Class<T> requiredType) throws BeansException;
Object getBean(String name, Object... args) throws BeansException;
boolean containsBean(String name);
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
boolean isTypeMatch(String name, Class targetType) throws
NoSuchBeanDefinitionException;
Class getType(String name) throws NoSuchBeanDefinitionException;
String[] getAliases(String name);
}
ApplicationContext的应用场景
ApplicationContext是一个高级形态意义的IOC容器,ApplicationContext在BeanFactory的基础上添加了很多附加功能。
IOC容器的初始化过程
简单来说,IOC容器的初始化是由refresh()方法来启动的。主要包括:
-
resource定位过程 桶装水,先找到水
-
BeanDefinition载入 水处理成合格的水
相当于把定义的BeanDefinition在IOC容器中转化成一个Spring内部表示的数据结构的过程。IOC容器对Bean的管理和依赖注入功能的实现,是通过对其持有的BeanDefinition进行各种相关相关操作来完成的。这些BeanDefinition数据在IOC容器中通过一个HashMap来保持和维护。
refresh()方法执行过程
prepareRefresh(); //这里是在子类中启动refreshBeanFactory()的地方 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. prepareBeanFactory(beanFactory); try { //设置BeanFactoy的后置处理 postProcessBeanFactory(beanFactory); //调用BeanFactory的后处理器,这些后处理器是在Bean定义中向容器注册的 invokeBeanFactoryPostProcessors(beanFactory); //注册Bean的后处理器,在Bean创建过程中调用。 registerBeanPostProcessors(beanFactory); //对上下文中的消息源进行初始化 initMessageSource(); //初始化上下文中的事件机制 initApplicationEventMulticaster(); //初始化其他的特殊Bean onRefresh(); //检查监听Bean并且将这些Bean向容器注册 registerListeners(); //实例化所有的(non-lazy-init)单件 finishBeanFactoryInitialization(beanFactory); //发布容器事件,结束Refresh过程 finishRefresh(); } catch (BeansException ex) { //为防止Bean资源占用,在异常处理中,销毁已经在前面过程中生成的单件Bean destroyBeans(); }
-
向IOC容器注册这些BeanDefinition 将水装入桶中
将BeanDefinition放入值beanDefinitionMap Map中
org.springframework.beans.factory.support.DefaultListableBeanFactory#registerBeanDefinition
总结:IOC容器的初始化过程主要是工作是在IOC容器中建立BeanDefinition数据映射
==注意:Bean定义的载入和依赖注入是两个独立的过程。依赖注入一般发生在应用第一次通过getBean向容器搜索Bean的时候(除了layzinit设置为true,设置了lazyinit属性那么这个Bean的依赖注入在IOC容器初始化时就预先完成了无需等待初始化完成后使用getBean去触发)。==
补充:lazy-init属性的处理,其实是直接采用getBean去触发依赖注入。所以说白了,与正常依赖注入的出发相比,只是触发的时间和场合不同,原理是一样的。
IOC容器的依赖注入
一句话:getBean是依赖注入的起点,之后会调用createBean,createBean不仅生成了Bean,还会对Bean初始化进行处理。比如实现了在BeanDefinition中的init-method属性定义,Bean后置处理器等。
主要涉及到方法:
-
org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean // 获取Bean
-
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean // 创建Bean
-
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBeanInstance // 创建Bean实例
提供了两种实例化Java对象方法,一种通过使用Java反射功能(BeanUtils.instantiateClass),一种是通过CGLIB生成。
public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) { // Don't override the class with CGLIB if no overrides. if (!bd.hasMethodOverrides()) { Constructor<?> constructorToUse; synchronized (bd.constructorArgumentLock) { constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod; if (constructorToUse == null) { final Class<?> clazz = bd.getBeanClass(); if (clazz.isInterface()) { throw new BeanInstantiationException(clazz, "Specified class is an interface"); } try { if (System.getSecurityManager() != null) { constructorToUse = AccessController.doPrivileged( (PrivilegedExceptionAction<Constructor<?>>) clazz::getDeclaredConstructor); } else { constructorToUse = clazz.getDeclaredConstructor(); } bd.resolvedConstructorOrFactoryMethod = constructorToUse; } catch (Throwable ex) { throw new BeanInstantiationException(clazz, "No default constructor found", ex); } } } return BeanUtils.instantiateClass(constructorToUse); } else { // Must generate CGLIB subclass. return instantiateWithMethodInjection(bd, beanName, owner); } }
-
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean // 填充Bean
-
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean(java.lang.String, java.lang.Object, org.springframework.beans.factory.support.RootBeanDefinition) // Bean的初始化方法调用
通过jdk反射机制得到Method对象,然后调用Bean定义中申明的初始化方法
-
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#invokeInitMethods 执行Bean初始化方法
-
如果Bean实现了InitializingBean接口,则先执行afterPropertiesSet()方法
-
执行Bean的init方法(Bean定义的initMethodName)
-
IOC容器中Bean生命周期
- Bean实例的创建
- 为Bean实例设置属性
- 调用Bean的初始化方法
- 应用使用Bean
- Bean销毁
FatoryBean的实现
通过org.springframework.beans.factory.support.FactoryBeanRegistrySupport#doGetObjectFromFactoryBean方法得知,获取Bean核心是factory.getObject(),也就说通过实现FactoryBean接口,开放getOject方法,供使用者自由发挥使用。
private Object doGetObjectFromFactoryBean(FactoryBean<?> factory, String beanName) throws BeanCreationException {
Object object;
try {
if (System.getSecurityManager() != null) {
AccessControlContext acc = getAccessControlContext();
try {
object = AccessController.doPrivileged((PrivilegedExceptionAction<Object>) factory::getObject, acc);
}
catch (PrivilegedActionException pae) {
throw pae.getException();
}
}
else {
object = factory.getObject();
}
}
catch (FactoryBeanNotInitializedException ex) {
throw new BeanCurrentlyInCreationException(beanName, ex.toString());
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "FactoryBean threw exception on object creation", ex);
}
// Do not accept a null value for a FactoryBean that's not fully
// initialized yet: Many FactoryBeans just return null then.
if (object == null) {
if (isSingletonCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(
beanName, "FactoryBean which is currently in creation returned null from getObject");
}
object = new NullBean();
}
return object;
}
可看出此时getBean是通过getObject()方法获取到的Bean
BeanPostProcessor的实现
简称“BPP” ,BPP是使用IOC容器时经常使用到的一个特性,这个Bean的后置处理器是一个监听器,可以监听容器触发的事件。将它向IOC容器注册后,容器中管理的Bean具备了接收IOC容器事件回调的能力。BPP是一个接口,主要只有以下两个方法:
public interface BeanPostProcessor {
/**
* 在Bean的初始化前提供回调入口
*/
@Nullable
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
/**
* 在Bean的初始化后提供回调入口
*/
@Nullable
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean(java.lang.String, java.lang.Object, org.springframework.beans.factory.support.RootBeanDefinition) 初始化Bean
如何与IOC结合?
autowiring(自动依赖装配)的实现
只有在类变量属性为类,此时需要在IOC容器中寻找依赖的对象,由此可推出自动依赖装配的过程是在属性填充过程中即populateBean()方法中实现。
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean
在populateBean方法中可找到autowireByName与autowireByType两个方法,通过类名/类型进行自动装配
autowireByName通过名称自动注入,直接根据属性名getBean()从IOC容器中获取Bean即可。
autowireByType稍微复杂一些,但是最终依然是通过getBean从IOC容器中获取Bean。
参考:
Spring技术内幕:深入解析Spring架构与设计原理(第2版)
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!