Spring IoC总结
Spring 复习
1.Spring IoC
1.1 基本概念
1.1.1 DIP(Dependency Inversion Principle)
字面意思依赖反转原则,即调用某个类的构造器创建对象时,其构造器的参数不应包含相关基层类的属性,只应传入相关基层类实例,也即将一个封装好的实例传给本类,也就是让相关基层类依赖更高层的类(不是指继承关系)
1.1.2 IoC(Inversion of Control)
字面意思为控制反转,其是DIP的一种具体策略,其目的是为了将对象的初始化和维护交由第三方(如Spring IoC容器)管理,当用户想要获取某个实例时,只需要提供实例的名称即可获取
1.1.3 DI(Dependency Injection)
字面意思为依赖注入,根据DIP,由于构造器传参时都是传入封装好的基层类实例,那么如何将这些实例赋值给(注入)当前类的属性中呢?那么就需要依赖注入,其执行策略如下
- 检查当前类的属性那些需要注入对象,如有a,b,c
- 对a,b,c对象从属的类分别执行上一步,也即分析其中是否有需要注入的其他对象
- 重复类似上一步的过程,直到没有需要注入的对象
- 开始反向遍历(类似于DFS的回流),按照先new对象,后注入到上一步的原则执行,直到创建出最初要创建的实例
由此可见,DI是一种IoC的实现方法
1.2 Spring IoC使用
两种方式均为先获取IoC容器,之后调用getBean方法传入bean的名称来获取对应的bean
1.2.1 基于XML
创建FileSystemXmlApplicationContext/ClassPathXmlApplicationContext对象,并调用getBean
1.2.2 基于注解
创建ClassPathXmlApplicationContext对象,并调用getBean
1.3 Spring IoC原理
bean的生命周期概览
- BeanFactory的创建
- 创建IoC容器(
obtainFreshBeanFactory()
)
- 创建IoC容器(
- Bean的创建
- 加载配置文件中 Spring Bean 的定义,注册为一个个BeanDefinitions(
obtainFreshBeanFactory()
) - 设置BeanFactory的类加载器,添加一些BeanPostProcessor(
prepareBeanFactory(beanFactory)
) - 如果有任何
BeanFactoryPostProcessor
的实现类,则执行postProcessBeanFactory
(invokeBeanFactoryPostProcessors(beanFactory)
) - 注册一些BeanPostProcessor的实现类(
registerBeanPostProcessors(beanFactory)
) finishBeanFactoryInitialization(beanFactory)
中的beanFactory.preInstantiateSingletons()
负责实例化(创建+初始化)所有非懒加载的singletons- Bean 容器利用 Java Reflection API 创建一个Bean的实例(仅仅是实例化)
- 如果涉及到一些属性值 利用
set()
方法设置一些属性值 - 如果 Bean 实现了
BeanNameAware
接口,调用setBeanName()
方法,传入Bean的名字 - 如果 Bean 实现了
BeanClassLoaderAware
接口,调用setBeanClassLoader()
方法,传入ClassLoader
对象的实例 - 与上面的类似,如果实现了其他
*.Aware
接口,就调用相应的方法 - 如果有和加载这个 Bean 的 Spring 容器相关的
BeanPostProcessor
对象,执行postProcessBeforeInitialization()
方法 - 如果Bean实现了
InitializingBean
接口,执行afterPropertiesSet()
方法 - 如果 Bean 在配置文件中的定义包含 init-method 属性(@PostConstruct),执行指定的方法
- 如果有和加载这个 Bean的 Spring 容器相关的
BeanPostProcessor
对象,执行postProcessAfterInitialization()
方法 - 将创建好的Bean以
(beanName,singletonObject)
放入IoC容器中,也即DefaultSingletonBeanRegistry的singletonObjects对象(ConcurrentHashMap)中
- 加载配置文件中 Spring Bean 的定义,注册为一个个BeanDefinitions(
- Bean的销毁
- 如果Bean实现了
DestructionAwareBeanPostProcessors
接口,执行postProcessBeforeDestruction
- 如果 Bean 实现了
DisposableBean
接口,执行destroy()
方法 - 如果 Bean 在配置文件中的定义包含 destroy-method (@PreDestroy)属性,执行指定的方法
- 如果Bean实现了
上面步骤借鉴自JavaGuide (gitee.io)
1.3.1 IoC容器分类
其中可见BeanFactory为所有IoC容器的顶级接口,其中给出了许多getBean
的重载方法,其中要注意到一个接口
AutowireCapableBeanFactory
,这个接口意为实现此类的工厂有自动注入的功能,其常用实现类DefaultListableBeanFactory
就实现了此接口,还额外实现了ListableBeanFactory和HierarchicalBeanFactory接口,是功能最全面的BeanFactory
为了让ClassPathXmlApplicationContext等容器也能具有自动依赖注入的功能其父类提供了一个DefaultListableBeanFactory
的实例,实际执行时调用的是此对象
1.3.2 IoC容器的创建
1)构造器
public ClassPathXmlApplicationContext(
String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
throws BeansException {
super(parent);
setConfigLocations(configLocations);
if (refresh) {
refresh();
}
}
2)refresh()
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
//创建容器前的一些准备工作,如校验xml文件格式等
prepareRefresh();
// 获取BeanFactory实例(DefaultListableBeanFactory对象),并读取xml文件注册bean的配置信息,以(beanName,BeanDefinition)的形式注册到beanDefinitionMap中
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 设置 BeanFactory 的类加载器,添加几个 BeanPostProcessor,手动注册几个特殊的 bean
prepareBeanFactory(beanFactory);
try {
// 留给子类实现
postProcessBeanFactory(beanFactory);
// 如果有BeanFactoryPostProcessor的实现类,则触发其postProcessBeanFactory方法
invokeBeanFactoryPostProcessors(beanFactory);
// 如果有BeanPostProcessor的实现类,则注册其进入BeanFactory
registerBeanPostProcessors(beanFactory);
// 初始化应用的Message Source
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// 检查Listeners并注册
registerListeners();
// 新建并初始化所有的非懒加载的Singletons
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
上面过程中,最为重要的几步如下
- prepareRefresh();
- obtainFreshBeanFactory();
- prepareBeanFactory(beanFactory);
- postProcessBeanFactory(beanFactory);
- invokeBeanFactoryPostProcessors(beanFactory);
- registerBeanPostProcessors(beanFactory);
- finishBeanFactoryInitialization(beanFactory);
此方法创建了IoC容器,并在这一步完成singleton的实例化
#附
#1. xxxAware原理
有一系列的xxxAware接口,如果一个bean先要注入xxx,那么只需要重写该接口中的setxxx方法即可
为什么重写了就可以呢?
因为在调用refresh()
方法时(调用ClassPathXmlApplicationContext构造器时会调用到),其中有如下步骤
prepareRefresh()
创建容器前的一些准备工作,如校验xml文件格式等
obtainFreshBeanFactory
其中会调用AbstractRefreshableApplicationContext#refreshBeanFactory方法创建DefaultListableBeanFactory(其是全能beanFactory)实例,之后调用AbstractRefreshableApplicationContext#getBeanFactory方法获取此实例
prepareBeanFactory(beanFactory)
--- 关键该方法传入刚刚获取的DefaultListableBeanFactory的实例,之后调用了下面这一句
beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
为beanFactory添加了一个BeanPostProcessor,其会在所有bean注册好后,在bean的属性被注入后,在bean初始化前(这里指执行
InitializingBean
的afterPropertiesSet()
方法之前,如果bean实现了该接口的话),会调用ApplicationContextAwareProcessor的postProcessBeforeInitialization方法,其中又调用invokeAwareInterfaces
方法,如果bean实现了ApplicationContextAware接口,就会调用其重写的方法将applicationContext注入此bean中
ApplicationContextAware一个常用的场景
当singleton的创建依赖一个prototype时,由于singleton的创建只发生一次,如果采用自动注入的方式,其注入的prototype也只会被创建一次,且每次getBean时,也只会获取创建好的singleton。为了让每次获取singleton时,对应的prototype域可以每次都新建,因而可以让singleton的类实现
ApplicationContextAware
接口,并重写其setApplicationContext方法,由于prototype在每次getBean时都会新建,因而只需要在singleton类的内部使用注入的ApplicationContext直接getBean即可达到目的示例如下
@Component
@Scope("singleton")
public class mySingleton implements ApplicationContextAware {
private ApplicationContext ac;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
ac = applicationContext;
}
public void doTheThing(){
getAs().print();
}
public myPrototype getAs(){
return (myPrototype) ac.getBean("myPrototype");
} }
@Component
@Scope("prototype")
public class myPrototype {
public void print(){
System.out.println(this);
}
}
public class client_bean_prototype {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ConfigClass.class);
mySingleton bean = (mySingleton)applicationContext.getBean("mySingleton");
bean.doTheThing(); bean = (mySingleton)applicationContext.getBean("mySingleton");
bean.doTheThing();
}
}
输出结果如下
ioctest1.singletonDependsPrototype.myPrototype@1a052a00
ioctest1.singletonDependsPrototype.myPrototype@4d826d77
可见,两次拿到的bean不同
#2. BeanFactory与FactoryBean
BeanFactory是IoC容器的顶层接口,负责实例化Bean并保存着所有的Bean对象
FactoryBean也是一个接口,其定义如下
public interface FactoryBean<T> {
String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";
//返回包装的对象
@Nullable
T getObject() throws Exception;
//返回包装的对象的类型
@Nullable
Class<?> getObjectType();
//判断是否为单例
default boolean isSingleton() {
return true;
}
}
对于某些创建过程比较复杂的对象,可以将其复杂重复的创建过程放入getObject()
方法中,并将最后创建好的对象由此方法返回
假设有下面例子
自定义
FactoryBean
public class myFactoryBean implements FactoryBean<accountService> {
@Override
public accountService getObject() {
//实现一些复杂的实例化逻辑
//可以为accountService对象set一些通用的属性
return new accountService();
} @Override
public Class<?> getObjectType() {
return accountService.class;
}
}
配置Bean
public class ConfigureClass { @Bean
public myFactoryBean myFactoryBean() {
return new myFactoryBean();
} @Bean
public accountService accountService() {
//同样可以为返回的accountService实例修改一些属性
return myFactoryBean().getObject();
} }
那么首先会创建名称为"myFactoryBean"的myFactoryBean实例,注意!仅仅是创建这个类的实例而已
之后再创建accountService的实例时,由于其由myFactoryBean负责,在preInstantiateSingletons方法中调用getBean方法时,其在调用createBean方法创建时,会通过反射,再次进入下面这段代码
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
final String beanName = transformedBeanName(name);
Object bean;
// Eagerly check singleton cache for manually registered singletons.
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
if (logger.isTraceEnabled()) {
if (isSingletonCurrentlyInCreation(beanName)) {
logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
"' that is not fully initialized yet - a consequence of a circular reference");
}
else {
logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
}
}
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
这里传入的name
是之前创建的myFactoryBean
,那么在getObjectForBeanInstance(sharedInstance, name, beanName, null);
方法中,就会调用到myFactoryBean
的getObject()
方法来创建accountService的实例,从而完成实例化
这里要注意,singletonObjects
这个map中,会同时存放myFactoryBean和accountService的beans,也即在使用FactoryBean创建好需要的bean之后,下次使用accountService实例就只需要按beanName直接获取即可。
由此可见,FactoryBean也是一种Bean,但其不同于工厂模式,其负责某一类的全部对象实例化过程中的复杂部分,也即创建每个FactoryBean接口泛型传入的类的对象时,均需要这个FactoryBean来插一手,这样就极大的简化了此类Bean的实例化流程
Spring IoC总结的更多相关文章
- 【初探Spring】------Spring IOC(三):初始化过程---Resource定位
我们知道Spring的IoC起到了一个容器的作用,其中装得都是各种各样的Bean.同时在我们刚刚开始学习Spring的时候都是通过xml文件来定义Bean,Spring会某种方式加载这些xml文件,然 ...
- 【初探Spring】------Spring IOC(一)
IOC:Inversion of Control(控制反转).IOC它所体现的并不是一种技术,而是一种思想,一种将设计好的对象交给容器来管理的思想.IOC的核心思想就体现在控制.反转这两个词上面,要理 ...
- spring ioc
spring ioc是spring的核心之一,也是spring体系的基础,那么spring ioc所依赖的底层技术是什么的?反射,以前我们开发程序的时候对象之间的相互调用需要用new来实现,现在所有的 ...
- Spring IoC源码解析——Bean的创建和初始化
Spring介绍 Spring(http://spring.io/)是一个轻量级的Java 开发框架,同时也是轻量级的IoC和AOP的容器框架,主要是针对JavaBean的生命周期进行管理的轻量级容器 ...
- spring笔记6 spring IOC的中级知识
1,spring ioc的整体流程,xml配置 spring ioc初始化的流程结合上图 步骤编号 完成的工作 1 spring容器读取配置文件,解析称注册表 2 根据注册表,找到相应的bean实现类 ...
- 谈谈对Spring IOC的理解(转)
学习过Spring框架的人一定都会听过Spring的IoC(控制反转) .DI(依赖注入)这两个概念,对于初学Spring的人来说,总觉得IoC .DI这两个概念是模糊不清的,是很难理解的,今天和大家 ...
- 自己动手编写spring IOC源码
前言:对于spring IOC概念不是很了解的朋友可以阅读我上一篇博客--轻松理解spring IOC(这两篇博客也是由于我的个人原因导致现在才发布,惭愧啊).通过这篇博客的理解之后,相信大家会对sp ...
- spring ioc 源码解析
什么是ioc? 通俗的解释是:(spring)框架中,完成对象的创建和注入的容器. springIOC体系结构: spring IOC的创建是典型的工厂模式,这一系列的bean工厂如上所示. 其核心是 ...
- Spring:源码解读Spring IOC原理
Spring IOC设计原理解析:本文乃学习整理参考而来 一. 什么是Ioc/DI? 二. Spring IOC体系结构 (1) BeanFactory (2) BeanDefinition 三. I ...
- 谈谈对Spring IOC的理解
学习过Spring框架的人一定都会听过Spring的IoC(控制反转) .DI(依赖注入)这两个概念,对于初学Spring的人来说,总觉得IoC .DI这两个概念是模糊不清的,是很难理解的,今天和大家 ...
随机推荐
- Flink-v1.12官方网站翻译-P018-Event Time
事件时间 在本节中,您将学习如何编写时间感知的Flink程序.请看一下及时流处理,了解及时流处理背后的概念. 关于如何在Flink程序中使用时间的信息请参考windowing和ProcessFunct ...
- JavaScript 、TypeScript 中的 Boolean
boolean 是 JavaScript 中一种有趣的原始数据类型.在TypeScript中,非严格模式下("strictNullChecks": false),它总共允许4个值 ...
- unix环境高级编程第三章笔记
文件描述符 1.文件描述符的概念 对于内核而言,所有打开的文件都会用一个文件描述符来引用,打开或和创建一个新文件的时候,内核会给进程返回一个文件描述符,而当使用read write时,可以使用这个文件 ...
- [AHOI2009] [BZOJ1799] 月之迷 (数位DP)
给出两个数a,ba,b,求出\([a,b]\)中各位数字之和能整除原数的数的个数. 我们按照模板的做法来想,枚举到第pos位时,要确定这一位的数字,可以更新现在所填数字的和,但对于最终的和无从得知,是 ...
- 1154 Vertex Coloring
题目前半略 Sample Input: 10 11 8 7 6 8 4 5 8 4 8 1 1 2 1 4 9 8 9 1 1 0 2 4 4 0 1 0 1 4 1 0 1 3 0 0 1 0 1 ...
- CF 1400G.Mercenaries 题解【SOSDP 组合数学】
CF 1400G.Mercenaries 题意: 有\(n\)个佣兵,问雇佣至少一名雇佣兵且满足下述条件的方案数 如果雇佣第\(i\)个佣兵必须要求最终雇佣的总人数\(x\)满足\(l_i\le x\ ...
- 【noi 2.6_7113】Charm Bracelet(DP)
题意:N个饰物,有重量和渴望程度.问在M的重量限制内能达到的最大的渴望度. 解法:经典的01问题,但有一个小技巧值得记住:用if比较大小比调用max函数快了不少,这题有100ms左右. 1 #incl ...
- Codeforces Round #681 (Div. 2, based on VK Cup 2019-2020 - Final) A. Kids Seating (规律)
题意:给你一个正整数\(n\),在\([1,4n]\)中找出\(n\)个数,使得这\(n\)个数中的任意两个数不互质且不能两两整除. 题解:这题我是找的规律,从\(4n\)开始,往前取\(n\)个偶数 ...
- HihoCoder - 1110
题意: 您的任务是判断输入是否是合法的正则表达式.正则表达式定义如下: 1: 0和1都是正则表达式. 2:如果P和Q是正则表达式,那么PQ就是正则表达式. 3:如果P是正则表达式,(P)就是正则表达式 ...
- Codeforces Round #498 (Div. 3) D. Two Strings Swaps (思维)
题意:给你两个长度相同的字符串\(a\)和\(b\),你可以将相同位置上的\(a\)和\(b\)的字符交换,也可以将\(a\)或\(b\)中某个位置和对应的回文位置上的字符交换,这些操作是不统计的,你 ...