目录

1、关键接口和类

1.1、关键类之 DefaultListableBeanFactory

1.2、关键类之XmlBeanDefinitionReader

1.3、关键类之ClassPathXmlApplicationContext

2、spring初始化过程中对外暴露的扩展接口

3、扩展点的启动顺序

spring的IOC容器初始化流程很复杂,本文只关注流程中的关键点,勾勒出主要轮廓,对容器的初始化有一个整体认识,以下基于spring的5.1.2.RELEASE分析,本文演示代码地址:https://github.com/amapleleaf/spring-code.git

本文分为两部分:《spring初始化源码浅析之关键类和扩展接口》、《spring初始化源码浅析之代码浅析》

1、关键接口和类
1.1、关键类之 DefaultListableBeanFactory
该类核心功能:

1、提供注册、获取等等与BeanDefinition对象操作相关的方法,BeanDefinition缓存在DefaultListableBeanFactory的beanDefinitionMap变量(ConcurrentHashMap类型)

2、提供创建、注册、获取、单例等等跟bean对象操作相关的方法供ApplicationContext使用,bean对象缓存在DefaultSingletonBeanRegistry的singletonObjects 变量(ConcurrentHashMap类型)

类关系图如下:

从类图中看到DefaultListableBeanFactory实现了很多接口,spring 根据该类的功能定义了不同层次的接口。接口核心功能主要分两类:1、AliasRegistry、BeanDefinitionRegistry接口主要提供BeanDefinition和alias注册、获取的方法,2、左半部分*BeanFactory相关接口、SingletonBeanRegistry接口提供对象的创建、缓存和获取等方法

1.2、关键类之XmlBeanDefinitionReader
该类负责分析xml中bean的定义,并解析成BeanDefinition对象,然后调用DefaultListableBeanFactory的注册方法缓存到DefaultListableBeanFactory中

1.3、关键类之ClassPathXmlApplicationContext
        先上类关系图:

这个就是spring上下文,是spring启动的入口类,从父类AbstractApplicationContext的refresh()方法中可以看出该类的主要功能:设置springContext.xml路径、创建DefaultListableBeanFactory、提供对象创建过程中的各种扩展点、事件的注册和分发等等。

2、spring初始化过程中对外暴露的扩展接口
1、BeanNameAware:void setBeanName(String name);

该bean获取自己在DefaultListableBeanFactory中的id或name ,在spring框架里用的多,我们一般很少用到。

2、BeanFactoryAware:void setBeanFactory(BeanFactory beanFactory)

获取创建该bean的DefaultListableBeanFactory对象,可以从该对象中回去bean对象,不过绝大多数时候我们是从ApplicationContext中来获取。

3、ApplicationContextAware:void setApplicationContext(ApplicationContext applicationContext)

获取该bean所属的applicationContext,从而可以获取到该上下文的bean对象。自己写一个工具类实现该接口然后在配置文件中配置或加上@Component注解,通过这个工具类就很方便的在应用中动态获取bean对象,这种工具类在很多老的项目中几乎是一个标配。

4、InitializingBean:void afterPropertiesSet()

spring提供两中方式来对bean初始化后的扩展,一种是实现InitializingBean接口,一种是使用通过init-method方法指定,spring初始化bean时在执行InitializingBean接口的afterPropertiesSet方法后就紧接着执行init-method指定的方法。使用init-method不会对spring产生依赖因此使用频率较高,但由于这种方式使用反射方式来调用所以性能上低于直接调用 InitializingBean接口的afterPropertiesSet方法,后面会有相应的代码分析。

5、指定init-method

用的较多,可以理解为spring在bean对象初始化完后会通过反射的方式来执行该bean中init-method指定的方法。通过在xml文件中的bean标签配置init-method或在该bean的方法上使用@PostConstruct注解达到效果。

6、BeanFactoryPostProcessor:void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)

从后面的代码分析中我们发现,实现BeanFactoryPostProcessor接口的bean的创建及接口方法的调用时间早于普通bean的创建。实现该接口可以拿到beanFactory对象,然后就看可以在普通bean对象创建之前进行干预调整,PropertyPlaceholderConfigurer类大家应该比较熟悉,该类实现BeanFactoryPostProcessor接口,在postProcessBeanFactory方法中将beanDefinitionMap中所有的BeanDefinition中的含有占位符的值修改为指定属性文件中的值,这样在创建对象的时候就能获取到真实值。

7、BeanPostProcessor:Object postProcessBeforeInitialization(Object bean, String beanName);

BeanPostProcessor:Object postProcessAfterInitialization(Object bean, String beanName);

该接口需要注意与BeanFactoryPostProcessor接口的区别:

BeanFactoryPostProcessor接口:A实现了该接口,spring启动的时候,在所有普通bean对象创建之前会先创建A对象并调用其postProcessBeanFactory方法,方法参数为beanFactory。

BeanPostProcessor接口:A实现了该接口,spring在创建普通的bean 对象B时,在B对象初始化之前将B对象的实例和beanname作为入参调用A的前置方法postProcessBeforeInitialization,在B对象初始化之后将B对象的实例和beanname作为入参调用A的后置方法postProcessAfterInitialization。由此也可知实现该接口bean的创建时间早于普通bean的创建。

通过实现该接口也可以完成对bean对象的调整,但与BeanFactoryPostProcessor还是有本质的区别,实现BeanFactoryPostProcessor可以理解为对创建的模板的调整,是对BeanDefinition对象的调整,而BeanPostProcessor则是在对象过程中做的临时的调整,是对创建好的bean对象的调整

使用BeanPostProcessor需要注意:

①、前置、后置方法需要将修改后的bean对象返回这样getbean时才能获取到正确的bean对象

②、针对layz的bean对象创建则不会回调该接口的方法

8、ApplicationListener:void onApplicationEvent(E event)

spring上下文启动完成后回调该接口,比较常用。

3、扩展点的启动顺序
1、HelloWorldService bean对象

public class HelloWorldService implements BeanFactoryAware,BeanNameAware,BeanFactoryPostProcessor,
BeanPostProcessor,InitializingBean ,
ApplicationListener<ContextRefreshedEvent>,ApplicationContextAware {
private String name;
private AtomicInteger count = new AtomicInteger(1);
private String getSeq(){
return count.getAndIncrement()+"->";
}
public HelloWorldService(){
System.err.println(getSeq()+"HelloWorldService constructor");
}
public void initMethod(){
System.err.println(getSeq()+"init method");
}
public void sayHello(){
System.err.println(getSeq()+name+"say:hello,world");
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
public void setBeanName(String name) {
System.err.println(getSeq()+"BeanNameAware.setBeanName:"+name);
}

public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
System.err.println(getSeq()+"BeanFactoryAware.setBeanFactory:"+beanFactory);
}

public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
System.err.println(getSeq()+"ApplicationContextAware.setApplicationContext:->"+applicationContext);
}

public void afterPropertiesSet() {
System.err.println(getSeq()+"InitializingBean.afterPropertiesSet");
}

public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
BeanDefinition beanDefinition = configurableListableBeanFactory.getBeanDefinition("peopleService");
beanDefinition.getPropertyValues();
MutablePropertyValues m = beanDefinition.getPropertyValues();
m.addPropertyValue("content", "i am ok");
System.err.println(getSeq()+"BeanFactoryPostProcessor.postProcessBeanFactory 将peopleService的content属性修改为i am ok");
}

public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.err.println(getSeq()+"BeanPostProcessor.postProcessBeforeInitialization->"+beanName);
return bean;
}

public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.err.println(getSeq()+"BeanPostProcessor.postProcessAfterInitialization->"+beanName);
return bean;
}

public void onApplicationEvent(ContextRefreshedEvent event) {
System.err.println(getSeq()+"ApplicationListener.onApplicationEvent: Refreshed->"+event.getApplicationContext());
}


}
2 、非lazy的普通bean对象,PeopleService

public class PeopleService{
private String content="";
public PeopleService(){
System.err.println("PeopleService constructor");
}
public void say(){
System.err.println("PeopleService say:["+content+"]");
}

public String getContent() {
return content;
}

public void setContent(String content) {
this.content = content;
}
}
3、启动类

public class AppMain {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
System.err.println("===============================================");
HelloWorldService helloWorldService = applicationContext.getBean("helloWorldService",HelloWorldService.class);
helloWorldService.sayHello();

PeopleService peopleService = applicationContext.getBean("peopleService",PeopleService.class);
peopleService.say();
}
}
代码执行结果:

从输入结果中我们得到以下信息:

1、HelloWorldService 实现 BeanFactoryPostProcessor接口所以创建时间早于普通非lazy的bean对象PeopleService

2、1-7为HelloWorldService 创建过程输出的日志,可以看到各扩展接口的执行顺序

3、第7步之后开始创建PeopleService对象,创建过程中回调用HelloWorldService(实现了BeanPostProcessor接口) 的前置和后置方法

4、spring上下文启动完成后发布ContextRefreshedEvent事件,输出第10步日志

《spring初始化源码浅析之代码浅析》将从代码来分析spring的初始化流程
————————————————
版权声明:本文为CSDN博主「a maple leaf」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/mapleleafforest/article/details/86523547

spring初始化源码浅析之关键类和扩展接口的更多相关文章

  1. 精尽Spring Boot源码分析 - SpringApplication 启动类的启动过程

    该系列文章是笔者在学习 Spring Boot 过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring Boot 源码分析 GitHub 地址 进行阅读 Sprin ...

  2. Spring mybatis源码篇章-NodeHandler实现类具体解析保存Dynamic sql节点信息

    前言:通过阅读源码对实现机制进行了解有利于陶冶情操,承接前文Spring mybatis源码篇章-XMLLanguageDriver解析sql包装为SqlSource SqlNode接口类 publi ...

  3. 【Spring】源码浅析 - ResponseEntity.ok 转载

    https://www.jianshu.com/p/1238bfb29ee1 ResponseEntity.ok具体源码

  4. Spring5源码分析之启动类的相关接口和注解

    一些基础但是核心的知识总结: Spring Boot项目启动的时候需要加@Configuration. @ComponentScan @Configuration + @Bean 把第三方jar包注入 ...

  5. Spring IOC 源码浅析

    控制反转(Inversion of Control,英文缩写为IoC)是一个重要的面向对象编程的法则来削减计算机程序的耦合问题,也是轻量级的Spring框架的核心. 控制反转一般分为两种类型,依赖注入 ...

  6. 转:Spring FactoryBean源码浅析

    http://blog.csdn.net/java2000_wl/article/details/7410714 在Spring BeanFactory容器中管理两种bean 1.标准Java Bea ...

  7. Spring mybatis源码学习指引目录

    前言: 分析了很多方面的mybatis的源码以及与spring结合的源码,但是难免出现错综的现象,为了使源码陶冶更为有序化.清晰化,特作此随笔归纳下分析过的内容.博主也为mybatis官方提供过pul ...

  8. 精尽Spring Boot源码分析 - 文章导读

    该系列文章是笔者在学习 Spring Boot 过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring Boot 源码分析 GitHub 地址 进行阅读 Sprin ...

  9. spring源码浅析——IOC

    =========================================== 原文链接: spring源码浅析--IOC   转载请注明出处! ======================= ...

随机推荐

  1. shell脚本(3)-格式化输出

    一个程序需要有0个或以上的输入,一个或更多输出 一.echo语法 1.功能:将内容输出到默认显示设备. echo命令功能在显示器上显示一段文字,一般提到提示的作用 2.语法:echo[-ne][字符串 ...

  2. R Studio Server install fails - hard coded libssl1.0.0 dependency out of date ...

    wget http://ftp.debian.org/debian/pool/main/o/openssl/libssl1.0.0_1.0.1t-1+deb8u6_amd64.deb md5sum l ...

  3. 痞子衡嵌入式:i.MXRT1010, 1170型号上不一样的SNVS GPR寄存器读写控制设计

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是i.MXRT1010, 1170型号上不一样的SNVS GPR寄存器读写控制设计. 痞子衡之前两篇文章 <在SBL项目实战中妙用i ...

  4. 基于小熊派Hi3861鸿蒙开发的IoT物联网学习【四】

    一.互斥锁基本概念: 1.互斥锁又称互斥型信号量,是一种特殊的二值性信号量[二值型信号量可以理解为任务与中断间或者两个任务间的标志,该标志非"满"即"空"],用 ...

  5. C++第三十七篇 -- 调试驱动程序

    上一篇写的KMDF程序是通过串口进行配置的,那么我们在VS中Attach to process外,可以直接用Winbdg进行调试,winbdg.exe所在路径为C:\Program Files (x8 ...

  6. PySpider爬取去哪儿攻略数据项目

    1 创建项目 点击WEB中的Create创建项目 填入相关项目名和其实爬取URL 创建后进入项目首页 右边 Handler 是pyspider的主类,整个爬虫一个Handler,其中可定义爬虫的爬取. ...

  7. 本地图片转base64编码

    通常获取图片的base64编码都是通过input的上传file属性获取转化,但是有时候需要的是本地图片不经过上传操作,直接拿本地图片转成base64编码就不行了,input上传操作需要人为操作一下,没 ...

  8. mysql 占用90%多的CPU,解决思路

    网站打开很慢,爆出了连接数据库的错误,进入服务器,top 看了下,mysql占用cpu 基本维持在90以上: mysql> show variables like '%slow%';      ...

  9. 论文笔记:(CVPR2017)PointNet: Deep Learning on Point Sets for 3D Classification and Segmentation

    目录 一. 存在的问题 二. 解决的方案 1.点云特征 2.解决方法 三. 网络结构 四. 理论证明 五.实验效果 1.应用 (1)分类: ModelNet40数据集 (2)部件分割:ShapeNet ...

  10. Python爬虫+可视化教学:爬取分析宠物猫咪交易数据

    前言 各位,七夕快到了,想好要送什么礼物了吗? 昨天有朋友私信我,问我能用Python分析下网上小猫咪的数据,是想要送一只给女朋友,当做礼物. Python从零基础入门到实战系统教程.源码.视频 网上 ...