`
j小虫
  • 浏览: 18825 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

spring源码学习(四)

    博客分类:
  • java
 
阅读更多

在(三)里我们已经进入了AbstractApplicationContext的刷新方法,refresh,代码如下

 

public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			// Prepare this context for refreshing.
			prepareRefresh();

			// Tell the subclass to refresh the internal bean factory.
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// Prepare the bean factory for use in this context.
			prepareBeanFactory(beanFactory);

			try {
				// Allows post-processing of the bean factory in context subclasses.
				postProcessBeanFactory(beanFactory);

				// Invoke factory processors registered as beans in the context.
				invokeBeanFactoryPostProcessors(beanFactory);

				// Register bean processors that intercept bean creation.
				registerBeanPostProcessors(beanFactory);

				// Initialize message source for this context.
				initMessageSource();

				// Initialize event multicaster for this context.
				initApplicationEventMulticaster();

				// Initialize other special beans in specific context subclasses.
				onRefresh();

				// Check for listener beans and register them.
				registerListeners();

				// Instantiate all remaining (non-lazy-init) singletons.
				finishBeanFactoryInitialization(beanFactory);

				// Last step: publish corresponding event.
				finishRefresh();
			}

			catch (BeansException ex) {
				// Destroy already created singletons to avoid dangling resources.
				destroyBeans();

				// Reset 'active' flag.
				cancelRefresh(ex);

				// Propagate exception to caller.
				throw ex;
			}
		}
	}

 

 

它由很多个步骤组成,我们在(四)中就研究

prepareRefresh

这个步骤,看看刷新的第一步做了什么

 

 

顾名思义,刷新前的预处理,处理了什么,我们点进去看一下

 

 

 

protected void prepareRefresh() {
		this.startupDate = System.currentTimeMillis();

		synchronized (this.activeMonitor) {
			this.active = true;
		}

		if (logger.isInfoEnabled()) {
			logger.info("Refreshing " + this);
		}

		// Initialize any placeholder property sources in the context environment
		initPropertySources();

		// Validate that all properties marked as required are resolvable
		// see ConfigurablePropertyResolver#setRequiredProperties
		this.environment.validateRequiredProperties();
	}

 可以看到,这个方法里它产生了容器的开始时间,接着改了active状态,这里稍微要注意一下

 

 

synchronized (this.activeMonitor) {
			this.active = true;
		}

他在改状态的时候用activeMonitor加了锁,并且改了active的状态为true,作者这么写的用意一定是为了让active在后续的某个操作或某个判断中去使用,既然active = true是在容器启动的开始时改的,那么后续肯定是在一些需要判断容器是否启动了来的地方使用,我们可以认为它是容器的激活状态,后续在任何修改容器的激活状态的地方,也一定会用activeMonitor来加锁。

 

 

 

再往下,打印一行refresh的日志

if (logger.isInfoEnabled()) {
			logger.info("Refreshing " + this);
		}

 

 

继续看

 

// Initialize any placeholder property sources in the context environment
		initPropertySources();

 

 

初始化propertySources,主意哈,这个propertySources不是一堆propertySource啊,而是一个容器

spring里有两个概念,一个是propertySource,一个是propertySources,后者不是简单的表示一堆前者,而是表示一个存放前者的容器。我之前也在强调,spring里有很多名词,我们都需要去熟悉它们,知道它们是干什么的。  这里稍微偏移了扩展一下,我们看看propertySource和PropertySources的源码

 

 

在core包下,我们找到propertySource

public abstract class PropertySource<T> {

	protected final Log logger = LogFactory.getLog(this.getClass());

	protected final String name;

	protected final T source;

	/**
	 * Create a new {@code PropertySource} with the given name and source object.
	 */
	public PropertySource(String name, T source) {
		Assert.hasText(name, "Property source name must contain at least one character");
		Assert.notNull(source, "Property source must not be null");
		this.name = name;
		this.source = source;
	}

 从泛型和构造我们可以大致了解,propertySource,是一个用来存放property的keyValue的实体,构造表示,我拿任意类型的对象进来,都可以设置一个key,存在成员name上,然后把对象存放在成员source上,是不是很像一个Map.Entry<String,Object>对象呢。

 

 

这里稍微需要了解下他有2个静态内部类StubPropertySource和ComparisonPropertySource

StubPropertySource是Property的子类,用于充当存根,它的注释中提到实际的PropertySource的初始化工作,不能在容器创建之前进行,所以用存根来充当一个占位符,他用来占用一个预期的propertySource位置,等容器refresh的时候用来进行替换,替换后成一个真正的值

 

如果这段话不好理解,可以先稍微知道有这么个东西,它是一个变量,用来占着你的property的位置,等刷新的时候再进行填充。

 

 

ComparisonPropertySource是存根的子类,仅仅用于集合的操作。注释中也是这么说,我们在propertySource的named方法上按f2

 

Return a PropertySource implementation intended for collection comparison purposes only. 

Primarily for internal use, but given a collection of PropertySource objects, may be used as follows: 

 List<PropertySource<?>> sources = new ArrayList<PropertySource<?>>();
 sources.add(new MapPropertySource("sourceA", mapA));
 sources.add(new MapPropertySource("sourceB", mapB));
 assert sources.contains(PropertySource.named("sourceA"));
 assert sources.contains(PropertySource.named("sourceB"));
 assert !sources.contains(PropertySource.named("sourceC"));

 这里的集合比较不是简单的比较大小的意思,而 是一系列集合可比较的操作,如contains,equals等。

 

 

 

看完propertySource,我们再看propertySources

public interface PropertySources extends Iterable<PropertySource<?>> {

	/**
	 * Return whether a property source with the given name is contained.
	 * @param name the {@linkplain PropertySource#getName() name of the property source} to find
	 */
	boolean contains(String name);

	/**
	 * Return the property source with the given name, {@code null} if not found.
	 * @param name the {@linkplain PropertySource#getName() name of the property source} to find
	 */
	PropertySource<?> get(String name);

}

 可以看到,他是propertySource的一个容器接口,具备迭代能力,有2个容器级别的方法,contains和get,这都是集合中常见的概念,它只有一个实现类,MutablePropertySources

 

再看看MutablePropertySources

public class MutablePropertySources implements PropertySources {

	static final String NON_EXISTENT_PROPERTY_SOURCE_MESSAGE = "PropertySource named [%s] does not exist";
	static final String ILLEGAL_RELATIVE_ADDITION_MESSAGE = "PropertySource named [%s] cannot be added relative to itself";

	private final Log logger;

	private final LinkedList<PropertySource<?>> propertySourceList = new LinkedList<PropertySource<?>>();

 

他有一个propertySourceList,是用链表List来实现的,用于存放所有的propertySource,并且MutablePropertySources实现了contanins,get等集合方法,还有addFirst,addLast等添加对象的方法。在我们还没看到propertyResolver之前,先可以认为propertySource的检索都由容器自己完成,后续会看到一个全新的工具resolver,专门用于propertySources的检索,解析等相关工作

 

 

扯远了,回到容器刷新的第一步,initPropertySources方法来

 

@Override
	protected void initPropertySources() {
		super.initPropertySources();
		WebApplicationContextUtils.initServletPropertySources(
				this.getEnvironment().getPropertySources(), this.servletContext,
				this.servletConfig);
	}

 

super的init是空实现,具体的初始化工作,直接由第2行开始

我们可以看到,initServletProperSources,顾名思义,就是初始化propertySources这个容器,那我们应该可以想到,它应该是往容器里塞点东西,  再看后面2个参数,一个是servletContext,一个servletConfig,那么我们知道了,它这个初始化,其实是想把servletContext,一个servletConfig这两个对象给存到容器的propertySources容器里去,我们点进去看看是不是和我们想得一样

 

 

public static void initServletPropertySources(
			MutablePropertySources propertySources, ServletContext servletContext, ServletConfig servletConfig) {
		Assert.notNull(propertySources, "propertySources must not be null");
		if(servletContext != null &&
				propertySources.contains(SERVLET_CONTEXT_PROPERTY_SOURCE_NAME) &&
				propertySources.get(SERVLET_CONTEXT_PROPERTY_SOURCE_NAME) instanceof StubPropertySource) {
			propertySources.replace(SERVLET_CONTEXT_PROPERTY_SOURCE_NAME, new ServletContextPropertySource(SERVLET_CONTEXT_PROPERTY_SOURCE_NAME, servletContext));
		}
		if(servletConfig != null &&
				propertySources.contains(SERVLET_CONFIG_PROPERTY_SOURCE_NAME) &&
				propertySources.get(SERVLET_CONFIG_PROPERTY_SOURCE_NAME) instanceof StubPropertySource) {
			propertySources.replace(SERVLET_CONFIG_PROPERTY_SOURCE_NAME, new ServletConfigPropertySource(SERVLET_CONFIG_PROPERTY_SOURCE_NAME, servletConfig));
		}
	}

 果然是这样,key是常量,value是一个PropertySource,

我们看下replace的实现,实现当然在propertySources的唯一一个实现类中,MutablePropertySources

 

 

 

/**
	 * Replace the property source with the given name with the given property source object.
	 * @param name the name of the property source to find and replace
	 * @param propertySource the replacement property source
	 * @throws IllegalArgumentException if no property source with the given name is present
	 * @see #contains
	 */
	public void replace(String name, PropertySource<?> propertySource) {
		logger.debug(String.format("Replacing [%s] PropertySource with [%s]",
				name, propertySource.getName()));
		int index = assertPresentAndGetIndex(name);
		this.propertySourceList.set(index, propertySource);
	}

 可以看到,他先用name查出这个propertySource的下标,然后进行替换

 

 

由于我们在之前容器里已经有servletContext了,那么现在servletContext就被propertySources接管了,它用replace方法,将servletContext存放了进来,key是一个常量,value是一个ServetPropertySource对象,ServetPropertySource就类似一个Entry.

 

 

读到这里我明白了,原来preRefresh预刷新,其实就是把记录一下开始时间,打印一下日志,然后把servletConfig和servletContext放到spring容器的propertySources容器里面.

 

 

好了,下一节我们一起去读refresh的第2步,

ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

 这一步里,spring开始使用Reader,Resource,Document,Registry,去解析配置文件,生成BeanDefinition,并且在注册器里进行注册,可以说,这一步,完成bean的读取解析的很重要的工作。

 

 

 

 

分享到:
评论

相关推荐

    Spring源码学习四:BeanDefinition装载前奏曲1

    在深入Spring源码的学习过程中,我们主要关注BeanDefinition的装载过程。BeanDefinition是Spring框架的核心概念,它包含了关于Bean的所有元数据,如类名、属性、依赖关系等。在Spring初始化时,会读取XML配置文件,...

    Spring学习笔记&源码

    本资料“Spring学习笔记&源码”是基于网易云课堂黑马程序员的Spring四天精通课程,旨在帮助学习者深入理解和实践Spring框架。 笔记部分可能会涵盖以下内容: 1. **Spring概述**:介绍Spring框架的历史、特点和主要...

    Spring源码学习加注释,方便学习.zip

    spring源码中文注释方便学习,spring源码学习加注释。 spring源码中文注释方便学习,spring源码学习加注释。 spring源码中文注释方便学习,spring源码学习加注释。 spring源码中文...

    Spring高级源码学习笔记.zip

    总之,Spring源码学习是一个深化编程技能,理解设计模式,以及提高问题解决能力的过程。通过深入研究,程序员不仅可以优化自己的代码,还能更高效地利用Spring框架提供的功能,提升项目的可维护性和扩展性。

    spring源码学习之思维导图

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

    spring 源码环境搭建

    "spring源码" 是指 Spring 框架的源代码。 部分内容解释 1. 下载 GitHub 客户端安装 下载 GitHub 客户端是因为 Spring 源码托管在 GitHub 上,所以我们需要下载 GitHub 客户端来 clone Spring 源码。安装成功后,...

    spring 源码中文注释

    Spring框架是Java开发中最广泛应用的轻量级框架之一,它以IoC(Inversion of Control,控制反转)和AOP...通过学习源码,我们可以更全面地掌握Spring框架,提升自己的技术水平,更好地应对各种复杂的软件开发挑战。

    spring源码注释中文

    Spring 源码注释中文版的提供,使得开发者能够更加深入地理解 Spring 的工作原理,无需经过复杂的编译过程,可以直接阅读源码注释来学习。 Spring 框架主要由以下几个关键模块组成: 1. **Core Container(核心...

    Spring源码解析.zip

    总的来说,"Spring源码解析"这个压缩包为我们提供了一个系统性学习Spring源码的途径。通过深入学习和理解Spring的IoC、AOP以及事务管理的源码,我们可以提升对Spring框架的掌握程度,进而提高我们的开发能力和问题...

    构建为eclipse项目的spring源码

    在Eclipse中构建Spring源码项目,可以帮助我们深入理解Spring的工作原理,从而更好地利用它来构建高效、可维护的Java应用。以下将详细阐述如何构建和探索Spring源码。 1. **获取源码** Spring源码可以从官方GitHub...

    spring源码

    学习Spring源码有助于深入理解其内部工作原理,例如bean的生命周期管理、AOP的实现、以及MVC的请求处理流程。这将有助于开发者更高效地利用Spring框架,编写出高质量、高性能的Java应用。通过分析源码,开发者还可以...

    Spring源码学习一:源码分析概述1

    Spring源码学习概述 Spring是Java生态系统中的一种流行的开源框架,由Rod Johnson创立于2003年。Spring框架的主要目标是使Java应用程序的开发变得更加简洁、灵活和可维护。Spring框架的核心思想是基于依赖注入...

    spring源码(注释+测试版)

    这份"spring源码(注释+测试版)"提供了Spring框架的源代码,带有注释和测试用例,对于开发者深入理解Spring的工作原理非常有帮助。 1. **spring-core**:这是Spring框架的基础模块,包含了核心的工具类和资源处理...

    spring源码分析专题学习资源

    spring源码分析专题,源码分析视频,对spring的源码进行分析

    spring 源码四

    《Spring源码解析》 ...通过对Spring源码的深入学习,不仅可以提高我们对Spring的理解,还能提升解决问题的能力,为实际开发提供强大的支持。Spring源码的世界广阔且深邃,每一次探索都会带来新的收获。

    struts+hibernate+spring源码学习:hr考勤管理系统.rar

    struts+hibernate+spring源码学习:hr考勤管理系统.rar

    idea+gradle构建spring源码环境.docx

    四、导入 Spring 源码到 IDEA 在 Spring 源码目录下,有一个名为 `import-into-idea.md` 的文件,该文件提供了将 Spring 源码导入 IDEA 的步骤。我们可以按照该文件中的步骤,一步步完成导入过程。 五、编译和构建...

    spring框架学习源码

    在这个"Spring框架学习源码"中,我们将会探讨以下几个关键知识点: 1. **SpringBoot**:SpringBoot简化了Spring应用程序的初始搭建以及开发过程。它内置了Tomcat服务器,提供了一种默认的配置,使得开发者可以快速...

    SpringiA4_SourceCode:Spring实战第四版源码,学习用。

    接下来,我们将围绕Spring框架、Spring 4.x版本的关键特性以及如何通过源码学习进行详细的讨论。 Spring框架是Java企业级应用开发的核心工具,由Rod Johnson创建,它提供了一个全面的编程和配置模型,旨在简化Java...

    马士兵老师spring框架学习笔记

    马士兵老师是知名的Java教育专家,他的Spring框架学习笔记深入浅出,对于初学者和进阶者来说都是一份宝贵的资源。这份笔记涵盖了Spring的核心概念、配置、AOP(面向切面编程)、DI(依赖注入)等关键知识点。 1. **...

Global site tag (gtag.js) - Google Analytics