Spring源码阅读-spring-core:refresh()上(2)

本文详细剖析了Spring的ClassPathXmlApplicationContext构造过程,包括构造函数、父类调用、配置文件定位以及refresh()方法中的prepareRefresh和obtainFreshBeanFactory。在obtainFreshBeanFactory中,主要讨论了createBeanFactory和loadBeanDefinitions的过程,后者涉及到XML文件的读取、BeanDefinition的加载和注册。整个过程展示了Spring如何将XML配置转化为Bean实例并存储在BeanFactory中。
摘要由CSDN通过智能技术生成

1、 ClassPathXmlApplicationContext

下面这行代码是对Spring很常见的应用:

ApplicationContext context = new ClassPathXmlApplicationContext(
				"classpath:META-INF/spring/applicationContext.xml");

这一行代码就是将applicationContext.xml中定义的bean进行初始化和装载,通过这个context也就可以获取相应的bean了,那么这个ClassPathXmlApplicationContext的构造函数干了什么呢?

	public ClassPathXmlApplicationContext(
			String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
			throws BeansException {

		super(parent);
		setConfigLocations(configLocations);
		if (refresh) {
			refresh();
		}
	}

这就是三个部分,一个是调用父类的构造方法,第二个是将xml文件转换为真正的文件地址,第三个就是最关键的refresh方法。

2、super(parent)

我们在调用ClassPathXmlApplicationContext的构造方法时,调用的是这个:

	public ClassPathXmlApplicationContext(String... configLocations) throws BeansException {
		this(configLocations, true, null);
	}

它会调用这个构造方法:

	public ClassPathXmlApplicationContext(
			String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
			throws BeansException {

		super(parent);
		setConfigLocations(configLocations);
		if (refresh) {
			refresh();
		}
	}

这里面的super(parent)最终会调用:

AbstractApplicationContext:
	public AbstractApplicationContext(@Nullable ApplicationContext parent) {
		this();
		setParent(parent);
	}

this()方法如下:

	public AbstractApplicationContext() {
		this.resourcePatternResolver = getResourcePatternResolver();
	}
	protected ResourcePatternResolver getResourcePatternResolver() {
		return new PathMatchingResourcePatternResolver(this);
	}

也就是初始化一个PathResourcePatternResolver,它的目的就是加载资源(比如这里的classpath:META-INF/spring/applicationContext.xml,它将解析为一个对应的Resource).。
setParent(parent)方法是将父ApplicationContext的环境变量进行融合:

	public void setParent(@Nullable ApplicationContext parent) {
		this.parent = parent;
		if (parent != null) {
			Environment parentEnvironment = parent.getEnvironment();
			if (parentEnvironment instanceof ConfigurableEnvironment) {就是
				getEnvironment().merge((ConfigurableEnvironment) parentEnvironment);
			}
		}
	}

3、setConfigLocations

setConfigLocations方法主要是对传入的资源文件路径进行整理,转换为实际的路径(根据源码上看,并不是转换为绝对路径,是转换为相对路径,通过classloader来获取,后面会提到),去除掉${}这个placeholder。
具体代码如下:

	public void setConfigLocations(@Nullable String... locations) {
		if (locations != null) {
			Assert.noNullElements(locations, "Config locations must not be null");
			this.configLocations = new String[locations.length];
			for (int i = 0; i < locations.length; i++) {
				this.configLocations[i] = resolvePath(locations[i]).trim();
			}
		}
		else {
			this.configLocations = null;
		}
	}

这里面就是将每一个文件路径通过resovepath进行转换:

AbstractRefreshableConfigApplicationContext:
	protected String resolvePath(String path) {
		return getEnvironment().resolveRequiredPlaceholders(path);
	}

AbstractPropertyResolver:
	public String resolveRequiredPlaceholders(String text) throws IllegalArgumentException {
		if (this.strictHelper == null) {
			this.strictHelper = createPlaceholderHelper(false);
		}
		return doResolvePlaceholders(text, this.strictHelper);
	}
	private String doResolvePlaceholders(String text, PropertyPlaceholderHelper helper) {
		return helper.replacePlaceholders(text, this::getPropertyAsRawString);
	}
	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) {

		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();
	}

在我的示例里面其实就是通过检测有没有使用${}包含的字段。
最终返回的路径就是classpath:META-INF/spring/applicationContext.xml;

4、refresh

refresh方法字面上就是刷新的方法,这是ClassPathXmlApplicationContext初始化最核心的方法:

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) {
				if (logger.isWarnEnabled()) {
					logger.warn("Exception encountered during context initialization - " +
							"cancelling refresh attempt: " + ex);
				}

				// Destroy already created singletons to avoid dangling resources.
				destroyBeans();

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

				// Propagate exception to caller.
				throw ex;
			}

			finally {
				// Reset common introspection caches in Spring's core, since we
				// might not ever need metadata for singleton beans anymore...
				resetCo
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
根据提供的引用内容,你遇到了一个未解决的依赖问题。具体来说,你遇到了一个Unresolved dependency: 'org.springframework.boot:spring-boot-starter-web:jar:unknown'的错误。 这个错误通常是由于Maven或Gradle无法找到所需的依赖项而引起的。解决这个问题的方法有以下几种: 1. 检查依赖项的版本:首先,确保你在项目的构建文件(如pom.xml或build.gradle)中正确指定了所需的依赖项。检查依赖项的版本号是否正确,并确保它们与你使用的Spring Boot版本兼容。 2. 清理本地仓库:有时候,本地仓库中的依赖项可能会损坏或不完整,导致无法解析依赖项。你可以尝试清理本地仓库,然后重新构建项目。在Maven中,你可以通过删除本地仓库目录(默认为~/.m2/repository)中的相关文件来清理本地仓库。在Gradle中,你可以运行`gradle clean build --refresh-dependencies`命令来清理本地仓库。 3. 检查网络连接:确保你的网络连接正常,并且可以访问所需的依赖项仓库。有时候,由于网络问题,Maven或Gradle无法下载所需的依赖项。 4. 使用其他镜像源:如果你使用的是国内的网络环境,*** 更新构建工具:确保你使用的是最新版本的Maven或Gradle。有时候,旧版本的构建工具可能会导致依赖项解析的问题。 6. 检查依赖项的可用性:如果你使用的是非官方的依赖项,可能会遇到依赖项不可用的情况。在这种情况下,你可以尝试使用其他可用的依赖项,或者联系依赖项的维护者以获取支持。 希望以上方法能够帮助你解决Unresolved dependency: 'org.springframework.boot:spring-boot-starter-web:jar:unknown'的问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值