`
h140465
  • 浏览: 21788 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

Spring源码学习记录--加载bean

 
阅读更多

 

public static void main( String[] args )
    {
    	ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
    }

    这是一段最简单的Spring加载bean配置的代码,spring加载bean的大致流程如下


 loadBeanDefinitions方法主要作用

    1)读取配置文件

    2)防止配置文件import自己导致死循环加载

 

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<>(4);
			this.resourcesCurrentlyBeingLoaded.set(currentResources);
		}
		//如果resource已经加载,就抛出异常(只有使用import引入自己的配置文件时才会导致异常)
		if (!currentResources.add(encodedResource)) {
			throw new BeanDefinitionStoreException(
					"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
		}
		try {
			InputStream inputStream = encodedResource.getResource().getInputStream();
			try {
				InputSource inputSource = new InputSource(inputStream);
				if (encodedResource.getEncoding() != null) {
					inputSource.setEncoding(encodedResource.getEncoding());
				}
				/**
				 * doLoadBeanDefinitions解析配置文件时,遇到import标签会递归调用loadBeanDefinitions方法
				 * 这个时候当前加载配置文件不会清除
				 */
				return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
			}
			finally { 
				inputStream.close();
			}
		}
		catch (IOException ex) {
			throw new BeanDefinitionStoreException(
					"IOException parsing XML document from " + encodedResource.getResource(), ex);
		}
		finally {
			//每次加载完就会从resourcesCurrentlyBeingLoaded清除掉当前加载的resource文件,
			//这样之后可以重新加载配置文件
			currentResources.remove(encodedResource);
			if (currentResources.isEmpty()) {
				this.resourcesCurrentlyBeingLoaded.remove();
				System.out.println(this.resourcesCurrentlyBeingLoaded.get());
			}
		}
	}
 

 

  doLoadBeanDefinitions:

  解析配置文件得到Document对象

 

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
			throws BeanDefinitionStoreException {
		try {
			Document doc = doLoadDocument(inputSource, resource);
			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

 

	public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
		//使用DefaultBeanDefinitionDocumentReader实例化BeanDefinitionDocumentReader
		BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
		//记录已经加载的bean数量
		int countBefore = getRegistry().getBeanDefinitionCount();
		//加载及注册bean
		documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
		//返回本次加载的bean数量
		return getRegistry().getBeanDefinitionCount() - countBefore;
	}
 

 

 

真正的加载及注册bean方法在DefaultBeanDefinitionDocumentReader中

 

默认标签的解析

 

 

	private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
		//解析import标签
		if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
			importBeanDefinitionResource(ele);
		}
		//解析alias标签
		else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
			processAliasRegistration(ele);
		}
		//解析bean标签
		else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
			processBeanDefinition(ele, delegate);
		}
		//解析beans标签
		else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
			// recurse
			doRegisterBeanDefinitions(ele);
		}
	}
 

 

bean标签解析


 
(1)首先委托BeanDefinitionDelegate类的parseBeanDefinitionElement方法进行元素的解析,返回

BeanDefinitionHolder类型的实例bdHolder。经过这个方法后,bdHolder实例已经包含配置文件中配置的各种属性了,例如class,name,id,alias之类的属性

 (2)当返回的bdHolder不为空的情况下,若存在默认标签的子节点下再有自定义属性,还需要对自定义标签进行解析

 (3)解析完成后,需要对解析后的bdHolder进行注册,同样,注册操作委托给了BeanDefinitionReaderUtils的registerBeanDefinition方法

 (4)最后发出响应事件,通知相关的监视器,这个bean已经加载完成

 

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
		//解析各元素
		BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
		if (bdHolder != null) {
			//如果子节点存在自定义标签,需要再次解析
			bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
			try {
				// 注册bean.
				BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
			}
			catch (BeanDefinitionStoreException ex) {
				getReaderContext().error("Failed to register bean definition with name '" +
						bdHolder.getBeanName() + "'", ele, ex);
			}
			// 发出响应事件.
			getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
		}
	}

 

 

/**
	 * containingBean参数,这个参数用来区分当前解析的bean是否是内部bean,
	 *	如果是非空值说明是内部bean,内部bean和非内部bean解析时只做了两件不同的事情:
	 *	1、id生成策略;2、内部bean需要继承其外部bean的scope。.
	 */
	@Nullable
	public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
		String id = ele.getAttribute(ID_ATTRIBUTE);
		String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);

		List<String> aliases = new ArrayList<>();
		if (StringUtils.hasLength(nameAttr)) {
			//将name以分号或者逗号进行分割
			String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
			//分割后的name作为别名
			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");
			}
		}

		if (containingBean == null) {
			//beanName,aliases唯一性检查,并且将beanName,aliases加入到当前已使用名称集合中usedNames
			checkNameUniqueness(beanName, aliases, ele);
		}

		//对标签其他属性的解析过程  
		AbstractBeanDefinition beanDefinition = 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);
						// 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.
						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);
		}

		return null;
	}

 

相关的类

XmlBeanDefinitionReader
DefaultBeanDefinitionDocumentReader
BeanDefinitionParserDelegate
BeanDefinitionReaderUtils

 

  • 大小: 53.6 KB
  • 大小: 48.9 KB
分享到:
评论

相关推荐

    spring源码-2022-08-14spring源码spring源码spring源码

    在2022年8月14日更新的Spring源码中,我们可以深入理解其设计理念和实现机制。 Spring框架的主要组成部分包括: 1. **核心容器**:这是Spring的基础,主要包括BeanFactory和ApplicationContext。BeanFactory负责...

    spring学习----工厂Bean

    在Spring框架中,"工厂Bean"是一个非常关键的概念,它扩展了传统的Bean定义,使得我们可以在Spring IoC容器中创建自定义的实例化逻辑。工厂Bean允许开发者在对象实例化时进行更复杂的控制,比如引入特定的初始化过程...

    【框架源码篇 04】Spring源码手写篇-Bean定义配置化

    Spring源码手写篇旨在帮助开发者理解Spring内部的工作原理,通过模仿Spring的核心功能,如Bean工厂和Bean定义,我们可以更好地掌握Spring框架的精髓。04-手写Bean配置-spring-v41.rar可能包含了一个简单的实现,你...

    Spring Boot源码(spring-boot-2.6.2.zip)

    通过阅读这些核心组件的源码,我们可以深入理解Spring Boot是如何加载配置、启动应用、自动配置bean以及与其他Spring框架组件协作的。这将有助于提升我们的开发技能,解决潜在的问题,并且更好地优化Spring Boot应用...

    官方源码 spring-framework-5.2.15.RELEASE.zip

    结合源码学习,我们可以更好地在实际项目中运用Spring框架,例如自定义拦截器、实现数据访问的事务管理、利用Spring Boot简化应用开发等。 通过深入学习Spring Framework 5.2.15.RELEASE的源码,开发者不仅可以...

    官方原版源码 spring-5.2.8.RELEASE.zip

    总的来说,Spring框架5.2.8.RELEASE源码的学习是一次深入理解Java企业级开发、掌握模块化设计和面向切面编程理念的宝贵机会。通过深入研究源码,开发者可以提升自己的编程技巧,为开发高质量的、可维护的应用程序...

    官方原版源码spring-framework-5.1.4.RELEASE.zip

    例如,关于IoC(Inversion of Control,控制反转)的实现,Spring使用了XML配置或注解来定义bean的依赖关系,通过反射机制动态加载和管理bean。另外,AOP模块实现了切面编程,允许我们定义横切关注点,如日志、事务...

    官方原版源码spring-framework-4.3.25.RELEASE.zip

    研究Spring源码能帮助开发者提升对Java和软件设计的理解,学习最佳实践,提高代码质量和可维护性。同时,对于解决框架使用中的问题,或自定义扩展Spring功能,源码学习至关重要。 7. **未来发展趋势** 虽然Spring...

    官方原版源码 spring-framework-5.2.9.RELEASE.zip

    1. **设计模式实践**:Spring源码中大量运用了工厂模式、单例模式、观察者模式等设计模式,学习源码能加深对设计模式的理解。 2. **性能优化**:通过对源码的学习,开发者可以了解Spring如何进行性能优化,如缓存、...

    spring-framework-1.0-m1.zip源码

    1. ApplicationContext:在Spring 1.0中,ApplicationContext是主要的上下文接口,负责加载配置文件,管理Bean,并提供事件发布等功能。它是所有其他Spring服务的基础。 2. BeanFactory:作为ApplicationContext的...

    spring源码解读-地址.txt

    根据提供的文件信息,本次解读将围绕Spring框架的核心概念与源码分析进行展开。Spring框架作为Java企业级开发中不可或缺的一部分,其源码的学习对于深入...希望以上内容能够为大家学习Spring源码提供一定的参考和帮助。

    spring-boot-2.0.0.M7 源码包

    通过深入研究这个源码包,开发者不仅可以理解Spring Boot的内部工作原理,还可以学习到如何设计和实现类似的框架,提升自己的Java和Spring技术栈能力。同时,对于遇到的问题,可以通过查看源码找到解决思路,有助于...

    第一节课-Spring源码分析之-常见底层核心注解-2 (2)1

    Spring框架是Java开发中不可或缺的一部分,它以其强大的功能和灵活性深受开发者喜爱。本文将深入探讨Spring框架中的核心注解和核心模块,以便...对于深入学习和使用Spring框架的开发者来说,掌握这些知识是至关重要的。

    spring-source-4.2.4源码

    通过源码,我们可以学习Spring如何管理WebSocket连接,以及消息的发送和接收。 `spring-instrument-tomcat-4.2.4.RELEASE-sources`是针对Tomcat服务器的类加载器工具,用于在部署时对Spring应用程序进行优化和调试...

    springspring-framework-3.0.0.M2

    4. **JSR-303/JSR-349(Bean Validation)集成**:Spring 3.0 开始支持 JSR-303/JSR-349 规范,提供了数据验证的功能,通过 @Valid 注解可以在控制器层轻松进行参数校验。 5. **RESTful 支持**:Spring MVC 3.0 ...

    官方原版源码spring-framework-5.1.9.RELEASE.zip

    《Spring Framework 5.1.9.RELEASE源码解析》 Spring Framework作为Java开发领域中的核心框架,一直以来都是开发者们关注的焦点。这次我们聚焦于官方原版源码spring-framework-5.1.9.RELEASE,这个版本包含了完整的...

    spring-framework-bom源码

    在源码中,我们可以找到关于BeanFactory和ApplicationContext的实现,理解它们如何加载配置、实例化和管理Bean。 4. **AOP(面向切面编程)**:Spring的AOP模块支持声明式事务管理和其他横切关注点,如日志记录、...

    spring-boot自定义starter的源码示例

    在Spring Boot生态系统中,自定义Starter是一种强大的方式来封装和重用代码,...通过深入学习和实践`demo-spring-boot-starter`的源码,你将更好地理解Spring Boot的自动配置原理,以及如何设计和实现自己的Starter。

    官方原版源码 spring-framework-5.1.16.RELEASE.zip

    《Spring Framework 5.1.16.RELEASE源码深度解析》 ...通过深入研究源码,开发者不仅能提升对Spring Framework的理解,还能学习到良好的设计原则和最佳实践,这对于成为一名优秀的Java开发者具有深远意义。

    spring源码合集spring源码合集

    《Spring源码合集:揭示Java后端开发的基石》 Spring框架作为Java后端开发的核心,其源码的研究对于提升开发者对系统架构理解和优化能力至关重要。本合集深入剖析了Spring的诸多关键特性,包括依赖注入、配置类解析...

Global site tag (gtag.js) - Google Analytics