3.2-2、Spring源码学习:reader.loadBeanDefinitions(configLocation);

本文深入剖析Spring框架加载配置文件的全过程,从application-*.xml的解析到具体bean定义的注册,涵盖AbstractBeanDefinitionReader、XmlBeanDefinitionReader及DefaultBeanDefinitionDocumentReader的核心作用与流程。

前言

体能状态先于精神状态,习惯先于决心,聚焦先于喜好。

配置文件加载思路

本文讲述的是Spring 在加载配置文件时的流程,从 application-*.xml 这种到具体分类型接续。
其中有颜色背景的两个是for循环处理。
蓝色线表示递归调用。
红线部分表示别名。
Spring 对配置文件的处理思路即使,location-多个配置文件-每个配置文件区分 profile——每个 profile 区分四种标签 import、alias、beans、bean

在这里插入图片描述

AbstractBeanDefinitionReader.loadBeanDefinitions(

org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(
负责将加载到的bean definitions 文件解析为具体的bean实例
从指定的 location 资源路径加载 bean 定义信息。location 可以是简单的路径,但是也可以是 ResourcePatternResolver 类型,这样的话就需要对 ResourcePatternResolver 进行处理了,在初始阶段其包含一个 资源的set。
location 值可能为空,表示调用者对于资源不感兴趣。
最终返回发现的bean definitions 的数量

/**
	 * @throws BeanDefinitionStoreException in case of loading or parsing errors
	 * @see #getResourceLoader()
	 * @see #loadBeanDefinitions(org.springframework.core.io.Resource)
	 * @see #loadBeanDefinitions(org.springframework.core.io.Resource[])
	 */
	public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
		//获取 资源加载器
		ResourceLoader resourceLoader = getResourceLoader();
		if (resourceLoader == null) {
			throw new BeanDefinitionStoreException(
					"Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
		}
		//如果资源加载器属于 ResourcePatternResolver,则需要解析
		if (resourceLoader instanceof ResourcePatternResolver) {
			// 对 location 进行解析,Pattern模式可以生成多个 Resource 类型数组
			try {
				Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
				//具体的对资源进行解析-你可以看到,这里是一个递归调用
				int loadCount = loadBeanDefinitions(resources);
				if (actualResources != null) {
					for (Resource resource : resources) {
						actualResources.add(resource);
					}
				}
				if (logger.isDebugEnabled()) {
					logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
				}
				return loadCount;
			}
			catch (IOException ex) {
				throw new BeanDefinitionStoreException(
						"Could not resolve bean definition resource pattern [" + location + "]", ex);
			}
		}
		else {
			// 使用 绝对URL只能加载单个的资源
			Resource resource = resourceLoader.getResource(location);
			int loadCount = loadBeanDefinitions(resource);
			if (actualResources != null) {
				actualResources.add(resource);
			}
			if (logger.isDebugEnabled()) {
				logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
			}
			return loadCount;
		}
	}
将 ResourcePatternResolver 转化为 Resource

ResourcePatternResolver 类型表示资源包含多个 Resource的资源,而Spring 是以Resource作为基本单位进行Bean definitions 的解析的。
简单来说就是,加入你配置的是 classpath:application-*.xml,他就是寻找所有符合该通配符命名的文件,比如你有文件是 application-A.xml 、application-B.xml,那么就会生成两个Resource来进行解析,然后每个 resource就会进入 int loadCount = loadBeanDefinitions(resource);的逻辑

org.springframework.core.io.support.PathMatchingResourcePatternResolver.getResources(

//locationPattern 的形式如 classpath:applicationContext.xml 或 WEB-INF/*.xml等
@Override
	public Resource[] getResources(String locationPattern) throws IOException {
		Assert.notNull(locationPattern, "Location pattern must not be null");  
		//路径是否以 classpath*: 开头
		if (locationPattern.startsWith(CLASSPATH_ALL_URL_PREFIX)) {
			// 一个类路径资源-可能包含多个同名文件
			if (getPathMatcher().isPattern(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()))) {
				//满足一个类路径的通配,Ant风格
				return findPathMatchingResources(locationPattern);
			}
			else {
				// 所有的类资源使用给定的名字
				return findAllClassPathResources(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()));
			}
		}
		else {
			// 仅仅查找去除前缀的匹配,比如 classpath:123.xml,则只匹配123.xml
			// (避免奇怪的前缀).
			int prefixEnd = locationPattern.indexOf(":") + 1;
			if (getPathMatcher().isPattern(locationPattern.substring(prefixEnd))) {
				// 查找 Ant 风格
				return findPathMatchingResources(locationPattern);
			}
			else {
				// 单个给定名字的文件
				return new Resource[] {getResourceLoader().getResource(locationPattern)};
			}
		}
	}
AbstractBeanDefinitionReader.loadBeanDefinitions(

org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(

@Override
	public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
		Assert.notNull(resources, "Resource array must not be null");
		int counter = 0;
		for (Resource resource : resources) {
			counter += loadBeanDefinitions(resource);
		}
		return counter;
	}
XmlBeanDefinitionReader.loadBeanDefinitions(

org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(
从指定的xml文件加载 bean definitions

/**
	 * @param resource the resource descriptor for the XML file
	 * @return the number of bean definitions found
	 * @throws BeanDefinitionStoreException in case of loading or parsing errors
	 */
	@Override
	public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
		//调用方法如下
		return loadBeanDefinitions(new EncodedResource(resource));
	}
	
/**
	 * @param encodedResource the resource descriptor for the XML file,
	 * allowing to specify an encoding to use for parsing the file
	 * @return the number of bean definitions found
	 * @throws BeanDefinitionStoreException in case of loading or parsing errors
	 */
	public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
		Assert.notNull(encodedResource, "EncodedResource must not be null");
		if (logger.isInfoEnabled()) {
			logger.info("Loading XML bean definitions from " + encodedResource.getResource());
		}

		Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
		if (currentResources == null) {
			currentResources = new HashSet<EncodedResource>(4);
			this.resourcesCurrentlyBeingLoaded.set(currentResources);
		}
		if (!currentResources.add(encodedResource)) {
			throw new BeanDefinitionStoreException(
					"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
		}
		try {
			InputStream inputStream = encodedResource.getResource().getInputStream();
			try {
				InputSource inputSource = new InputSource(inputStream);
				if (encodedResource.getEncoding() != null) {
					inputSource.setEncoding(encodedResource.getEncoding());
				}
				//xml文件以指定字符格式转化为了输入流,在这里进行解析
				return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
			}
			finally {
				inputStream.close();
			}
		}
		catch (IOException ex) {
			throw new BeanDefinitionStoreException(
					"IOException parsing XML document from " + encodedResource.getResource(), ex);
		}
		finally {
			currentResources.remove(encodedResource);
			if (currentResources.isEmpty()) {
				this.resourcesCurrentlyBeingLoaded.remove();
			}
		}
	}
XmlBeanDefinitionReader.doLoadBeanDefinitions(

org.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadBeanDefinitions
对输入流进行具体解析,到这里才开始真正的解析xml文件

/**
	 * @param inputSource the SAX InputSource to read from
	 * @param resource the resource descriptor for the XML file
	 * @return the number of bean definitions found
	 * @throws BeanDefinitionStoreException in case of loading or parsing errors
	 * @see #doLoadDocument
	 * @see #registerBeanDefinitions
	 */
	protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
			throws BeanDefinitionStoreException {
		try {
			//将输入流转化为 Document 格式
			Document doc = doLoadDocument(inputSource, resource);
			//注册bean
			return registerBeanDefinitions(doc, resource);
		}
		catch (BeanDefinitionStoreException ex) {
			throw ex;
		}
		catch (SAXParseException ex) {
			throw new XmlBeanDefinitionStoreException(resource.getDescription(),
					"Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
		}
		catch (SAXException ex) {
			throw new XmlBeanDefinitionStoreException(resource.getDescription(),
					"XML document from " + resource + " is invalid", ex);
		}
		catch (ParserConfigurationException ex) {
			throw new BeanDefinitionStoreException(resource.getDescription(),
					"Parser configuration exception parsing XML from " + resource, ex);
		}
		catch (IOException ex) {
			throw new BeanDefinitionStoreException(resource.getDescription(),
					"IOException parsing XML document from " + resource, ex);
		}
		catch (Throwable ex) {
			throw new BeanDefinitionStoreException(resource.getDescription(),
					"Unexpected exception parsing XML document from " + resource, ex);
		}
	}
.XmlBeanDefinitionReader.registerBeanDefinitions(

org.springframework.beans.factory.xml.XmlBeanDefinitionReader.registerBeanDefinitions
注册 DOM document 中包含的 bean definitions

/**
	 * @param doc the DOM document
	 * @param resource the resource descriptor (for context information)
	 * @return the number of bean definitions found
	 * @throws BeanDefinitionStoreException in case of parsing errors
	 * @see #loadBeanDefinitions
	 * @see #setDocumentReaderClass
	 * @see BeanDefinitionDocumentReader#registerBeanDefinitions
	 */
	public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
		BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
		int countBefore = getRegistry().getBeanDefinitionCount();
		documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
		return getRegistry().getBeanDefinitionCount() - countBefore;
	}
DefaultBeanDefinitionDocumentReader.registerBeanDefinitions(

org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.registerBeanDefinitions(
开始解析Document 文件了,但是依旧是委托给了另一个方法

@Override
	public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
		this.readerContext = readerContext;
		logger.debug("Loading bean definitions");
		Element root = doc.getDocumentElement();
		doRegisterBeanDefinitions(root);
	}
DefaultBeanDefinitionDocumentReader.doRegisterBeanDefinitions(:解析 beans 标签

org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.doRegisterBeanDefinitions(

  • beans 标签和 profile 标签一起使用,即多版本
 <beans profile="test2">
 	<import resource=""></import>
 	<beans profile="test1">
 		<import resource=""></import>
 		<beans profile="dev">
			<import resource=""></import>
		</beans>
 	</beans>
 </beans>

这个标签用于划分版本,指定多版本时可以指定一个为激活, 关于 profile标签的使用可以参考 Spring 中的 profile

解析 profile 标签

/**
	 * Register each bean definition within the given root {@code <beans/>} element.
	 */
	protected void doRegisterBeanDefinitions(Element root) {
		// Any nested <beans> elements will cause recursion in this method. In
		// order to propagate and preserve <beans> default-* attributes correctly,
		// keep track of the current (parent) delegate, which may be null. Create
		// the new (child) delegate with a reference to the parent for fallback purposes,
		// then ultimately reset this.delegate back to its original (parent) reference.
		// this behavior emulates a stack of delegates without actually necessitating one.
		BeanDefinitionParserDelegate parent = this.delegate;
		this.delegate = createDelegate(getReaderContext(), root, parent);

		if (this.delegate.isDefaultNamespace(root)) {
		    //profile 标签
			String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
			if (StringUtils.hasText(profileSpec)) {
			    //MULTI_VALUE_ATTRIBUTE_DELIMITERS 为  “,;” 
				String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
						profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
				if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
					if (logger.isInfoEnabled()) {
						logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec +
								"] not matching: " + getReaderContext().getResource());
					}
					return;
				}
			}
		}

		//空实现
		preProcessXml(root);
		//真正开始处理节点
		parseBeanDefinitions(root, this.delegate);
		//空实现
		postProcessXml(root);

		this.delegate = parent;
	}
DefaultBeanDefinitionDocumentReader.parseBeanDefinitions(:准备解析 “import”, “alias”, “bean”. 标签

org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.parseBeanDefinitions(
解析 “import”, “alias”, “bean”. 标签

	protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
		if (delegate.isDefaultNamespace(root)) {
			NodeList nl = root.getChildNodes();
			for (int i = 0; i < nl.getLength(); i++) {
				Node node = nl.item(i);
				//判断是否是合法节点-空格、注释什么的都忽略
				if (node instanceof Element) {
					Element ele = (Element) node;
					//判断是否是默认 namespaceUri 是否是 http://www.springframework.org/schema/beans
					if (delegate.isDefaultNamespace(ele)) {
					    //对该节点进行具体解析-按照类型  import、alias、bean、beans
						parseDefaultElement(ele, delegate);
					}
					else {
						//用户自定义了 namespaceUri
						delegate.parseCustomElement(ele);
					}
				}
			}
		}
		else {
			//用户自定义的标签
			delegate.parseCustomElement(root);
		}
	}
DefaultBeanDefinitionDocumentReader.parseDefaultElement(:分类解析标签 import、alias、bean、beans

org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.parseDefaultElement(
按照类型解析 import、alias、bean、beans。
进入具体源码后我们可以发现一个规律,以下四种类型的划分不是绝对平等的,因为 import可以引用其他文件,beans可以包含多个bean,

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
		//解析 import 标签,可以引用其他配置文件
		if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
			importBeanDefinitionResource(ele);
		}
		// 解析 alias 标签,可以为bean设置不同的别名
		else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
			processAliasRegistration(ele);
		}
		//解析 bean 标签
		else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
			processBeanDefinition(ele, delegate);
		}
		//解析 beans 标签
		else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
			// recurse
			doRegisterBeanDefinitions(ele);
		}
	}

总结

限于篇幅,对于具体 import、alias、bean、beans 标签的具体解析我将再新起一篇文章( Spring源码学习:DefaultBeanDefinitionDocumentReader.parseDefaultElement),但是看完本文基本可以看到Spring在处理配置文件的基本思路了。

F:\apache-tomcat-9.0.111-windows-x64\apache-tomcat-9.0.111\bin\catalina.bat run [2025-10-19 06:17:24,551] Artifact untitled3:war: Waiting for server connection to start artifact deployment... Using CATALINA_BASE: "C:\Users\ZhuanZ1\AppData\Local\JetBrains\IntelliJIdea2020.1\tomcat\Unnamed_untitled3" Using CATALINA_HOME: "F:\apache-tomcat-9.0.111-windows-x64\apache-tomcat-9.0.111" Using CATALINA_TMPDIR: "F:\apache-tomcat-9.0.111-windows-x64\apache-tomcat-9.0.111" Using JRE_HOME: "C:\Program Files\Java\jdk1.8.0_202" Using CLASSPATH: "F:\apache-tomcat-9.0.111-windows-x64\apache-tomcat-9.0.111\bin\bootstrap.jar;F:\apache-tomcat-9.0.111-windows-x64\apache-tomcat-9.0.111\bin\tomcat-juli.jar" Using CATALINA_OPTS: "" Connected to the target VM, address: &#39;127.0.0.1:50342&#39;, transport: &#39;socket&#39; 19-Oct-2025 18:17:25.636 信息 [main] org.apache.catalina.startup.VersionLoggerListener.log Server.服务器版本: Apache Tomcat/9.0.111 19-Oct-2025 18:17:25.639 信息 [main] org.apache.catalina.startup.VersionLoggerListener.log 服务器构建: Oct 10 2025 14:13:20 UTC 19-Oct-2025 18:17:25.639 信息 [main] org.apache.catalina.startup.VersionLoggerListener.log 服务器版本号: 9.0.111.0 19-Oct-2025 18:17:25.639 信息 [main] org.apache.catalina.startup.VersionLoggerListener.log 操作系统名称: Windows 10 19-Oct-2025 18:17:25.639 信息 [main] org.apache.catalina.startup.VersionLoggerListener.log OS.版本: 10.0 19-Oct-2025 18:17:25.639 信息 [main] org.apache.catalina.startup.VersionLoggerListener.log 架构: amd64 19-Oct-2025 18:17:25.639 信息 [main] org.apache.catalina.startup.VersionLoggerListener.log Java 环境变量: C:\Program Files\Java\jdk1.8.0_202\jre 19-Oct-2025 18:17:25.640 信息 [main] org.apache.catalina.startup.VersionLoggerListener.log Java虚拟机版本: 1.8.0_202-b08 19-Oct-2025 18:17:25.640 信息 [main] org.apache.catalina.startup.VersionLoggerListener.log JVM.供应商: Oracle Corporation 19-Oct-2025 18:17:25.640 信息 [main] org.apache.catalina.startup.VersionLoggerListener.log CATALINA_BASE: C:\Users\ZhuanZ1\AppData\Local\JetBrains\IntelliJIdea2020.1\tomcat\Unnamed_untitled3 19-Oct-2025 18:17:25.640 信息 [main] org.apache.catalina.startup.VersionLoggerListener.log CATALINA_HOME: F:\apache-tomcat-9.0.111-windows-x64\apache-tomcat-9.0.111 19-Oct-2025 18:17:25.640 信息 [main] org.apache.catalina.startup.VersionLoggerListener.log 命令行参数: -Djava.util.logging.config.file=C:\Users\ZhuanZ1\AppData\Local\JetBrains\IntelliJIdea2020.1\tomcat\Unnamed_untitled3\conf\logging.properties 19-Oct-2025 18:17:25.640 信息 [main] org.apache.catalina.startup.VersionLoggerListener.log 命令行参数: -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager 19-Oct-2025 18:17:25.640 信息 [main] org.apache.catalina.startup.VersionLoggerListener.log 命令行参数: -agentlib:jdwp=transport=dt_socket,address=127.0.0.1:50342,suspend=y,server=n 19-Oct-2025 18:17:25.640 信息 [main] org.apache.catalina.startup.VersionLoggerListener.log 命令行参数: -javaagent:C:\Users\ZhuanZ1\AppData\Local\JetBrains\IntelliJIdea2020.1\captureAgent\debugger-agent.jar 19-Oct-2025 18:17:25.640 信息 [main] org.apache.catalina.startup.VersionLoggerListener.log 命令行参数: -Dcom.sun.management.jmxremote= 19-Oct-2025 18:17:25.640 信息 [main] org.apache.catalina.startup.VersionLoggerListener.log 命令行参数: -Dcom.sun.management.jmxremote.port=1099 19-Oct-2025 18:17:25.640 信息 [main] org.apache.catalina.startup.VersionLoggerListener.log 命令行参数: -Dcom.sun.management.jmxremote.ssl=false 19-Oct-2025 18:17:25.641 信息 [main] org.apache.catalina.startup.VersionLoggerListener.log 命令行参数: -Dcom.sun.management.jmxremote.password.file=C:\Users\ZhuanZ1\AppData\Local\JetBrains\IntelliJIdea2020.1\tomcat\Unnamed_untitled3\jmxremote.password 19-Oct-2025 18:17:25.641 信息 [main] org.apache.catalina.startup.VersionLoggerListener.log 命令行参数: -Dcom.sun.management.jmxremote.access.file=C:\Users\ZhuanZ1\AppData\Local\JetBrains\IntelliJIdea2020.1\tomcat\Unnamed_untitled3\jmxremote.access 19-Oct-2025 18:17:25.641 信息 [main] org.apache.catalina.startup.VersionLoggerListener.log 命令行参数: -Djava.rmi.server.hostname=127.0.0.1 19-Oct-2025 18:17:25.641 信息 [main] org.apache.catalina.startup.VersionLoggerListener.log 命令行参数: -Djdk.tls.ephemeralDHKeySize=2048 19-Oct-2025 18:17:25.641 信息 [main] org.apache.catalina.startup.VersionLoggerListener.log 命令行参数: -Djava.protocol.handler.pkgs=org.apache.catalina.webresources 19-Oct-2025 18:17:25.641 信息 [main] org.apache.catalina.startup.VersionLoggerListener.log 命令行参数: -Dsun.io.useCanonCaches=false 19-Oct-2025 18:17:25.641 信息 [main] org.apache.catalina.startup.VersionLoggerListener.log 命令行参数: -Dignore.endorsed.dirs= 19-Oct-2025 18:17:25.651 信息 [main] org.apache.catalina.startup.VersionLoggerListener.log 命令行参数: -Dcatalina.base=C:\Users\ZhuanZ1\AppData\Local\JetBrains\IntelliJIdea2020.1\tomcat\Unnamed_untitled3 19-Oct-2025 18:17:25.651 信息 [main] org.apache.catalina.startup.VersionLoggerListener.log 命令行参数: -Dcatalina.home=F:\apache-tomcat-9.0.111-windows-x64\apache-tomcat-9.0.111 19-Oct-2025 18:17:25.651 信息 [main] org.apache.catalina.startup.VersionLoggerListener.log 命令行参数: -Djava.io.tmpdir=F:\apache-tomcat-9.0.111-windows-x64\apache-tomcat-9.0.111 19-Oct-2025 18:17:25.654 信息 [main] org.apache.catalina.core.AprLifecycleListener.lifecycleEvent 使用APR版本[1.7.4]加载了基于APR的Apache Tomcat本机库[1.3.1]。 19-Oct-2025 18:17:25.654 信息 [main] org.apache.catalina.core.AprLifecycleListener.lifecycleEvent APR功能:IPv6[true]、sendfile[true]、accept filters[false]、random[true]、UDS [true]。 19-Oct-2025 18:17:25.654 信息 [main] org.apache.catalina.core.AprLifecycleListener.lifecycleEvent APR/OpenSSL配置:useAprConnector[false],useOpenSSL[true] 19-Oct-2025 18:17:25.659 信息 [main] org.apache.catalina.core.AprLifecycleListener.initializeSSL OpenSSL成功初始化 [OpenSSL 3.0.14 4 Jun 2024] 19-Oct-2025 18:17:25.885 信息 [main] org.apache.coyote.AbstractProtocol.init 初始化协议处理器 ["http-nio-8080"] 19-Oct-2025 18:17:25.901 信息 [main] org.apache.catalina.startup.Catalina.load 服务器在[480]毫秒内初始化 19-Oct-2025 18:17:25.931 信息 [main] org.apache.catalina.core.StandardService.startInternal 正在启动服务[Catalina] 19-Oct-2025 18:17:25.931 信息 [main] org.apache.catalina.core.StandardEngine.startInternal 正在启动 Servlet 引擎:[Apache Tomcat/9.0.111] 19-Oct-2025 18:17:25.942 信息 [main] org.apache.coyote.AbstractProtocol.start 开始协议处理句柄["http-nio-8080"] 19-Oct-2025 18:17:25.959 信息 [main] org.apache.catalina.startup.Catalina.start [57]毫秒后服务器启动 Connected to server [2025-10-19 06:17:26,117] Artifact untitled3:war: Artifact is being deployed, please wait... 19-Oct-2025 18:17:27.095 信息 [RMI TCP Connection(3)-127.0.0.1] org.apache.jasper.servlet.TldScanner.scanJars 至少有一个JAR被扫描用于TLD但尚未包含TLD。 为此记录器启用调试日志记录,以获取已扫描但未在其中找到TLD的完整JAR列表。 在扫描期间跳过不需要的JAR可以缩短启动时间和JSP编译时间。 19-Oct-2025 18:17:27.324 信息 [RMI TCP Connection(3)-127.0.0.1] org.springframework.web.servlet.FrameworkServlet.initServletBean FrameworkServlet &#39;spring&#39;: initialization started 19-Oct-2025 18:17:27.351 信息 [RMI TCP Connection(3)-127.0.0.1] org.springframework.context.support.AbstractApplicationContext.prepareRefresh Refreshing WebApplicationContext for namespace &#39;spring-servlet&#39;: startup date [Sun Oct 19 18:17:27 CST 2025]; root of context hierarchy 19-Oct-2025 18:17:27.393 信息 [RMI TCP Connection(3)-127.0.0.1] org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions Loading XML bean definitions from class path resource [spring-mvc.xml] 19-Oct-2025 18:17:27.955 信息 [RMI TCP Connection(3)-127.0.0.1] org.springframework.web.servlet.handler.AbstractHandlerMethodMapping$MappingRegistry.register Mapped "{[/index]}" onto public java.lang.String com.springmvc1.index() 19-Oct-2025 18:17:28.046 信息 [RMI TCP Connection(3)-127.0.0.1] org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.initControllerAdviceCache Looking for @ControllerAdvice: WebApplicationContext for namespace &#39;spring-servlet&#39;: startup date [Sun Oct 19 18:17:27 CST 2025]; root of context hierarchy 19-Oct-2025 18:17:28.089 信息 [RMI TCP Connection(3)-127.0.0.1] org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.initControllerAdviceCache Looking for @ControllerAdvice: WebApplicationContext for namespace &#39;spring-servlet&#39;: startup date [Sun Oct 19 18:17:27 CST 2025]; root of context hierarchy 19-Oct-2025 18:17:28.211 信息 [RMI TCP Connection(3)-127.0.0.1] org.springframework.web.servlet.FrameworkServlet.initServletBean FrameworkServlet &#39;spring&#39;: initialization completed in 886 ms [2025-10-19 06:17:28,233] Artifact untitled3:war: Artifact is deployed successfully [2025-10-19 06:17:28,233] Artifact untitled3:war: Deploy took 2,116 milliseconds 19-Oct-2025 18:17:35.956 信息 [Catalina-utility-1] org.apache.catalina.startup.HostConfig.deployDirectory 把web 应用程序部署到目录 [F:\apache-tomcat-9.0.111-windows-x64\apache-tomcat-9.0.111\webapps\manager] 19-Oct-2025 18:17:36.007 信息 [Catalina-utility-1] org.apache.jasper.servlet.TldScanner.scanJars 至少有一个JAR被扫描用于TLD但尚未包含TLD。 为此记录器启用调试日志记录,以获取已扫描但未在其中找到TLD的完整JAR列表。 在扫描期间跳过不需要的JAR可以缩短启动时间和JSP编译时间。 19-Oct-2025 18:17:36.016 信息 [Catalina-utility-1] org.apache.catalina.startup.HostConfig.deployDirectory Web应用程序目录[F:\apache-tomcat-9.0.111-windows-x64\apache-tomcat-9.0.111\webapps\manager]的部署已在[59]毫秒内完成 ִ���� 19-Oct-2025 18:17:45.508 警告 [http-nio-8080-exec-3] org.springframework.web.servlet.DispatcherServlet.noHandlerFound No mapping found for HTTP request with URI [/WEB-INF/views/index123.html] in DispatcherServlet with name &#39;spring&#39; 19-Oct-2025 18:17:55.141 警告 [http-nio-8080-exec-6] org.springframework.web.servlet.DispatcherServlet.noHandlerFound No mapping found for HTTP request with URI [/WEB-INF/views/index123.html] in DispatcherServlet with name &#39;spring&#39; ִ���� 19-Oct-2025 18:18:06.043 警告 [http-nio-8080-exec-7] org.springframework.web.servlet.DispatcherServlet.noHandlerFound No mapping found for HTTP request with URI [/index123] in DispatcherServlet with name &#39;spring&#39; 19-Oct-2025 18:18:15.870 警告 [http-nio-8080-exec-8] org.springframework.web.servlet.DispatcherServlet.noHandlerFound No mapping found for HTTP request with URI [/index123.html] in DispatcherServlet with name &#39;spring&#39; 19-Oct-2025 18:18:20.091 警告 [http-nio-8080-exec-10] org.springframework.web.servlet.DispatcherServlet.noHandlerFound No mapping found for HTTP request with URI [/index123.html] in DispatcherServlet with name &#39;spring&#39; 19-Oct-2025 18:18:20.859 警告 [http-nio-8080-exec-1] org.springframework.web.servlet.DispatcherServlet.noHandlerFound No mapping found for HTTP request with URI [/index123.html] in DispatcherServlet with name &#39;spring&#39; 19-Oct-2025 18:18:21.077 警告 [http-nio-8080-exec-2] org.springframework.web.servlet.DispatcherServlet.noHandlerFound No mapping found for HTTP request with URI [/index123.html] in DispatcherServlet with name &#39;spring&#39; ִ���� 19-Oct-2025 18:18:28.758 警告 [http-nio-8080-exec-5] org.springframework.web.servlet.DispatcherServlet.noHandlerFound No mapping found for HTTP request with URI [/WEB-INF/views/index123.html] in DispatcherServlet with name &#39;spring&#39;
最新发布
10-20
Spring MVC 的 DispatcherServlet 出现 “No mapping found for HTTP request” 错误,通常是因为请求的 URL 没有对应的处理器映射。以下是针对请求 `/WEB-INF/views/index123.html`、`/index123`、`/index123.html` 等找不到映射问题的解决办法: #### 1. 静态资源映射配置 若请求的是静态资源(如 HTML 文件),需要在 Spring MVC 配置文件里配置静态资源映射。可使用 `<mvc:resources>` 标签,示例如下: ```xml <mvc:resources location="/WEB-INF/views/" mapping="/index123.html"/> <mvc:resources location="/" mapping="/index123"/> ``` 上述配置把 `/index123.html` 映射到 `/WEB-INF/views/` 目录下的 `index123.html` 文件,把 `/index123` 映射到根目录下的资源。 #### 2. 视图解析器配置 使用 `InternalResourceViewResolver` 作为视图解析器,把视图后缀设置为 `.html`。示例如下: ```xml <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/views/"/> <property name="suffix" value=".html"/> </bean> ``` 这样,当控制器返回视图名称 `index123` 时,会自动解析为 `/WEB-INF/views/index123.html`。 #### 3. 控制器配置 编写控制器方法,返回视图名称。示例如下: ```java import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @Controller public class PageController { @RequestMapping("/index123") public String index123() { return "index123"; } } ``` 上述代码中,`/index123` 请求会返回 `index123` 视图,根据视图解析器的配置,会渲染 `/WEB-INF/views/index123.html` 页面。 #### 4. 检查 `web.xml` 配置 确保 `web.xml` 中的 `DispatcherServlet` 配置正确,且没有拦截静态资源。示例如下: ```xml <servlet> <servlet-name>springDispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:springmvc.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>springDispatcherServlet</servlet-name> <!-- 注意这里的配置,避免拦截静态资源 --> <url-pattern>/</url-pattern> </servlet-mapping> ``` #### 5. 检查 Tomcat 配置 有时候,Tomcat 配置不正确也可能导致该问题。可尝试右键项目,选择 `build path configure`,删除以前默认的 Tomcat,更换为新的 Tomcat,然后右键项目,选择 `maven update project` 或者点击项目 `ALT + F5` 更新一下 Maven,再重新启动项目 [^5]。
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值