Please visit the original blog: http://zhuqiuhui.space/Blogs/2017/03/spring-classpath.html
Spring in the Classpath and classpath* has been the development of the heart, no time to calm down to study, finally coherent. Summary of the online also more, a variety of statements, but also as a personal interpretation of spring in the source code, so the next time to use the heart more comfortable, welcome support.
I. Description of the problem
The path lookup order is unknown when the import resource file is used with spring (doubt in development), or the load resource fails (does not know how to change the path).
Second, classpath code analysis
Directly on the code, this is the main class Pathmatchingresourcepatternresolver class in the code, located under the package Org.springframework.core.io.support, the getresources in the class Functions are the core of logic, as follows:
Public resource[] Getresources (String locationpattern) throws IOException {if Locationpattern.startswith (CLASSPATH _all_url_prefix)) {if (Getpathmatcher (). Ispattern (Locationpattern.substring (Classpath_all_url_prefix.length ())) {//Case 1: If it starts with classpath* and contains.
or *, for example, find: classpath*: Applicationcontext-*.xml return findpathmatchingresources (Locationpattern); else {//Case 2: not included. or *, direct full name lookup, such as find: classpath*: Applicationcontext-test.xml return findallclasspathresources (locationpattern.subs
Tring (Classpath_all_url_prefix.length ()));
} else {//to classpath: Opening int prefixend = Locationpattern.indexof (":") +1; if (Getpathmatcher (). Ispattern (Locationpattern.substring (prefixend)) {//Case 3: If not beginning with classpath* and contained.
or *, for example, find: Classpath:applicationcontext-*.xml return findpathmatchingresources (Locationpattern);
} else {//Case 4: If not beginning with classpath* and not included. or *, such as looking for: Classpath:applicationcontext-test.xml returnnew resource[] {getresourceloader (). GetResource (Loca
Tionpattern)}; }
}
}
2.1 If starting with classpath* and containing. or *
For example, find: classpath*: Applicationcontext-*.xml, using the Findpathmatchingresources function, look at the function:
Protected resource[] Findpathmatchingresources (String locationpattern) throws IOException {/* function det
The Erminerootdir function is to get the directory that can be determined, such as classpath*:/aaa/bbb/applicationcontext-*.xml to return classpath*:/aaa/bbb/
Classpath*:/aaa/*/applicationcontext-*.xml, then return to classpath*:/aaa/(code will no longer paste, interested can look at the source code) * *
String Rootdirpath = Determinerootdir (Locationpattern);
Gets the contents of the Locationpattern string subpattern = Locationpattern.substring (Rootdirpath.length ());
Recursive loading has been determined by the content resource[] rootdirresources = getresources (Rootdirpath);
Set result = new linkedhashset<> (16);
for (Resource rootdirresource:rootdirresources) {Rootdirresource = Resolverootdirresource (Rootdirresource);
URL Rootdirurl = Rootdirresource.geturl (); if (Equinoxresolvemethod!=null) {if (Rootdirurl.getprotocol (). StartsWith ("bundle")) {ROOTDI Rurl = (URL) reflectionutils.invokeMethod (Equinoxresolvemethod,null, Rootdirurl);
Rootdirresource = new Urlresource (Rootdirurl); } if (Rootdirurl.getprotocol (). StartsWith (RESOURCEUTILS.URL_PROTOCOL_VFS)) {/are general J Boss VFS resource: "VFS" Result.addall (Vfsresourcematchingdelegate.findmatchingresources (Rootdirurl, Subpatte
RN, Getpathmatcher ())); } elseif (Resourceutils.isjarurl (rootdirurl) | | isjarresource (ROOTDIRRESOURCE)) {/Is zip, jar, WSJA
R or Vfszip one loads the Result.addall (dofindpathmatchingjarresources (Rootdirresource, Rootdirurl, Subpattern));
else {Result.addall (dofindpathmatchingfileresources (Rootdirresource, subpattern)); } if (logger.isdebugenabled ()) {Logger.debug ("Resolved location pattern [" + Locationpattern +] to Res
Ources "+ result);
Return Result.toarray (New Resource[result.size ()); }
Overall: The function splits the locationpattern into two parts: Rootdirpath and Subpattern,rootdirpath are root paths, Subpattern are subdirectory path matching rule strings. Iterate through all subdirectories under the root directory, and get all subdirectories in the Dofindpathmatchingfileresources (Rootdirresource, Subpattern) method, and then match subpattern one by one, one at a child directory.
2.2 If starting with classpath* and not included. or *, direct full name lookup
For example, find: classpath*: Applicationcontext-test.xml, using the Findallclasspathresources function, look at the function:
Protected resource[] Findallclasspathresources (string location) throws IOException {
string path = location;
if (Path.startswith ("/")) {
path = path.substring (1);
}
Set result = dofindallclasspathresources (path);
if (logger.isdebugenabled ()) {
Logger.debug ("Resolved classpath location [" + Location + "] to" + result);
} return
Result.toarray (new Resource[result.size ());
}
In fact, the core is dofindallclasspathresources, which scans all the data in the path, and the function body is as follows:
Protected set Dofindallclasspathresources (String path) throws IOException {
set result= new Linkedhashset<> ( );
ClassLoader cl = getClassLoader ();
Enumeration resourceurls = (cl!= null? Cl.getresources (PATH): classloader.getsystemresources (path));
while (Resourceurls.hasmoreelements ()) {
URL url = resourceurls.nextelement ();
Result.add (Convertclassloaderurl (URL));
}
if ("". Equals (Path)) {
//If the path is empty, all jar packages are found, added to result (the function is no longer in depth)
addallclassloaderjarroots (cl, result);
} return result
;
}
Important In this function: enumeration resourceurls = (CL!= null? Cl.getresources (PATH): classloader.getsystemresources (path)); That is, if the current class loader CL is not empty, call cl.getresources (path), and Cl is empty to use the System class loader to load, in fact cl.getresources (path) content is used is the parent delegation model (the current ClassLoader loading resources, If a parent loader exists, it is loaded with the parent loader.
2.3 If not beginning with classpath* and containing. or *
For example, find: Classpath:applicationcontext-*.xml, use function findpathmatchingresources, similar to 2.1 cases.
2.4 If it does not start with classpath* and does not contain. or *
For example find: Classpath:applicationcontext-test.xml, direct "Getresourceloader (). GetResource (Locationpattern)", That is, the direct use of the natural resource loader to load, where the default is to use Defaultresourceloader ().
Third, summary and test
Wrote a small test, the test code is as follows:
Private staticvoid output (String location) throws Exception {
Resourcepatternresolver resourceloader = new Pathmatchingresourcepatternresolver ();
resource[] Source = resourceloader.getresources (location); System.out.println ("source.size:" + source.length);
for (int i =0 i < source.length; i++) {
Resource Resource = source[i];
System.out.println (Resource);
}
A number of test results are shown below: