Spring的主要思想体现在IOC及AOP上;本文是LZ自己在看Spring源码时的一些记录,大家也可以跟着这个思路看一看Spring的一些源码,从主线上了解其原理;
本文的主线:就是Spring通过加载一个配置文件,生成一个BeanFactory的过程;
这是实现IOC的基础,读取配置,生成BeanFactory。所以不会在意一些小细节或跟主线不重要的类,不然看源码就会很迷糊。因为继承体系复杂,子类复用父类的方法较多,在各个类中跳跃会比较多。现在从源码上捋捋Spring是怎么实现IOC的;
IOC是将对象的创建和依赖关系交给容器,即将对象BEAN的创建及依赖反转到框架中;
如下的例子就是一般我们测试Spring的时候写的小方法:
public class MainApp {、
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
HelloWorld obj = (HelloWorld) context.getBean("helloWorld");
obj.getMessage();
}
}
还有如下的各种形式去生成BeanFactory
BeanFactory beanFactory=newFileSystemXmlApplicationContext("classpath:applicationContext.xml");
上面的ApplicationContext是BeanFactory的子类,所以其实质是一样的;所以我们的主线就是通过这个FileSystemXmlApplicationContext类,看他是如何加载配置生成Bean的容器的。
首先需要介绍各个重要的类或接口:
BeanFactory类 源码:
public interface BeanFactory {
String FACTORY_BEAN_PREFIX = "&";
Object getBean(String name) throws BeansException;
<T> T getBean(String name, Class<T> requiredType) throws BeansException;
<T> T getBean(Class<T> requiredType) throws BeansException;
Object getBean(String name, Object... args) throws BeansException;
boolean containsBean(String name); boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
boolean isTypeMatch(String name, Class targetType) throws NoSuchBeanDefinitionException;
Class<?> getType(String name) throws NoSuchBeanDefinitionException; String[] getAliases(String name);
}
BeanFactory 是最上层的接口,也就是俗称的IOC容器的祖宗,各种IOC容器都只是它的实现或者为了满足特别需求的扩展实现,包括我们平时用的最多的ApplicationContext。从上面的方法就可以看出,这些工厂的实现最大的作用就是根据bean的名称亦或类型等等,来返回一个bean的实例。
BeanDefinition接口
顾名思义,这个便是spring中的bean定义接口,所以其实我们工厂里持有的bean定义,就是一堆这个或者其实现类和子接口。这个接口并非直接的祖宗接口,他所继承的两个接口一个是core下面的AttributeAccessor,继承这个接口就以为这我们的bean定义接口同样具有处理属性的能力,而另外一个是beans下面的BeanMetadataElement,字面翻译这个接口就是bean的元数据元素,它可以获得bean的配置定义的一个元素。在XML文件中来说,就是会持有一个bean标签。
认识了BeanFactory和BeanDefinition,一个是IOC的核心工厂接口,一个是IOC的bean定义接口,上章提到说我们无法让BeanFactory持有一个Map<String,Object>来完成bean工厂的功能,是因为spring的初始化是可以控制的,可以到用的时候才将bean实例化供开发者使用,除非我们将bean的lazy-init属性设置为true,初始化bean工厂时采用延迟加载。
知道了上述两个接口,我相信不少人甚至不看源码都已经猜到spring是如何做的了。没错,就是让bean工厂持有一个Map<String,BeanDefinition>,这样就可以在任何时候我们想用哪个bean,取到它的bean定义,我们就可以创造出一个新鲜的实例。
接口当然不可能持有这样一个对象,那么这个对象一定是在BeanFactory的某个实现类或者抽象实现类当中所持有的,来看DefaultListableBeanFactory。
DefaultListableBeanFactory 类是很重要的一个类;
其源码中持有一个beanDefinitionMap来存储配置中读入的各个bean,其getBean的各种重载方法就是用来从map中读取数据,然后通过一系列复杂的判断返回或实例化成对象返回真正的Bean。这个是后续的事,现在先不看。
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
看到这个Map,应该就大体清楚了一切,当然,下面从代码的角度,看看Spring是如何实现将配置实始化成BeanFactory的
1.从FileSystemXmlApplicationContext类开始,我记录下主要方法,将源码的脉络记录下来。其构造方法中通过传入的配置文档路径生成Bean容器,其中执行refresh()方法
refresh()方法具体实现在AbstractApplicationContext中,当前类是继承下来的,Spring的继承体系很复杂,但是通过继承关系及类图就可以找到,refresh源码如下:
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
prepareRefresh();
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
prepareBeanFactory(beanFactory);
try {
postProcessBeanFactory(beanFactory);
invokeBeanFactoryPostProcessors(beanFactory);
registerBeanPostProcessors(beanFactory);
initMessageSource();
initApplicationEventMulticaster();
onRefresh();
registerListeners();
finishBeanFactoryInitialization(beanFactory);
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
destroyBeans();
cancelRefresh(ex);
throw ex;
}
finally {
resetCommonCaches();
}
}
}
继续跟进看obtainFreshBeanFactory()方法,具体实现还在AbstractApplicationContext类中,获取更新过的BeanFactory,那应该在这个方法里面会将原来容器里的bean按新的配置更新,然后返回BeanFactory;
如下:
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
refreshBeanFactory();
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (logger.isDebugEnabled()) {
logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
}
return beanFactory;
}
AbstractRefreshableApplicationContext类中的refreshBeanFactory(); 更新beanFactory然后获取beanFactory(),接下来看如何更新容器的;
在其子类AbstractRefreshableApplicationContext中是具体实现,如下:
protected 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);
}
}
如果存在beanFactory,先销毁,然后再清空引用;然后createBeanFactory();创建一个beanFactory,之后进行一些设置并加载BeanDefinitions(),现在看来最重要的应该就是如何加载BeanDefinition了,先看一下,是具体是创建的哪个BeanFactory类的实例,然后再细细分晰各个方法;
createBeanFactory方法里返回的是DefaultListableBeanFactory的实例,那然后就是对这个实例的填充了;
beanFactory.setSerializationId(getId());方法是将这个实例PUT入静态MAP中;
loadBeanDefinitions的具体实现在 AbstractXmlApplicationContext.java中,此类是FileSystemXmlApplicationContext的父类,下面是源码:
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// Create a new XmlBeanDefinitionReader for the given BeanFactory.
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// Configure the bean definition reader with this context's
// resource loading environment.
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// Allow a subclass to provide custom initialization of the reader,
// then proceed with actually loading the bean definitions.
initBeanDefinitionReader(beanDefinitionReader);
loadBeanDefinitions(beanDefinitionReader);
}
跟踪代码,最后在XmlBeanDefinitionReader类中实现了loadBeanDefinitions(EncodedResource encodedResource)是加载实现;
最终在如下方法实现加载beanDefinition
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
int countBefore = getRegistry().getBeanDefinitionCount();
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}
具本类DefaultBeanDefinitionDocumentReader.java实现了registerBeanDefinitions方法,其中调用了
doRegisterBeanDefinitions(root)方法,进行加载DOM文件;可后续的方法中可以加载自定义及默认的文件;
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recurse
doRegisterBeanDefinitions(ele);
}
}
节点元素分成四类:import,alias,bean,nested_beans
在BeanDefinitionReaderUtils.java类中,是对BEAN配置的加载,如下我们看下是怎么实现的;
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
// Register bean definition under primary name.
String beanName = definitionHolder.getBeanName();
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
// Register aliases for bean name, if any.
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
registry.registerAlias(beanName, alias);
}
}
}
registry其实是之前的beanFactory实例;
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
实现将beanName对应的BeanDefinition注册进Beanfactory;
此方法的实现在DefaultListableBeanFactory.java中,及这个beanFactory实现了很多重要的方法;
最后的bean放在如下的MAP中;
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
所以如上所分析,一个应用中,可以有多个beanFactory,一个beanFactory中有一个MAP来存入BEAN,beanName不能重复,且可配置可否覆盖bean;
相关推荐
以下是常见的C++笔试面试题及其核心知识点解析,帮助您系统复习
计算机短期培训教案.pdf
计算机二级Access笔试题库.pdf
下是一份关于C++毕业答辩的心得总结,内容涵盖技术准备、答辩技巧和注意事项,供参考
内容概要:本文档详细介绍了英特尔为苹果公司构建的基于智能处理单元(IPU)的Cassandra集群的技术验证(PoC)。主要内容涵盖IPU存储用例、已建存储PoC、MEV到MMG400的过渡、苹果构建IPU-Cassandra集群的动机以及PoC开发进展。文档还探讨了硬件配置、软件环境设置、性能调优措施及其成果,特别是针对延迟和吞吐量的优化。此外,文档展示了六节点Cassandra集群的具体架构和测试结果,强调了成本和复杂性的降低。 适合人群:对分布式数据库系统、NoSQL数据库、IPU技术感兴趣的IT专业人员和技术管理人员。 使用场景及目标:适用于希望了解如何利用IPU提升Cassandra集群性能的企业技术人员。主要目标是展示如何通过IPU减少服务器部署的成本和功耗,同时提高数据处理效率。 其他说明:文档中涉及的内容属于机密级别,仅供特定授权人员查阅。文中提到的技术细节和测试结果对于评估IPU在大规模数据中心的应用潜力至关重要。
计算机二级考试C语言题.pdf
计算机发展史.pdf
计算机仿真技术系统的分析方法.pdf
yolo编程相关资源,python编程与YOLO算法组成的坐姿检测系统,功能介绍: 一:实时检测学生错误坐姿人数 二:通过前端阿里云平台显示上传数据,实现数据可视化
办公室网安全监控uptime-kuma,docker镜像离线压缩包
计算机课程设计-网络编程项目源码.zip
将该dll包放入项目并引用,可以操作打印机
杰奇2.3内核淡绿唯美小说网站源码 PC+手机版 自动采集 全站伪静态,送10.1版本关关采集器
计算机辅助教学.pdf
内容概要:本文详细介绍了如何利用天文相机和其他相关硬件设备搭建一套高画质、高帧率的流星监控系统,以及针对红色精灵闪电这一特殊自然现象的捕捉方法。文中不仅涵盖了硬件的选择标准如CMOS靶面尺寸、量子效率等重要参数,还提供了基于Python和OpenCV实现的基本监控代码示例,包括亮度突变检测、运动检测算法等关键技术点。此外,对于安装位置的选择、供电方式、成本控制等方面也有具体的指导建议。 适用人群:对天文摄影感兴趣的爱好者,尤其是希望捕捉流星和红色精灵闪电等瞬时天文现象的专业人士或业余玩家。 使用场景及目标:适用于希望搭建个人天文观测站,用于科学研究或个人兴趣爱好的场景。目标是能够稳定可靠地捕捉到流星和红色精灵闪电等难以捉摸的天文现象,为研究提供高质量的数据资料。 其他说明:文中提到的一些技术和方法虽然较为复杂,但对于有一定编程基础和技术动手能力的人来说是非常实用的参考资料。同时,文中提供的省钱技巧也为预算有限的用户提供了一些有价值的建议。
时间序列分析-基于R(第2版)习题数据
内容概要:本文详细介绍了如何使用LabVIEW通过网口读取阿特拉斯PM4000控制器的扭矩值。主要内容涵盖开放式通讯协议的理解、阿特拉斯调试软件和测试软件的应用、LabVIEW程序的具体实现步骤,包括初始化网络连接、发送读取扭矩值命令、接收并解析扭矩值数据,以及关闭网络连接。文中还提供了多个调试技巧和注意事项,如硬件接线配置、数据解析方法、常见错误及其解决办法,以及性能优化建议。 适合人群:从事工业自动化领域的工程师和技术人员,尤其是那些需要集成阿特拉斯设备并与之进行数据交互的专业人士。 使用场景及目标:适用于需要实时监控和采集阿特拉斯PM4000控制器扭矩值的工业应用场景,旨在提高数据采集效率和准确性,确保设备运行状态的良好监测。 其他说明:文中提供的代码片段和调试经验有助于快速定位和解决问题,提升开发效率。此外,还强调了数据解析过程中需要注意的细节,如字节序问题和超时设置等。
计算机二级VB重点知识点解析.pdf
计算机二级c语言(操作题105套其余参考).pdf