Spring's uniform resource loading strategy

write in front

What are resources? The most basic file on the local disk, a picture of a remote server, a local jar package, an embedded jar package/embedded class file/embedded properties file in a local jar package, these are all resources , in order to meet the reading of various resources, spring defines a unified resource loading strategy.

1: Resource interface

This interface is in spring'sspring-coreIn a module, the full class nameorg.springframework.core.io.Resource, the interface is used to abstract the definition of resources, and the final resource will be encapsulated as an object of the interface type, and then use the object to operate the resource. The source code is as follows:

public  interface  Resource  extends  InputStreamSource  {

	// Determine if the file exists 
	boolean  exists ( ) ;

	// Determine whether the file is readable or not. This method is a default method that provides a default implementation 
	default  boolean  isReadable ( )  { 
		return  exists ( ) ; 
	}

	// To determine whether the file handle is opened, this method is the default method, which provides the default implementation 
	default  boolean  isOpen ( )  { 
		return  false ; 
	}

	// Determine whether it is a file, this method is a default method, providing default implementation 
	default  boolean  isFile ( )  { 
		return  false ; 
	}

	// Get the URL corresponding to the file (such as http://,jar://,ftp://,file://, etc.) 
	URL getURL ( )  throws IOException ; 
	// Returns the URI of the resource 
	URI getURI ( )  throws IOException ; 
	/ / Get the file object 
	File getFile ( )  throws IOException ;

	// Returns the ReadableByteChannel object of nio, which is the default method, directly use 
	// the static method newChannel of Channels to create a 
	default ReadableByteChannel readableChannel ( )  throws IOException { 
		return Channels . newChannel ( getInputStream ( ) ) ; 
	}

	// return the length of the file 
	long  contentLength ( )  throws IOException ;

	// Returns the last modification time of the file 
	long  lastModified ( )  throws IOException ;
	
	// According to the relative path of the current resource, Resource 
	Resource createRelative ( String relativePath )  throws IOException ;

	// Get the name of the file 
	@Nullable 
	String getFilename ( ) ;

	// Get the description of the resource 
	String getDescription ( ) ;

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50

It inherits the interfaceorg.springframework.core.io.InputStreamSource, the interface only defines a method to obtain the input stream, the source code is as follows:

public  interface  InputStreamSource  { 
	// Get the input stream object corresponding to the resource 
	InputStream getInputStream ( )  throws IOException ;

}
  • 1
  • 2
  • 3
  • 4
  • 5

1.1: AbstractResource

This class is an abstract implementation class of the Resource interface, and the fully qualified name isorg.springframework.core.io.AbstractResource, the class is defined as follows:

public  abstract  class  AbstractResource  implements  Resource  { 
	@Override 
	public  boolean  exists ( )  { 
		// Get the existence directly according to the File, if it cannot be obtained normally, enter an exception 
		// Try other ways to get 
		try  { 
			return  getFile ( ) . exists ( ) ; 
		} 
		catch  ( IOException ex )  { 
			// Try to get it through InputStream, if it can be closed, return true 
			// otherwise, return false when entering an exception to indicate that the resource does not exist 
			try  { 
				getInputStream ( ) . close ( ); 
				return  true ; 
			} 
			catch  ( Throwable isEx )  { 
				return  false ; 
			} 
		} 
	}

	@Override 
	public  boolean  isReadable ( )  { 
		// read exists 
		return  exists ( ) ; 
	}

	@Override 
	public  boolean  isOpen ( )  { 
		return  false ; 
	}

	@Override 
	public  boolean  isFile ( )  { 
		return  false ; 
	}

	@Override 
	public URL getURL ( )  throws IOException { 
		// Throws an exception, force it to the subclass to implement 
		throw  new  FileNotFoundException ( getDescription ( )  +  " cannot be resolved to URL" ) ; 
	}

	// URL to URI 
	@Override 
	public URI getURI ( )  throws IOException { 
		URL url =  getURL ( ) ; 
		try  { 
			return ResourceUtils . toURI ( url ) ; 
		} 
		catch  ( URISyntaxException ex )  { 
			throw  new  NestedIOException ( "Invalid URI ["  + url +  "]" , ex ) ; 
		} 
	}

	// Throwing an exception directly forces subclasses to implement 
	@Override 
	public File getFile ( )  throws IOException { 
		throw  new  FileNotFoundException ( getDescription ( )  +  " cannot be resolved to absolute file path" ) ; 
	}

	@Override 
	public ReadableByteChannel readableChannel ( )  throws IOException { 
		return Channels . newChannel ( getInputStream ( ) ) ; 
	}

	// Get the length of the file, which is actually the number of bytes of the file content 
	@Override 
	public  long  contentLength ( )  throws IOException { 
		// Get the input stream 
		InputStream is =  getInputStream ( ) ; 
		try  { 
			long size =  0 ; 
			byte [ ] buf =  new  byte [ 256 ] ; 
			int read ; 
			// Read into the byte array through the while loop and accumulate to get the final length 
			while  ( ( read = is . read (buf ) )  !=  - 1 )  { 
				size += read ; 
			} 
			return size ; 
		} 
		finally  { 
			try  { 
				is . close ( ) ; 
			} 
			catch  ( IOException ex )  { 
			} 
		} 
	}

	// Get the last modification time of the resource 
	@Override 
	public  long  lastModified ( )  throws IOException { 
		File fileToCheck =  getFileForLastModifiedCheck ( ) ; 
		long lastModified = fileToCheck . lastModified ( ) ; 
		if  ( lastModified ==  0 L &&  ! fileToCheck . exists ( ) )  { 
			throw  new  FileNotFoundException ( getDescription ()  + 
					" cannot be resolved in the file system for checking its last-modified timestamp" ) ; 
		} 
		return lastModified ; 
	}

	protected File getFileForLastModifiedCheck ( )  throws IOException { 
		return  getFile ( ) ; 
	}

	// Create other new resource objects based on the relative path of the current resource and throw it directly 
	// FileNotFoundException forces subclasses to implement 
	@Override 
	public Resource createRelative ( String relativePath )  throws IOException { 
		throw  new  FileNotFoundException ( "Cannot create a relative resource for "  +  getDescription ( ) ) ; 
	}

	// Get the file name, return null directly, give it to but do not force the subclass to implement 
	@Override 
	@Nullable 
	public String getFilename ( )  { 
		return null ; 
	}

	@Override 
	public  boolean  equals ( Object other )  { 
		return  ( this  == other ||  ( other instanceof  Resource  && 
				( ( Resource ) other ) . getDescription ( ) . equals ( getDescription ( ) ) ) ) ; 
	}

	@Override 
	public  int  hashCode ( )  { 
		return  getDescription ( ) . hashCode ( ) ; 
	}

	@Override 
	public String toString ( )  { 
		return  getDescription ( ) ; 
	}

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137

If we want to customize Resource to directly inherit this abstract class, we can.

2: ResourceLoader interface

The interface is in spring's spring-core module, and the qualified name isorg.springframework.core.io.ResourceLoader. This interface is used to define the loading of resources. In fact, it uses the Resource interface internally. The source code is not very complicated, as follows:

public  interface  ResourceLoader  {
	
	// public static final String CLASSPATH_URL_PREFIX = "classpath:"; 
	String CLASSPATH_URL_PREFIX = ResourceUtils . CLASSPATH_URL_PREFIX ;

	// Get the resource object according to the location 
	// The value can be a URL such as file:c:/test.txt, and a classpath such as classpath:resources.xml 
	// It can also be a relative path, such as WEB-INF/web.xml 
	Resource getResource ( String location ) ;

	// Get the class loader 
	@Nullable 
	ClassLoader getClassLoader ( ) ;

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

2.1: DefaultResourceLoader

This class is the default implementation class of ResourceLoader. Note that it is a class, not an abstract class. Let's first look at an example of use, the code:

@Test 
public  void  testResourceLoader ( )  throws Exception { 
    // define resourceloader 
    ResourceLoader resourceLoader =  new  DefaultResourceLoader ( ) ; 
    // http://img.ewebweb.com/uploads/20191006/20/1570365161-shmEFlWfHU.jpg 
    String resourceLocation =  "https ://www.baidu.com/" ; 
    Resource resource = resourceLoader . getResource ( resourceLocation ) ; 
    System . out . println ( resource.getFilename ( ) ) ; 
    System . out . println ( resource . contentLength ( ) ) ; 
    System . out . println ( resource . getURL ( ) ) ; 
    System . out . println ( resource . getURI ( ) ) ; _
InputStream 
    inputStream = resource.getInputStream ( ) ; _

    StringBuffer out =  new  StringBuffer ( ) ; 
    byte [ ] b =  new  byte [ 4096 ] ; 
    for  ( int n ;  ( n = inputStream . read ( b ) )  !=  - 1 ;  )  { 
        out . append ( new  String ( b ,  0 , n ) ) ;
    } 
    System . out . println ( out ) ; 
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

test:

2443 
https://www.baidu.com/https://www.baidu.com/ < ! DOCTYPE html > < ! -- STATUS OK -- > < html > ... snip ... title 
> Baidu , _ _ 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ you know < / title >

 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

Let's analyze with an exampleorg.springframework.core.io.ResourceLoader#getResourcePart of the source code:

org . springframework . core . io . DefaultResourceLoader#getResource
 @Override 
publicResource getResource ( String location )  { 
	// Assert judgment 
	Assert . notNull ( location ,  "Location must not be null" ) ; 
	// <DefaultResourceLoader#getResource_1> 
	for  ( ProtocolResolver protocolResolver :  getProtocolResolvers ( ) )  { 
		Resource resource = protocolResolver .resolve ( location ,  this ) ; 
		if  ( resource != null )  { 
			return resource ; 
		} 
	} 
	// If it starts with /, call the getResourceByPath method, generally subclasses can override this method 
	if  ( location . startsWith ( "/ " ) )  { 
		// <DefaultResourceLoader#getResource_2> 
		return  getResourceByPath ( location ) ; 
	} 
	// If it starts with classpath, return ClassPathResouce object 
	else  if  ( location .startsWith ( CLASSPATH_URL_PREFIX ) )  { 
		return  new  ClassPathResource ( location . substring ( CLASSPATH_URL_PREFIX . length ( ) ) ,  getClassLoader ( ) ) ; 
	} 
	else  { 
		try  { 
			// create url object based on location 
			URL url =  new  URL ( location ) ; 
			// < DefaultResourceLoader# 
			getResource_3 > return  ( ResourceUtils.isFileURL( url )  ?  new  FileUrlResource ( url )  :  new  UrlResource ( url ) ) ; 
		} 
		catch  ( MalformedURLException ex )  { 
			// No URL -> resolve as resource path. 
			return  getResourceByPath ( location ) ; 
		} 
	} 
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

<DefaultResourceLoader#getResource_1>There are no elements in this example, which will be analyzed later, and will be ignored here for the time being, as shown in the following figure:
insert image description here
This is used by the protocol extension.<DefaultResourceLoader#getResource_2>source code:

protected Resource getResourceByPath ( String path )  { 
	return  new  ClassPathContextResource ( path ,  getClassLoader ( ) ) ; 
}
  • 1
  • 2
  • 3

Directly use the ClassPathContextResource class to create objects for use as Resource.<DefaultResourceLoader#getResource_3>is the location of the code that we will actually execute, will first passResourceUtils.isFileURL(url)Determine whether it is a file address, such asfile://is the file address, the operation effect is as follows:
insert image description here
we are herehttpsprotocol, so the result isfalse,So<DefaultResourceLoader#getResource_3>The final executed code at isnew UrlResource(url)), which eventually returnsUrlResouceas Resource.
exist<DefaultResourceLoader#getResource_1>There is no ResourceResolver, this is an extension port left for us by spring, which can be implemented byorg.springframework.core.io.ProtocolResolverinterface, and then through the methodorg.springframework.core.io.DefaultResourceLoader#addProtocolResolverAdd, the source code of this method is as follows:

org . springframework . core . io . DefaultResourceLoader#addProtocolResolver
 public  void  addProtocolResolver ( ProtocolResolver resolver )  { 
	Assert . notNull ( resolver ,  "ProtocolResolver must not be null" ) ; 
	// private final Set<ProtocolResolver> protocolResolvers = new LinkedHashSet<>(4 ); 
	this . protocolResolvers . add ( resolver ) ; 
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

Let's define a schema asdongshidaddy:The custom protocol resolver of , before that, let's take a look at the source code of ProtocolResolver:

@FunctionalInterface 
public  interface  ProtocolResolver  {

	@Nullable 
	Resource resolve ( String location , ResourceLoader resourceLoader ) ;

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

note the note@FunctionalInterface, This annotation is used in the compilation phase to limit the interface to only one abstract method, the following is not allowed: the
insert image description here
following abstract, a default is possible:
insert image description here
only one abstract method is naturally also possible:
insert image description here
the method in the interfaceResource resolve(String location, ResourceLoader resourceLoader)It is the only method we need to implement. Finally, we can parse our custom protocol and return Resource. The following custom protocol parser:

public  class  DongshiDaddyProtocolResolver  implements  ProtocolResolver  { 
    private  static  final String DONGSHIDADDY_SCHEMA = "dongshidaddy:" ;

    @Override 
    public Resource resolve ( String location , ResourceLoader resourceLoader )  { 
        if  ( ! location . startsWith ( DONGSHIDADDY_SCHEMA ) ) 
            return null ; 
        String realPath = location . substring ( 13 ) ; 
        String classPath =  "classpath:"  + realPath ;

        return resourceLoader . getResource ( classPath ) ; 
    } 
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

Test code:

@Test 
public  void  testSeftDefineProtocolResolver ( )  throws Exception { 
    DefaultResourceLoader resourceLoader = new  DefaultResourceLoader ( ) ; 
    resourceLoader . addProtocolResolver ( new  DongshiDaddyProtocolResolver ( ) ) ; 
    Resource resource = resourceLoader . getResource ( "dongshidaddy:test.txt" ) ; 
    InputStream inputStream = resource . getInputStream( ) ; 
    StringBuffer out =  new  StringBuffer ( ) ; 
    byte [ ] b =  new  byte [ 4096 ] ; 
    for  ( int n ;  ( n = inputStream . read ( b ) )  !=  - 1 ;  )  { 
        out . append ( new  String ( b ,  0 , n) ) ; 
    } 
    System . out . println ( out ) ; 
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

The tested files are as follows:
insert image description here
run:

testing!!!

Process finished with exit code 0
  • 1
  • 2
  • 3

debug as followsorg.springframework.core.io.DefaultResourceLoader#getResource:
insert image description here
so it can be said thatProtocolResolverIt was left to us by springextension point.

Next, we will look at the mainResouceLoaderWhich are there.

3: FileSystemResourceLoader

The resource class returned by this resource loader isFileSystemContextResource.

3.1: Test code

@Test 
public  void  testFileSystemResourceLoader ( )  { 
    String path =  "d:\\test\\mystarter-1.0-SNAPSHOT.jar" ; 
    FileSystemResourceLoader fileSystemResourceLoader =  new  FileSystemResourceLoader ( ) ; 
    Resource resource = fileSystemResourceLoader . getResource ( path ) ; 
    System . out . println ( "resource instanceof FileSystemResource: "  +  ( resource instanceof FileSystemResource ) ) ; 
    System . out . println ( "resource. getFilename() is: "  + resource . getFilename ( ) ) ; 
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

First execute the following code:

org . springframework . core . io . DefaultResourceLoader#getResource
 @Override 
public Resource getResource ( String location )  { 
	Assert . notNull ( location ,  "Location must not be null" ) ;

	. . . snip . . . 
	else  { 
		try  { 
			. . . snip . . . 
		} 
		catch  ( MalformedURLException ex )  { 
			// <2021-02-22 13:38> 
			return  getResourceByPath ( location ) ; 
		} 
	} 
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

will execute the code<2021-02-22 13:38>At the end, the method is finally calledorg.springframework.core.io.FileSystemResourceLoader#getResourceByPath, the source code is as follows:

org . springframework . core . io . FileSystemResourceLoader#getResourceByPath
 @Override 
protected Resource getResourceByPath ( String path )  { 
	// Handle the case at the beginning of a slash, generally in a linux environment, in this case the test is a windows environment 
	// The value is d:\\test \mystarter-1.0-SNAPSHOT.jar, so if is false 
	if  ( path . startsWith ( "/" ) )  { 
		path = path . substring ( 1 ) ; 
	} 
	// <2021-02-22 10:41>, direct new a FileSystemContextResource
	return  new  FileSystemContextResource ( path ) ; 
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

<2021-02-22 10:41>Create the FileSystemContextResource class directly, this class is a private inner class, the source code is as follows:

org . springframework . core . io . FileSystemResourceLoader . FileSystemContextResource
 private  static  class  FileSystemContextResource  extends  FileSystemResource  implements  ContextResource  { 
	// <2021-02-22 10:41> constructor 
	public  FileSystemContextResource ( String path )  { 
		super ( path ) ; 
	}

	@Override 
	public String getPathWithinContext ( )  { 
		return  getPath ( ) ; 
	} 
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

in the constructorsuper(path);Directly call the following code to complete the creation:

org . springframework . core . io . FileSystemResource# FileSystemResource ( java . lang . String ) 
public  FileSystemResource ( String path )  { 
	Assert . notNull ( path ,  "Path must not be null" ) ; 
	// <2021-02-22 10: 56> The processing path is in the standard format 
	this . path = StringUtils . cleanPath ( path ) ; 
	// Create a file object 
	this. file =  new  File ( path ) ; 
	// record the file path 
	this . filePath =  this . file . toPath ( ) ; 
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

<2021-02-22 10:56>can refer to here .

4: ClassRelativeResourceLoader

This class loads resources based on the relative path of the class. Let's look at an example first, inresourcesCreated in the directorytest.xml, and then write the following test code:

@Test 
public  void  testClassRelativeResourceLoader ( )  throws Exception { 
    ResourceLoader resourceLoader =  new  ClassRelativeResourceLoader ( MyFirstSpringBean . class ) ; 
    Resource resource = resourceLoader . getResource ( "../../test.xml" ) ; 
    File file = resource . getFile ( ) ; 
    System.out.println ( file _ _ _ _.getPath ( ) ) ; 
} _
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

after compilationMyFirstSpringBean.classandtest.xmlThe path relationship is as follows:
insert image description here
Execute the test code:

E:\workspace-idea\java-life\target\classes\test.xml

Process finished with exit code 0
  • 1
  • 2
  • 3

Let's see if it is the actual system path: Let's take a
insert image description here
look at the source code execution process, first execute the code:

@Override 
public Resource getResource ( String location )  { 
	Assert . notNull ( location ,  "Location must not be null" ) ;

	. . . snip . . . 
	else  { 
		try  { 
			. . . snip . . . 
		} 
		catch  ( MalformedURLException ex )  { 
			// <2021-02-22 11:42> 
			return  getResourceByPath ( location ) ; 
		} 
	} 
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

<2021-02-22 11:42>execute codeorg.springframework.core.io.ClassRelativeResourceLoader#getResourceByPath, the source code is as follows:

org.springframework.core.io.ClassRelativeResourceLoader #
 getResourceByPath @Override protected Resource getResourceByPath ( String path ) { return new ClassRelativeContextResource ( path , this.clazz ) ; } _ _ _ _ _ _ _
 
	   

  • 1
  • 2
  • 3
  • 4
  • 5

continue:

org . springframework . core . io . ClassRelativeResourceLoader . ClassRelativeContextResource#ClassRelativeContextResource
 public  ClassRelativeContextResource ( String path , Class < ? > clazz )  { 
	// <2021-02-22 11:43> 
	super ( path , clazz ) ; 
	this . clazz = clazz ; 
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

<2021-02-22 11:43>Execute the following code:


	org.springframework.core.io.ClassPathResource # ClassPathResource ( java.lang.String , java.lang.Class < ? > ) public ClassPathResource ( String path , @Nullable Class < ? > clazz ) { Assert.notNull ( path , " Path _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ must not be null" ) ;
    
	// The path before processing here is ../../test.xml, and the path after processing is still ../../test.xml 
	this . path = StringUtils . cleanPath ( path ) ; 
	// Record clazz into the attribute 
	this.clazz = clazz ; } _ _

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

Finally, let's look at the test codeFile file = resource.getFile();How to get the file object through class and relative path, and execute the code:

org.springframework.core.io.AbstractFileResolvingResource # getFile ( ) @Override public File getFile ( ) throws IOException { // < 2021-02-22 11:59 > URL 
	url = getURL ( ) ; if ( url.getProtocol ( ) . _ _ _ _ startsWith ( ResourceUtils . URL_PROTOCOL_VFS ) ) { return VfsResourceDelegate

 
	 
	  
		.getResource ( url ) .getFile ( ) ; } return ResourceUtils .getFile ( url , getDescription ( ) ) ; } _ _ _
	
	 

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

look directly<2021-02-22 11:59>source code:

org . springframework . core . io . ClassPathResource#getURL
 @Override 
public URL getURL ( )  throws IOException { 
	// <2021-02-22 12:00> 
	URL url =  resolveURL ( ) ; 
	if  ( url == null )  { 
		throw  new  FileNotFoundException ( getDescription ( )  +  " cannot be resolved to URL because it does not exist" ) ; 
	} 
	returnurl ; 
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

keep reading<2021-02-22 12:00>source code:

@Nullable 
protected URL resolveURL ( )  { 
	// will enter if 
	if  ( this . clazz != null )  { 
		// get URL directly through java.lang.Class#getResource 
		return  this . clazz . getResource ( this . path ) ; 
	} 
	else  if  ( this . classLoader != null )  { 
		return  this . classLoader . getResource ( this. path ) ; 
	} 
	else  { 
		return ClassLoader . getSystemResource ( this . path ) ; 
	} 
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

Here, the URL of the corresponding file is obtained through the class object, and the subsequent operations are based on the URL.

5: ResourcePatternResolver

The API defined by the ResourceLoader interface can only obtain resource objects through one location. If we have the need to obtain multiple resources according to multiple paths, there is no other way except the for loop to obtain one by one. In order to solve this problem, spring defines interfaceorg.springframework.core.io.support.ResourcePatternResolver, the source code is as follows:

public  interface  ResourcePatternResolver  extends  ResourceLoader  { 
	// Predefined multiple path protocols based on classpath 
	String CLASSPATH_ALL_URL_PREFIX =  "classpath*:" ;

	// Method to get multiple resources according to pattern path 
	Resource [ ]  getResources ( String locationPattern )  throws IOException ;

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

One of the most commonly used subclasses isPathMatchingResourcePatternResolver, Let's first look at the main usage of this class.

5.1: PathMatchingResourcePatternResolver usage

5.1.1: Loading a single file

  • code
@Test 
public  void  testPathMatchingResourcePatternResolverWithClass ( )  throws Exception { 
    PathMatchingResourcePatternResolver resolver =  new  PathMatchingResourcePatternResolver ( ; for ( Resource resource : resources ) { 
        System . out . println ( resource . getClass ( ) ) ; 
    Resource [ ] resources = resolver . getResources ( "yudaosourcecode/spring/DiUser. class" ) ; 
    System . out . println ( "resources.length is: "  + resources . length )
       ) ; 
        System . out . println ( resource . getFile ( ) . getPath ( ) ) ; 
    } 
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • test
resources.length is: 1
class org.springframework.core.io.DefaultResourceLoader$ClassPathContextResource
E:\workspace-idea\java-life\target\classes\yudaosourcecode\spring\DiUser.class
  • 1
  • 2
  • 3

5.1.2: Loading multiple files via classpath*

  • code
@Test 
public  void  testPathMatchingResourcePatternResolverWithClasspathStart ( )  throws IOException { 
    PathMatchingResourcePatternResolver resolver =  new  PathMatchingResourcePatternResolver ( ) ; 
    Resource [ ] resources = resolver . getResources ( "classpath*:yudaosourcecode/spring/*. class" ) ; 
    System .  out . println ( "resources. length is: " + resources . length) ; for ( Resource resource : resources ) { 
      
        System . out . println ( resource . getClass ( ) ) ; 
        System . out . println ( resource . getFile ( ) . getPath ( ) ) ; 
    } 
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • test
resources.length is: 9
class org.springframework.core.io.FileSystemResource
E:\workspace-idea\java-life\target\classes\yudaosourcecode\spring\ConstructorDi.class
class org.springframework.core.io.FileSystemResource
E:\workspace-idea\java-life\target\classes\yudaosourcecode\spring\DiPerson.class
class org.springframework.core.io.FileSystemResource
E:\workspace-idea\java-life\target\classes\yudaosourcecode\spring\DiUser.class
class org.springframework.core.io.FileSystemResource
E:\workspace-idea\java-life\target\classes\yudaosourcecode\spring\DongshiDaddyProtocolResolver.class
class org.springframework.core.io.FileSystemResource
...snip...
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

5.2: PathMatchingResourcePatternResolver source code

The class path isorg.springframework.core.io.support.PathMatchingResourcePatternResolver, in addition to supporting the newly added ResourcePatternResolverclasspath*:In addition, Ant-style path matching patterns are also supported, such as**/*/*.xml. Look at the source code, first to the methodorg.springframework.core.io.support.PathMatchingResourcePatternResolver#getResources, the source code is as follows:

org . springframework . core . io . support . PathMatchingResourcePatternResolver#getResources
 @Override 
public Resource [ ]  getResources ( String locationPattern )  throws IOException { 
	Assert . notNull ( locationPattern ,  "Location pattern must not be null" ) ; 
	// with CLASSPATH_ALL_URL_PREFIX, which is org .springframework.core.io.support.ResourcePatternResolver#CLASSPATH_ALL_URL_PREFIX 
	// String CLASSPATH_ALL_URL_PREFIX = "classpath*:"; at the beginning
	if  ( locationPattern . startsWith ( CLASSPATH_ALL_URL_PREFIX ) )  { 
		// <2021-02-22 15:50> 
		// Determine if the path is an Ant-style path through the Ant path matcher AntPathMatcher 
		if  ( getPathMatcher ( ) . isPattern ( locationPattern . substring ( CLASSPATH_ALL_URL_PREFIX .length ( ) ) ) ) { // < 2021-02-22 15:53> return findPathMatchingResources ( locationPattern ) ; } // if the path is not Ant style 
			
			 
		
		
		else  { 
			// <2021-02-22 15:54> 
			return  findAllClassPathResources ( locationPattern . substring ( CLASSPATH_ALL_URL_PREFIX . length ( ) ) ) ; 
		} 
	} 
	// if not starting with classpath* 
	else  { 
		int prefixEnd =  ( locationPattern . startsWith ( "war:" )  ? locationPattern . indexOf ( "*/" )  +  1  : 
				locationPattern . ( indexOf ':' )  +  1 ) ; 
		if  ( getPathMatcher ( ) . isPattern ( locationPattern . substring ( prefixEnd ) ) )  { 
			return  findPathMatchingResources ( locationPattern ) ; 
		} 
		else  { 
			// a single resource with the given name 
			return  new  Resource [ ]  { getResourceLoader ( ) . getResource ( locationPattern) } ; 
		} 
	} 
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

<2021-02-22 15:50>code, methodgetPathMatcher()The source code is as follows:

public PathMatcher getPathMatcher ( )  { 
	// private PathMatcher pathMatcher = new AntPathMatcher(); 
	// Returns the implementation class AntPathMatcher of the interface PathMatcher 
	return  this . pathMatcher ; 
}
  • 1
  • 2
  • 3
  • 4
  • 5

Then call the methodorg.springframework.util.AntPathMatcher#isPatternThe source code is as follows:

@Override 
public  boolean  isPattern ( String path )  { 
	boolean uriVar =  false ; 
	for  ( int i =  0 ; i < path . length ( ) ; i ++ )  { 
		char c = path . charAt ( i ) ; 
		if  ( c ==  '*'  || c ==  '?' )  { 
			return true ; 
		} 
		if  ( c ==  '{' )  { 
			uriVar =  true ; 
			continue ; 
		} 
		if  ( c ==  '}'  && uriVar )  { 
			return  true ; 
		} 
	} 
	return  false ; 
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

Mainly to determine whether to include*,?,{}If there are wildcards, return true, otherwise return false.<2021-02-22 15:53>The source code is as follows:

org . springframework . core . io . support . PathMatchingResourcePatternResolver#findPathMatchingResources
 protected Resource [ ]  findPathMatchingResources ( String locationPattern )  throws IOException { 
	// <2021-02-22 15:54> Get the part of the path without wildcards as the root path 
	String rootDirPath =  determineRootDir ( locationPattern ) ; 
	// Get the partial path of wildcard characters, such as classpath*:yudaosourcecode/spring/*.class 
	// The result of subPattern is *.class 
	String subPattern =  locationPattern. substring ( rootDirPath . length ( ) ) ; 
	// Call getResources repeatedly to get the Resouces array of all resources 
	// This is generally a resource corresponding to a folder, so the length is generally 1 
	Resource [ ] rootDirResources =  getResources ( rootDirPath ) ; 
	/ / Finally, the result set of the resources we need 
	Set < Resource > result =  new  LinkedHashSet < > ( 16 ) ; 
	// Loop through all the resources, leaving only the resources we need 
	for  ( Resource rootDirResource : rootDirResources )  {
		// The method returns itself directly, so this line of code has no actual effect at present 
		rootDirResource =  resolveRootDirResource ( rootDirResource ) ; 
		URL rootDirUrl = rootDirResource . getURL ( ) ; 
		( equinoxResolveMethod != null && rootDirUrl . getProtocol ( ) . startsWith ( "bundle" ) ) { 
			URL resolvedUrl = ( URL ) ReflectionUtils
		    .invokeMethod ( equinoxResolveMethod , null , rootDirUrl ) ; 
			if  ( resolvedUrl != null )  { 
				rootDirUrl = resolvedUrl ; 
			} 
			rootDirResource =  new  UrlResource ( rootDirUrl ) ; 
		} 
		// handle vfg, don't know much about it, ignore 
		if  ( rootDirUrl . getProtocol ( ) . startsWith ( ResourceUtils . URL_PROTOCOL_VFS ) )  { 
			result . addAll ( VfsResourceMatchingDelegate . findMatchingResources ( rootDirUrl , subPattern ,  getPathMatcher ( ) ) ) ; 
		} 
		// Process jar:, war:, etc. protocol paths 
		else  if  ( ResourceUtils . isJarURL ( rootDirUrl )  ||  isJarResource ( rootDirResource ) )  { 
			result . addAll ( doFindPathMatchingJarResources ( rootDirResource ,rootDirUrl , subPattern ) ) ; 
		} 
		// Process other path protocols, including classpath*: 
		else  { 
		// The code here obtains the corresponding resource from the folder resource according to the resource and the required file wildcard, if the path is classpath*: yudaosourcecode/spring/*.class, then rootDirResource is 
		// URL [file:/E:/workspace-idea/java-life/target/classes/yudaosourcecode/spring/] 
		// subPattern is *.class 
		// It is more complicated, Let 's not delve into 
		result.addAll ( doFindPathMatchingFileResources ( rootDirResource , subPattern ) ) ; } } if ( logger . isTraceEnabled ( ) )
		
	
	  { 
		logger . trace ( "Resolved location pattern ["  + locationPattern +  "] to resources "  + result ) ; 
	} 
	return result . toArray ( new  Resource [ 0 ] ) ; 
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47

Tags: Spring's uniform resource loading strategy

spring java spring

Related: Spring's uniform resource loading strategy