内容介绍

在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. Python学习(一)——配套《PyTorch深度学习实战》

    记录一下Python学习过程中的一些问题: 1. 在JupyterLab中查询当前文件的地址 import os print(os.getcwd()) #查询该文件的地址 2. 新建cell 在 Ju ...

  2. C51--05---LCD1602调试工具

    一.LCD1602调试工具 单片机调试工具: 数码管 液晶屏 串口 数码管需要不断进行扫描,一旦扫描不及时就会不断闪烁,并且可显示的数据太过局限: 串口需要使用电脑进行发送指令,不易操作与携带: 所以 ...

  3. Java中hashCode() 和 equals()

    该文章为转载(原文链接在结尾),虽然篇幅偏长,但是却能使你真正理解hashCode和queals各自的作用以及之间的联系,尤其是第四部分,读完肯定会让你有所收获. 第1部分 equals() 的作用 ...

  4. WPF刮刮乐

    WPF刮刮乐 <Window x:Class="WpfApp2.MainWindow" xmlns="http://schemas.microsoft.com/wi ...

  5. 免费-高清免费视频录像软件OBS

    OBS studio 是免费开源的. https://obsproject.com/download 中文绿色版: http://www.xitongzhijia.net/soft/151705.ht ...

  6. VueJs(1)---操作指南

    [VueJs入门] 一.上手步骤 vue.js和jquery一样,只要引入vue.js就可以了. 1.创建一个Vue实例: 先引入Vue.js文件,然后new一个Vue的实例即可.如下面的代码,通过& ...

  7. RocketMQ -- offset管理

    正文首先来明确一下 Offset 的含义, RocketMQ 中, 一 种类型的消息会放到 一 个 Topic 里,为了能够并行, 一般一个 Topic 会有多个 Message Queue (也可以 ...

  8. ctfshow--web10 php代码逻辑漏洞with rollup注入

    dirsearch没有扫到文件 查看源代码发现 有个style.css文件点击查看 查看index.phps代码 又是代码审计 点击查看代码 <?php $flag=""; ...

  9. 小程序uni-app图片预览uni.previewImage会触发onshow这个生命周期

    小程序单张uni-app图片预览 uni.previewImage({ current:0, urls: [mess.honorIcon],//['你的图片地址'] }); 小程序多张图片预览 < ...

  10. DAB实现中用到的主要设计模式

    DAB C++ 版本设计模式应用实践 1. 命令模式 (Command Pattern) 设计目标 模块解耦:实现各模块独立编译.测试.运行,消除模块间直接依赖 扩展准备:为桥接模式实现奠定基础 依赖 ...