内容介绍

在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. 记录一次线上服务OOM排查

    外面,阳光明媚,一切正好. 就在我欢天喜地准备迎来愉快的双休时,忽然之间,天塌了. 刚上线一小时的服务内存异常OOM了! 老实说,在我印象里OOM问题只存在于网上案例中,练习编码时常两年半,还是第一次 ...

  2. linux进入横线 "-" 开头的文件夹

    在linux进行文件操作时,会遇到一类文件是以"-"开头的. 例如我们想要进入名称为-126943579的文件时,我们无法直接使用cd -126943579命令来进入该文件. 那我 ...

  3. .NET 依赖注入中的 Captive Dependency

    大家好,上一篇我们分析了 .NET 依赖注入的默认行为,其实呢还没完全讲完.今天我先给大家出一道题: public interface IDbContext { } public class SqlS ...

  4. SpringCloud Alibaba(一) - Nacos 服务注册与发现,OpenFeign远程调用

    1.基础项目过目介绍 1.1 数据库创建 1.2 项目模块分布 1.3 测试http接口调用 1.3.1 http接口调用配置类 //http接口调用配置类 @Configuration public ...

  5. 京东从 OpenStack 改用 Kubernetes 的始末

    构建集群的历史 物理机器的时代(2004年-2014年) 在2014年之前,我们公司的应用程序都部署在物理机器上.在物理机器时代,为了给即将上线的应用程序分配物理机器,我们平均需要等上一周的时间.由于 ...

  6. linux:redis

    查询: 链接 redis初了解 是一个 "开源.免费" 的高性能的 key - value 的数据库 安装 yum添加epel源 yum install epel-release ...

  7. linxu7下安装pacemaker+corosync集群-01

    1.yum仓库的配置-自行配置 2.安装软件包: yum -y install pacemaker* corosync* pcs* psmisc yum -y install pcs fence-ag ...

  8. SQL注入之联合查询注入

    SQL注入之联合查询注入 一.联合查询注入原理 联合查询注入是一种常见的SQL注入攻击手法,其核心原理是利用SQL中的UNION操作符将多个SELECT语句的结果集合并,从而返回一个统一的结果集.在使 ...

  9. Q:Oracle表空间使用权限错误:ORA-01950

    使用A用户账号(默认表空间tablespace_A),A用户表中插入数据报错ORA-01950 报错处理方法: 方法1:授予用户A unlimited tablespace权限 grant unlim ...

  10. Q:oracle备份表语句

    oracle备份还原表语句 方法1.sql语句(同一数据库服务器) 备份 create table xxx_temp as select * from xxx; 还原 truncate table x ...