To avoid duplicate JAR resources on MS Windows.

If you use mybatis-plus and your mapper-locations starts with classpath*:, Spring will find duplicate UrlResources for XML files which results in a crash on application startup.

Since their getCleanedUrl values are different mybatis will throw an exception, even though they represent the same resource.

Comment From: pivotal-cla

@huyachigege Please sign the Contributor License Agreement!

Click here to manually synchronize the status of this Pull Request.

See the FAQ for frequently asked questions.

Comment From: pivotal-cla

@huyachigege Thank you for signing the Contributor License Agreement!

Comment From: huyachigege

ClassLoader.getResources("") will generate a path like jar:file:/F:xyz.xml; whereas, addClassPathManifestEntries() will generate a path like jar:file:F:xyz.xml.

They reference the same underlying resource, but UrlResource.getCleanedUrl() is different for each of them, so you get duplicate UrlResources returned from PathMatchingResourcePatternResolver.getResources(location).

My English is not good, I hope you can understand.

Comment From: sbrannen

Hi @huyachigege,

Congratulations on creating your first PR ever! 👍

We'll look into it.

My English is not good, I hope you can understand.

Yes, your explanation is good enough to follow.

I edited the text to make it slightly clearer, and hopefully I didn't change the meaning you wished to convey.

Comment From: huyachigege

Hi @huyachigege,

Congratulations on creating your first PR ever! 👍

We'll look into it.

My English is not good, I hope you can understand.

Yes, your explanation is good enough to follow.

I edited the text to make it slightly clearer, and hopefully I didn't change the meaning you wished to convey.

Thanks, that is what I mean.

Comment From: jhoeller

addClassPathManifestEntries delegates to hasDuplicate before adding a new entry, and that method is meant to detect exactly such a difference in leading slashes. Could you try to find out why this is not correctly identifying the duplicate in your scenario?

Comment From: huyachigege

addClassPathManifestEntries delegates to hasDuplicate before adding a new entry, and that method is meant to detect exactly such a difference in leading slashes. Could you try to find out why this is not correctly identifying the duplicate in your scenario?

line 497 will get root resources, it will contain jar:file:/F:xxx/app.jar!/boot-info/classes and jar:file:F:xxx/app.jar!. They are different now. But child of them will be jar:file:/F:xxx/app.jar!/.../xxx.xml and jar:file:F:xxx/app.jar!/.../xxx.xml. They are duplicate on windows. If on Linux, they are both jar:file:/..., so it works on Linux.

You can try on windows and use properties I just told.

Comment From: sbrannen

Hi @huyachigege,

line 497 will get root resources,

That doesn't map to the current code on the main branch.

With which version of Spring Framework are you experiencing the duplicates?

Comment From: huyachigege

With which version of Spring Framework are you experiencing the duplicates?

5.3.x,sorry for that

Now is 23 clock in china,I need to go to bed. If there are any questions,I will reply tomorrow

Comment From: sbrannen

Related Issues

  • 20539

  • 20665

Comment From: sbrannen

If you use mybatis-plus and your mapper-locations starts with classpath*:

Can you please provide us the exact location pattern you are using?

Specifically, what do you have for <???> in classpath*:<???>?

Comment From: huyachigege

If you use mybatis-plus and your mapper-locations starts with classpath*:

Can you please provide us the exact location pattern you are using?

Specifically, what do you have for <???> in classpath*:<???>?

classpath*:/**/*_MYSQL.xml

Comment From: sbrannen

classpath://_MYSQL.xml

Thanks. That helps.

Can you please tell us which version of Java and Spring Boot you're using as well?

Comment From: huyachigege

classpath://_MYSQL.xml

Thanks. That helps.

Can you please tell us which version of Java and Spring Boot you're using as well?

OpenJDK-11,SpringBoot 2.6.12.

Comment From: sbrannen

line 497 will get root resources, it will contain jar:file:/F:xxx/app.jar!/boot-info/classes and jar:file:F:xxx/app.jar!. They are different now. But child of them will be jar:file:/F:xxx/app.jar!/.../xxx.xml and jar:file:F:xxx/app.jar!/.../xxx.xml. They are duplicate on windows. If on Linux, they are both jar:file:/..., so it works on Linux.

Based on that feedback, it appears that we are getting two different roots (rootDirUrl values) for the same underlying JAR:

  • jar:file:F:xxx/app.jar! -- for the actual Spring Boot application JAR
  • jar:file:/F:xxx/app.jar!/boot-info/classes -- from the MANIFEST of the Spring Boot application JAR

Then, in findPathMatchingResources() we add the results of doFindPathMatchingJarResources(rootDirResource, rootDirUrl, subPattern) without checking for duplicates.

The hasDuplicate() method Juergen mentioned is used at a lower level and therefore does not detect duplicates at this higher level.

Comment From: sbrannen

This has been merged into main in d5874ab99ef62b3da8a9269aa526b43328e968a9, revised in e71117dcdf8c713b0a364d896ea2221183eac64b, and backported to 5.3.x.

Thanks