spring源码解读系列(一):Bean生命周期图解

追求适度,才能走向成功;人在顶峰,迈步就是下坡;身在低谷,抬足既是登高;弦,绷得太紧会断;人,思虑过度会疯;水至清无鱼,人至真无友,山至高无树;适度,不是中庸,而是一种明智的生活态度。

导读:本篇文章讲解 spring源码解读系列(一):Bean生命周期图解,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

一、注意事项:

本次源码解读基于Spring-Framework 5.2.9版本,可自行通过官网下载源码,本地安装好gradle后可自行编译和运行,跟随本教程走进Spring底层

二、spring创建bean的整体流程

在这里插入图片描述

三、查看入口代码

通过main方法自定义测试类

        ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
        Person bean = ac.getBean(Person.class);
        Person bean2 = ac.getBean(Person.class);

四、入口代码解析

通过new ClassPathXmlApplicationContext(“applicationContext.xml”)查看spring源码,调用的如下方法:

	/**
	 * 
	 * 通过给定的parent创建一个ClassPathXmlApplicationContext对象,将xml文件中的配置转换成bean对象
	 * Create a new ClassPathXmlApplicationContext with the given parent,
	 * loading the definitions from the given XML files.
	 * @param configLocations array of resource locations
	 * @param refresh whether to automatically refresh the context,
	 * loading all bean definitions and creating all singletons.
	 * Alternatively, call refresh manually after further configuring the context.
	 * @param parent the parent context
	 * @throws BeansException if context creation failed
	 * @see #refresh()
	 */
	public ClassPathXmlApplicationContext(
			String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
			throws BeansException {
		// 调用父类构造方法,进行相关的对象创建等操作,包含属性的赋值操作
		super(parent);
		// 将给定的xml文件转成string(支持spEL表达式的解析)对象,保存在configLocations中
		setConfigLocations(configLocations);
		if (refresh) {
			refresh();
		}
	}

五、super(parent)源码跟踪

我们知道java中子类的创建必须先调用父类的构造方法,我们不断向上跟进,看看spring做了哪些事情

AbstractApplicationContext

跟进到父类AbstractApplicationContext中发现,该有参构造中主要做了两件事情

	/**
     * 主要做两件事情 1 调用无参构造创建资源模式处理器PathMatchingResourcePatternResolver
     *              2 设置parent,此处为null,在springMVC中有扩展
	 * Create a new AbstractApplicationContext with the given parent context.
	 * @param parent the parent context
	 */
	public AbstractApplicationContext(@Nullable ApplicationContext parent) {
		this();
		setParent(parent);
	}
	/**
	 * 创建一个无父类的AbstractApplicationContext对象
	 * Create a new AbstractApplicationContext with no parent.
	 */
	public AbstractApplicationContext() {
		// 创建资源模式处理器
		this.resourcePatternResolver = getResourcePatternResolver();
	}
	protected ResourcePatternResolver getResourcePatternResolver() {
		// 创建一个资源模式解析器(其实就是用来解析xml配置文件)
		return new PathMatchingResourcePatternResolver(this);
	}

DefaultResourceLoader

从AbstractApplicationContext继续向上跟进,进入DefaultResourceLoader的无参构造

	/**
	 * 基于当前线程初始化类加载器
	 */
	public DefaultResourceLoader() {
		this.classLoader = ClassUtils.getDefaultClassLoader();
	}
	@Nullable
	public static ClassLoader getDefaultClassLoader() {
		// 定义类加载器变量c1
		ClassLoader cl = null;
		try {
			// 获取当前线程的上下文类加载器
			cl = Thread.currentThread().getContextClassLoader();
		}
		catch (Throwable ex) {
			// Cannot access thread context ClassLoader - falling back...
		}
		// 如果cl为null
		if (cl == null) {
			// No thread context class loader -> use class loader of this class.
			// 没有线程上下文类加载器 -> 使用这个类的类加载器
			// 获取ClassUtils的类加载器
			cl = ClassUtils.class.getClassLoader();
			// 如果c1还为null
			if (cl == null) {
				// getClassLoader() returning null indicates the bootstrap ClassLoader
				// getClassLoader() 返回null表示boostrap ClassLoader
				try {
					// 获取系统的类加载器
					cl = ClassLoader.getSystemClassLoader();
				}
				catch (Throwable ex) {
					// Cannot access system ClassLoader - oh well, maybe the caller can live with null...
				}
			}
		}
		return cl;
	}

六、setConfigLocations(configLocations)源码跟踪

本方法主要功能在于将给定的xml文件转成string(支持spEL表达式的解析)对象,保存在configLocations中。其中有几个关键点需要注意

初始化环境对象

追踪该方法,发现调用了org.springframework.context.support.AbstractRefreshableConfigApplicationContext#resolvePath

	/**
	 * 解析给定的xml路径,并将路径中的占位符使用环境变量替代
	 */
	protected String resolvePath(String path) {
		return getEnvironment().resolveRequiredPlaceholders(path);
	}
	/**
	 * 查询Environment对象,如果有,则返回,如果没有,则创建(这里我们是第一次进来,environment为null,需要创建)
	 * 支持用户通过实现类,进行自定义扩展
	 * Return the {@code Environment} for this application context in configurable
	 * form, allowing for further customization.
	 * <p>If none specified, a default environment will be initialized via
	 * {@link #createEnvironment()}.
	 */
	@Override
	public ConfigurableEnvironment getEnvironment() {
		if (this.environment == null) {
			this.environment = createEnvironment();
		}
		return this.environment;
	}
	/**
	 * 系统默认创建并返回一个StandardEnvironment对象
	 * 支持用户通过实现类,进行自定义扩展
	 * Create and return a new {@link StandardEnvironment}.
	 * <p>Subclasses may override this method in order to supply
	 * a custom {@link ConfigurableEnvironment} implementation.
	 */
	protected ConfigurableEnvironment createEnvironment() {
		return new StandardEnvironment();
	}


跟进StandardEnvironment的父类无参构造,查看

public AbstractEnvironment() {
		customizePropertySources(this.propertySources);
	}

发现调用了子类的customizePropertySources方法,我们这里是调用的StandardEnvironment的

	/**
	 * 将系统中Java环境的属性资源添加到MutablePropertySources中
	 */
	@Override
	protected void customizePropertySources(MutablePropertySources propertySources) {
		propertySources.addLast(
				new PropertiesPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));
		propertySources.addLast(
				new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));
	}

自此,完成了环境对象StandardEnvironment的创建,并将系统资源保存在变量MutablePropertySources中,方便后续直接使用,下面是StandardEnvironment的类图请添加图片描述

解析文件路径中的占位符

将上图中的getEnvironment().resolveRequiredPlaceholders(path)方法继续追踪resolveRequiredPlaceholders(path)方法,确认最终核心实现逻辑org.springframework.util.PropertyPlaceholderHelper#parseStringValue

	/**
	 * 以递归的方式解析xml路径,进行占位符的解析,最终返回解析后的完整路径
	 *
	 */
	protected String parseStringValue(
			String value, PlaceholderResolver placeholderResolver, @Nullable Set<String> visitedPlaceholders) {

		int startIndex = value.indexOf(this.placeholderPrefix);
		// 不包含占位符,则直接返回
		if (startIndex == -1) {
			return value;
		}

		StringBuilder result = new StringBuilder(value);
		// 包含占位符,则进行解析,使用环境变量中的属性对占位符进行替代
		while (startIndex != -1) {
			int endIndex = findPlaceholderEndIndex(result, startIndex);
			if (endIndex != -1) {
				String placeholder = result.substring(startIndex + this.placeholderPrefix.length(), endIndex);
				String originalPlaceholder = placeholder;
				if (visitedPlaceholders == null) {
					visitedPlaceholders = new HashSet<>(4);
				}
				if (!visitedPlaceholders.add(originalPlaceholder)) {
					throw new IllegalArgumentException(
							"Circular placeholder reference '" + originalPlaceholder + "' in property definitions");
				}
				// 递归调用,解析路径中的占位符
				// Recursive invocation, parsing placeholders contained in the placeholder key.
				placeholder = parseStringValue(placeholder, placeholderResolver, visitedPlaceholders);
				// Now obtain the value for the fully resolved key...
				String propVal = placeholderResolver.resolvePlaceholder(placeholder);
				if (propVal == null && this.valueSeparator != null) {
					int separatorIndex = placeholder.indexOf(this.valueSeparator);
					if (separatorIndex != -1) {
						String actualPlaceholder = placeholder.substring(0, separatorIndex);
						String defaultValue = placeholder.substring(separatorIndex + this.valueSeparator.length());
						propVal = placeholderResolver.resolvePlaceholder(actualPlaceholder);
						if (propVal == null) {
							propVal = defaultValue;
						}
					}
				}
				if (propVal != null) {
					// 递归调用,解析路径中的占位符
					// Recursive invocation, parsing placeholders contained in the
					// previously resolved placeholder value.
					propVal = parseStringValue(propVal, placeholderResolver, visitedPlaceholders);
					result.replace(startIndex, endIndex + this.placeholderSuffix.length(), propVal);
					if (logger.isTraceEnabled()) {
						logger.trace("Resolved placeholder '" + placeholder + "'");
					}
					startIndex = result.indexOf(this.placeholderPrefix, startIndex + propVal.length());
				}
				else if (this.ignoreUnresolvablePlaceholders) {
					// Proceed with unprocessed value.
					startIndex = result.indexOf(this.placeholderPrefix, endIndex + this.placeholderSuffix.length());
				}
				else {
					throw new IllegalArgumentException("Could not resolve placeholder '" +
							placeholder + "'" + " in value \"" + value + "\"");
				}
				visitedPlaceholders.remove(originalPlaceholder);
			}
			else {
				startIndex = -1;
			}
		}
		return result.toString();
	}

至此,spring工厂的准备工作完成,接下来将进入解析xml至bean初始化完成的核心流程。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/130186.html

(0)
飞熊的头像飞熊bm

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!