摘要:前言以下源码基于版本解析。实现源码分析对于的实现,总结来说就是定位加载和注册。定位就是需要定位配置文件的位置,加载就是将配置文件加载进内存注册就是通过解析配置文件注册。下面我们从其中的一种使用的方式一步一步的分析的实现源码。
前言
以下源码基于Spring 5.0.2版本解析。
什么是IOC容器?容器,顾名思义可以用来容纳一切事物。我们平常所说的Spring IOC容器就是一个可以容纳对象的东西。IOC全名Inversion of Control,即控制反转,什么是控制反转?平时我们代码里需要创建一个对象是需要通过new操作或者反射等方式创建,也就是说现在是我们人为地创建对象,控制对象,那么控制反转的意思就显而易见了,就是将原来属于我们的控制权交由Spring框架进行管理,由Spring替我们创建对象,管理对象以及对象之间的依赖关系。当我们需要使用对象的时候直接问Spring取就可以了。
IOC实现源码分析对于Spring IOC的实现,总结来说就是定位、加载和注册。定位就是Spring需要定位配置文件的位置,加载就是将配置文件加载进内存,注册就是通过解析配置文件注册BeanDefinition。
下面我们从其中的一种使用Spring的方式一步一步的分析IOC的实现源码。我们平时编程式地使用Spring框架如下代码所示。
public class TestSpring { public static void main(String[] args) { //传入配置文件路径信息,初始化Spring IOC容器 ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml"); //从容器中获取名称为world的bean实例 context.getBean("world"); } }
所以我们分析Spring IOC实现的入口也就是ClassPathXmlApplicationContext的构造方法。ClassPathXmlApplicationContext的构造方法源码如下:
//这里是我们上述初始化IOC容器的地方 public ClassPathXmlApplicationContext(String configLocation) throws BeansException { this(new String[]{configLocation}, true, (ApplicationContext)null); } //实际调用的是这个核心方法初始化IOC容器的 public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException { //设置父容器信息 super(parent); //设置配置文件路径,方便接下来的查找 this.setConfigLocations(configLocations); //判断是否需要重新刷新IOC容器 if (refresh) { //调用父类AbstractApplicationContext的refresh方法 this.refresh(); } }
所以这里我们需要进入refresh方法分析Spring IOC是如何刷新的,源码如下:
public void refresh() throws BeansException, IllegalStateException { Object var1 = this.startupShutdownMonitor; //这里需要进行同步操作,防止多个线程同时刷新容器 synchronized(this.startupShutdownMonitor) { //刷新前的准备工作,获取容器的当时时间,同时给容器设置同步标识。 this.prepareRefresh(); //这里是IOC容器初始化的核心方法,告诉子类启动refreshBeanFactory()方法,Bean定义资源文件的载入从子类的refreshBeanFactory()方法启动 ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory(); //对容器进行一些相关设置,为BeanFactory配置容器特性,例如类加载器、事件处理器等 this.prepareBeanFactory(beanFactory); try { //为容器的某些子类指定特殊的BeanPost事件处理器 this.postProcessBeanFactory(beanFactory); //调用bean的后置处理器 this.invokeBeanFactoryPostProcessors(beanFactory); //为BeanFactory注册BeanPost事件处理器.BeanPostProcessor是Bean后置处理器,用于监听容器触发的事件 this.registerBeanPostProcessors(beanFactory); //初始化信息源,和国际化相关. this.initMessageSource(); //初始化容器事件传播器. this.initApplicationEventMulticaster(); //调用子类的某些特殊Bean初始化方法 this.onRefresh(); //为事件传播器注册事件监听器. this.registerListeners(); //初始化所有剩余的单例Bean this.finishBeanFactoryInitialization(beanFactory); //初始化容器的生命周期事件处理器,并发布容器的生命周期事件 this.finishRefresh(); } catch (BeansException var9) { if (this.logger.isWarnEnabled()) { this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9); } //销毁已创建的Bean this.destroyBeans(); //取消refresh操作,重置容器的同步标识. this.cancelRefresh(var9); throw var9; } finally { //缓存清理工作 this.resetCommonCaches(); } } }
这里我们主要分析AbstractApplicationContext的obtainFreshBeanFactory方法,源码如下:
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() { //这里使用了委派设计模式,父类定义了抽象的refreshBeanFactory()方法,具体实现调用子类容器的refreshBeanFactory()方法 refreshBeanFactory(); ConfigurableListableBeanFactory beanFactory = getBeanFactory(); if (logger.isDebugEnabled()) { logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory); } return beanFactory; }
分析AbstractApplicationContext子类AbstractRefreshableApplicationContext的refreshBeanFactory方法,源码如下:
protected final void refreshBeanFactory() throws BeansException { //如果已经有容器,销毁容器中的bean,关闭容器 if (hasBeanFactory()) { destroyBeans(); closeBeanFactory(); } try { //创建IOC容器,这里是直接实例化DefaultListableBeanFactory对象 DefaultListableBeanFactory beanFactory = createBeanFactory(); beanFactory.setSerializationId(getId()); //对IOC容器进行定制化,如设置启动参数,开启注解的自动装配等 customizeBeanFactory(beanFactory); //调用载入Bean定义的方法,主要这里又使用了一个委派模式,在当前类中只定义了抽象的loadBeanDefinitions方法,具体的实现调用子类容器 loadBeanDefinitions(beanFactory); synchronized (this.beanFactoryMonitor) { this.beanFactory = beanFactory; } } catch (IOException ex) { throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex); } }
这里主要分析Spring是如何载入Bean定义的,进入AbstractXmlApplicationContext的loadBeanDefinitions方法,源码如下:
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { // Create a new XmlBeanDefinitionReader for the given BeanFactory. //创建XmlBeanDefinitionReader,即创建Bean读取器,并通过回调设置到容器中去,容器使用该读取器读取Bean定义资源 XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); // Configure the bean definition reader with this context"s // resource loading environment. //为Bean读取器设置Spring资源加载器,AbstractXmlApplicationContext的 //祖先父类AbstractApplicationContext继承DefaultResourceLoader,因此,容器本身也是一个资源加载器 beanDefinitionReader.setEnvironment(this.getEnvironment()); beanDefinitionReader.setResourceLoader(this); //为Bean读取器设置SAX xml解析器 beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); // Allow a subclass to provide custom initialization of the reader, // then proceed with actually loading the bean definitions. //当Bean读取器读取Bean定义的Xml资源文件时,启用Xml的校验机制 initBeanDefinitionReader(beanDefinitionReader); //Bean读取器真正实现加载的方法 loadBeanDefinitions(beanDefinitionReader); }
可以看到这里通过创建Bean读取器,最后调用loadBeanDefinitions实现定位、加载和注册。loadBeanDefinitions方法的源码如下:
//Xml Bean读取器加载Bean定义资源 protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException { //获取Bean定义资源的定位 Resource[] configResources = getConfigResources(); if (configResources != null) { //Xml Bean读取器调用其父类AbstractBeanDefinitionReader读取定位 //的Bean定义资源 reader.loadBeanDefinitions(configResources); } //如果子类中获取的Bean定义资源定位为空,则获取FileSystemXmlApplicationContext构造方法中setConfigLocations方法设置的资源 String[] configLocations = getConfigLocations(); if (configLocations != null) { //Xml Bean读取器调用其父类AbstractBeanDefinitionReader读取定位 //的Bean定义资源 reader.loadBeanDefinitions(configLocations); } }
接下来分析其父类AbstractBeanDefinitionReader是如何读取定位Bean定义资源的,查看AbstractBeanDefinitionReader的loadBeanDefinitions方法,源码如下:
public int loadBeanDefinitions(String location, @Nullable SetactualResources) throws BeanDefinitionStoreException { //获取在IoC容器初始化过程中设置的资源加载器 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 { //将指定位置的Bean定义资源文件解析为Spring IOC容器封装的资源 //加载多个指定位置的Bean定义资源文件 Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location); //委派调用其子类XmlBeanDefinitionReader的方法,实现加载功能 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 { // Can only load single resources by absolute URL. //将指定位置的Bean定义资源文件解析为Spring IOC容器封装的资源 //加载单个指定位置的Bean定义资源文件 Resource resource = resourceLoader.getResource(location); //委派调用其子类XmlBeanDefinitionReader的方法,实现加载功能 int loadCount = loadBeanDefinitions(resource); if (actualResources != null) { actualResources.add(resource); } if (logger.isDebugEnabled()) { logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]"); } return loadCount; } }
可以看到这里最后委派调用其子类XmlBeanDefinitionReader的方法实现加载功能,XmlBeanDefinitionReader的loadBeanDefinitions方法源码如下:
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException { //将读入的XML资源进行特殊编码处理 return loadBeanDefinitions(new EncodedResource(resource)); } /** * Load bean definitions from the specified XML file. * @param encodedResource the resource descriptor for the XML file, * allowing to specify an encoding to use for parsing the file * @return the number of bean definitions found * @throws BeanDefinitionStoreException in case of loading or parsing errors */ //这里是载入XML形式Bean定义资源文件方法 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()); } SetcurrentResources = this.resourcesCurrentlyBeingLoaded.get(); if (currentResources == null) { currentResources = new HashSet<>(4); this.resourcesCurrentlyBeingLoaded.set(currentResources); } if (!currentResources.add(encodedResource)) { throw new BeanDefinitionStoreException( "Detected cyclic loading of " + encodedResource + " - check your import definitions!"); } try { //将资源文件转为InputStream的IO流 InputStream inputStream = encodedResource.getResource().getInputStream(); try { //从InputStream中得到XML的解析源 InputSource inputSource = new InputSource(inputStream); if (encodedResource.getEncoding() != null) { inputSource.setEncoding(encodedResource.getEncoding()); } //这里是具体的读取过程 return doLoadBeanDefinitions(inputSource, encodedResource.getResource()); } finally { //关闭从Resource中得到的IO流 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(); } } }
查看doLoadBeanDefinitions方法,源码如下:
//从特定XML文件中实际载入Bean定义资源的方法 protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { try { //将XML文件转换为DOM对象,解析过程由documentLoader实现 Document doc = doLoadDocument(inputSource, resource); //这里是启动对Bean定义解析的详细过程,该解析过程会用到Spring的Bean配置规则 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); } }
这里的registerBeanDefinitions方法即对Bean定义解析的详细过程。
//按照Spring的Bean语义要求将Bean定义资源解析并转换为容器内部数据结构 public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { //得到BeanDefinitionDocumentReader来对xml格式的BeanDefinition解析 BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); //获得容器中注册的Bean数量 int countBefore = getRegistry().getBeanDefinitionCount(); //解析过程入口,这里使用了委派模式,BeanDefinitionDocumentReader只是个接口, //具体的解析实现过程有实现类DefaultBeanDefinitionDocumentReader完成 documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); //统计解析的Bean数量 return getRegistry().getBeanDefinitionCount() - countBefore; }
这里最后调用了DefaultBeanDefinitionDocumentReader的registerBeanDefinitions方法对xml文件的解析和注册。源码如下:
//根据Spring DTD对Bean的定义规则解析Bean定义Document对象 @Override public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) { //获得XML描述符 this.readerContext = readerContext; logger.debug("Loading bean definitions"); //获得Document的根元素 Element root = doc.getDocumentElement(); doRegisterBeanDefinitions(root); } protected void doRegisterBeanDefinitions(Element root) { // Any nestedelements will cause recursion in this method. In // order to propagate and preserve default-* attributes correctly, // keep track of the current (parent) delegate, which may be null. Create // the new (child) delegate with a reference to the parent for fallback purposes, // then ultimately reset this.delegate back to its original (parent) reference. // this behavior emulates a stack of delegates without actually necessitating one. //具体的解析过程由BeanDefinitionParserDelegate实现, //BeanDefinitionParserDelegate中定义了Spring Bean定义XML文件的各种元素 BeanDefinitionParserDelegate parent = this.delegate; this.delegate = createDelegate(getReaderContext(), root, parent); if (this.delegate.isDefaultNamespace(root)) { String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE); if (StringUtils.hasText(profileSpec)) { String[] specifiedProfiles = StringUtils.tokenizeToStringArray( profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS); if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) { if (logger.isInfoEnabled()) { logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec + "] not matching: " + getReaderContext().getResource()); } return; } } } //在解析Bean定义之前,进行自定义的解析,增强解析过程的可扩展性 preProcessXml(root); //从Document的根元素开始进行Bean定义的Document对象 parseBeanDefinitions(root, this.delegate); //在解析Bean定义之后,进行自定义的解析,增加解析过程的可扩展性 postProcessXml(root); this.delegate = parent; }
这里主要分析观察parseBeanDefinitions方法是如何解析配置文件的。源码如下:
//使用Spring的Bean规则从Document的根元素开始进行Bean定义的Document对象 protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) { //Bean定义的Document对象使用了Spring默认的XML命名空间 if (delegate.isDefaultNamespace(root)) { //获取Bean定义的Document对象根元素的所有子节点 NodeList nl = root.getChildNodes(); for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); //获得Document节点是XML元素节点 if (node instanceof Element) { Element ele = (Element) node; //Bean定义的Document的元素节点使用的是Spring默认的XML命名空间 if (delegate.isDefaultNamespace(ele)) { //使用Spring的Bean规则解析元素节点 parseDefaultElement(ele, delegate); } else { //没有使用Spring默认的XML命名空间,则使用用户自定义的解//析规则解析元素节点 delegate.parseCustomElement(ele); } } } } else { //Document的根节点没有使用Spring默认的命名空间,则使用用户自定义的 //解析规则解析Document根节点 delegate.parseCustomElement(root); } }
可以看到这里有针对默认命名空间的解析过程,也有针对自定义命名空间的解析过程,这里要注意的是针对非默认命名空间是通过NamespaceHandler的handler方法解析的(这里在讲解aop的时候会重点讲),所以现在我们来分析针对默认命名空间的解析过程parseDefaultElement方法,源码如下:
//使用Spring的Bean规则解析Document元素节点 private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) { //如果元素节点是导入元素,进行导入解析 if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) { importBeanDefinitionResource(ele); } //如果元素节点是 别名元素,进行别名解析 else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) { processAliasRegistration(ele); } //元素节点既不是导入元素,也不是别名元素,即普通的 元素, //按照Spring的Bean规则解析元素 else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) { processBeanDefinition(ele, delegate); } else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) { // recurse doRegisterBeanDefinitions(ele); } }
可以看到的是这里包括对
//解析导入元素,从给定的导入路径加载Bean定义资源到Spring IoC容器中 protected void importBeanDefinitionResource(Element ele) { //获取给定的导入元素的location属性 String location = ele.getAttribute(RESOURCE_ATTRIBUTE); //如果导入元素的location属性值为空,则没有导入任何资源,直接返回 if (!StringUtils.hasText(location)) { getReaderContext().error("Resource location must not be empty", ele); return; } // Resolve system properties: e.g. "${user.dir}" //使用系统变量值解析location属性值 location = getReaderContext().getEnvironment().resolveRequiredPlaceholders(location); Set actualResources = new LinkedHashSet<>(4); // Discover whether the location is an absolute or relative URI //标识给定的导入元素的location是否是绝对路径 boolean absoluteLocation = false; try { absoluteLocation = ResourcePatternUtils.isUrl(location) || ResourceUtils.toURI(location).isAbsolute(); } catch (URISyntaxException ex) { // cannot convert to an URI, considering the location relative // unless it is the well-known Spring prefix "classpath*:" //给定的导入元素的location不是绝对路径 } // Absolute or relative? //给定的导入元素的location是绝对路径 if (absoluteLocation) { try { //使用资源读入器加载给定路径的Bean定义资源 int importCount = getReaderContext().getReader().loadBeanDefinitions(location, actualResources); if (logger.isDebugEnabled()) { logger.debug("Imported " + importCount + " bean definitions from URL location [" + location + "]"); } } catch (BeanDefinitionStoreException ex) { getReaderContext().error( "Failed to import bean definitions from URL location [" + location + "]", ele, ex); } } else { // No URL -> considering resource location as relative to the current file. //给定的导入元素的location是相对路径 try { int importCount; //将给定导入元素的location封装为相对路径资源 Resource relativeResource = getReaderContext().getResource().createRelative(location); //封装的相对路径资源存在 if (relativeResource.exists()) { //使用资源读入器加载Bean定义资源 importCount = getReaderContext().getReader().loadBeanDefinitions(relativeResource); actualResources.add(relativeResource); } //封装的相对路径资源不存在 else { //获取Spring IOC容器资源读入器的基本路径 String baseLocation = getReaderContext().getResource().getURL().toString(); //根据Spring IOC容器资源读入器的基本路径加载给定导入路径的资源 importCount = getReaderContext().getReader().loadBeanDefinitions( StringUtils.applyRelativePath(baseLocation, location), actualResources); } if (logger.isDebugEnabled()) { logger.debug("Imported " + importCount + " bean definitions from relative location [" + location + "]"); } } catch (IOException ex) { getReaderContext().error("Failed to resolve current resource location", ele, ex); } catch (BeanDefinitionStoreException ex) { getReaderContext().error("Failed to import bean definitions from relative location [" + location + "]", ele, ex); } } Resource[] actResArray = actualResources.toArray(new Resource[actualResources.size()]); //在解析完 元素之后,发送容器导入其他资源处理完成事件 getReaderContext().fireImportProcessed(location, actResArray, extractSource(ele)); }
可以看到这里主要对路径的解析,针对绝对路径或者相对路径调用XmlBeanDefinitionReader的loadBeanDefinitions方法进行BeanDefinition的解析定位,并且在解析完成之后发送容器导入其他资源处理完成事件。
下面分析
//解析别名元素,为Bean向Spring IoC容器注册别名 protected void processAliasRegistration(Element ele) { //获取 别名元素中name的属性值 String name = ele.getAttribute(NAME_ATTRIBUTE); //获取 别名元素中alias的属性值 String alias = ele.getAttribute(ALIAS_ATTRIBUTE); boolean valid = true; // 别名元素的name属性值为空 if (!StringUtils.hasText(name)) { getReaderContext().error("Name must not be empty", ele); valid = false; } // 别名元素的alias属性值为空 if (!StringUtils.hasText(alias)) { getReaderContext().error("Alias must not be empty", ele); valid = false; } if (valid) { try { //向容器的资源读入器注册别名 getReaderContext().getRegistry().registerAlias(name, alias); } catch (Exception ex) { getReaderContext().error("Failed to register alias "" + alias + "" for bean with name "" + name + """, ele, ex); } //在解析完 元素之后,发送容器别名处理完成事件 getReaderContext().fireAliasRegistered(name, alias, extractSource(ele)); } }
可以看到这里的解析过程比较简单,分别去除bean名称以及别名,然后向容器注册别名,最后发送容器注册别名处理完成事件。下面继续分析对
//解析Bean定义资源Document对象的普通元素 protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); // BeanDefinitionHolder是对BeanDefinition的封装,即Bean定义的封装类 //对Document对象中元素的解析由BeanDefinitionParserDelegate实现 // BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); if (bdHolder != null) { bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); try { // Register the final decorated instance. //向Spring IOC容器注册解析得到的Bean定义,这是Bean定义向IOC容器注册的入口 BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()); } catch (BeanDefinitionStoreException ex) { getReaderContext().error("Failed to register bean definition with name "" + bdHolder.getBeanName() + """, ele, ex); } // Send registration event. //在完成向Spring IOC容器注册解析得到的Bean定义之后,发送注册事件 getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder)); } }
可以看到这里主要包括两个过程,第一个是对节点的解析封装,第二个是bean定义的注册。我们先看对节点的解析封装,这里的解析是委托给了BeanDefinitionParserDelegate的parseBeanDefinitionElement方法,源码如下:
//解析Bean定义资源文件中的元素,这个方法中主要处理 元素的id,name和别名属性 @Nullable public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) { //获取 元素中的id属性值 String id = ele.getAttribute(ID_ATTRIBUTE); //获取 元素中的name属性值 String nameAttr = ele.getAttribute(NAME_ATTRIBUTE); //获取 元素中的alias属性值 List aliases = new ArrayList<>(); //将 元素中的所有name属性值存放到别名中 if (StringUtils.hasLength(nameAttr)) { String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS); aliases.addAll(Arrays.asList(nameArr)); } String beanName = id; //如果 元素中没有配置id属性时,将别名中的第一个值赋值给beanName if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) { beanName = aliases.remove(0); if (logger.isDebugEnabled()) { logger.debug("No XML "id" specified - using "" + beanName + "" as bean name and " + aliases + " as aliases"); } } //检查 元素所配置的id或者name的唯一性,containingBean标识 //元素中是否包含子 元素 if (containingBean == null) { //检查 元素所配置的id、name或者别名是否重复 checkNameUniqueness(beanName, aliases, ele); } //详细对 元素中配置的Bean定义进行解析的地方 AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean); if (beanDefinition != null) { if (!StringUtils.hasText(beanName)) { try { if (containingBean != null) { //如果 元素中没有配置id、别名或者name,且没有包含子元素 // 元素,为解析的Bean生成一个唯一beanName并注册 beanName = BeanDefinitionReaderUtils.generateBeanName(beanDefinition, this.readerContext.getRegistry(), true); } else { //如果 元素中没有配置id、别名或者name,且包含了子元素 // 元素,为解析的Bean使用别名向IOC容器注册 beanName = this.readerContext.generateBeanName(beanDefinition); // Register an alias for the plain bean class name, if still possible, // if the generator returned the class name plus a suffix. // This is expected for Spring 1.2/2.0 backwards compatibility. //为解析的Bean使用别名注册时,为了向后兼容 //Spring1.2/2.0,给别名添加类名后缀 String beanClassName = beanDefinition.getBeanClassName(); if (beanClassName != null && beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() && !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) { aliases.add(beanClassName); } } if (logger.isDebugEnabled()) { logger.debug("Neither XML "id" nor "name" specified - " + "using generated bean name [" + beanName + "]"); } } catch (Exception ex) { error(ex.getMessage(), ele); return null; } } String[] aliasesArray = StringUtils.toStringArray(aliases); return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray); } //当解析出错时,返回null return null; }
可以看到这里的具体的解析过程交给了parseBeanDefinitionElement方法。
public AbstractBeanDefinition parseBeanDefinitionElement( Element ele, String beanName, @Nullable BeanDefinition containingBean) { //记录解析的this.parseState.push(new BeanEntry(beanName)); //这里只读取 元素中配置的class名字,然后载入到BeanDefinition中去 //只是记录配置的class名字,不做实例化,对象的实例化在依赖注入时完成 String className = null; //如果 元素中配置了parent属性,则获取parent属性的值 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 { //根据 元素配置的class名称和parent属性值创建BeanDefinition //为载入Bean定义信息做准备 AbstractBeanDefinition bd = createBeanDefinition(className, parent); //对当前的 元素中配置的一些属性进行解析和设置,如配置的单态(singleton)属性等 parseBeanDefinitionAttributes(ele, beanName, containingBean, bd); //为 元素解析的Bean设置description信息 bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT)); //对 元素的meta(元信息)属性解析 parseMetaElements(ele, bd); //对 元素的lookup-method属性解析 parseLookupOverrideSubElements(ele, bd.getMethodOverrides()); //对 元素的replaced-method属性解析 parseReplacedMethodSubElements(ele, bd.getMethodOverrides()); //解析 元素的构造方法设置 parseConstructorArgElements(ele, bd); //解析 元素的 设置 parsePropertyElements(ele, bd); //解析 元素的qualifier属性 parseQualifierElements(ele, bd); //为当前解析的Bean设置所需的资源和依赖对象 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(); } //解析 元素出错时,返回null return null; }
然后我们分析注册部分,源码如下:
//将解析的BeanDefinitionHold注册到容器中 public static void registerBeanDefinition( BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException { // Register bean definition under primary name. //获取解析的BeanDefinition的名称 String beanName = definitionHolder.getBeanName(); //向IOC容器注册BeanDefinition registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); // Register aliases for bean name, if any. //如果解析的BeanDefinition有别名,向容器为其注册别名 String[] aliases = definitionHolder.getAliases(); if (aliases != null) { for (String alias : aliases) { registry.registerAlias(beanName, alias); } } }
接下来分析BeanDefinitionRegistry的registerBeanDefinition方法,这里的BeanDefinitionRegistry是一个接口,最终由容器DefaultListableBeanFactory进行注册,源码如下:
//向IOC容器注册解析的BeanDefiniton @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"); //校验解析的BeanDefiniton 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); ListupdatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1); updatedDefinitions.addAll(this.beanDefinitionNames); updatedDefinitions.add(beanName); this.beanDefinitionNames = updatedDefinitions; if (this.manualSingletonNames.contains(beanName)) { Set updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames); updatedSingletons.remove(beanName); this.manualSingletonNames = updatedSingletons; } } } else { // Still in startup registration phase this.beanDefinitionMap.put(beanName, beanDefinition); this.beanDefinitionNames.add(beanName); this.manualSingletonNames.remove(beanName); } this.frozenBeanDefinitionNames = null; } //检查是否有同名的BeanDefinition已经在IOC容器中注册 if (oldBeanDefinition != null || containsSingleton(beanName)) { //重置所有已经注册过的BeanDefinition的缓存 resetBeanDefinition(beanName); } }
可以看到最终将bean定义注册进了一个Map结构的beanDefinitionMap。
至此,IOC容器的初始化工作进行完毕,下篇会分析bean的后置处理器是如何工作的。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/76977.html
摘要:初始化我们知道容器初始化后会对容器中非懒加载的,单例的以及非抽象的定义进行的初始化操作,所以我们分析源码的入口也就是在容器初始化的入口,分析容器初始化后在什么地方开始第一次的初始化。 前言 Spring IOC容器在初始化之后会对容器中非懒加载的,单例的以及非抽象的bean定义进行bean的初始化操作,同时会也涉及到Bean的后置处理器以及DI(依赖注入)等行为。对于Bean的初始化,...
摘要:的在单例被破坏时由进行方法调用。定义并实现这两个接口容器创建完成注解是的缩写,意思是规范提案。在创建完成并且属性赋值完成来执行初始化方法在容器销毁之前回调通知支持自动装配,类似。 Spring注解应用篇--IOC容器Bean生命周期 这是Spring注解专题系类文章,本系类文章适合Spring入门者或者原理入门者,小编会在本系类文章下进行企业级应用实战讲解以及spring源码跟进。本文...
摘要:前言这篇是专题初始化的第二篇,主要对初始化具体过程的源码分析。上篇博客专题之初始化源码分析中我们对如何开始初始化以及初始化的总体过程有了大致的了解,接下来就继续上篇博客的结尾处开始来分析初始化的具体过程。 前言 这篇是Spring专题Bean初始化的第二篇,主要对bean初始化具体过程的源码分析。上篇博客Spring专题之Bean初始化源码分析(1)中我们对Spring如何开始初始化b...
摘要:用法先创建个组件,,,分别在类上加上注解。发现有一个属性源码注释这样说的自动检测使用组件。在的方法中,表示不匹配,代表匹配。这就说明使用注册组件有种方式。 Spring注解应用篇--IOC容器Bean组件注册 这是Spring注解专题系类文章,本系类文章适合Spring入门者或者原理入门者,小编会在本系类文章下进行企业级应用实战讲解以及spring源码跟进。 环境准备 编译器IDEA...
阅读 3735·2023-01-11 11:02
阅读 4244·2023-01-11 11:02
阅读 3050·2023-01-11 11:02
阅读 5180·2023-01-11 11:02
阅读 4737·2023-01-11 11:02
阅读 5534·2023-01-11 11:02
阅读 5313·2023-01-11 11:02
阅读 3986·2023-01-11 11:02