spring5.0源码-容器源码阅读笔记

2023-12-14 06:32

本文主要是介绍spring5.0源码-容器源码阅读笔记,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Spring 版本 分支5.0.x Spring git地址
代码运行说明

  • git clone git@github.com:spring-projects/spring-framework.git
    cd spring-framework

  • gradlew.bat build

读head first,讲述如何更好的去提高效率,通过交互来提高,来引导整个流程,我也仿制一下
1.Spring 是怎么用的
Spring使用非常广泛,从基础来讲,肯定是容器为基础,有了容器才有操作的空间,下面的代码是容器的基础加载
这段代码是容器的的基础使用

public class SpringIocTest {public static void main(String[] args) {ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("config.xml");EasyBean bean = context.getBean(EasyBean.class);bean.sayHello();context.close();}
}<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd"><bean class="com.cn.smartqq.EasyBean"></bean>
</beans>
下面这个会失败
<?xml version="1.0" encoding="UTF-8"?>
<beans><bean class="com.cn.smartqq.EasyBean"></bean>
</beans>

这里贴的XML代码里面包括了beans的定义,我非常奇怪,有一些的源码解读里面为什么不带这个能运行起来,这个下面会解析的
代码贴出来了,那么现在关注代码的运行

  • 首先将config.xml加载进来,进行初始化工作
  • 然后通过传入class获取bean
  • 剩下就是调用
  • 关闭容器

下面有两个问题,从实际出发,先看这2个问题

  • 当beans不声明的时候,无法加载成功,为什么?
  • 传入class就能获取bean,为什么?
    直接拿代码了,主要代码是这个类,注释里面写出了问题1的答案
public class DefaultDocumentLoader implements DocumentLoader {省略N多public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);if (logger.isDebugEnabled()) {logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]");}DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);return builder.parse(inputSource);  这里是关键,但是这里又是工厂模式建立的,看下面的关键方法}protected DocumentBuilderFactory createDocumentBuilderFactory(int validationMode, boolean namespaceAware)throws ParserConfigurationException {DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();factory.setNamespaceAware(namespaceAware);if (validationMode != XmlValidationModeDetector.VALIDATION_NONE) {factory.setValidating(true);if (validationMode == XmlValidationModeDetector.VALIDATION_XSD) { 最后就是进入了这里,感慨一句,代码是越来越方便维护,以前这里都是硬写的参数3,1之类的,写成true了,所以必须要有写成true之后是必须读xlmns里面的内容的// Enforce namespace aware for XSD...factory.setNamespaceAware(true);try {factory.setAttribute(SCHEMA_LANGUAGE_ATTRIBUTE, XSD_SCHEMA_LANGUAGE);}catch (IllegalArgumentException ex) {ParserConfigurationException pcex = new ParserConfigurationException("Unable to validate using XSD: Your JAXP provider [" + factory +"] does not support XML Schema. Are you running on Java 1.4 with Apache Crimson? " +"Upgrade to Apache Xerces (or Java 1.5) for full XSD support.");pcex.initCause(ex);throw pcex;}}}return factory;}
}

再来看问题2,首先个人猜了一下实现,可能放到一个hash表里面,可能是类似于concurrenthashmap,线程安全,然后通过反射的形势生成对象,因为spring这里不一定是jvm加载就生成所有对象,spring使用中也有单例,非单例的形式。所以肯定有生成对象的代码只是if条件不符合,忽略过去了(大概猜了个3成)
看代码如何getbean,但是跳入getbean就会发现,竟然不是ClassPathXmlApplicationContext这个类的实际获取,这里就看下代码的整个继承关系
在这里插入图片描述
右点击打开可能看的更清楚,我这里就不换图,因为后面的代码都是通过这里,getBean方法调用的是
AbstractApplicationContext这个类里面的内容,然后这里需要开始插入设计的好处了,非常明显,下面是2个代码的具体,其他省略

	设计非常清晰,我这个方法就是子类实现,那么因为这里除了xml的这个bean加载,还有其他的加载说到这里,我就要吐槽,前端设计真的还是比不上后端,就像腾讯去设计前端,改动也非常多,什么对继承开放,对修改关闭都不是很严格,更不用说更菜的设计了,不过前端也改变非常快@Overridepublic abstract ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;//---------------------------------------------------------------------// Implementation of BeanFactory interface//---------------------------------------------------------------------@Overridepublic Object getBean(String name) throws BeansException {assertBeanFactoryActive();//这里只是一个监控是否已经关掉容器,assert断言就能说明了,命名很棒return getBeanFactory().getBean(name);}

然后又是追踪了,虽然设计很好,但是追踪起来一样吐血

AbstractRefreshableApplicationContext 这个类里面看到这段语句@Overridepublic final ConfigurableListableBeanFactory getBeanFactory() {synchronized (this.beanFactoryMonitor) {//同步锁,虽然concurrent包有很多好用的锁工具,还是syn这个简洁明了if (this.beanFactory == null) {throw new IllegalStateException("BeanFactory not initialized or already closed - " +"call 'refresh' before accessing beans via the ApplicationContext");}return this.beanFactory;}}为什么看这个方法了,因为这个类里面只有这个方法去复制beanFactory,这里可能有问题说,有没有可能还是子类去实现set beanFactory这个动作,的确有可能,并且这个方法的type是protected的,那么最多子类去实现了,但是这里已经有default实现,那么我们先看看default实现@Overrideprotected final void refreshBeanFactory() throws BeansException {if (hasBeanFactory()) {destroyBeans();closeBeanFactory();}try {DefaultListableBeanFactory beanFactory = createBeanFactory();//看这步beanFactory.setSerializationId(getId());customizeBeanFactory(beanFactory);loadBeanDefinitions(beanFactory);synchronized (this.beanFactoryMonitor) {this.beanFactory = beanFactory;}}catch (IOException ex) {throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);}}下面的跳转,其实普通是猜不错来的,但是这里还是继续下去,后面,再讲这里具体的实现,下面代码省略很多,文章篇幅会太长其实是到了DefaultListBeanFactory这个类里面,下面开始删代码,太长了public <T> T getBean(Class<T> requiredType, Object... args) throws BeansException {NamedBeanHolder<T> namedBean = this.resolveNamedBean(requiredType, args);通过namedBean返回实例}NamedBeanHolder 这个结构是name,加实例,进入这个方法,这个方阿飞的private <T> NamedBeanHolder<T> resolveNamedBean(Class<T> requiredType, Object... args) throws BeansException {省略这句很关键,getbeanreturn new NamedBeanHolder(beanName, this.getBean(beanName, requiredType, args));省略}关键代码到了sharedInstance = this.getSingleton(beanName, new ObjectFactory<Object>() {public Object getObject() throws BeansException {try {return AbstractBeanFactory.this.createBean(beanName, mbd, args);} catch (BeansException var2) {AbstractBeanFactory.this.destroySingleton(beanName);throw var2;}}});其实这个createBean里面会包含非常多的信息,有一个非常有趣的类叫做RootBeanDefinition这个就会包含我们最爱的scope,lazyinit信息之类的信息然后经过非常长的追踪到了Class<?> resolvedClass = ClassUtils.forName(className, classLoader); 这里就是获取bean了,其实最后,猜了个5成,直接用的hashmap,名字叫做commonClassCache,所以只有公共的才去获取的到所以不用管获取然后获取是反射,这段代码return clToUse != null?clToUse.loadClass(name):Class.forName(name); 

其实上面看不出啥了refreshBeanFactory这个方法你只能连蒙带猜的去看怎么实现,那么这个refreshBeanFactory又是什么时候去实现的?
是ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(“config.xml”);这里去实现的。
这段代码关键在于对于父类的构造方法的调用,其次是refresh();

public ClassPathXmlApplicationContext(String[] paths, Class<?> clazz, @Nullable ApplicationContext parent)throws BeansException {super(parent); //首先是父类的调用,但是请注意,我们这里传入的是nullAssert.notNull(paths, "Path array must not be null");Assert.notNull(clazz, "Class argument must not be null");this.configResources = new Resource[paths.length];for (int i = 0; i < paths.length; i++) {this.configResources[i] = new ClassPathResource(paths[i], clazz);}refresh();
}

父类的构造函数一直向上追寻到AbstractApplicationContext这个类

public AbstractApplicationContext(@Nullable ApplicationContext parent) {this();setParent(parent); 
}

这个this()里面去构造了一个支持ant location的的一个resourceload的解析器,这里只是发生的set操作
然后使用setParent,注意这里传入的null,所以里面的代码是没执行的,功能是将父的contentxt,merge到现有的context里面去,有兴趣的可以去看看
现在回过来看看refresh()这个方法

再来定几个疑问
1.怎么初始化xml文件
2.bean工厂初始化干了什么?

public void refresh() throws BeansException, IllegalStateException {synchronized (this.startupShutdownMonitor) {// Prepare this context for refreshingprepareRefresh();// Tell the subclass to refresh the internal bean factory.ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();// Prepare the bean factory for use in this context.prepareBeanFactory(beanFactory);try {// Allows post-processing of the bean factory in context subclasses. 上下文对beanfactory的一些处理,但是现在调用是没有任何处理的postProcessBeanFactory(beanFactory);// Invoke factory processors registered as beans in the context.invokeBeanFactoryPostProcessors(beanFactory);// Register bean processors that intercept bean creation.registerBeanPostProcessors(beanFactory);// Initialize message source for this context. 国际化用的,在beanfacoty里面判断是否包含messageSource字符串来判断是否开启国际化initMessageSource();// Initialize event multicaster for this context.initApplicationEventMulticaster();// Initialize other special beans in specific context subclasses.onRefresh();// Check for listener beans and register them.registerListeners();// Instantiate all remaining (non-lazy-init) 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()这个没啥好说的就是准备
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
这里有一个写法,在AbstractApplicationContext里面去调用了一个abstract方法refreshBeanFactory,但是实现在于子类AbstractRefreshableApplicationContext中
这个方法我关注的是beanfactory的刷新/创建,并且读取xml文件读bean就隐藏在这里面

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {refreshBeanFactory();return getBeanFactory();
}
@Override
protected final void refreshBeanFactory() throws BeansException {if (hasBeanFactory()) {destroyBeans();closeBeanFactory();}try {DefaultListableBeanFactory beanFactory = createBeanFactory();beanFactory.setSerializationId(getId());customizeBeanFactory(beanFactory);loadBeanDefinitions(beanFactory); //读取xml文件synchronized (this.beanFactoryMonitor) {this.beanFactory = beanFactory;}}catch (IOException ex) {throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);}
}
protected DefaultListableBeanFactory createBeanFactory() {return new DefaultListableBeanFactory(getInternalParentBeanFactory()); //这里面的不用管,现在的调用是空,那么只用考虑DefaultListableBeanFactory的实现
}

那么我们拿到的factory类就是DefaultListableBeanFactory实现
prepareBeanFactory(beanFactory); 这个方法也就是对factory进行参数设置
这里有一个写法也值得讲一下
在这里插入图片描述
AbstractAutowireCapableBeanFactory,ConfigurableListableBeanFacotry有相同的方法ignoreDependencyType同
注意了,这个方法再DefaultListableBeanFactory是没有实现的,而是直接调用的AbstractAutowireCapableBeanFactory里面的实现方法
loadBeanDefinitions(beanFactory) 就是读取xml文件,会校验xml填写的对不对,bean的一些数量信息都会有提现

-----------------------我是分割线-------------------------------

这里还要讲一下代码设计中的策略模式和工厂模式

Define a family of algorithms,encapsulate each one,and make them
interchangeable.(定义一组 算法,将每个算法都封装起来,并且使它们之间可以互换。)

就拿ClassPathXmlApplicationContext 这个类的继承图来说,他继承实现AbstractApplicationContext类,这个类里面将 resourceLoader作为一批算法,然后DefaultResourceLoader作为算法簇中的一个,context作为调用算法的类,这里写的非常灵活,用继承方式,将具体实现引入,而不是通过New的方式去引入
在这里插入图片描述

工厂模式

Define an interface for creating an object,but let subclasses decide
which class to instantiate.Factory Method lets a class defer
instantiation to subclasses.(定义一个用于创建对象的
接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。)

工厂模式然后会有抽象工厂模式,也就是更复杂的情况下的工厂模式,但是其根本定义还是这个
在这里插入图片描述在这里插入图片描述

来我们看下BeanFactory的一个部分继承图,其实这里根本没有实现,实现在其他的里面,在下一张图里面
在这里插入图片描述

这里,把具体实现扔到了这里在这里插入图片描述

那么这是工厂模式吗?看起来和工厂模式没有关系啊,

设计模式是告诉我们如何组织类和对象以解决某种问题
出自headFirst,这本书真推荐,比国内的设计模式讲的更清晰,我之前看的一些设计模式的书和资料,从心啊在看来都太死板了

他这里是使用反射的方式去生成类的,是传入参数,class,而不是传入具体类,请注意这个虚线号,不是继承,也不是实现,是一个构成。

这篇关于spring5.0源码-容器源码阅读笔记的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/491477

相关文章

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟&nbsp;开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚&nbsp;第一站:海量资源,应有尽有 走进“智听

【学习笔记】 陈强-机器学习-Python-Ch15 人工神经网络(1)sklearn

系列文章目录 监督学习:参数方法 【学习笔记】 陈强-机器学习-Python-Ch4 线性回归 【学习笔记】 陈强-机器学习-Python-Ch5 逻辑回归 【课后题练习】 陈强-机器学习-Python-Ch5 逻辑回归(SAheart.csv) 【学习笔记】 陈强-机器学习-Python-Ch6 多项逻辑回归 【学习笔记 及 课后题练习】 陈强-机器学习-Python-Ch7 判别分析 【学

Java ArrayList扩容机制 (源码解读)

结论:初始长度为10,若所需长度小于1.5倍原长度,则按照1.5倍扩容。若不够用则按照所需长度扩容。 一. 明确类内部重要变量含义         1:数组默认长度         2:这是一个共享的空数组实例,用于明确创建长度为0时的ArrayList ,比如通过 new ArrayList<>(0),ArrayList 内部的数组 elementData 会指向这个 EMPTY_EL

系统架构师考试学习笔记第三篇——架构设计高级知识(20)通信系统架构设计理论与实践

本章知识考点:         第20课时主要学习通信系统架构设计的理论和工作中的实践。根据新版考试大纲,本课时知识点会涉及案例分析题(25分),而在历年考试中,案例题对该部分内容的考查并不多,虽在综合知识选择题目中经常考查,但分值也不高。本课时内容侧重于对知识点的记忆和理解,按照以往的出题规律,通信系统架构设计基础知识点多来源于教材内的基础网络设备、网络架构和教材外最新时事热点技术。本课时知识

如何在Visual Studio中调试.NET源码

今天偶然在看别人代码时,发现在他的代码里使用了Any判断List<T>是否为空。 我一般的做法是先判断是否为null,再判断Count。 看了一下Count的源码如下: 1 [__DynamicallyInvokable]2 public int Count3 {4 [__DynamicallyInvokable]5 get

工厂ERP管理系统实现源码(JAVA)

工厂进销存管理系统是一个集采购管理、仓库管理、生产管理和销售管理于一体的综合解决方案。该系统旨在帮助企业优化流程、提高效率、降低成本,并实时掌握各环节的运营状况。 在采购管理方面,系统能够处理采购订单、供应商管理和采购入库等流程,确保采购过程的透明和高效。仓库管理方面,实现库存的精准管理,包括入库、出库、盘点等操作,确保库存数据的准确性和实时性。 生产管理模块则涵盖了生产计划制定、物料需求计划、

K8S(Kubernetes)开源的容器编排平台安装步骤详解

K8S(Kubernetes)是一个开源的容器编排平台,用于自动化部署、扩展和管理容器化应用程序。以下是K8S容器编排平台的安装步骤、使用方式及特点的概述: 安装步骤: 安装Docker:K8S需要基于Docker来运行容器化应用程序。首先要在所有节点上安装Docker引擎。 安装Kubernetes Master:在集群中选择一台主机作为Master节点,安装K8S的控制平面组件,如AP

Spring框架5 - 容器的扩展功能 (ApplicationContext)

private static ApplicationContext applicationContext;static {applicationContext = new ClassPathXmlApplicationContext("bean.xml");} BeanFactory的功能扩展类ApplicationContext进行深度的分析。ApplicationConext与 BeanF

论文阅读笔记: Segment Anything

文章目录 Segment Anything摘要引言任务模型数据引擎数据集负责任的人工智能 Segment Anything Model图像编码器提示编码器mask解码器解决歧义损失和训练 Segment Anything 论文地址: https://arxiv.org/abs/2304.02643 代码地址:https://github.com/facebookresear

Spring 源码解读:自定义实现Bean定义的注册与解析

引言 在Spring框架中,Bean的注册与解析是整个依赖注入流程的核心步骤。通过Bean定义,Spring容器知道如何创建、配置和管理每个Bean实例。本篇文章将通过实现一个简化版的Bean定义注册与解析机制,帮助你理解Spring框架背后的设计逻辑。我们还将对比Spring中的BeanDefinition和BeanDefinitionRegistry,以全面掌握Bean注册和解析的核心原理。