深入理解-Spring-之源码剖析IOC(一)

简介:

引言

作为Java程序员,Spirng我们再熟悉不过,可以说比自己的女朋友还要亲密,每天都会和他在一起,然而我们真的了解spring吗?

我们都知道,Spring的核心是IOC和AOP,但楼主认为,如果从这两个核心中挑选一个更重要的,那非IOC莫属。AOP也是依赖于IOC,从某些角度讲,AOP就是IOC的一个扩展功能。

什么是IOC? IOC解决了什么问题?IOC的原理是什么?Spring的IOC是怎么实现的?今天我们将会将这几个问题一起解决。

1. 什么是IOC?

控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。

其中最常见的方式叫做依赖注入(Dependency Injection,简称DI),还有一种方式叫“依赖查找”(Dependency Lookup)。

通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体,将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。

这是维基百科的说法,楼主按照自己的思路分析一下IOC,楼主认为,分析一个问题,或者说证明一个事情,有2种方法,一是正向验证,即按照该事物的逻辑去验证正确性,还有一种是反向验证,证明该事物是否正确。

楼主想反向证明IOC,我们提出一个疑问:如果没有IOC会怎么样?

想象一下,在没有IOC的世界里,我们的系统会有大量的对象,这些对象有些是数据,有些是处理数据的。

并且各个对象相互依赖,我们需要控制他们的依赖关系,什么时候new ,什么时候销毁,什么时候需要单例,什么时候不需要单例等等这些问题。

你能想象吗?当你一个系统有几千个类,你如何管理他们的依赖关系,说起依赖,我们可能会想起 maven 或者 gradle,他们管理着我们的 jar 包依赖,而我们的系统代码呢?

但是如果有一种东西,他能够帮助我们管理所有类的创建,销毁,是否是单例模式,类与类之间的多层依赖关系(在我们的MVC框架中,3层依赖已经是最少),那该多好,我们只需要关注业务逻辑。于是 ,IOC诞生了。

2. IOC 解决了什么问题?

简单来说, IOC 解决了类与类之间的依赖关系。程序员将控制类与类之间依赖的权利交给了IOC,即:控制被反转了。

3. IOC 的原理是什么?

其实 IOC 的原理很简单,底层就是java的反射。给定一个字符串能创建一个实例,利用set方法对实例的依赖进行注入。

我们来一段代码证明一下是多么的简单:

2bb8bc0140376abbd0299515341dfb3e35c83d17

可以看到该代码非常的简单,但实际上IOC 就是这么简单,在真正的开发中,我们只需要在配置文件给定一个类名字符串,就什么都不用管了,对象就会创建出来,系统启动完毕之后,我们只需要直接使用该对象就好了,不必自己去new。

解决了我们的对象创建问题。我们通过反射调用该方法的setText方法,完成了依赖注入。我们不再需要去new,然后去set,IOC 已经为我们做好了一切。

介绍完了几个基本知识,其实都是为我们今天的重头戏做准备,Spring的IOC是怎么实现的?

4. Spring的IOC是怎么实现的?

这是一个浩大的问题,虽然底层实现可能就那么几行代码,但楼主说过,所有框架的底层技术都很简单,但是我们的框架大师们为了软件的健壮性,扩展性和性能,做了无数的优化,我们的系统源码也就变得越来越复。

spirng的 release 版本至今已经到了 5.0.3,和最初的 interface21 已经有了翻天复地的变化,现在也有了springboot, springcloud,俨然一个庞大的spring家族,想分析源码的我们该从哪里下手呢?

万剑归宗,始于一处。

Bean。

我们要研究spring的IOC,我们要了解的就是spring的bean,这是spring的核心的核心。虽然bena依赖着context 模块提供bean的环境,依赖core 提供着一系列强化的工具。

但今天我们不关心,我们只关系bean。只关心IOC。就像这个信息过载,技术不断更新的时代,程序们需要有自己的判断,自己需要研究什么,什么是最重要的?扯远了。

在开始研究源码之前,楼主有必要介绍一下IOC的一些核心组件,否则一旦进入源码,就会被细节捆住,无法从宏观的角度理解IOC。

BeanFactory:这是IOC容器的接口定义,如果将IOC容器定位为一个水桶,那么BeanFactory 就定义了水桶的基本功能,能装水,有把手。这是最基本的,他的实现类可以拓展水桶的功能。
ApplicationContext:这是我们最常见的,上面我们说水桶,BeanFactory是最基本的水桶,而 ApplicationContext 则是扩展后的水桶,它通过继承 MessageSource,ResourceLoader,ApplicationEventPublisher 接口,在BeanFactory 简单IOC容器的基础上添加了许多对高级容器的支持。

BeanDefinition:我们知道,每个bean都有自己的信息,各个属性,类名,类型,是否单例,这些都是bena的信息,spring中如何管理bean的信息呢?对,就是 BeanDefinition, Spring通过定义 BeanDefinition 来管理基于Spring的应用中的各种对象以及他们直接的相互依赖关系。BeanDefinition 抽象了我们对 Bean的定义,是让容器起作用的主要数据类型。对 IOC 容器来说,BeanDefinition 就是对依赖反转模式中管理的对象依赖关系的数据抽象,也是容器实现依赖反转功能的核心数据结构

4.1. 搭建源码研究环境

楼主研究源码的思路有2个,一个是创建一个简单的spirng maven 项目,还有一个是直接从 spirng 的github 上 clone 源码。

这是楼主的普通 maven 项目:

c8823d533a141fd90589bc084a0c36638856848b

这是楼主的 clone 的 spring-framework 源码:

ddd5d19182910542eb6ea9fbf59ab3dc971ca501

注意:clone 该源码的时候,楼主很艰辛,因为要科学上网,否则 gradle 无法下载依赖会导致报错。如果各位无法科学上网,可以使用 maven 项目勉强学习。

4.2. 开启研究源码第一步

我们打开spring-framework 源码。

还记的我们初学spring的写的第一行代码是什么吗?

5d0b336d8eeb58af1c904bd644c679cc2c7b23af

怎么写配置文件楼主就不说了,我们回忆一下我们最初学spring的时候,虽然现在都是2017年了,我们都用springboot,都是用注解了,但spring的核心代码还是 spring 之父 Rod Johnson 在 2001 年写的。所以不影响我们学习spring 的核心。

我们仔细看看该代码(该代码位置必须在spring-context模块下):

 

package test; import org.springframework.beans.tests.Person; import org.springframework.context.ApplicationContext; import org.springframework.context.support.FileSystemXmlApplicationContext; public class Test {
public static void main (String[] args) throws ClassNotFoundException {

ApplicationContext ctx = new FileSystemXmlApplicationContext
( "spring-beans/src/test/resources/beans.xml" );
System.out.println( "number : " + ctx.getBeanDefinitionCount());
((Person) ctx.getBean( "person" )).work();
}
}

熟悉的 ApplicatContext ,看名字是应用上下文,什么意思呢?就是spirng整个运行环境的背景,好比一场舞台剧,ApplicatContext 就是舞台,IOC 管理的Bean 就是演员,Core 就是道具。而ApplicatContext 的标准实现是 FileSystemXmlApplicationContext。

该类的构造方法中包含了容器的启动,IOC的初始化。所以我们 debug 启动该项目,运行main方法。打好断点。

4.3. 从 FileSystemXmlApplicationContext 开始剖析

从这里开始,我们即将进入复杂的源码。

我们进入 FileSystemXmlApplicationContext 的构造方法:

cb59886a5976c6c5ae08cf4bdbf72171329d581b

可以看到该构造方法被重载了,可以传递 configLocation 数组,也就是说,可以传递过个配置文件的地址。默认刷新为true,parent 容器为null。进入另一个构造器:

0c101a3859bc8aa47d7bd14e6eda9b0b8fa1a81e

该构造器做了2件事情,一是设置配置文件,二是刷新容器,我们可以感觉到,refresh 方法才是初始化容器的重要方法。我们进入该方法看看:该方法是 FileSystemXmlApplicationContext 的父类 AbstractApplicationContext 的方法。

4.4. AbstractApplicationContext.refresh() 方法实现

 

/**
*
* 1. 构建Be按Factory,以便产生所需要的bean定义实例
* 2. 注册可能感兴趣的事件
* 3. 创建bean 实例对象
* 4. 触发被监听的事件
*
*/
@Override
public void refresh () throws BeansException, IllegalStateException {
synchronized ( this .startupShutdownMonitor) {
// 为刷新准备应用上下文
prepareRefresh();
// 告诉子类刷新内部bean工厂,即在子类中启动refreshBeanFactory()的地方----创建bean工厂,根据配置文件生成bean定义
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 在这个上下文中使用bean工厂
prepareBeanFactory(beanFactory);

try {
// 设置BeanFactory的后置处理器
postProcessBeanFactory(beanFactory);
// 调用BeanFactory的后处理器,这些后处理器是在Bean定义中向容器注册的
invokeBeanFactoryPostProcessors(beanFactory);
// 注册Bean的后处理器,在Bean创建过程中调用
registerBeanPostProcessors(beanFactory);
//对上下文的消息源进行初始化
initMessageSource();
// 初始化上下文中的事件机制
initApplicationEventMulticaster();
// 初始化其他的特殊Bean
onRefresh();
// 检查监听Bean并且将这些Bean向容器注册
registerListeners();
// 实例化所有的(non-lazy-init)单件
finishBeanFactoryInitialization(beanFactory);
// 发布容器事件,结束refresh过程
finishRefresh();
}

catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn( "Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// 为防止bean资源占用,在异常处理中,销毁已经在前面过程中生成的单件bean
destroyBeans();
// 重置“active”标志
cancelRefresh(ex);
throw ex;
}

finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}

可以说该方法就是整个IOC容器初始化的所有逻辑。因此,如果读懂了该方法的每一行代码,就了解了spring的整个功能。该方法的调用层次之深可以想象一下。

我们先大致说下该方法的步骤:

构建BeanFactory,以便于产生所需的 Bean。
注册可能感兴趣的事件。
常见Bean实例对象。
触发被监听的事件。

我们一个个来看: 首先构建BeanFactory,在哪里实现的呢?也就是obtainFreshBeanFactory 方法,返回了一个ConfigurableListableBeanFactory,该方法调用了 refreshBeanFactory() ,该方法是个模板方法,交给了 AbstractRefreshableApplicationContext 去实现。我们看看该方法实现:

 

@Override
protected final void refreshBeanFactory () throws BeansException {
if (hasBeanFactory()) {
// 如果存在就销毁
destroyBeans();
closeBeanFactory();
}
try {
// new DefaultListableBeanFactory(getInternalParentBeanFactory())
DefaultListableBeanFactory beanFactory = createBeanFactory();
// 设置序列化
beanFactory.setSerializationId(getId());
// 定制的BeanFactory
customizeBeanFactory(beanFactory);
// 使用BeanFactory加载bean定义 AbstractXmlApplicationContext
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的创建过程,首先判断是否存在了 BeanFactory,如果有则销毁重新创建,调用 createBeanFactory 方法,该方法中就是像注释写的:创建了 DefaultListableBeanFactory ,也既是说,DefaultListableBeanFactory 就是 BeanFactory的默认实现。然后我们看到一个很感兴趣的方法。

就是 loadBeanDefinitions(beanFactory),看名字是加载 Definitions,这个我们很感兴趣,我们之前说过, Definition 是核心之一,代表着 IOC 中的基本数据结构。该方法也是个抽象方法,默认实现是 AbstractXmlApplicationContext ,我们看看该方法实现:

 

@Override
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 ,用于读取XML中配置,设置了环境,资源加载器,最后初始化,加载。可以说,该方法将加载,解析Bean的定义,也就是把用户定义的数据结构转化为 IOC容器中的特定数据结构。而我们关心的则是最后一行的 loadBeanDefinitions(beanDefinitionReader) 方法。

 

protected void loadBeanDefinitions (XmlBeanDefinitionReader reader) throws BeansException, IOException {
Resource[] configResources = getConfigResources();
if (configResources != null ) {
reader.loadBeanDefinitions(configResources);
}
String[] configLocations = getConfigLocations();
if (configLocations != null ) { // 加载给定的路径文件
reader.loadBeanDefinitions(configLocations);
}
}

该方法会略过第一个if块,进入第二个if块,进入 AbstractBeanDefinitionReader.loadBeanDefinitions(String… locations) 方法,该方法内部循环加载配置文件:

 

@Override
public int loadBeanDefinitions (String... locations) throws BeanDefinitionStoreException {
Assert.notNull(locations, "Location array must not be null" );
int counter = 0 ;
for (String location : locations) {
counter += loadBeanDefinitions(location);
}
return counter;
}

我们关心的是 for 循环中的loadBeanDefinitions(location)方法,该方法核心逻辑在 AbstractBeanDefinitionReader.loadBeanDefinitions(String location, @Nullable SetactualResources) 方法中:

 

public int loadBeanDefinitions (String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
ResourceLoader resourceLoader = getResourceLoader();
if (resourceLoader == null ) {
throw new BeanDefinitionStoreException(
"Cannot import bean definitions from location [" + location + "]: no ResourceLoader available" );
}

if (resourceLoader instanceof ResourcePatternResolver) {
// Resource pattern matching available.
try {
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
int loadCount = loadBeanDefinitions(resources); // 根据配置文件加载bean定义
if (actualResources != null ) {
for (Resource resource : resources) {
actualResources. add (resource);
}
}
if (logger.isDebugEnabled()) {
logger.debug( "Loaded " + loadCount + " bean definitions from location pattern [" + location + "]" );
}
return loadCount;
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"Could not resolve bean definition resource pattern [" + location + "]" , ex);
}
}
else {
// Can only load single resources by absolute URL.
Resource resource = resourceLoader.getResource(location);
int loadCount = loadBeanDefinitions(resource);
if (actualResources != null ) {
actualResources. add (resource);
}
if (logger.isDebugEnabled()) {
logger.debug( "Loaded " + loadCount + " bean definitions from location [" + location + "]" );
}
return loadCount;
}
}

该方法首先获取资源加载器,然后进入 if 块,获取资源数组,调用 loadBeanDefinitions(resources) ,根据配置文件加载Bean定义。

进入该方法后,循环加载resource 资源数组,进入 loadBeanDefinitions(resource) 方法中,最后进入到 XmlBeanDefinitionReader.loadBeanDefinitions(EncodedResource encodedResource) 方法中,该方法主要调用 doLoadBeanDefinitions(inputSource, encodedResource.getResource()) 方法。我们有必要看看该方法实现:

 

protected int doLoadBeanDefinitions (InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
Document doc = doLoadDocument(inputSource, resource);
return registerBeanDefinitions(doc, resource); // 真正的注册bean
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (SAXParseException ex) {
throw new XmlBeanDefinitionStoreException(resource.getDescription(),
"Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid" , ex);
}
catch (SAXException ex) {
throw new XmlBeanDefinitionStoreException(resource.getDescription(),
"XML document from " + resource + " is invalid" , ex);
}
catch (ParserConfigurationException ex) {
throw new BeanDefinitionStoreException(resource.getDescription(),
"Parser configuration exception parsing XML from " + resource, ex);
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(resource.getDescription(),
"IOException parsing XML document from " + resource, ex);
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(resource.getDescription(),
"Unexpected exception parsing XML document from " + resource, ex);
}
}

可以看出该方法主要逻辑是根据输入流加载 Document 文档对象,然后根据得到的文档对象注册到容器,因此我们看看倒是是如何注册到容器的。

该方法首先创建一个 BeanDefinitionDocumentReader, 用于读取 BeanDefinition,该对象会调用 registerBeanDefinitions(doc, createReaderContext(resource)) 方法。

该方法最后从文档对象获取根元素,最后调用 DefaultBeanDefinitionDocumentReader.doRegisterBeanDefinitions(root) 进行注册。

该方法最核心的逻辑就是调用 parseBeanDefinitions(root, this.delegate),我们看看该方法具体实现:

 

protected void parseBeanDefinitions (Element root, BeanDefinitionParserDelegate delegate ) {
if ( delegate .isDefaultNamespace(root)) {
NodeList nl = root.getChildNodes();
for ( int i = 0 ; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
if ( delegate .isDefaultNamespace(ele)) {
parseDefaultElement(ele, delegate ); // 解析
}
else {
delegate .parseCustomElement(ele);
}
}
}
}
else {
delegate .parseCustomElement(root);
}
}

该方法就是一个解析XML 文档的步骤,核心是调用 parseDefaultElement(ele, delegate),我们进入该方法查看,该方法调用了 processBeanDefinition(ele, delegate) 方法进行解析。我们有必要看看该方法:

 

protected void processBeanDefinition (Element ele, BeanDefinitionParserDelegate delegate ) {
BeanDefinitionHolder bdHolder = delegate .parseBeanDefinitionElement(ele); // 解析
if (bdHolder != null ) {
bdHolder = delegate .decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// Register the final decorated instance.
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()); // 开始注册
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error( "Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'" , ele, ex);
}
// Send registration event.
getReaderContext().fireComponentRegistered( new BeanComponentDefinition(bdHolder));
}
}

首先创建一个 BeanDefinitionHolder,该方法会调用 BeanDefinitionReaderUtils.registerBeanDefinition 方法, 最后执行容器通知事件。该静态方法实现如下:

 

*/
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 );
}
}
}

可以看到首先从bean的持有者那里获取了beanName,然后调用 registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()), 将bena的名字和 BeanDefinition 注册,我们看看最后的逻辑:

 

@Override
public void registerBeanDefinition (String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {

Assert.hasText(beanName, "Bean name must not be empty" );
Assert.notNull(beanDefinition, "BeanDefinition must not be null" );

if (beanDefinition instanceof AbstractBeanDefinition) {
try {
((AbstractBeanDefinition) beanDefinition).validate();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Validation of bean definition failed" , ex);
}
}

BeanDefinition oldBeanDefinition;

oldBeanDefinition = this .beanDefinitionMap. get (beanName);
if (oldBeanDefinition != null ) {
if (!isAllowBeanDefinitionOverriding()) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
"': There is already [" + oldBeanDefinition + "] bound." );
}
else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {
// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
if ( this .logger.isWarnEnabled()) {
this .logger.warn( "Overriding user-defined bean definition for bean '" + beanName +
"' with a framework-generated bean definition: replacing [" +
oldBeanDefinition + "] with [" + beanDefinition + "]" );
}
}
else if (!beanDefinition. equals (oldBeanDefinition)) {
if ( this .logger.isInfoEnabled()) {
this .logger.info( "Overriding bean definition for bean '" + beanName +
"' with a different definition: replacing [" + oldBeanDefinition +
"] with [" + beanDefinition + "]" );
}
}
else {
if ( this .logger.isDebugEnabled()) {
this .logger.debug( "Overriding bean definition for bean '" + beanName +
"' with an equivalent definition: replacing [" + oldBeanDefinition +
"] with [" + beanDefinition + "]" );
}
}
this .beanDefinitionMap.put(beanName, beanDefinition);
}
else {
if (hasBeanCreationStarted()) {
// Cannot modify startup-time collection elements anymore (for stable iteration)
synchronized ( this .beanDefinitionMap) {
this .beanDefinitionMap.put(beanName, beanDefinition);
List<String> updatedDefinitions = new ArrayList<>( this .beanDefinitionNames.size() + 1 );
updatedDefinitions.addAll( this .beanDefinitionNames);
updatedDefinitions. add (beanName);
this .beanDefinitionNames = updatedDefinitions;
if ( this .manualSingletonNames.contains(beanName)) {
Set<String> updatedSingletons = new LinkedHashSet<>( this .manualSingletonNames);
updatedSingletons. remove (beanName);
this .manualSingletonNames = updatedSingletons;
}
}
}
else {
// Still in startup registration phase // 最终放进这个map 实现注册
this .beanDefinitionMap.put(beanName, beanDefinition); // 走这里 // private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
this .beanDefinitionNames. add (beanName);
this .manualSingletonNames. remove (beanName);
}
this .frozenBeanDefinitionNames = null ;
}

if (oldBeanDefinition != null || containsSingleton(beanName)) {
resetBeanDefinition(beanName);
}
}

该方法可以说是注册bean的最后一步,将beanName和 beanDefinition 放进一个 ConcurrentHashMap(256) 中。

那么这个 beanDefinition 是时候创建的呢? 就是在 DefaultBeanDefinitionDocumentReader.processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) 方法中,在这里创建了 BeanDefinitionHolder, 而该实例中解析Bean并将Bean 保存在该对象中。

所以称为持有者。该实例会调用 parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) 方法,该方法用于解析XML文件并创建一个 BeanDefinitionHolder 返回,该方法会调用 parseBeanDefinitionElement(ele, beanName, containingBean) 方法, 我们看看该方法:

 

@ Nullable
public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, @Nullable BeanDefinition containingBean) {

this .parseState.push( new BeanEntry(beanName));

String className = null ;
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
className = ele.getAttribute(CLASS_ATTRIBUTE).trim(); // 类全限定名称
}
String parent = null ;
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}

try {
AbstractBeanDefinition bd = createBeanDefinition(className, parent); // 创建

parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));

parseMetaElements(ele, bd);
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());

parseConstructorArgElements(ele, bd);
parsePropertyElements(ele, bd);
parseQualifierElements(ele, bd);

bd.setResource( this .readerContext.getResource());
bd.setSource(extractSource(ele));

return bd;
}
catch (ClassNotFoundException ex) {
error( "Bean class [" + className + "] not found" , ele, ex);
}
catch (NoClassDefFoundError err) {
error( "Class that bean class [" + className + "] depends on not found" , ele, err);
}
catch (Throwable ex) {
error( "Unexpected failure during bean definition parsing" , ele, ex);
}
finally {
this .parseState.pop();
}

return null ;
}

我们看看该方法,可以看到,该方法从XML元素中取出 class 元素,然后拿着className调用 createBeanDefinition(className, parent) 方法,该方法核心是调用 BeanDefinitionReaderUtils.createBeanDefinition 方法,我们看看该方法:


 

public static AbstractBeanDefinition createBeanDefinition(
@Nullable String parentName, @Nullable String className, @Nullable ClassLoader classLoader) throws ClassNotFoundException {

GenericBeanDefinition bd = new GenericBeanDefinition(); // 泛型的bean定义,也就是最终生成的bean定义。
bd.setParentName(parentName);
if (className != null ) {
if (classLoader != null ) {
bd.setBeanClass(ClassUtils.forName(className, classLoader)); // 设置Class 对象
}
else {
bd.setBeanClassName(className);
}
}
return bd;
}

该方法很简单,创建一个 Definition 的持有者,然后设置该持有者的Class对象,该对象就是我们在配置文件中配置的Class对象。最后返回。

到这里,我们一走完了第一步,创建bean工厂,生成Bean定义。但还没有实例化该类。


原文发布时间为:2018-09-6
本文作者:莫那鲁道
本文来自云栖社区合作伙伴“ Java架构沉思录”,了解相关信息可以关注“ Java架构沉思录”。
相关文章
|
1月前
|
XML 缓存 Java
Spring源码之 Bean 的循环依赖
循环依赖是 Spring 中经典问题之一,那么到底什么是循环依赖?简单说就是对象之间相互引用, 如下图所示: 代码层面上很好理解,在 bean 创建过程中 class A 和 class B 又经历了怎样的过程呢? 可以看出形成了一个闭环,如果想解决这个问题,那么在属性填充时要保证不二次创建 A对象 的步骤,也就是必须保证从容器中能够直接获取到 B。 一、复现循环依赖问题 Spring 中默认允许循环依赖的存在,但在 Spring Boot 2.6.x 版本开始默认禁用了循环依赖 1. 基于xml复现循环依赖 定义实体 Bean java复制代码public class A {
|
2月前
|
监控 数据可视化 关系型数据库
微服务架构+Java+Spring Cloud +UniApp +MySql智慧工地系统源码
项目管理:项目名称、施工单位名称、项目地址、项目地址、总造价、总面积、施工准可证、开工日期、计划竣工日期、项目状态等。
307 6
|
2月前
|
Java 关系型数据库 数据库连接
Spring源码解析--深入Spring事务原理
本文将带领大家领略Spring事务的风采,Spring事务是我们在日常开发中经常会遇到的,也是各种大小面试中的高频题,希望通过本文,能让大家对Spring事务有个深入的了解,无论开发还是面试,都不会让Spring事务成为拦路虎。
35 1
|
1月前
|
Java 测试技术 数据库连接
【Spring源码解读!底层原理高级进阶】【下】探寻Spring内部:BeanFactory和ApplicationContext实现原理揭秘✨
【Spring源码解读!底层原理高级进阶】【下】探寻Spring内部:BeanFactory和ApplicationContext实现原理揭秘✨
|
1月前
|
Java 数据库连接 API
【Spring】1、Spring 框架的基本使用【读取配置文件、IoC、依赖注入的几种方式、FactoryBean】
【Spring】1、Spring 框架的基本使用【读取配置文件、IoC、依赖注入的几种方式、FactoryBean】
49 0
|
5天前
|
Java 关系型数据库 MySQL
一套java+ spring boot与vue+ mysql技术开发的UWB高精度工厂人员定位全套系统源码有应用案例
UWB (ULTRA WIDE BAND, UWB) 技术是一种无线载波通讯技术,它不采用正弦载波,而是利用纳秒级的非正弦波窄脉冲传输数据,因此其所占的频谱范围很宽。一套UWB精确定位系统,最高定位精度可达10cm,具有高精度,高动态,高容量,低功耗的应用。
一套java+ spring boot与vue+ mysql技术开发的UWB高精度工厂人员定位全套系统源码有应用案例
|
20天前
|
XML Java 数据格式
Spring(一)IOC小案例
Spring(一)IOC小案例
|
1月前
|
Java Spring
使用spring实现邮件的发送(含测试,源码,注释)
使用spring实现邮件的发送(含测试,源码,注释)
7 0
|
1月前
|
XML Java 数据格式
Spring 的奇幻起源:从 IoC 容器到 Bean 的魔法世界 (下)
Spring 的奇幻起源:从 IoC 容器到 Bean 的魔法世界
|
1月前
|
XML Java 数据格式
Spring 的奇幻起源:从 IoC 容器到 Bean 的魔法世界 (上)
Spring 的奇幻起源:从 IoC 容器到 Bean 的魔法世界 (上)