内容介绍

在Spring中应用上下文ApplicationContext是相较于BeanFacotry更为先进的IOC容器,BeanFacotry是Spring实现IOC最基础最核心的接口,使得Spring管理不同Java对象成为可能。而ApplicationContext是建立在ApplicationContext的基础上,提供了更多面向应用的功能。除了拥有BeanFactory的所有功能外,还支持特殊类型bean,如上一节中讲到的BeanFactoryPostProcessor和BeanPostProcessor两者的自动识别、资源加载、容器事件和监听器、国际化支持、单例bean自动初始化等。

BeanFactory是spring的底层基础设施,面向spring本身;而ApplicationContext面向spring的使用者,在开发中应该使用ApplicationContext。

我们一般称BeanFactory为Ioc容器,称ApplicationContext为应用上下文,通常ApplicationContext也被为Spring容器。

ApplicationContext添加了BeanFactoryPostProcessor和BeanPostProcessor的自动识别,不在需要我们像上一篇文章中一样手动添加了,只需要在xml中配置两者即可。

目前bean 的生命周期如下:

当前bean生命周期

当前ApplicationContext的继承体系

当前ApplicationContext的继承体系

代码分支

GitHub - yihuiaa/little-spring at application-context剖析Spring源码,包括常见特性IOC、AOP、三级缓存等... Contribute to yihuiaa/little-spring development by creating an account on GitHub.https://github.com/yihuiaa/little-spring/tree/application-context

核心代码

ConfigurableApplicationContext

添加刷新容器的方法

public interface ConfigurableApplicationContext extends ApplicationContext {

	/**
* 刷新容器
*
* @throws BeansException
*/
void refresh() throws BeansException;
}

AbstractRefreshableApplicationContext

/**
* ● @author: YiHui
* ● @date: Created in 23:40 2023/4/26
*/
public abstract class AbstractRefreshableApplicationContext extends AbstractApplicationContext{
private DefaultListableBeanFactory beanFactory; /**
* 创建beanFactory并加载BeanDefinition
* @throws BeansException
*/
@Override protected void refreshBeanFactory() throws BeansException {
DefaultListableBeanFactory factory = createBeanFactory();
loadBeanDefinitions(factory);
this.beanFactory = factory;
}
/**
* 加载BeanDefinition
*
* @param beanFactory
* @throws BeansException
*/
protected abstract void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException;
/**
* 创建bean工厂
*
* @return
*/
protected DefaultListableBeanFactory createBeanFactory() {
return new DefaultListableBeanFactory();
}
public DefaultListableBeanFactory getBeanFactory() {
return beanFactory;
}
}

AbstractXmlApplicationContext

/**
* ● @author: YiHui
* ● @date: Created in 21:07 2023/6/12
*/
public abstract class AbstractXmlApplicationContext extends AbstractRefreshableApplicationContext{ @Override protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException {
XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(beanFactory, this);
String[] configLocations = getConfigLocations();
if(configLocations!=null){
xmlBeanDefinitionReader.loadBeanDefinitions(configLocations);
}
} protected abstract String[] getConfigLocations();
}

AbstractApplicationContext

refresh()方法是AbstractApplicationContext类中一个重要的方法。它的主要作用是刷新应用程序上下文,即重新加载或更新上下文中的所有bean定义和配置。当调用refresh()方法时,Spring框架会执行以下关键步骤:

  1. 关闭旧的应用程序上下文(如果存在):首先,如果已经存在一个旧的应用程序上下文,refresh()方法会关闭它。这确保在刷新之前释放旧的资源,并清理任何可能的旧状态。

  2. 加载bean定义和配置:refresh()方法会重新加载上下文中的bean定义和配置。它会解析XML文件、注解或其他配置方式,并将这些定义转化为内部的数据结构,以便Spring能够了解和管理这些bean。

  3. 创建和初始化bean:refresh()方法会实例化和初始化所有定义的bean。它会根据bean定义的配置信息,使用适当的构造函数或工厂方法创建bean实例,并将其装配(注入)所需的依赖关系。

  4. 注册bean实例:已创建和初始化的bean实例将在应用程序上下文中注册,以便其他部分可以访问它们。这样,其他组件或类可以通过依赖注入或查找方式获取所需的bean。

  5. 完成刷新过程:在完成上述步骤后,refresh()方法会触发一些后续操作,如事件发布、AOP代理的创建等。这些步骤将上下文准备好,使其可以在应用程序中使用。

package org.springframework.context.support;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.io.DefaultResourceLoader; import java.util.Map; /**
* ● @author: YiHui
* ● @date: Created in 21:43 2023/4/26
* ● @notes: 抽象应用上下文
*/
public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {
@Override public void refresh() throws BeansException {
//创建BeanFactory,并加载BeanDefinition
refreshBeanFactory();
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
//执行BeanFactoryPostProcessor
invokeBeanFactoryPostProcessors(beanFactory);
//BeanPostProcessor的实例化提前与其他bean
registerBeanPostProcessors(beanFactory); //提前实例化单例Bean
beanFactory.preInstantiateSingletons();
} /**
* 注册BeanPostProcessor
* @param beanFactory
*/
private void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {
Map<String, BeanPostProcessor> beanPostProcessorMap = beanFactory.getBeansOfType(BeanPostProcessor.class);
for(BeanPostProcessor beanPostProcessor : beanPostProcessorMap.values()) {
beanFactory.addBeanPostProcessor(beanPostProcessor);
}
} private void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
Map<String, BeanFactoryPostProcessor> beanFactoryPostProcessorMap = beanFactory.getBeansOfType(BeanFactoryPostProcessor.class);
for(BeanFactoryPostProcessor beanFactoryPostProcessor : beanFactoryPostProcessorMap.values()){
beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);
}
} /**
* 创建BeanFactory,并加载BeanDefinition
* @throws BeansException
*/
protected abstract void refreshBeanFactory() throws BeansException;
public abstract ConfigurableListableBeanFactory getBeanFactory(); @Override public Object getBean(String name) throws BeansException {
return getBeanFactory().getBean(name);
} @Override public Object getBean(String name, Object[] args) {
return getBeanFactory().getBean(name, args);
} @Override public <T> Map<String, T> getBeansOfType(Class<T> type) throws BeansException {
return getBeanFactory().getBeansOfType(type);
} @Override public String[] getBeanDefinitionNames() {
return new String[0];
} @Override public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
return getBeanFactory().getBean(name, requiredType);
}
}

测试

spring.xml

配置BeanFactoryPostProcessor和BeanPostProcessor的实现。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd"> <bean id="person" class="bean.Person">
<property name="name" value="yiHui"/>
<property name="car" ref="car"/>
</bean> <bean id="car" class="bean.Car">
<property name="name" value="Rolls-Royce"/>
</bean> <bean class="common.CustomBeanFactoryPostProcessor"/>
<bean class="common.CustomerBeanPostProcessor"/> </beans>

ApplicationContextTest

    @Test
public void testApplicationContext(){
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring.xml");
Person person = applicationContext.getBean("person", Person.class);
//name属性在CustomBeanFactoryPostProcessor中被修改为YiHuiComeOn
System.out.println(person); Car car = applicationContext.getBean("car", Car.class);
//brand属性在CustomerBeanPostProcessor中被修改为Maserati
System.out.println(car);
}

ClassPathXmlApplicationContext初始化的时序图

ClassPathXmlApplicationContext初始化的时序图

测试结果

processor执行成功

执行bean[customBeanFactoryPostProcessor]的初始化方法
执行bean[customerBeanPostProcessor]的初始化方法
CustomerBeanPostProcessor#postProcessBeforeInitialization
执行bean[car]的初始化方法
CustomerBeanPostProcessor#postProcessAfterInitialization
CustomerBeanPostProcessor#postProcessBeforeInitialization
执行bean[person]的初始化方法
CustomerBeanPostProcessor#postProcessAfterInitialization
Person{name='YiHuiComeOn', age='null', car=Car{name='Maserati'}}
Car{name='Maserati'}

[源码系列:手写spring] IOC第九节:应用上下文ApplicationContext的更多相关文章

  1. Spring源码分析 手写简单IOC容器

    Spring的两大特性就是IOC和AOP. IOC Container,控制反转容器,通过读取配置文件或注解,将对象封装成Bean存入IOC容器待用,程序需要时再从容器中取,实现控制权由程序员向程序的 ...

  2. 《四 spring源码》手写springioc框架

    手写SpringIOCXML版本 /** * 手写Spring专题 XML方式注入bean * * * */ public class ClassPathXmlApplicationContext { ...

  3. 框架源码系列十:Spring AOP(AOP的核心概念回顾、Spring中AOP的用法、Spring AOP 源码学习)

    一.AOP的核心概念回顾 https://docs.spring.io/spring/docs/5.1.3.RELEASE/spring-framework-reference/core.html#a ...

  4. 从零开始手写 spring ioc 框架,深入学习 spring 源码

    IoC Ioc 是一款 spring ioc 核心功能简化实现版本,便于学习和理解原理. 创作目的 使用 spring 很长时间,对于 spring 使用非常频繁,实际上对于源码一直没有静下心来学习过 ...

  5. 框架源码系列六:Spring源码学习之Spring IOC源码学习

    Spring 源码学习过程: 一.搞明白IOC能做什么,是怎么做的  1. 搞明白IOC能做什么? IOC是用为用户创建.管理实例对象的.用户需要实例对象时只需要向IOC容器获取就行了,不用自己去创建 ...

  6. 《四 spring源码》手写springmvc

    手写SpringMVC思路 1.web.xml加载  为了读取web.xml中的配置,我们用到ServletConfig这个类,它代表当前Servlet在web.xml中的配置信息.通过web.xml ...

  7. Spring源码 20 手写模拟源码

    参考源 https://www.bilibili.com/video/BV1tR4y1F75R?spm_id_from=333.337.search-card.all.click https://ww ...

  8. 利用递归,反射,注解等,手写Spring Ioc和Di 底层(分分钟喷倒面试官)了解一下

    再我们现在项目中Spring框架是目前各大公司必不可少的技术,而大家都知道去怎么使用Spring ,但是有很多人都不知道SpringIoc底层是如何工作的,而一个开发人员知道他的源码,底层工作原理,对 ...

  9. Spring源码剖析3:Spring IOC容器的加载过程

    本文转自五月的仓颉 https://www.cnblogs.com/xrq730 本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https ...

  10. 源码分析 | 手写mybait-spring核心功能(干货好文一次学会工厂bean、类代理、bean注册的使用)

    作者:小傅哥 博客:https://bugstack.cn - 汇总系列原创专题文章 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言介绍 一个知识点的学习过程基本分为:运行helloworld ...

随机推荐

  1. java多线程---总结(1)

    线程创建.start.run 一.创建线程方式 java创建线程的方式,主要有三种:类Thread.接口Runnable.接口Callable. 1.Thread和Runnable进行比较 他们之间的 ...

  2. linux如何使用ssh进行远程服务

    /*********************linux SSH 简单学习********************/ 什么是SSH SSH 为 Secure Shell 的缩写,由 IETF 的网络小组 ...

  3. 字符流:FileReader/FileWriter的使用

    读取文件 1.建立一个流对象,将已存在的一个文件加载进流. FileReader fr = new FileReader(new File("Test.txt"));2.创建一个 ...

  4. java子类父类有相同的方法优先调用子类-重写-递归

    子类和父类有相同的方法,优先调用子类.如果子类没有,父类. package studyDemo9yue; public class study01 { public static void main( ...

  5. 支持S3协议的S3cmd工具简单使用

    本文分享自天翼云开发者社区<支持S3协议的S3cmd工具简单使用>,作者:付****健 一:安装方法 #wget http://nchc.dl.sourceforge.net/projec ...

  6. FLink写入Clickhouse优化

    一.背景 ck因为有合并文件操作,适合批量写入.如单条插入则速度太慢 二.Flink写入ck优化 改为分批插入,代码如下 DataStream<Row> stream = ... stre ...

  7. KUKA库卡机器人维修

    KUKA库卡机器人作为生产线上的核心设备,一旦出现KUKA机械手故障,将直接影响整个生产线的运行效率.及时的库卡机器人维修工作不仅能够迅速恢复机器人的工作状态,减少生产停滞时间,还能通过预防性维护降低 ...

  8. 解决easyexcel合并单元格数组求和重复问题

    背景 EasyExcel(根据条件动态合并单元格的重复数据))_Violet-CSDN博客_easyexcel动态合并单元格现有的订单导出是使用的easyExcel完成的.对于相同单元格的合并是自定义 ...

  9. vue中的find()函数的用法与扩展用法

    find函数基本格式:let obj=this.list.find(item=>item.code===val)首先在这里我们要知道的两个基础知识一.find是一个查找函数.二.箭头函数find ...

  10. openEuler 24.03 SP1下载clang-18的办法(RISC-V版)

      距离openEuler 24.03 SP1发版已经过去了一段时间了,值得注意的是,在这个新的发行版中,可以直接通过命令行下载clang18了!之前的发行版中clang的版本是17,可以直接dnf ...