内容简介:作为一个java程序员,保守估计一年里也都有300天要和Spring有亲密接触~~像我这种怕是每天都要撸撸Spring,所以这次也要做个深入了解!这次就来看看Spring是怎么注:阅读本文时一定要在IDE进行跳转
作为一个 java 程序员,保守估计一年里也都有300天要和Spring有亲密接触~~像我这种怕是每天都要撸撸Spring,所以这次也要做个深入了解!这次就来看看Spring是怎么 初始化IoC容器的
注:阅读本文时一定要在IDE进行跳转
我们都是知道Spring为企业级应用提供了丰富的功能,而这些功能底层都依赖于底层最核心的两种特性IOC和AOP。
IOC实现里主要包括两部分,一个是IOC容器初始,另外一个是依赖注入,由于两部分是相对对立的部分,所以分成不同文章讲解,本篇主要 讲述IOC容器的初始化 。
一、IoC的概念
控制反转(Inversion of Control,缩写为 IoC ),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的 耦合度 。其中最常见的方式叫做 依赖注入 (Dependency Injection,简称 DI ),还有一种方式叫“依赖查找”(Dependency Lookup)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体,将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。
上面这个概念是来自维基百科,在 Expert Spring MVC and Web Flow 和 Expert One-on-One J2EE without EJB 等书中都也是将依赖注入看作是IoC的一种方式。不过有些地方会把IoC和DI看成一个概念(例如Spring in Action、Spring揭密等书),不过没关系,不影响我们理解就可以。
白话版
A类的正常的运行需要B类
没有IoC之前我们在使用A类的某个方法时,总会依赖于B类的一些功能,这样我们就要去new个B对象,有时还要考虑什么时候销毁,什么时候使用单例模式等等,类少了还好管理,这要是多起来真是再聪明的人怕也是要十个头九个大了, 而且A、B之间的依赖关系使各代码紧密耦合,一旦B类的出现问题,或者某天干脆不用B了用C,那是不是A类里的new B()全得换成new C()?想象都觉得累…
有了IoC之后,对象创建都交给第三方容器处理,A中的B通过注入来完成,B出问题了,或者需要换成C了,只要把注入的B换成C就可以(现实开发中B、C可以实现相同的接口解决~所以啊,Spring是面向接口编程鸭)。
Tips
- Expert One-on-One J2EE without EJB 这本书是spring爸爸Rod Johnson写的,进入Spring的BeanFactory类里面看看作者就是他,哈哈!
- 浅谈控制反转与依赖注入 :这是我看过最好的一篇对控制反转的解释,强烈推荐!
二、IoC容器初始化
预备内容
本节只讲解IoC容器的初始化,其中包括创建容器和将bean装入到容器中,下面这三件事是该部分的核心:
BeanDefinition的Resource定位
BeanDefinition的载入和解析
BeanDefinition在容器中的注册
因为Spring的IoC容器实现太复杂了,各种类之间的调用很容易就让我们陷入到细节之中,结果就走的太远忘记了为啥要出发了,本文主要将述容器初始化时最主要的三件事。
先了解几个概念:
BeanFactory:这是IOC容器的接口定义,提供了IoC最基本的功能,如果说容器是个汽车工厂,那么这个结构就规定了汽车工厂最基本的功能,能储存零件,能组装汽车。
public interface BeanFactory { /** * 使用容器获取bean时,添加转义自符&可以获取到FactoryBean本身而吧是Factory产生的对象 */ String FACTORY_BEAN_PREFIX = "&"; /** * 通过bean的名字来获取bean */ Object getBean(String name) throws BeansException; /** * 通过bean的类型和类型来获取bean */ <T> T getBean(String name, Class<T> requiredType) throws BeansException; /** * 通过bean的类型来获取bean */ <T> T getBean(Class<T> requiredType) throws BeansException; /** * 通过名字和参数来获取bean */ Object getBean(String name, Object... args) throws BeansException; /** * 是否包含名字为name的bean */ boolean containsBean(String name); /** * 是否单例 */ boolean isSingleton(String name) throws NoSuchBeanDefinitionException; /** * 是否原型 */ boolean isPrototype(String name) throws NoSuchBeanDefinitionException; /** * 名字为name的bean是否是targetType类型 */ boolean isTypeMatch(String name, Class<?> targetType) throws NoSuchBeanDefinitionException; /** * 获取名字为name的bean类型 */ Class<?> getType(String name) throws NoSuchBeanDefinitionException; /** * 获取名字为name的bean的别名字集合 */ String[] getAliases(String name); }
ApplicationContext:升级版汽车厂,除了上面的功能,还提供很多人性化的服务,继承了 MessageSource,ResourceLoader,ApplicationEventPublisher等等接口,在BeanFactory 简单IoC容器的基础上添加了许多对高级容器的支持。
public interface ApplicationContext extends EnvironmentCapable,ListableBeanFactory,HierarchicalBeanFactory,MessageSource,ApplicationEventPublisher,ResourcePatternResolver { /** * 返回该上下文的id(unique) */ String getId(); /** * 返回上下文所属应用的名字 */ String getApplicationName(); /** * 返回这个上下文友好的名字 */ String getDisplayName(); /** * 返回上下文首次加载的时间 */ long getStartupDate(); /** * 返回父类上下文 */ ApplicationContext getParent(); /** * 功能性的暴露自动装配的工厂,并不常用 */ AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException; }
这里面的方法也不多,主要的方法都在继承的接口里
BeanDifinition:储存 Spring中 Bean的信息,包括各属性名,类名,是否单例等,抽象了我们对 Bean的定义,是让容器起作用的主要数据类型。对 IOC 容器来说,BeanDefinition 就是对控制反转模式中管理的对象依赖关系的数据抽象。
接下来正式进入IoC容器初始化的分析,以FileSystemXmlApplicationContext为例,下面是FileSystemXmlApplicationContext的继承关系~(这形状,满满的爱啊,哈哈)
BeanDefinition的Resource定位
public class FileSystemXmlApplicationContext extends AbstractXmlApplicationContext { /** * 无参构造 */ public FileSystemXmlApplicationContext() { } /** * 传入父类上下文 */ public FileSystemXmlApplicationContext(ApplicationContext parent) { super(parent); } /** * 核心构造方法,其他几个都基于本构造方法 * configLocations 传入xml配置文件位置集合 * refresh 是否自动刷新容器(是refresh方法的调用,初始化上下文的核心方法) * parent 父类上下文 * 1.传入配置文件地址 * 2.刷新容器 */ public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException { super(parent); setConfigLocations(configLocations); if (refresh) { refresh(); } } /** * 通过给定的路径在文件系统中定位BeanDefinition并返回一个FileSystemResource * 这个方法是BeanDefinitionReader的loadBeanDefinition中被调用, * loadBeanDefinition采用了模板模式,具体实现在不同的子类中(默认是类路径) */ @Override protected Resource getResourceByPath(String path) { if (path != null && path.startsWith("/")) { path = path.substring(1); } return new FileSystemResource(path); } }
看以看出,本类对所有configLocation都进行了处理,使所有以xml形式存在的BeanDefinition都得到了处理,其中这个refresh就最最关键点方法,接下来对refresh进行解析。
refresh是在AbstractApplicationContext中实现,理解了refresh方法,基本就理解了IoC初始化的全过程了。
public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // 刷新前准备活动 prepareRefresh(); // 关键方法构建beanFactory——>接下来会详解本方法 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // 为在这个上下文中使用beanFactory做准备 prepareBeanFactory(beanFactory); try { // 设置后置处理器 postProcessBeanFactory(beanFactory); // 调用bean的后置处理器,这些处理器在上下文中被注册为bean的形式 // Invoke factory processors registered as beans in the context. invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation. // 注册拦截bean创建的处理器 registerBeanPostProcessors(beanFactory); // Initialize message source for this context. // 为上下文初始化消息源,国际化功能 initMessageSource(); // Initialize event multicaster for this context. // 初始化上下文的时间机制 initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. // 初始化其他特殊bean在特殊上下文子类中 onRefresh(); // Check for listener beans and register them. // 检查监听的bean,并将他们注册到容器中 registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. // 初始化所有的非懒加载单件 finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. // 发布相关事件,结束refresh finishRefresh(); } catch (BeansException ex) { // Destroy already created singletons to avoid dangling resources. // 出现异常销毁beans destroyBeans(); // Reset 'active' flag. // 这个active在上面的prepare中被设置为了true cancelRefresh(ex); // Propagate exception to caller. throw ex; } } }
接下来我们详细来看一下容器的构建过程,在类AbstractRefreshableApplicationContext中
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() { // 刷新beanfactory,这个方法很重要,就是它构建的bean,【继续往里走】 refreshBeanFactory(); ConfigurableListableBeanFactory beanFactory = getBeanFactory(); if (logger.isDebugEnabled()) { logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory); } return beanFactory; }
@Override protected final void refreshBeanFactory() throws BeansException { // 如果已经存在beanfactory那就销毁掉bean并把工厂关了,避免对接下来的初始化造成影响 if (hasBeanFactory()) { destroyBeans(); closeBeanFactory(); } try { // 这里创建了一个DefaultListableBeanFactory DefaultListableBeanFactory beanFactory = createBeanFactory(); // 设置唯一id,用于序列化 beanFactory.setSerializationId(getId()); // 自定义bean工厂 customizeBeanFactory(beanFactory); // 向工厂中加载BeanDefinition,这个很重要,【继续往里走】 loadBeanDefinitions(beanFactory); synchronized (this.beanFactoryMonitor) { this.beanFactory = beanFactory; } } catch (IOException ex) { throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex); } }
loadBeanDefinitions在AbstractRefreshableApplicationContext中是个抽象方法,我直接找到了在子类AbstractXmlApplicationContext(其实有三个实现类,但是我们现在研究的是FileSystemXmlApplicationContext)中的实现。
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { // 看这名字,显然就是用这家伙对来将xml配置文件中的信息读取放到容器里的. XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); //使用这个上下文的环境资源对这个reader进行配置 beanDefinitionReader.setEnvironment(this.getEnvironment()); beanDefinitionReader.setResourceLoader(this); beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); // 允许子类个性化对这个reader初始化 initBeanDefinitionReader(beanDefinitionReader); // 然后开始真正的加载BeanDefinition,【继续往里走】 loadBeanDefinitions(beanDefinitionReader); }
这次是跳转到AbstractXmlApplicationContext里面,继续阅读
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException { // 获取配置资源~~不过直接返回了一个null,不过有子类进行重写的(如果看过HashMap源码,那肯定记得里面有几个空实现是给LinkedHashMap用的,这里同样的道理) Resource[] configResources = getConfigResources(); if (configResources != null) { reader.loadBeanDefinitions(configResources); } // 这个是类AbstractRefreshableConfigApplicationContext中的方法(跳来跳去脑壳都大了。。。) // FileSystemXmlApplicationContext在refresh前就设置了 String[] configLocations = getConfigLocations(); if (configLocations != null) { // 终于开始解析了,【继续往里走】 reader.loadBeanDefinitions(configLocations); } }
AbstractBeanDefinitionReade中的方法,就是在这里进行加载的
@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; }
还在本类里~不用跳了
public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException { // 获取资源加载器 ResourceLoader resourceLoader = getResourceLoader(); // 空就抛异常 if (resourceLoader == null) { throw new BeanDefinitionStoreException( "Cannot import bean definitions from location [" + location + "]: no ResourceLoader available"); } // 这个是用来解析classpath*:这种的路径,可以是多个配置文件 if (resourceLoader instanceof ResourcePatternResolver) { // Resource pattern matching available. try { //到这里getResource【完成了具体的定位】 Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location); // 开始载入BeanDefinition int loadCount = loadBeanDefinitions(resources); 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 { // 到这里getResource,resource接口中封装了很多与I/O相关的操作 // 至此【完成了具体的定位】 Resource resource = resourceLoader.getResource(location); // 开始载入BeanDefinition【继续往里跳】 int loadCount = loadBeanDefinitions(resource); if (actualResources != null) { actualResources.add(resource); } if (logger.isDebugEnabled()) { logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]"); } return loadCount; } }
refresh方法完成IoC的整个初始化,其中 refreshBeanFactory()方法非常的重要,本小节讲到定位,下一小节开始讲解BeanDefinition解析与加载。
BeanDefinition的载入和解析
对于IoC容器来说,这个载入过程相当于把xml中的BeanDefinition转换成一个Spring内部的数据结构的过程。IoC容器对Bean的管理和依赖注入功能是通过对其持有的BeanDefinition进行各种相关操作来实现的。这些BeanDefinition是通过一个HashMap来实现的。
承接上文,loadBeanDefinitions()是对BeanDefinition载入和解析的核心方法。具体实现在XMLBeanDefinitionReader里面。
public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException { Assert.notNull(resources, "Resource array must not be null"); int counter = 0; for (Resource resource : resources) { // 这里是循环加载,【继续往里面跳】 counter += loadBeanDefinitions(resource); } return counter; }
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException { // 对Resource进行包装,提供通过不同编码方式读取资源文件,【继续往里面跳】 return loadBeanDefinitions(new EncodedResource(resource)); }
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException { Assert.notNull(encodedResource, "EncodedResource must not be null"); if (logger.isInfoEnabled()) { logger.info("Loading XML bean definitions from " + encodedResource.getResource()); } // 获取已经加载的Resource Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get(); if (currentResources == null) { currentResources = new HashSet<EncodedResource>(4); this.resourcesCurrentlyBeingLoaded.set(currentResources); } // 解决重复依赖问题,encodedResource的equals方法已经被重写 if (!currentResources.add(encodedResource)) { throw new BeanDefinitionStoreException( "Detected cyclic loading of " + encodedResource + " - check your import definitions!"); } // 这里获取IO准备读取XML中的BeanDefinition try { InputStream inputStream = encodedResource.getResource().getInputStream(); try { InputSource inputSource = new InputSource(inputStream); if (encodedResource.getEncoding() != null) { inputSource.setEncoding(encodedResource.getEncoding()); } // 【继续往里面跳】 return doLoadBeanDefinitions(inputSource, encodedResource.getResource()); } finally { inputStream.close(); } } catch (IOException ex) { throw new BeanDefinitionStoreException( "IOException parsing XML document from " + encodedResource.getResource(), ex); } finally { currentResources.remove(encodedResource); if (currentResources.isEmpty()) { this.resourcesCurrentlyBeingLoaded.remove(); } } }
这个是在XMLBeanDefinitionReader中实现(没有进其他类)
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { try { // 这里取得XML的document对象,解析由documentLoader完成,感兴趣看以进去看看步骤 // 虽然已经对xml进行解析但是并没有按照bean的规则,所以需要继续解析 Document doc = doLoadDocument(inputSource, resource); // 这里启动的是对beanDefinition解析的详细过程,很重要的方法,【继续往里面跳】 return registerBeanDefinitions(doc, resource); } 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); } }
仍然在本类中
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { // 创建BeanDefinitionDocumentReader对document进行解析 BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); documentReader.setEnvironment(this.getEnvironment()); int countBefore = getRegistry().getBeanDefinitionCount(); // 具体的解析过程,【继续往里面跳】 documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); return getRegistry().getBeanDefinitionCount() - countBefore; }
进入DefaultBeanDefinitionDocumentReader类中,在文档元素中获取根元素,并继续调用doRegisterBeanDefinition进行注册。
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) { this.readerContext = readerContext; logger.debug("Loading bean definitions"); Element root = doc.getDocumentElement(); // 【继续往里面跳】 doRegisterBeanDefinitions(root); }
protected void doRegisterBeanDefinitions(Element root) { String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE); if (StringUtils.hasText(profileSpec)) { Assert.state(this.environment != null, "Environment must be set for evaluating profiles"); String[] specifiedProfiles = StringUtils.tokenizeToStringArray( profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS); if (!this.environment.acceptsProfiles(specifiedProfiles)) { return; } } BeanDefinitionParserDelegate parent = this.delegate; this.delegate = createDelegate(this.readerContext, root, parent); preProcessXml(root); // 对BeanDefinition进行解析,该方法的核心逻辑,【继续往里面跳】 parseBeanDefinitions(root, this.delegate); postProcessXml(root); this.delegate = parent; }
// 不难看出,这是对xml的解析过程 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); } }
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) { // import标签 if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) { importBeanDefinitionResource(ele); } // 别名 else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) { processAliasRegistration(ele); } // bean标签 else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) { // 对bean标签进行解析,【继续往里面跳】 processBeanDefinition(ele, delegate); } // beans标签 else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) { // recurse doRegisterBeanDefinitions(ele); } }
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { // 这个是BeanDefinitionHolder里装有BeanDefinition对象和beanname,别名集合等信息 // parseBeanDefinitionElement()这个方法将xml中bean的定义进行解析,有兴趣可以进去深入了解 BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); if (bdHolder != null) { bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); try { // 向IoC容器注册解析到的BeanDefinition,【继续往里面跳】 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)); } }
至此,XML中的BeanDefinition的解析和载入全部完成,接下来进入bean的注册部分。
BeanDefinition在IoC容器中的注册
经过定位和载入后,BeanDefinition已经在IoC建立起相应的数据结构,为了更友好的使用这些BeanDefinition,需要在IoC容器中将这些BeanDefinition进行注册。
该方法在BeanDefinitionReaderUtils类中
public static void registerBeanDefinition( BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException { String beanName = definitionHolder.getBeanName(); // 这个很明显是将BeanDefinition注册的方法,【继续往里面跳】 registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); // Register aliases for bean name, if any. String[] aliases = definitionHolder.getAliases(); if (aliases != null) { for (String aliase : aliases) { registry.registerAlias(beanName, aliase); } } }
跳转到DefaultListableBeanFactory类中,前面创建工厂时用的就是这个工厂
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); } } // 为了保证数据一致性,注册时加个synchronized线程锁 synchronized (this.beanDefinitionMap) { // 检查在IoC容器中是否有同名bean,有同名的还不让覆盖的就是抛异常 BeanDefinition oldBeanDefinition = this.beanDefinitionMap.get(beanName); if (oldBeanDefinition != null) { if (!this.allowBeanDefinitionOverriding) { 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()) { 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 (this.logger.isInfoEnabled()) { this.logger.info("Overriding bean definition for bean '" + beanName +"': replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]"); } } } else { this.beanDefinitionNames.add(beanName); this.frozenBeanDefinitionNames = null; } // 把BeanDefinition装到如到beanDefinitionMap中 // 【至此Spring IoC容器初始化完成~】 // beanDeinitionMap是初始长度64的ConcurrentHashMap this.beanDefinitionMap.put(beanName, beanDefinition); } resetBeanDefinition(beanName); }
到这里,注册完成。我们创建bean工厂,将BeanDefinition注册到了IoC容器持有的Map中。这些信息是控制反转的基础。
三、小结
本文开始简略解释IoC的概念,然后从FileSystemXmlApplicationContext着手,根据源码一步一步讲述从bean工厂创建到BeanDefinition在IoC中注册核心逻辑。Spring源码确实细节太多,在阅读源码过程中,一定要抓住核心逻辑。
本文是博主在学习Spring源码过程中对IoC的总结,希望对想要阅读源码但不知从何下手的同学有所帮助!如有错误希望大家指正。
本文首发于cdream个人博客
欢迎转载,转载请注明出处!
本文参考:
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- Spring IoC 容器初始化(二)
- Spring MVC 源码解析(二)— 容器初始化
- Laravel 之道第四章:步调 Laravel 容器 Application 的初始化
- C++ 的一大误区——深入解释直接初始化与复制初始化的区别
- 初始化监听端口
- 类初始化导致死锁
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。