Skip to content

PathMatchingResourcePatternResolver can not work in spring boot 1.4.0.RELEASE when package a executable jar #7003

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
peace0phmind opened this issue Sep 23, 2016 · 7 comments
Assignees
Milestone

Comments

@peace0phmind
Copy link

peace0phmind commented Sep 23, 2016

Hi,

I'm using spring boot 1.3.0.RELEASE and using PathMatchingResourcePatternResolver to find mybatis mapper files with under code:

PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
sessionFactoryBean.setMapperLocations(resolver.getResources("classpath*:mybatis*/**/*Mapper.xml"));

It works fine under 1.3.0.RELEASE. For some reason I upgrade spring boot to 1.4.0.RELEASE, then it can not work.

I debug into the source ,find the code in PathMatchingResourcePatternResolver:

protected Set<Resource> doFindAllClassPathResources(String path) throws IOException {
        Set<Resource> result = new LinkedHashSet<Resource>(16);
        ClassLoader cl = getClassLoader();
        Enumeration<URL> resourceUrls = (cl != null ? cl.getResources(path) : ClassLoader.getSystemResources(path));
        while (resourceUrls.hasMoreElements()) {
            URL url = resourceUrls.nextElement();
            result.add(convertClassLoaderURL(url));
        }
        if ("".equals(path)) {
            // The above result is likely to be incomplete, i.e. only containing file system references.
            // We need to have pointers to each of the jar files on the classpath as well...
            addAllClassLoaderJarRoots(cl, result);
        }
        return result;
    }

The different between 1.4.0 and 1.3.0, the 'resourceUrls.hasMoreElements()' returned false and true.It seems that 'cl.getResources(path)' returned different object.

So, why this happened and how to resolve it?

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Sep 23, 2016
@wilkinsona
Copy link
Member

@peace0phmind Can you provide a sample that reproduces the problem please? I'd like to be able to see the exact layout of your projects and exactly where the mapper files are. We also need to know how you're running your application. In your IDE, using Maven or Gradle, as a fat jar, something else?

@wilkinsona wilkinsona added status: waiting-for-feedback We need additional information before we can continue and removed status: waiting-for-triage An issue we've not yet triaged labels Sep 23, 2016
@peace0phmind
Copy link
Author

Here is my sample: https://github.com/peace0phmind/spring-boot-mybatis-sample

mvn clean package
java -jar target/sample-1.0-SNAPSHOT.jar

you will get an error output log:

2016-09-23 17:39:19.238 ERROR 27684 --- [ main] com.sample.Application : resources is null.

but, when you change spring boot version in the pom to 1.3.0.RELEASE, and do it again, it is ok.

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels Sep 23, 2016
@wilkinsona
Copy link
Member

wilkinsona commented Sep 23, 2016

Thanks for the sample. The problem's a result of the location pattern that you're using:

"classpath*:mybatis*/**/*Mapper.xml"

The mybatis* part requires Spring Framework to find the root of every item on the classpath and see if it contains a directory that begins with mybatis. Aside from the fact that it doesn't work in 1.4, that's a very costly thing to do as it requires the scanning of the entire classpath. I'd recommend replacing the location pattern with this:

"classpath*:mybatis/**/*Mapper.xml"

This works in both 1.3 and 1.4.

The original location pattern works in 1.3 because of the hack that was added to fix #420. It was subsequently removed in 1.4 as part of cleaning up the delegation model of Boot's class loader (87fe0b2).

@wilkinsona wilkinsona added for: team-attention An issue we'd like other members of the team to review and removed status: feedback-provided Feedback has been provided labels Sep 23, 2016
@wilkinsona
Copy link
Member

wilkinsona commented Sep 23, 2016

It also works (in 1.3 and 1.4) with the original location pattern when the jar is shaded, but only due to this logic in PathMatchingResourcePatternResolver:

if ("".equals(path)) {
    // The above result is likely to be incomplete, i.e. only containing file system references.
    // We need to have pointers to each of the jar files on the classpath as well...
    addAllClassLoaderJarRoots(cl, result);
}

With a shaded jar run with java -jar, the system class loader returns an empty enumeration from a call to getResources(""). Spring Framework then adds in jar URLs for all of the jars on the class path.

The behaviour of the system class loader is why the hack that was added to #420 was removed as it made our class loader behave in a non-standard way as it would return URLs for all of the jar roots on the class path. However, I think we need to update JarURLConnection so that the class loader will return a URL for an entry for a root that's nested within another jar, e.g. for /BOOT-INF/classes.

@wilkinsona
Copy link
Member

I have a possible fix for this. @philwebb Could you please take a look and see if it seems like a reasonable change to you?

@peace0phmind
Copy link
Author

Get it, Thanks.

Because in my real product, i have mybatis and mybatis-generator in the classpath, so i use the mybatis* instead.

@wilkinsona
Copy link
Member

My proposed fix will require #7021 to be fixed as well

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants