你不知道系列--Spring是如何加载配置文件

本文探讨了Spring如何加载配置文件,从web.xml中的ContextLoaderListener开始,详细解释了Spring启动时加载applicationContext.xml的过程。通过ServletContextListener接口的适配方法,Spring将配置文件对象存入ServletContext,依赖Servlet的生命周期来管理配置文件的加载与销毁。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Spring如何加载配置文件

面试的时候经常会有面试官问Spring知识点,面试官问到Spring是如何加载配置文件的,流程清楚吗?求职者:在web.xml中会指定pring配置文件路径,就会实现加载了。面试官:那你能说说流程吗?求职者:emmmm…这个时候就会很尴尬了。

任何事情不能局限于表面,需要有求知意识,尽自己能力和理解去探求真相,当然这个也不是一蹴而就,是一个循序渐进的过程,重要的是在这条路上需要不断求索。

spring加载配置文件实现流程

大家都知道,Spring配置Bean对象,默认都是在applicationContext.xml中,Spring启动的时候会加载配置文件,来看一段Spring容器配置:

<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>classpath:spring/applicationContext*.xml</param-value>
	</context-param>
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>

这个配置大家都很熟悉,在web.xml中配置监听对象ContextLoaderListener,那这个对象中有什么,为什么要监听这个对象?我们先来看看这个对象中都有些什么

public class ContextLoaderListener extends ContextLoader implements ServletContextListener {


	public ContextLoaderListener() {
	}

	
	public ContextLoaderListener(WebApplicationContext context) {
		super(context);
	}


	/**
	 * Initialize the root web application context.
	 */
	@Override
	public void contextInitialized(ServletContextEvent event) {
		initWebApplicationContext(event.getServletContext());
	}


	/**
	 * Close the root web application context.
	 */
	@Override
	public void contextDestroyed(ServletContextEvent event) {
		closeWebApplicationContext(event.getServletContext());
		ContextCleanupListener.cleanupAttributes(event.getServletContext());
	}

}

原来这个ContextLoaderListener是继承了ContextLoader实现了ServletContextListener接口,这个玩法非常眼熟的嘛,定睛一看就是java设计模式中类的适配模式(目标target方法需要适配,接口类创建适配方法,定义Adapter适配类去实现接口方法),既然ContextLoaderListener是目标类有方法待适配,那我们就看看ServletContextListener 接口类有什么适配方法

public interface ServletContextListener extends EventListener {

   
    default public void contextInitialized(ServletContextEvent sce) {}

    
    default public void contextDestroyed(ServletContextEvent sce) {}
}

ServletContextListener 接口类中有两个适配方法分别为初始化和销毁方法,结合ContextLoaderListener 类的contextInitialized方法(上下文配置初始化方法),这里目标类调用了initWebApplicationContext(event.getServletContext()),初始化WEB项目的applicationContext,还传入了一个ServletContext对象,那这是想干嘛嘞?我们来看看Adapter适配类ContextLoader 都做了些什么

public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
		if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
			throw new IllegalStateException(
					"Cannot initialize context because there is already a root application context present - " +
					"check whether you have multiple ContextLoader* definitions in your web.xml!");
		}

		Log logger = LogFactory.getLog(ContextLoader.class);
		servletContext.log("Initializing Spring root WebApplicationContext");
		if (logger.isInfoEnabled()) {
			logger.info("Root WebApplicationContext: initialization started");
		}
		long startTime = System.currentTimeMillis();

		try {
			// Store context in local instance variable, to guarantee that
			// it is available on ServletContext shutdown.
			if (this.context == null) {
				this.context = createWebApplicationContext(servletContext);
			}
			if (this.context instanceof ConfigurableWebApplicationContext) {
				ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
				if (!cwac.isActive()) {
					// The context has not yet been refreshed -> provide services such as
					// setting the parent context, setting the application context id, etc
					if (cwac.getParent() == null) {
						// The context instance was injected without an explicit parent ->
						// determine parent for root web application context, if any.
						ApplicationContext parent = loadParentContext(servletContext);
						cwac.setParent(parent);
					}
					configureAndRefreshWebApplicationContext(cwac, servletContext);
				}
			}
			servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);

			ClassLoader ccl = Thread.currentThread().getContextClassLoader();
			if (ccl == ContextLoader.class.getClassLoader()) {
				currentContext = this.context;
			}
			else if (ccl != null) {
				currentContextPerThread.put(ccl, this.context);
			}

			if (logger.isDebugEnabled()) {
				logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" +
						WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");
			}
			if (logger.isInfoEnabled()) {
				long elapsedTime = System.currentTimeMillis() - startTime;
				logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms");
			}

			return this.context;
		}
		catch (RuntimeException ex) {
			logger.error("Context initialization failed", ex);
			servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
			throw ex;
		}
		catch (Error err) {
			logger.error("Context initialization failed", err);
			servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err);
			throw err;
		}
	}

这里重点关注
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);

把Spring配置文件对象ApplicationContext的WEB开发实现类WebApplicationContext设置到ServletContext中,依靠Servlet启动来实现加载。

我们来梳理一下:

  • 众所周知当服务启动,会为每一个web项目分配一个Servlet对象,ServletContextListener是Servlet监听类(监听Servlet启动,销毁方法)
  • ContextLoaderListener它通过类适配实现了ServletContextListener类的上下文初始化contextInitialized()方法
  • 当服务启动,加载Spring配置文件ApplicationContext对象,其web开发中实现类 webApplicationContext会存入到ServletContext对象中
  • ContextLoaderListener则实现了服务启动创建Servlet对象时,把Spring配置文件设置到Servlet上下文中,监听webApplicationContext,从而知道Spring配置文件开始加载

总结:
这里就是Spring配置文件实例化的流程,通过依赖Servlet的创建和销毁,来实现Soring配置文件加载和销毁,其实ContextLoaderListener是web组件,真正实例化的是Tomcat,Tomcat在启动加载web.xml实例化并识别Lintenter配置,Spirng是维护了ApplicationContext对象,通过ApplicationContext对象读取配置文件。

### 如何在Spring Boot Java应用启动时指定外部配置文件位置及名称 #### 通过命令行参数指定外部配置文件的位置和名称 当使用 `java -jar` 启动 Spring Boot 应用程序时,可以通过传递命令行参数来指定外部配置文件的位置和名称。这允许应用程序从同的环境读取相应的配置。 对于位于类路径外的配置文件,可以利用 `--spring.config.location` 参数提供其具体路径: ```bash java -jar web-0.0.1.jar --spring.config.location=file:/path/to/your/application-local.yml ``` 上述命令使得 Spring Boot 能够定位并加载 `/path/to/your/application-local.yml` 中定义的属性[^2]。 如果偏好于仅更改配置文件的名字而非路径,则可通过 `--spring.config.name` 来达成目的。例如,要让 Spring Boot 寻找名为 `application-local.yml` 的文件作为主要配置源之一,可执行如下指令: ```bash java -jar web-0.0.1.jar --spring.config.name=application-local ``` 此操作指示框架寻找与给定名字相匹配的应用配置文件。 另外,在某些场景下可能需要同时指明多个同来源的配置文件;这时可以在 `spring.config.location` 参数后面附加逗号分隔的一系列资源地址: ```bash java -jar myjava.jar --spring.config.location=classpath:/default.properties,classpath:/override.properties ``` 这段命令告诉 Spring Boot 首先尝试加载默认设置,之后再覆盖任何存在于 override 属性中的值[^4]。 #### 利用虚拟机选项 `-Dspring.config.location` 设置配置文件路径 除了命令行参数之外,还可以借助 JVM 系统属性的方式设定配置文件的位置。这种方式适用于那些支持直接解析命令行参数的情况。下面的例子展示了如何通过 `-D` 开头的 VM option 将配置文件指向磁盘上某个具体的 .properties 或者 .yml 文件: ```bash java -jar -Dspring.config.location=D:\config\config.properties springbootrestdemo-0.0.1-SNAPSHOT.jar ``` 这里采用的是 Windows 平台下的绝对路径示例,实际部署环境中应当依据实际情况调整路径字符串[^3]。 #### 动态激活特定profile以间接影响所使用的配置文件 有时并需要改变整个项目的全局配置文件,而是希望通过激活同的 profile 达到灵活控制的目的。此时只需简单地向启动脚本追加 `--spring.profiles.active=<profile_name>` 即可轻松完成切换工作: ```bash java -jar myproject.jar --spring.profiles.active=test ``` 一旦设置了活动 Profile 为 "test", Spring Boot 自动会在 classpath 下搜寻诸如 `application-test.yml` 这样的文件,并将其优先级置于通用配置之上[^5]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值