`
j小虫
  • 浏览: 19019 次
  • 性别: 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学习笔记&源码

    本资料“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. **IoC容器**:Spring的核心是IoC容器,它负责管理对象的生命周期和依赖关系。通过XML配置文件或...

    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(核心...

    构建为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源码的世界广阔且深邃,每一次探索都会带来新的收获。

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

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

    spring源码雷神spring源码雷神

    "Spring源码雷神"可能指的是对Spring框架深入源码级别的理解和解析。以下将详细阐述Spring框架的一些核心知识点: 1. **IoC(Inversion of Control)控制反转**:Spring的核心特性之一,它通过依赖注入(Dependency...

    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. **...

    spring 源码分析CHM

    摘自javaEye 学习spring 源码的必备资料. 多多下载

Global site tag (gtag.js) - Google Analytics