惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

aimingoo的专栏
aimingoo的专栏
Microsoft Security Blog
Microsoft Security Blog
S
SegmentFault 最新的问题
Forbes - Security
Forbes - Security
爱范儿
爱范儿
Stack Overflow Blog
Stack Overflow Blog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
C
CERT Recently Published Vulnerability Notes
S
Schneier on Security
Scott Helme
Scott Helme
C
Check Point Blog
T
Tenable Blog
博客园 - 三生石上(FineUI控件)
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
L
Lohrmann on Cybersecurity
Google DeepMind News
Google DeepMind News
人人都是产品经理
人人都是产品经理
N
News and Events Feed by Topic
B
Blog
P
Privacy International News Feed
I
Intezer
T
Threatpost
Google DeepMind News
Google DeepMind News
L
LangChain Blog
Last Week in AI
Last Week in AI
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
L
LINUX DO - 最新话题
博客园_首页
H
Hackread – Cybersecurity News, Data Breaches, AI and More
Hacker News - Newest:
Hacker News - Newest: "LLM"
C
Cybersecurity and Infrastructure Security Agency CISA
N
News and Events Feed by Topic
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
N
News | PayPal Newsroom
The Hacker News
The Hacker News
S
Security @ Cisco Blogs
罗磊的独立博客
PCI Perspectives
PCI Perspectives
Y
Y Combinator Blog
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
大猫的无限游戏
大猫的无限游戏
U
Unit 42
Hacker News: Ask HN
Hacker News: Ask HN
D
Docker
AI
AI
小众软件
小众软件
博客园 - 叶小钗
H
Help Net Security
TaoSecurity Blog
TaoSecurity Blog
P
Privacy & Cybersecurity Law Blog

博客园 - _herbert

小程序拍证件并局部裁剪 信创-为什么ORACLE使用JDBC查询SYSDATE时,RS.getDate能获取到时间部分? linux宝塔面板使用API自动部署更新文件 JSON JAVA找出哪个类import了不存在的类 LocalDate,LocalDateTime,Date,日期串相互转换 PostMan加载三方JS PostMan内置对象 微信支付集成_JSAPI VUE中使用AXIOS包装API代理 spring中el表达式安全和扩展 Spring使用el表达式 springboot中File默认路径 Springboot启动时记录进程ID ORACLE解析游标生成JSON ORACLE游标序列化 AI教我一条SQL实现明细转树形结构 Maven 构建知识库 MAVEN构建分离依赖JAR JAVA实现读取最后几行日志 信创-ORACLE迁移到KingbaseV9 信创-ORACLE迁移到DM8 sql分组 group by rollup,cube,grouping sets,group_id,groupingId
Springboot构建包使用BOOT-INF中的class覆盖依赖包中的class
_herbert · 2025-08-08 · via 博客园 - _herbert

Springboot构建包使用BOOT-INF中的class覆盖依赖包中的class

1. 背景说明

在Springboot项目中,使用 spring-boot-maven-plugin 插件,构建采用layout:ZIP模式的可执行jar包(非FAT模式).项目中需要覆盖某个依赖包对应的class文件.实际打包后,依赖包中的class并没有覆盖.以下有两种场景,

  • Springboot主包项目中直接覆盖依赖包中的class
  • 多模块项目中,子项目间class相互覆盖

2. 复制依赖包class到主包BOOT-INF/classes

在多模块项目中,子项目之间的class需要相互覆盖,是想的方案为,把子项目需要覆盖的class复制到主项目BOOT-INF/classes目录下.这里需要借助maven-dependency-plugin插件,对应配置如下

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
	<execution>
		<id>unpack-dependency</id>
		<phase>process-resources</phase>
		<goals>
			<goal>unpack</goal>
		</goals>
		<configuration>
			<artifactItems>
				<artifactItem>
          <!--小游戏 地心侠士 公众号:小满小慢 QQ:464884492-->
					<groupId>com.herbert.a.common</groupId>
					<artifactId>common-web</artifactId>
					<version>1.0.0</version>
					<type>jar</type>
					<overWrite>true</overWrite>
					<includes>com/herbert/dxxs/**</includes>
					<outputDirectory>${project.build.outputDirectory}</outputDirectory>
				</artifactItem>
			</artifactItems>
		</configuration>
	</execution>
</executions>
</plugin>

其中 <includes>com/herbert/dxxs/**</includes> 为需要复制的class文件路径.支持通配符.

3. 主包BooT-INF/classes没有覆盖依赖包中的class

在主包jar同级的目录添加文件loader.properties,配置内容为

loader.path=lib,resources

在执行命令 java -jar hebert-1.0.0-SNAPSHOT.jar 时,采用org.springframework.boot.loader.PropertiesLauncher启动程序.在配置loader.path的的情况下,PropertiesLauncher会优先按照配置的classpath顺序加载class,最后再加载jar包中的 /BOOT-INF/classes/BOOT-INF/lib

对应代码如下:

// org.springframework.boot.loader.PropertiesLauncher.ClassPathArchives

ClassPathArchives() throws Exception {
	this.classPathArchives = new ArrayList<>();
  // 1. 加载 loader.path 配置class
  // 小游戏 地心侠士 公众号:小满小慢 QQ:464884492
	for (String path : PropertiesLauncher.this.paths) {
		for (Archive archive : getClassPathArchives(path)) {
			addClassPathArchive(archive);
		}
	}
  //2. 加载 jar 包中的 /BOOT-INF/classes 和 /BOOT-INF/lib
	addNestedEntries();
}

private void addNestedEntries() {
// The parent archive might have "BOOT-INF/lib/" and "BOOT-INF/classes/"
// directories, meaning we are running from an executable JAR. We add nested
// entries from there with low priority (i.e. at end).
  try {
    Iterator<Archive> archives = PropertiesLauncher.this.parent.getNestedArchives(null,
    		JarLauncher.NESTED_ARCHIVE_ENTRY_FILTER);
    while (archives.hasNext()) {
      // 小游戏 地心侠士 公众号:小满小慢 QQ:464884492
    	this.classPathArchives.add(archives.next());
    }
  }
  catch (IOException ex) {
  // Ignore
  }
}

private List<Archive> getClassPathArchives(String path) throws Exception {
	String root = cleanupPath(handleUrl(path));
	List<Archive> lib = new ArrayList<>();
	File file = new File(root);
  // 小游戏 地心侠士 公众号:小满小慢 QQ:464884492
	if (!"/".equals(root)) {
		if (!isAbsolutePath(root)) {
			file = new File(PropertiesLauncher.this.home, root);
		}
		if (file.isDirectory()) {
			debug("Adding classpath entries from " + file);
			Archive archive = new ExplodedArchive(file, false);
			lib.add(archive);
		}
	}
	Archive archive = getArchive(file);
	if (archive != null) {
		debug("Adding classpath entries from archive " + archive.getUrl() + root);
		lib.add(archive);
	}
	List<Archive> nestedArchives = getNestedArchives(root);
	if (nestedArchives != null) {
		debug("Adding classpath entries from nested " + root);
		lib.addAll(nestedArchives);
	}
	return lib;
}

其中 NESTED_ARCHIVE_ENTRY_FILTER 对应代码如下

// org.springframework.boot.loader.JarLauncher
static final EntryFilter NESTED_ARCHIVE_ENTRY_FILTER = (entry) -> {
if (entry.isDirectory()) {
  //小游戏 地心侠士 公众号:小满小慢 QQ:464884492
	return entry.getName().equals("BOOT-INF/classes/");
}
return entry.getName().startsWith("BOOT-INF/lib/");
};

4. 修改loader.path

通过以上代码可以知道,在配置loader.path后,class的加载顺序与我们配置顺序有关.BOOT-INF/classes/BOOT-INF/lib/不显示配置的情况下,默认最后加载.
最终 loader.properties 配置内容为

loader.path=loader.path=BOOT-INF/classes,lib,resources

启动时需要查看加载了哪些classpath,可以在启动时添加参数 -Dloader.debug=true,完整命令如下

java -Dloader.debug=true -jar hebert-1.0.0-SNAPSHOT.jar

5. 确定class到底从哪里加载

  1. 使用jps命令获取进程ID
  2. 使用 java -jar .\arthas-boot.jar 进程ID
  3. 使用 sc -d 获取class加载信息,code-source属性为class的加载路径
[arthas@13192]$ sc -d com.herbert.a.dxxs.commandUtils
 class-info        com.herbert.a.dxxs.commandUtils
 code-source       file:/D:/hebert-1.0.0-SNAPSHOT.jar!/BOOT-INF/classes!/
 name              com.herbert.a.dxxs.commandUtils
 isInterface       false
 isAnnotation      false
 isEnum            false
 isAnonymousClass  false
 isArray           false
 isLocalClass      false
 isMemberClass     false
 isPrimitive       false
 isSynthetic       false
 simple-name       commandUtils
 modifier          public
 annotation
 interfaces
 super-class       +-java.lang.Object
 class-loader      +-org.springframework.boot.loader.LaunchedURLClassLoader@5ce65a89
                     +-sun.misc.Launcher$AppClassLoader@5c647e05
                       +-sun.misc.Launcher$ExtClassLoader@45c8e616
 classLoaderHash   5ce65a89

6. 调试Springboot加载过程

主包的项目在pom文件中添加loader依赖

<dependency>
   <!--小游戏 地心侠士 公众号:小满小慢 QQ:464884492-->
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-loader</artifactId>
   <version>${spring-boot.version}</version>
   <scope>provided</scope>
   <optional>true</optional>
</dependency>

启动时添加远程调试,并设置在未连接时,挂起进程,对应命令如下

java -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005 -jar hebert-1.0.0-SNAPSHOT.jar