Spring源码-IOC之ClassPathXmlApplicationContext

版本 Spring Framework 6.0.9

        在学习SpringIOC的时候,ClassPathXmlApplicationContext经常作为入门的IOC容器,浅析下IOC容器的创建过程。

1. 构造方法

ClassPathXmlApplicationContext的构造方法可以分成两种,一种是参数有class对象的构造方法,另外一种则没有,主要区别在于资源路径的处理。其他构造方法最终调用其中一种,

1.1 不带class对象参数的构造方法

调用父类AbstractRefreshableConfigApplicationContext的setConfigLocations方法,将资源路径解析后,添加到父类属性configLocations数组中,数组类型是String

1.2 带class对象参数的构造方法

将资源路径转换成ClassPathResource对象,赋值给上下文的属性configResources数组中,数组类型是Resource

1.3 实例化ClassPathXmlApplicationContext

       实际上不带class对象参数的构造方法,path在Spring的refresh方法中obtainFreshBeanFactory阶段,最终还是会转换成Resource对象,调用org.springframework.beans.factory.xml.XmlBeanDefinitionReader的 public int loadBeanDefinitions(EncodedResource encodedResource)方法。

        参考官方示例,使用不带class对象参数的构造方法创建IOC容器

2. 实例化过程

构造方法执行过程分为三个部分,调用父类的构造方法、设置上下文的配置路径,调用refresh()方法

2.1 super(parent)

2.1.1 源码

super(parent)调用父类AbstractApplicationContext的有参构造,包含两段代码

    // this()
	public AbstractApplicationContext() {
		this.resourcePatternResolver = getResourcePatternResolver();
	}

    // getResourcePatternResolver()
	protected ResourcePatternResolver getResourcePatternResolver() {
		return new PathMatchingResourcePatternResolver(this);
	}

    // new PathMatchingResourcePatternResolver(this)
	public PathMatchingResourcePatternResolver(ResourceLoader resourceLoader) {
		Assert.notNull(resourceLoader, "ResourceLoader must not be null");
		this.resourceLoader = resourceLoader;
	}

this()实际上是AbstractApplicationContext的无参构造方法,该方法创建了一个资源路径解析器PathMatchingResourcePatternResolver并赋值给上下文的resourcePatternResolver属性

另外PathMatchingResourcePatternResolver是包含当前上下文对象的,给该实例的resourceLoader属性赋值,提供加载资源的能力。

PathMatchingResourcePatternResolver用于在上下文refresh过程中,加载bean定义loadBeanDefinitions时将配置路径转成Resource对象。

	public void setParent(@Nullable ApplicationContext parent) {
		this.parent = parent;
		// 父环境非空并且是ConfigurableEnvironment实例,将父环境合并到当前(子)环境中
		if (parent != null) {
			Environment parentEnvironment = parent.getEnvironment();
			if (parentEnvironment instanceof ConfigurableEnvironment configurableEnvironment) {
				// 如果当前上下文没有环境对象,getEnvironment会先创建一个,再合并
				getEnvironment().merge(configurableEnvironment);
			}
		}
	}

    // getEnvironment().merge(configurableEnvironment);
	public void merge(ConfigurableEnvironment parent) {
		// 合并环境配置
		for (PropertySource<?> ps : parent.getPropertySources()) {
			if (!this.propertySources.contains(ps.getName())) {
				this.propertySources.addLast(ps);
			}
		}
		// 合并激活环境配置
		String[] parentActiveProfiles = parent.getActiveProfiles();
		if (!ObjectUtils.isEmpty(parentActiveProfiles)) {
			synchronized (this.activeProfiles) {
				Collections.addAll(this.activeProfiles, parentActiveProfiles);
			}
		}
		// 合并默认环境配置
		String[] parentDefaultProfiles = parent.getDefaultProfiles();
		if (!ObjectUtils.isEmpty(parentDefaultProfiles)) {
			synchronized (this.defaultProfiles) {
				this.defaultProfiles.remove(RESERVED_DEFAULT_PROFILE_NAME);
				Collections.addAll(this.defaultProfiles, parentDefaultProfiles);
			}
		}
	}

setParent(parent);主要作用是设置上下文的parent属性。如果父环境非空,则将父环境属性添加到当前环境,本文示例没传父上下文。

2.1.2 总结

  • 实例化PathMatchingResourcePatternResolver,给属性resourcePatternResolver赋值
  • 给属性parent赋值,当父环境不为空是,合并父子环境

2.2 setConfigLocations(configLocations)

2.2.1 源码

上面提过该方法是用来解析上下文配置路径,这里循环locations数组解析资源路径,入参为 services.xml 和 beans.xml。

resolvePath方法也是两段代码,getEnvironment和resolveRequiredPlaceholders

  • getEnvironment
	@Override
	public ConfigurableEnvironment getEnvironment() {
		if (this.environment == null) {
            // 创建一个StandardEnvironment
			this.environment = createEnvironment();
		}
		return this.environment;
	}

	protected ConfigurableEnvironment createEnvironment() {
		return new StandardEnvironment();
	}

    // org.springframework.core.env.StandardEnvironment#StandardEnvironment()
	public StandardEnvironment() {
	}

getEnvironment()方法主要是获取当前上下文环境,但也有创建环境的能力,赋值给上下文的environment属性。本文示例在这里创建ClassPathXmlApplicationContext的环境StandardEnvironment。

public class StandardEnvironment extends AbstractEnvironment {

	public StandardEnvironment() {
	}
    
    // 省略其他...
}


public abstract class AbstractEnvironment implements ConfigurableEnvironment {

	public AbstractEnvironment() {
		this(new MutablePropertySources());
	}

    protected AbstractEnvironment(MutablePropertySources propertySources) {
		this.propertySources = propertySources;
        // 创建一个属性源解析器PropertySourcesPropertyResolver
		this.propertyResolver = createPropertyResolver(propertySources);
		// 子类自定义操作propertySources
		customizePropertySources(propertySources);
	}

	protected ConfigurablePropertyResolver createPropertyResolver(MutablePropertySources propertySources) {
		return new PropertySourcesPropertyResolver(propertySources);
	}

	protected void customizePropertySources(MutablePropertySources propertySources) {
	}

    // 省略其他代码...

}

虽然StandardEnvironment的无参构造是个空方法,但其父类AbstractEnvironment的无参构造对环境做了一些初始化。

无参构造创建了一个MutablePropertySources对象(包含一个或多个
PropertySource源对象,实现并拓展了接口PropertySources操作属性源能力),作为入参调用另外一个有参构造。

MutablePropertySources对象赋值给了环境StandardEnvironment的propertySources属性

createPropertyResolver方法创建一个PropertySourcesPropertyResolver,赋值给环境StandardEnvironment的propertyResolver属性,提供解析属性源的能力。

customizePropertySources是个空方法,由子类实现。在springframework体系中,两个子类实现了该方法,一个是web环境的StandardServletEnvironment,另外一个是非web环境,即本文示例中创建的StandardEnvironment。

public class StandardEnvironment extends AbstractEnvironment {

	public static final String SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME = "systemEnvironment";

	public static final String SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME = "systemProperties";

	@Override
	protected void customizePropertySources(MutablePropertySources propertySources) {
		// JVM 系统属性属性源,名称systemProperties
		propertySources.addLast(
				new PropertiesPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));
		// 系统属性属性源,名称systemEnvironment
		propertySources.addLast(
				new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));
	}

    // 省略其他代码...

}

StandardEnvironmen环境下,重写的customizePropertySources方法会加载JVM 系统属性和系统属性。

public class StandardServletEnvironment extends StandardEnvironment implements ConfigurableWebEnvironment {

	public static final String SERVLET_CONTEXT_PROPERTY_SOURCE_NAME = "servletContextInitParams";

	public static final String SERVLET_CONFIG_PROPERTY_SOURCE_NAME = "servletConfigInitParams";

	public static final String JNDI_PROPERTY_SOURCE_NAME = "jndiProperties";

	@Override
	protected void customizePropertySources(MutablePropertySources propertySources) {
		// Servlet 配置初始化参数属性源,名称servletConfigInitParams
		propertySources.addLast(new StubPropertySource(SERVLET_CONFIG_PROPERTY_SOURCE_NAME));
		// Servlet 上下文初始化参数属性源,名称servletContextInitParams
		propertySources.addLast(new StubPropertySource(SERVLET_CONTEXT_PROPERTY_SOURCE_NAME));
		if (jndiPresent && JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable()) {
			// JNDI 属性源,名称jndiProperties
			propertySources.addLast(new JndiPropertySource(JNDI_PROPERTY_SOURCE_NAME));
		}
		// 调用org.springframework.core.env.StandardEnvironment.customizePropertySources
		// JVM 系统属性属性源,名称systemProperties
		// 系统属性属性源,名称systemEnvironment
		super.customizePropertySources(propertySources);
	}

    //省略其他代码...
}

StandardServletEnvironment环境下,除了加载JVM 系统属性和系统属性,还会加载web环境的属性源(Servlet 配置初始化参数属性源、Servlet 上下文初始化参数属性源,JNDI 属性源)

另外当前Servlet 配置属性源StubPropertySource和Servlet 上下文属性源StubPropertySource只是一个占位符,如果上下文的属性对象ServletConfig和ServletContext不为空,则会在refresh#prepareRefresh阶段替换成真正的属性源。

  • resolveRequiredPlaceholders
public abstract class AbstractEnvironment implements ConfigurableEnvironment {
    
    private final ConfigurablePropertyResolver propertyResolver;

	@Override
	public String resolveRequiredPlaceholders(String text) throws IllegalArgumentException {
		return this.propertyResolver.resolveRequiredPlaceholders(text);
	}

    // 省略其他代码...

}

由上面可以知道,本文示例创建的环境类是StandardEnvironmen,该类继承了抽象类AbstractEnvironment。第二段代码即调用父类AbstractEnvironment的resolveRequiredPlaceholders方法。同时我们也可以知道AbstractEnvironment的propertyResolver属性值是在getEnvironment()方法中,初始化环境创建的PropertySourcesPropertyResolver

第二段代码最终调用的是 PropertySourcesPropertyResolver 的 父类AbstractPropertyResolver#resolveRequiredPlaceholders方法

public class PropertySourcesPropertyResolver extends AbstractPropertyResolver {
    //省略其他代码...
}

public abstract class AbstractPropertyResolver implements ConfigurablePropertyResolver {

	@Nullable
	private PropertyPlaceholderHelper strictHelper;

	@Override
	public String resolveRequiredPlaceholders(String text) throws IllegalArgumentException {
		if (this.strictHelper == null) {
			this.strictHelper = createPlaceholderHelper(false);
		}
		return doResolvePlaceholders(text, this.strictHelper);
	}

    //省略其他代码...

}

AbstractPropertyResolver#resolveRequiredPlaceholders方法包含两部分,给strictHelper属性赋值,调用doResolvePlaceholders方法。

public abstract class AbstractPropertyResolver implements ConfigurablePropertyResolver {

	private String placeholderPrefix = SystemPropertyUtils.PLACEHOLDER_PREFIX;

	private String placeholderSuffix = SystemPropertyUtils.PLACEHOLDER_SUFFIX;

	@Nullable
	private String valueSeparator = SystemPropertyUtils.VALUE_SEPARATOR;

	private PropertyPlaceholderHelper createPlaceholderHelper(boolean ignoreUnresolvablePlaceholders) {
        // 返回一个使用 ${ 作为前缀、 } 作为后缀、 : 作为占位符变量与关联的默认值之间的分隔符的PropertyPlaceholderHelper实例
		return new PropertyPlaceholderHelper(this.placeholderPrefix, this.placeholderSuffix,
				this.valueSeparator, ignoreUnresolvablePlaceholders);
	}

    //省略其他代码...
}


public abstract class SystemPropertyUtils {

	public static final String PLACEHOLDER_PREFIX = "${";

	public static final String PLACEHOLDER_SUFFIX = "}";

	public static final String VALUE_SEPARATOR = ":";

    //省略其他代码...
}

createPlaceholderHelper方法创建一个使用 ${ 作为前缀、 } 作为后缀、 : 作为占位符变量与关联的默认值之间的分隔符的PropertyPlaceholderHelper实例,参数ignoreUnresolvablePlaceholders用于是否忽略无法解析的占位符,这里值为false。即AbstractPropertyResolver的strictHelper属性是一个PropertyPlaceholderHelper实例。

public abstract class AbstractPropertyResolver implements ConfigurablePropertyResolver {

	private String doResolvePlaceholders(String text, PropertyPlaceholderHelper helper) {
		return helper.replacePlaceholders(text, this::getPropertyAsRawString);
	}

	@Nullable
	protected abstract String getPropertyAsRawString(String key);

    // 省略其他代码...
}

doResolvePlaceholders方法的内容是调用刚刚实例化的PropertyPlaceholderHelper对象的replacePlaceholders(String value, PlaceholderResolver placeholderResolver)方法

public class PropertyPlaceholderHelper {

	public String replacePlaceholders(String value, PlaceholderResolver placeholderResolver) {
		Assert.notNull(value, "'value' must not be null");
		return parseStringValue(value, placeholderResolver, null);
	}

    protected String parseStringValue(
			String value, PlaceholderResolver placeholderResolver, @Nullable Set<String> visitedPlaceholders) {
        // 判断value是否包含前缀placeholderPrefix
		int startIndex = value.indexOf(this.placeholderPrefix);
		if (startIndex == -1) {
			return value;
		}

		StringBuilder result = new StringBuilder(value);
		while (startIndex != -1) {

            // 截取${}中的值
            String placeholder = result.substring(startIndex + this.placeholderPrefix.length(), endIndex);

            // 省略其他代码...

            // 获取环境中名称为placeholder的属性
            String propVal = placeholderResolver.resolvePlaceholder(placeholder);

            // 省略其他代码...

        }
        return result.toString();
        
    }

	@FunctionalInterface
	public interface PlaceholderResolver {

		/**
		 * Resolve the supplied placeholder name to the replacement value.
		 * @param placeholderName the name of the placeholder to resolve
		 * @return the replacement value, or {@code null} if no replacement is to be made
		 */
		@Nullable
		String resolvePlaceholder(String placeholderName);
	}

    // 省略其他代码...

}

replacePlaceholders方法有两个入参,参数value是资源路径,值为本文示例的services.xml或beans.xml参数placeholderResolver是函数式接口,值为抽象类AbstractPropertyResolver 的抽象方法getPropertyAsRawString,回想一下,现在我们一直看的都是抽象类AbstractPropertyResolver 的源码,但当前实例对象其实是其子类PropertySourcesPropertyResolver,所以parseStringValue方法里调用函数式接口的方法placeholderResolver.resolvePlaceholder(placeholder),实际上是抽象类AbstractPropertyResolver子类方法PropertySourcesPropertyResolver#getPropertyAsRawString。

函数式接口的唯一方法虽然不被当成抽象方法,实际上是抽象方法

本文示例在parseStringValue方法,value的值为services.xml或beans.xml,不包含 "${" 匹配符,都直接退出了。

拓展一下,将main方法中创建ClassPathXmlApplicationContext对象的代码改成如下

往系统添加属性servicesPath=services.xml,将资源路径services.xml改为占位符的方式${servicesPath},重新运行main方法

这样匹配逻辑就通过了

继续debug代码,可以发现${servicesPath}最终可以解析成系统servicesPath属性的关联值services.xml。

2.2.2 总结

  • 获取环境,当环境为空时创建环境并初始化,给上下文属性environment赋值
  • 解析资源路径,如果存在占位符,则获取占位符的关联值,给上下文属性configLocations赋值

2.3 refresh()

2.3.1 源码

	@Override
	public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			// 标志context的刷新方法开始
			StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");

			// 准备上下文环境,设置启动时间、活动状态,初始化属性源和事件监听器等。
			prepareRefresh();

			// 重置BeanFactory,加载Bean定义并返回 Bean 工厂, 获取的是DefaultListableBeanFactory,。
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// 对beanFactory填充功能,进行一些配置,如设置类加载器、表达式解析器、属性编辑器等。
			prepareBeanFactory(beanFactory);

			try {
				// 留给子类覆盖,对BeanFactory进行额外的处理。
				postProcessBeanFactory(beanFactory);

				// 标志后置处理器相关处理步骤开始
				StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
				// 激活BeanFactoryPostProcessor,允许BeanFactoryPostProcessor对BeanFactory进行后置处理
				invokeBeanFactoryPostProcessors(beanFactory);

				// 注册BeanPostProcessor,用于Bean实例化时的回调处理。
				registerBeanPostProcessors(beanFactory);
				// 标识后置处理器相关处理步骤已完成
				beanPostProcess.end();

				// 初始化国际化相关的属性
				initMessageSource();

				// 初始化事件广播器,用于事件的发布和监听。
				initApplicationEventMulticaster();

				// 模板方法,留给子类覆盖,用于扩展refresh动作。
				onRefresh();

				// 注册事件监听器,从BeanFactory中找出实现了ApplicationListener接口的Bean并注册到事件广播器中。
				registerListeners();

				// 实例化所有非懒加载的单例Bean,调用BeanFactory的preInstantiateSingletons()方法。
				finishBeanFactoryInitialization(beanFactory);

				// 完成刷新过程,通知生命周期处理器和事件监听器,发布ContextRefreshedEvent事件。
				finishRefresh();
			}

			catch (BeansException ex) {
				if (logger.isWarnEnabled()) {
					logger.warn("Exception encountered during context initialization - " +
							"cancelling refresh attempt: " + ex);
				}

				// 销毁已创建的单例bean
				destroyBeans();

				// 设置“active”属性为false
				cancelRefresh(ex);

				throw ex;
			}

			finally {
				// 用于重置Spring的一些公共的反射元数据缓存,包括ReflectionUtils,AnnotationUtils,ResolvableType和CachedIntrospectionResults等
				// 这些缓存是为了提高Spring的性能和效率,但是在某些情况下,可能需要清除这些缓存,比如在刷新上下文或者切换类加载器的时候.
				resetCommonCaches();
				// 标志context的刷新方法结束
				contextRefresh.end();
			}
		}
	}

调用父类AbstractApplicationContext的refresh方法,这里贴一下大致流程,另写个文章浅析一下。

2.3.2 总结

  • 25
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值