`
h140465
  • 浏览: 20855 次
  • 性别: 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源码学习之思维导图

    1、spring 的整体架构 2、spring的基本实现(xml的加载原理、标签解析、bean加载) 3、容器扩展 4、ioc和aop 5、事务 6、springmvc 7、dispatcherServlet

    java查看sun包源码-learning-spring:Spring框架讲解

    在SpringIOC容器读取bean配置创建bean实例之前,必须对它进行实例化。只有在容器实例化后,才可以从IOC容器里获取bean实例并使用 Spring提供了两种类型的IOC容器实现 BeanFactory:IOC容器的基本实现,在调用getBean...

    Java基础 反射篇.md

    反射是一个非常重要的知识点,在学习Spring 框架时,Bean的初始化用到了反射,在破坏单例模式时也用到了反射,在获取标注的注解时也会用到反射······ 当然了,反射在日常开发中,我们没碰到过多少,至少我没...

    Spring开发参考手册.chm

    Spring是Java EE编程领域的一个轻量级开源框架,该框架由一个叫Rod Johnson的程序员在 2002 年最早提出...在整个框架中,各类型的功能被抽象成一个个的 Bean,这样就可以实现各种功能的管理,包括动态加载和切面编程。

    java8stream源码-JPP:JPP

    源码 JPP JPP - Java Promotion Process 是一个Java能力提升的计划,与君共勉 在这里: 你可以记录有趣的案例Demo 你可以链接有深度的博客文章 你可以分享自己的学习历程 这个计划需要你们的加入 Peace~Yo 计算机...

    基于Java开发的动态定时任务管理系统源码+使用说明.zip

    - Bean 名称:这是项目中注入 Spring 的 Bean 名称,测试代码中以 `SchedulingTaskDemo.java` 为例。 - 方法名称:参数 1 中 bean 里边的方法名称。 - 方法参数:参数 2 中方法的参数。 - Cron 表达式:定时任务的 ...

    java8集合源码分析-java-agent:基于java5Instrumentapi实现的mock框架

    集合源码分析 1 介绍 1.1 用途 单元测试mock 联调、集成测试mock 支持mock静态方法,final方法,私有方法 非常容易实现spring bean的mock 支持对dubbo接口的mock 1.2 原理 java5引入了一个api,叫做Instrument,它...

    LSFramework:手写山寨版spring学习aop,ioc思想的demo,没看过spring的源码,因为实在是太庞大了,参考部分网上博客和开源代码完成

    手写山寨版“ spring”,学习aop,ioc思想的demo,没看过spring的源码,因为实在是太庞大了,参考部分网上博客和开源代码完成。 主要功能模块: 国际奥委会 哎呀 的jdbc rpc 其他资源 关键代码笔记,不想看所有...

    学习SSM源码分析的一次实践,自己实现SSM框架

    对应 mybatis 是采用代理的方式 对接口进行生成对应的代理对象,加载对应的xml 中的 数据和 对应的 执行占位符的情况 对占位符数据进行set 对应的数值。并且通过动态注入的方式进行 接口的调用,并且实现对应的调用...

    JAVA上百实例源码以及开源项目源代码

    Java 源码包 Applet钢琴模拟程序java源码 2个目标文件,提供基本的音乐编辑功能。编辑音乐软件的朋友,这款实例会对你有所帮助。 Calendar万年历 1个目标文件 EJB 模拟银行ATM流程及操作源代码 6个目标文件,EJB来...

    JAVA上百实例源码以及开源项目

    笔者当初为了学习JAVA,收集了很多经典源码,源码难易程度分为初级、中级、高级等,详情看源码列表,需要的可以直接下载! 这些源码反映了那时那景笔者对未来的盲目,对代码的热情、执着,对IT的憧憬、向往!此时此...

    基于SSM框架+Mysql的企业CRM客户关系管理系统项目源码+数据库+项目说明.zip

    2、本项目适合作为计算机、数学、电子信息等专业的课程设计、期末大作业和毕设项目,作为参考资料学习借鉴。 3、本资源作为“参考资料”如果需要实现其他功能,需要能看懂代码,并且热爱钻研,自行调试。 基于SSM...

    经典JAVA.EE企业应用实战.基于WEBLOGIC_JBOSS的JSF_EJB3_JPA整合开发.pdf

    该案例既提供了ide无关的、基于ant管理的项目源码,也提供了基于netbeans ide的项目源码,最大限度地满足读者的需求。 作者简介: 李刚,从事10年的Java EE应用开发。曾任LITEON公司的J2EE技术主管,负责该公司的...

    基于J2EE框架的个人博客系统项目毕业设计论文(源码和论文)

    由于J2EE的开源的框架中提供了MVC模式实现框架Struts、对象关系模型中的Hibernate 的框架及拥有事务管理和依赖注入的Spring。利用现存框架可以更快开发系统。所以选择Java技术作为blog 的开发工具。 为了增加系统的...

Global site tag (gtag.js) - Google Analytics