测试类

//Person的属性类
package com.bruis.learnspring.context;


import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Person {
    private String name;
    private int age;


}

主函数

package com.bruis.learnspring;

import com.bruis.learnspring.context.Person;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

@SpringBootApplication
public class LearnspringApplication {

    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        Person person = (Person)context.getBean("person");
        System.out.println(person);
    }

}

xml配置文件

<?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.xsd">

    <bean id="person" class="com.bruis.learnspring.context.Person">
        <property name="age" value="23"/>
        <property name="name" value="JOKE"/>
    </bean>

</beans>

ClassPathXmlApplicationContext.class
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, @Nullable ApplicationContext parent) throws BeansException {
        super(parent);
        this.setConfigLocations(configLocations);
        if (refresh) {
            //对你配置过的xml文件经行扫描
            this.refresh();
        }

    }

接下来是refresh函数里面的内容,也是Spring容器的核心刷新方法

AbstractApplicationContext.class
//Spring容器的核心刷新方法,完成IoC容器的初始化和启动过程
public void refresh() throws BeansException, IllegalStateException {
        //用同步锁保证,线程安全,避免多次创建或重复创建
        synchronized(this.startupShutdownMonitor) {
            //设置容器状态,用来初始化资源的
            this.prepareRefresh();
            //获取或创建BeanFactory
            ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
            //配置BeanFactory的基本特性
            this.prepareBeanFactory(beanFactory);

            try {
                this.postProcessBeanFactory(beanFactory);
                //关键步骤:执行BeanFactoryPostProcessor,处理bean定义(如解析@Configuration、@Bean等)
                this.invokeBeanFactoryPostProcessors(beanFactory);
                this.registerBeanPostProcessors(beanFactory);

                //消息的初始化
                this.initMessageSource();
                this.initApplicationEventMulticaster();

                //完成初始化
                this.onRefresh();
                this.registerListeners();
                // 关键步骤:初始化所有非懒加载的单例bean
                this.finishBeanFactoryInitialization(beanFactory);
                this.finishRefresh();
            } catch (BeansException var9) {
                BeansException ex = var9;
                if (this.logger.isWarnEnabled()) {
                    this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + ex);
                }
                //Spring容器刷新过程中出现异常时的清理和状态恢复操作
                this.destroyBeans();
                this.cancelRefresh(ex);
                throw ex;
            } finally {
                //清理各种缓存,防止内存泄漏,确保下次刷新时从干净状态开始。
                this.resetCommonCaches();
            }

        }
    }

懒加载:在spring启动时不会加载对应bean的实例,只有在第一次使用的时候会创建对应bean的实例。

//// 核心逻辑:执行所有的BeanFactoryPostProcessor
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
        PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, this.getBeanFactoryPostProcessors());
        if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean("loadTimeWeaver")) {
            beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
            beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
        }

    }

AbstractRefreshableApplicationContext.class

/*
    通知子类刷新内部bean工厂,初始化BeanFactory并进行XML文件的解析、读取。obtain就是指获得的含义,这个方法obtaiinFreshBeanFactory正是实现BeanFactory的地方,也就是经过这个方法,ApplicationContext就已经拥有了BeanFactory的全部功能(也就是BeanFactory包含在了Spring容器里了)。
*/
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
    this.refreshBeanFactory();
    ...
    return beanFactory;
}
/*
此实现执行此上下文的基础bean工厂的实际刷新,关闭先前的bean工厂(如果有)并初始化上一个生命周期的下一阶段的新bean工厂。

经过debug,最后从refreshBeanFactory()方法返回后,this也就是AbstractRefreshableApplicationContext实例对象

*/
@Override
protected final void refreshBeanFactory() throws BeansException {
    //如果有bean工厂,则关闭该工厂
	if (hasBeanFactory()) {
		destroyBeans();
		closeBeanFactory();
	}
	try {
	    //创建一个新bean工厂,这里的DefaultListableBeanFactory就是前面将的Spring核心类,这个类真的很重要!
		DefaultListableBeanFactory beanFactory = createBeanFactory();
		//为了序列化指定ID,如果需要的话,让这个BeanFactory从ID反序列化掉BeanFactory对象
		beanFactory.setSerializationId(getId());
		//定制beanFactory,设置相关属性,包括是否允许覆盖同名称的不同定义的对象以及循环依赖以及设置@Autowired和@Qualifier注解解析器QualifierAnnotationAutowireCandidateResolver
		customizeBeanFactory(beanFactory);
		//加载bean定义信息,这一步实际上就从XML配置文件里的bean信息给读取到了Factory里了。
		loadBeanDefinitions(beanFactory);
		synchronized (this.beanFactoryMonitor) {
			this.beanFactory = beanFactory;
		}
	}
	catch (IOException ex) {
	    ...
	}	
}

在beanFactory变量里面存放着一个ConcurrentHashMap变量,用于存放着person这个KV键值对,Key为person,Value为一个ArrayList的变量,里面存放着person的两个属性:age、name。

那么是如何将这对应的属性值拷贝到实例对象里的。

AbstractXmlApplicationContext.class

protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
    //为给定的BeanFactory创建一个新的XmlBeanDefinitionReader
    XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

    //// 2. 配置读取器的基础环境
    beanDefinitionReader.setEnvironment(this.getEnvironment());
    beanDefinitionReader.setResourceLoader(this);
    beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

    //允许子类提供reader的自定义初始化
    this.initBeanDefinitionReader(beanDefinitionReader);
    //真正加载bean定义信息
    this.loadBeanDefinitions(beanDefinitionReader);
}

protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
    //// 方式1:通过Resource对象加载(更灵活)
	Resource[] configResources = getConfigResources();
	if (configResources != null) {
		reader.loadBeanDefinitions(configResources);
	}
    //// 方式2:通过配置文件路径加载(常用方式)
	String[] configLocations = getConfigLocations();
	if (configLocations != null) {
		reader.loadBeanDefinitions(configLocations);
	}
}

在refreshBeanFactory中已经把DefaultListableBeanFactory给初始化了,Xml的文件配置也在loadBeanDefinitions里面初始化完成了,这样就可以对配置文件经行读取了。

AbstractBeanDefinitionReader.class

//多位置配置文件的批量加载功能
public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
        Assert.notNull(resources, "Resource array must not be null");
        int count = 0;
        Resource[] var3 = resources;
        int var4 = resources.length;

        for(int var5 = 0; var5 < var4; ++var5) {
            Resource resource = var3[var5];
            count += this.loadBeanDefinitions((Resource)resource);
        }

        return count;
}

//从指定的资源位置加载bean定义
public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
        ResourceLoader resourceLoader = this.getResourceLoader();
        if (resourceLoader == null) {
            ........
        } else {
            int count;
            if (resourceLoader instanceof ResourcePatternResolver) {
                //// 资源模式匹配可用
                try {
                    Resource[] resources = ((ResourcePatternResolver)resourceLoader).getResources(location);
                    count = this.loadBeanDefinitions(resources);
                    if (actualResources != null) {
                        Collections.addAll(actualResources, resources);
                    }

                    if (this.logger.isTraceEnabled()) {
                        this.logger.trace("Loaded " + count + " bean definitions from location pattern [" + location + "]");
                    }

                    return count;
                } catch (IOException var6) {
                    ...........
                }
            } else {
                //// 只能通过绝对URL加载单个资源
                Resource resource = resourceLoader.getResource(location);
                count = this.loadBeanDefinitions((Resource)resource);
                if (actualResources != null) {
                    actualResources.add(resource);
                }

                if (this.logger.isTraceEnabled()) {
                    this.logger.trace("Loaded " + count + " bean definitions from location [" + location + "]");
                }

                return count;
            }
        }
}



PathMatchingResourcePatternResolver.class

//将不同的资源路径模式转换为具体的Resource对象数组
@Override
public Resource[] getResources(String locationPattern) throws IOException {
	Assert.notNull(locationPattern, "Location pattern must not be null");
	if (locationPattern.startsWith(CLASSPATH_ALL_URL_PREFIX)) {
		// 类路径资源(可能有多个同名资源)
		if (getPathMatcher().isPattern(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()))) {
			// 类路径资源模式
			return findPathMatchingResources(locationPattern);
		}
		else {
			// 具有给定名称的所有类路径资源
			return findAllClassPathResources(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()));
		}
	}
	else {
		// 通常只在前缀后面查找一个模式,并且在Tomcat之后只有“* /”分隔符之后的“war:”协议。
		int prefixEnd = (locationPattern.startsWith("war:") ? locationPattern.indexOf("*/") + 1 :
				locationPattern.indexOf(':') + 1);
		if (getPathMatcher().isPattern(locationPattern.substring(prefixEnd))) {
			// 文件模式
			return findPathMatchingResources(locationPattern);
		}
		else {
			// 具有给定名称的单个资源
			return new Resource[] {getResourceLoader().getResource(locationPattern)};
		}
	}
}

XmlBeanDefinitionReader.class

/*
    从XML配置文件中获取bean定义信息
*/
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
	Assert.notNull(encodedResource, "EncodedResource must not be null");
    ...
	Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
	if (currentResources == null) {
		currentResources = new HashSet<>(4);
		this.resourcesCurrentlyBeingLoaded.set(currentResources);
	}
	...
	try {
		InputStream inputStream = encodedResource.getResource().getInputStream();
		try {
			InputSource inputSource = new InputSource(inputStream);
			if (encodedResource.getEncoding() != null) {
				inputSource.setEncoding(encodedResource.getEncoding());
			}
			//获取到读取xml配置文件的InputStream流后,进行BeanDefinitions的加载
			return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
		}
		finally {
			inputStream.close();
		}
	}
	catch (IOException ex) {
	    ...
	}
	finally {
		currentResources.remove(encodedResource);
		if (currentResources.isEmpty()) {
			this.resourcesCurrentlyBeingLoaded.remove();
		}
	}
}

/*
    真正从xml配置文件中加载Bean定义信息
*/
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
		throws BeanDefinitionStoreException {

	try {
	    //获取xml的配置信息并封装为Document对象
		Document doc = doLoadDocument(inputSource, resource);
		return this.registerBeanDefinitions(doc, resource);
	}
	catch (BeanDefinitionStoreException ex) {
	    ...
	}
}

    在加载完Bean的定义信息以后,就要解析xml配置里面的bean的具体属性值

    DefaultBeanDefinitionDocumentReader.class

    /*
        任何嵌套的<beans>元素都将导致此方法的递归。为了正确传播和保留beans的default属性,需要跟踪当前(父)委托,该委托可以为null。
    */
    protected void doRegisterBeanDefinitions(Element root) {
        BeanDefinitionParserDelegate parent = this.delegate;
        this.delegate = this.createDelegate(this.getReaderContext(), root, parent);
        if (this.delegate.isDefaultNamespace(root)) {
            String profileSpec = root.getAttribute("profile");
            if (StringUtils.hasText(profileSpec)) {
                String[] specifiedProfiles = StringUtils.tokenizeToStringArray(profileSpec, ",; ");
                if (!this.getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
                    if (this.logger.isInfoEnabled()) {
                        this.logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec + "] not matching: " + this.getReaderContext().getResource());
                    }
    
                    return;
                }
            }
        }
        
        this.preProcessXml(root);
        //解析beanDefinitions信息,经过这个方法,beanFactory中就会保存从xml配置文件中解析而来的信息
        this.parseBeanDefinitions(root, this.delegate);
        this.postProcessXml(root);
        this.delegate = parent;
    }
    
    /*
        解析文档中根级别的元素:import、alias、bean
    */
    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)) {
                        //解析默认元素
                        this.parseDefaultElement(ele, delegate);
                    } else {
                        delegate.parseCustomElement(ele);
                    }
                }
            }
        } else {
            delegate.parseCustomElement(root);
        }
    
    }
    
    private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
        if (delegate.nodeNameEquals(ele, "import")) {
            this.importBeanDefinitionResource(ele);
        } else if (delegate.nodeNameEquals(ele, "alias")) {
            this.processAliasRegistration(ele);
        } else if (delegate.nodeNameEquals(ele, "bean")) {
            //读取到xml配置文件的<bean>节点
            this.processBeanDefinition(ele, delegate);
        } else if (delegate.nodeNameEquals(ele, "beans")) {
            this.doRegisterBeanDefinitions(ele);
        }
    }
     
    protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
        BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
        ...
    }

    BeanDefinitionParserDelegate.class

    /*
        解析bean定义本身,而不考虑名称或别名,如果解析期间出错则返回null。
    */
    @Nullable
    public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
        String id = ele.getAttribute("id");
        String nameAttr = ele.getAttribute("name");
        List<String> aliases = new ArrayList();
        if (StringUtils.hasLength(nameAttr)) {
            String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, ",; ");
            aliases.addAll(Arrays.asList(nameArr));
        }
    
        String beanName = id;
        if (!StringUtils.hasText(id) && !aliases.isEmpty()) {
            beanName = (String)aliases.remove(0);
            ...
        }
    
        if (containingBean == null) {
            this.checkNameUniqueness(beanName, aliases, ele);
        }
        
        //终于,这里要解析beanDefinition了
        AbstractBeanDefinition beanDefinition = this.parseBeanDefinitionElement(ele, beanName, containingBean);
        if (beanDefinition != null) {
            if (!StringUtils.hasText(beanName)) {
                try {
                    if (containingBean != null) {
                        beanName = BeanDefinitionReaderUtils.generateBeanName(beanDefinition, this.readerContext.getRegistry(), true);
                    } else {
                        beanName = this.readerContext.generateBeanName(beanDefinition);
                        String beanClassName = beanDefinition.getBeanClassName();
                        if (beanClassName != null && beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() && !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
                            aliases.add(beanClassName);
                        }
                    }
                    ...
                } catch (Exception var9) {
                    ....
                }
            }
            
            //别名数组
            String[] aliasesArray = StringUtils.toStringArray(aliases);
            return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
        } else {
            return null;
        }
    }
    
    @Nullable
    public AbstractBeanDefinition parseBeanDefinitionElement(Element ele, String beanName, @Nullable BeanDefinition containingBean) {
        this.parseState.push(new BeanEntry(beanName));
        String className = null;
        if (ele.hasAttribute("class")) {
            className = ele.getAttribute("class").trim();
        }
    
        String parent = null;
        if (ele.hasAttribute("parent")) {
            parent = ele.getAttribute("parent");
        }
    
        try {
            //创建BeanDefinition
            AbstractBeanDefinition bd = this.createBeanDefinition(className, parent);
            this.parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
            bd.setDescription(DomUtils.getChildElementValueByTagName(ele, "description"));
            this.parseMetaElements(ele, bd);
            this.parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
            this.parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
            
            //通过构造器解析参数值
            this.parseConstructorArgElements(ele, bd);
            //通过property的value解析值,本文的程序xml就是通过property属性设置bean的值的,最终被这一方法所解析出来。
            this.parsePropertyElements(ele, bd);
            
            this.parseQualifierElements(ele, bd);
            bd.setResource(this.readerContext.getResource());
            bd.setSource(this.extractSource(ele));
            AbstractBeanDefinition var7 = bd;
            return var7;
        } catch (ClassNotFoundException var13) {
            ...
        }
        return null;
    }
    
    
    public void parsePropertyElements(Element beanEle, BeanDefinition bd) {
        NodeList nl = beanEle.getChildNodes();
        for(int i = 0; i < nl.getLength(); ++i) {
            Node node = nl.item(i);
            if (this.isCandidateElement(node) && this.nodeNameEquals(node, "property")) {
                //解析出参数值来,这里就真正的讲age的23,和name的bruis值解析出来并防止在一个组装的类里面存放着。因为这里有两个bean,所以要循环调用两次parsePropertyElement()方法
                this.parsePropertyElement((Element)node, bd);
            }
        }
    }
    
    public void parsePropertyElement(Element ele, BeanDefinition bd) {
        String propertyName = ele.getAttribute("name");
        if (!StringUtils.hasLength(propertyName)) {
            this.error("Tag 'property' must have a 'name' attribute", ele);
        } else {
            this.parseState.push(new PropertyEntry(propertyName));
    
            try {
                if (!bd.getPropertyValues().contains(propertyName)) {
                    Object val = this.parsePropertyValue(ele, bd, propertyName);
                    PropertyValue pv = new PropertyValue(propertyName, val);
                    this.parseMetaElements(ele, pv);
                    pv.setSource(this.extractSource(ele));
                    
                    //就是这一步,将K为age、name,值分别为23、bruis的KV对存放在了Spring容器里。
                    bd.getPropertyValues().addPropertyValue(pv);
                    return;
                }
            } finally {
                this.parseState.pop();
            }
    
        }
    }

    这个类里的函数就是对bean属性加载属性值的一个过程,最后返回到最开始的refresh方法里面,把这个beanFactory放到Spring容器里面,完成属性的注入

    总结一下

    在Spring容器的初始化过程中

    1.就是找到对应的配置文件的过程,读取的你要配置的xml文件,就是BeanDefinition的资源定位。

    2.就是BeanDefinition的载入过程,把用户想要实例化的POJO对象的数据结构保存成IOC容器内部的数据结构,从而确保IOC能够更方便的对POJO进行管理。

    3.属性注入过程, 就是IOC容器注册的BeanDefinition的过程,是通过用BeanDefinitionRegistry接口的实现来完成的,我们也知道了IOC容器会把BeanDefinition注册到ConcurrentHashMap当中。

    通过上面的过程,我们知道了Spring是如何初始化xml文件里的对象以及属性。

    最后提问

    Spring容器的生命周期分为几个阶段?Spring容器的初始化发生在什么时候?Spring容器的销毁过程又发生了什么?

    1.创建,使用,销毁

    2.(1).Web应用启动时  (2)独立应用启动时 (3)Spring Boot应用启动时 (4)测试环境

    3.反向顺序:后创建的Bean先销毁   优雅关闭:确保资源正确释放    异常容忍:单个Bean销毁失败不影响其他Bean销毁


    Spring容器什么时候读取xml配置文件?并且把配置信息读取?Spring容器用什么数据结构存储用于创建bean的K/V信息?

    1.refresh()方法中的obtainFreshBeanFactory()时读取XML配置文件,这时会创建XmlBeanDefinitionReader来解析XML文件。

    2.解析过程中遇到<bean>标签时,Spring会把每个bean的配置信息封装成BeanDefinition对象。

    3.这些BeanDefinition对象以beanName作为key,存储在DefaultListableBeanFactory中的一个ConcurrentHashMap<String, BeanDefinition>里,这个map就是Spring容器内部用于存储所有bean创建蓝图的核心数据结构。
    Spring容器获取了用于创建bean的K/V信息后,在什么时候去创建并且初始化bean?

    1.Spring容器在refresh()方法的finishBeanFactoryInitialization(beanFactory)这一步去创建和初始化bean。这个方法会触发所有非懒加载单例bean的实例化,包括调用构造函数创建对象、进行依赖注入、执行初始化回调等完整生命周期。

    Logo

    码道开发者社区,聚焦华为云码道 CodeArts 代码智能体,沉淀 Agent、Skill、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

    更多推荐