对Spring初始化容器的底层源码分析
测试类
//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.classpublic 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的实例化,包括调用构造函数创建对象、进行依赖注入、执行初始化回调等完整生命周期。
更多推荐



所有评论(0)