Spring
Extensions to resources
Resource
Resource
Abstract class diagram, originaljava
Only toUrl
Resources are loaded.Spring
The file,Url
, resources under the classpath for integration abstraction.
Because the Java standard URL protocol and its extension are relatively complex, and the API itself lacks the ability to determine the existence of related resources, Spring introduces its own Resource abstraction.
In my opinion, Resource is Spring’s integration of some common types of Resource apis. Give developers a common way to access all kinds of resources, such as FileSystem, Classpath, UrlResource, etc.
As shown in figure
Resource: contains the access mode of unified resources. For different resources, the Resource have similar getFile getUrl to apply different kinds of resources, including FileSystemresource, ClassPathResource is corresponding to the file system and the realization of the classpath.
ResourceLoader
The class diagram is as follows:
And the interface declaration
public interface ResourceLoader {
String CLASSPATH_URL_PREFIX = ResourceUtils.CLASSPATH_URL_PREFIX; // classpath prefix constant
// Get resources according to different position strings
Resource getResource(String location);
// Get class loaders. Why do you need class loaders? The resources of the ClassPath/FileSystem are needed.
ClassLoader getClassLoader(a);
}
Copy the code
Interface Default implementationDefaultResoueceLoader
/** * Gets Resource * by default@paramLocation Resource path *@return Resource
*/
@Override
public Resource getResource(String location) {
Assert.notNull(location, "Location must not be null");
// Use a protocol parser to parse path parameters
//1. Implement a ProtocolResolver and add it via DefaultClassLoader#addProtocolResolver()
for (ProtocolResolver protocolResolver : getProtocolResolvers()) {
Resource resource = protocolResolver.resolve(location, this);
if(resource ! =null) {
returnresource; }}/ / 2. If the '/' at the beginning, then returned through the different application context ClassPathContextResource/FileSystemContextResource/ServletContextResource
if (location.startsWith("/")) {
return getResourceByPath(location);
}
// If it starts with 'classpath:' the ClassPathResource is returned.
else if (location.startsWith(CLASSPATH_URL_PREFIX)) {
return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());
}
else {
try {
// Try to parse the location as a URL...
URL url = new URL(location);
// FileUrlResource is returned if it is FileUrl, UrlResource is returned if it is not.
return (ResourceUtils.isFileURL(url) ? new FileUrlResource(url) : new UrlResource(url));
}
catch (MalformedURLException ex) {
// No URL -> resolve as resource path.
returngetResourceByPath(location); }}}Copy the code
-
A custom ProtocolResolver is preferred for loading resources
-
Path parameters that start with/go to getResourceByPath(String Location) to get different resources based on the application context
-
Starting with classpath: returns the ClassPathResource
-
If it is a file, use FileUrlResource. If it is not a file, use UrlResource. If it is not a file, use a MalformedURLException. So, get getResourceByPath, eg: test/ a.ml, and that path will go to getResourceByPath
Core methodgetResourceByPath(String location)
The default is implemented by ClassPathContextResource, and methods use other types by subclass override
FileSystemResource, ClassRelativeContextResource, for example
protected Resource getResourceByPath(String path) {
return new ClassPathContextResource(path, getClassLoader());
}
Copy the code
ContextResource
Is a secondary enhancement interface to Resource that gets the relative path to the relative context root in an application context such as ServletContext via an additional getPathWithinContext. In a file or classpath, also used. FileSystemContextResource ClassRelativeContextResource etc.
ResourcePatternResolver
Resource template parser that parses multiple resources at once. Parsing is done using an Ant style URL
public interface ResourcePatternResolver extends ResourceLoader {
// Carrying the prefix will query all jars
String CLASSPATH_ALL_URL_PREFIX = "classpath*:";
// Get the resource set
Resource[] getResources(String locationPattern) throws IOException;
}
Copy the code
PathMatchingPatternResourceResolver
Is the default implementation class of ResourcePatternResolver
The core is getResources()
public Resource[] getResources(String locationPattern) throws IOException {
Assert.notNull(locationPattern, "Location pattern must not be null");
//1. Whether to start with classpath*
if (locationPattern.startsWith(CLASSPATH_ALL_URL_PREFIX)) {
// 2. Ant style path (multipath matching with *)
if (getPathMatcher().isPattern(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()))) {
// 2.1 Search resources for pattern matching
return findPathMatchingResources(locationPattern);
}
else {
Use ClassLoader to return all classpath and jar resources.
returnfindAllClassPathResources(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length())); }}else {
// Handle prefixes
int prefixEnd = (locationPattern.startsWith("war:")? locationPattern.indexOf("* /") + 1 :
locationPattern.indexOf(':') + 1);
//4. Obtain wildcard resources for non-CLASspath resources
if (getPathMatcher().isPattern(locationPattern.substring(prefixEnd))) {
return findPathMatchingResources(locationPattern);
}
else {
//5. Obtain a single resource
return newResource[] {getResourceLoader().getResource(locationPattern)}; }}}Copy the code
Core steps:
- If the path of the classpath* flag is used, proceed
Ant
If the pattern matches, the matching resource is searched - If there is no wildcard, the ClassLoader loads the path resource
- If it is not the CLASSPath tag and is ant style, pattern matching is performed
- If not Ant style, use DefaultResourceLoader for resource loading
There are several core methods
findPathMatchingResources(String locationPattern)
: This method is used to resolve carryAnt
Style Url resource parsing
protected Resource[] findPathMatchingResources(String locationPattern) throws IOException {
String rootDirPath = determineRootDir(locationPattern); Classpath *:/ web-INF /. XML -> classpath*:/ web-INF /
String subPattern = locationPattern.substring(rootDirPath.length()); // Get the string except the root directory -> *.xml
Resource[] rootDirResources = getResources(rootDirPath); // Get the resource set in the root directory
Set<Resource> result = new LinkedHashSet<>(16);
for (Resource rootDirResource : rootDirResources) {
rootDirResource = resolveRootDirResource(rootDirResource); // Give subclasses the opportunity to modify the resource load
URL rootDirUrl = rootDirResource.getURL();
// Bundle resources resolve WebSphere
if(equinoxResolveMethod ! =null && rootDirUrl.getProtocol().startsWith("bundle")) {
URL resolvedUrl = (URL) ReflectionUtils.invokeMethod(equinoxResolveMethod, null, rootDirUrl);
if(resolvedUrl ! =null) {
rootDirUrl = resolvedUrl;
}
rootDirResource = new UrlResource(rootDirUrl);
}
// VFS file parsing
if (rootDirUrl.getProtocol().startsWith(ResourceUtils.URL_PROTOCOL_VFS)) {
result.addAll(VfsResourceMatchingDelegate.findMatchingResources(rootDirUrl, subPattern, getPathMatcher()));
}
// Jar path parsing, parsing all entries in jar
else if (ResourceUtils.isJarURL(rootDirUrl) || isJarResource(rootDirResource)) {
// Add the result of parsing to LinkedHashSet
result.addAll(doFindPathMatchingJarResources(rootDirResource, rootDirUrl, subPattern));
}
//FileSystem Path resolution: Fill the root directory recursively
else{ result.addAll(doFindPathMatchingFileResources(rootDirResource, subPattern)); }}return result.toArray(new Resource[0]);
}
Copy the code
findAllClassPathResources(String location)
This method is used to load specific resources in the classpath
protected Resource[] findAllClassPathResources(String location) throws IOException {
String path = location;
if (path.startsWith("/")) {
path = path.substring(1);
}
// use the ClassLoader to load resources in the same path, where if path= "then all jars under the ClassLoader will be loaded
Set<Resource> result = doFindAllClassPathResources(path);
if (logger.isTraceEnabled()) {
logger.trace("Resolved classpath location [" + location + "] to resources " + result);
}
return result.toArray(new Resource[0]);
}
Copy the code
Extensions to the Java URL protocol
Common URL protocols in Java include HTTP, HTTPS, FTP,file and so on. So how to extend the protocol based on java.net.URL?
URL#setURLStreamHandlerFactory
Set the protocol factory globally- implementation
Handler
And, inSun.net.www.protocol.{protocol name}.handler
Under fixed position - implementation
Handler
, and through the-Djava.protocol.handler.pkgs
Specify the name of a user-defined protocol package
The relevant implementation code is as follows
Custom protocol factory mode
//1. Customize the protocol named LazyLittle and inherit URLStreamHandlerFactory
public class LazylittleStreamHandlerFactory implements URLStreamHandlerFactory {
@Override
public URLStreamHandler createURLStreamHandler(String protocol) {
return new URLStreamHandler() {
@Override
protected URLConnection openConnection(URL u) throws IOException {
return newLazylittleUrlConnection(u); }}; }/ / 2. Inheritance URLConnection
static class LazylittleUrlConnection extends URLConnection {
private ClassPathResource classPathResource;
//3. Delegate to ClasspathResource
protected LazylittleUrlConnection(URL url) {
super(url);
if(! url.toString().startsWith("lazylittle")) {
throw new UnsupportedOperationException("invalid prefix " + url);
}
//delegate
classPathResource = new ClassPathResource(url.getPath());
}
@Override
public InputStream getInputStream(a) throws IOException {
return classPathResource.getInputStream();
}
@Override
public void connect(a) throws IOException {}}Copy the code
Demo code
//1. The highest priority and will override the following policy, ClassLoader global!
URL.setURLStreamHandlerFactory(new LazylittleStreamHandlerFactory());
URL url = new
URL("lazylittle:/spring/in/action/resource/custom/LazylittleStreamHandlerFactory.class");
IoUtil.copy(url.openStream(), System.out); // Output to the console
Copy the code
Fixed package under implementation
This is done by implementing URLStreamHandler and then placing it under sun.net.www.protocol.{custom protocol name}. The relevant implementation looks at the factory implementation above
The specified-D
Customize package name parameters
- Implemented under defined packages
URLStreamHandler
- On the startup parameters
- Djava. Protocol. The handler. PKGS = a custom package name
Can be