`
javahacker2
  • 浏览: 43474 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Spring框架源码学习(一) IOC的BEAN加载

 
阅读更多
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;
分享到:
评论

相关推荐

    Spring IoC源码深度剖析开源架构源码2021.pdf

    标题《Spring IoC源码深度剖析开源架构源码2021.pdf》和描述《Spring IoC源码深度剖析开源架构源码2021.pdf》表明该文档主要面向于分析Spring框架中控制反转(IoC)容器的核心源码,解析和理解其内部的工作机制及...

    Spring源码分析_Spring_IOC

    在探讨Spring框架的核心组件之一——IOC(Inversion of Control,控制反转)容器之前,首先需要理解它在Spring框架中的角色与地位。对于Spring的使用者而言,IOC容器不仅是管理Bean(即应用中的对象实例)生命周期的...

    springBean加载过程源码解析文档,附有代码类名和行数

    Spring Bean 加载过程是 Spring 框架中最核心的部分之一,涉及到 ApplicationContext 的初始化、Bean 的加载和注册等过程。在 Spring Boot 应用程序中,SpringApplication 负责加载和管理 Bean。 SpringApplication...

    Spring ioc源码解读

    Spring框架的核心功能之一便是依赖注入(Dependency Injection, DI),而这一功能主要通过IoC容器来实现。在Spring框架中,IoC容器负责管理应用对象的生命周期、配置和装配。简单来说,IoC容器就是一个工厂,它可以...

    Spring IOC源码解读

    Spring IOC,即Inversion of Control(控制反转),是Spring框架的核心特性之一,它负责管理和装配应用程序中的对象。...理解并掌握Spring的IOC源码,对于深入学习Spring框架以及提升系统设计能力具有重要意义。

    【框架源码篇 01】Spring源码-手写IOC

    在本系列的第一篇中,我们将深入探讨Spring框架的核心特性——依赖注入(Dependency Injection,简称DI),并通过手动实现一个简易的IOC容器来理解其工作原理。Spring框架是Java开发中的基石,它提供了许多功能,如...

    Spring框架系列(7) - Spring IOC实现原理详解之IOC初始化流程.doc

    Spring 框架采用的 IOC 模式是通过一个容器来管理对象之间的依赖关系。 本文的目标是分析 Spring 框架如何实现将资源配置(以 xml 配置为例)通过加载、解析、生成 BeanDefination 并注册到 IOC 容器中的。 IOC ...

    Spring框架源码的下载

    在本文中,我们将探讨Spring框架源码的下载以及学习源码的重要性。 首先,下载Spring框架源码是深入理解其工作原理的关键步骤。通过阅读源码,开发者可以了解到Spring如何实现其核心功能,如IoC(Inversion of ...

    Spring4 IOC 示例源码

    Spring4 IOC(Inversion of Control,控制...通过这个Spring4 IOC示例源码的学习,不仅可以加深对Spring框架的理解,还能提升在实际项目中的应用能力。在实践中不断探索和掌握,将有助于成为一名更优秀的Java开发者。

    Spring源码学习六:bean初始化1

    在Spring框架中,Bean的初始化是其生命周期中的关键步骤,特别是在单例模式下,Spring容器会确保在应用程序启动时创建并初始化所有非延迟加载的单例Bean。在本篇文章中,我们将深入探讨Spring源码中关于Bean初始化的...

    Spring源码分析.pdf

    在 Spring 框架中,IOC 容器扮演着核心角色,本文将深入分析 Spring 源码,了解 IOC 容器的实现机制和基本原理。 一、IOC 容器 IOC容器是 Spring 框架的核心组件之一,它提供了一个统一的方式来管理应用程序中的...

    spring IOC学习源码

    Spring IOC(Inversion of Control,控制反转)是Spring框架的核心特性,它极大地简化了Java应用的开发。在本文中,我们将深入探讨Spring IOC的概念、工作原理,并通过源码分析来理解其实现方式。 首先,控制反转...

    基于Maven构建的Spring IoC源码实例

    **Spring IoC(Inversion of Control)**,即控制反转,是Spring框架的核心特性之一,它使得应用程序的组件不再需要自行管理依赖关系,而是由Spring容器来负责管理和装配。Maven则是一个强大的项目管理和构建工具,...

    Spring源码深度解析第二版

    XmlBeanFactory是Spring框架的容器实现之一,主要用于加载和管理Bean。XmlBeanFactory的基础实现主要包括了加载Bean、实例化Bean和依赖注入等。 2.5.1 自己直文件封装 XmlBeanFactory的自己直文件封装主要包括了...

    Java仿Spring框架IOC控制反转利用反射简单实现(源码)

    本篇文章将详细探讨如何通过反射机制来实现一个简单的Java IOC容器,以此来模仿Spring框架的行为。 首先,理解控制反转(IOC)的概念至关重要。在传统的编程模式中,对象通常自行创建依赖对象,而在IOC中,这种创建...

    Spring的IoC容器初始化源码解析

    ### Spring的IoC容器初始化源码解析 #### 一、Spring框架的核心——IoC容器 Spring框架是一个开源的轻量级Java开发框架,其核心功能...对于希望深入学习Spring框架的开发者来说,理解IoC容器的工作原理是非常重要的。

    Java进阶之SpringIoC源码深度剖析共19页.pd

    【标题】"Java进阶之SpringIoC源码深度剖析共19页.pd" 提供的是一项关于Java开发中的高级主题,特别是针对Spring框架的依赖注入(Inversion of Control,IoC)部分的深入研究。Spring IoC是Spring框架的核心特性之一...

    Spring2的IOC利器统管Bean世界.rar

    在IT行业中,Spring框架是Java开发中的一个基石,尤其在企业级应用开发中扮演着至关重要的角色。Spring2版本引入了IOC(Inversion of Control,控制反转)这一核心概念,极大地提升了代码的可维护性和可扩展性。本...

    一步步实现Spring框架(一)项目搭建

    在本系列教程的第一部分,我们将着手构建一个Spring框架的基础架构,以深入了解其工作原理和内在机制。这将有助于我们深入理解Java编程,并增强对依赖注入(Dependency Injection, DI)和控制反转(Inversion of ...

    Spring_IOC详解.pdf

    Spring框架作为Java企业级应用开发的基石,其核心组件之一便是IOC(Inverse of Control)容器。IOC容器负责管理应用程序中的对象及其依赖关系,实现了控制反转,极大地简化了Java应用的开发流程。本文将基于Spring...

Global site tag (gtag.js) - Google Analytics