Spring resource loading

In Java, resources from different sources are abstracted into URL s, and the read logic of resources from different sources is processed by registering different handlers (urlstreamhandlers).

However, the URL does not have a default definition of a handler relative to resources such as Classpath or ServletContext, although you can register your own URLStreamHandler to resolve specific URL prefixes (protocols). However, the URL does not provide basic methods, such as checking whether the current resource exists, checking whether the resource exists, etc.

URL: I can load various resources....XXX

Spring: you are a good man

Spring implements its own resource loading strategy

  • Clear division of functions. There are clear boundaries between resource definition and resource loading
  • Unified abstraction, unified resource definition and resource loading strategy.

Unified resources

org.springframework.core.io.Resource Interface abstracts everything Spring The underlying resources used internally, such as File,URL,Classpath.

public interface Resource extends InputStreamSource {
    .......
    .......
}

And org.springframework.core.io.InputStreamSource Encapsulates any class that can return InputStream, such as File, resource under Classpath and ByteArray. This interface has only one method getInputStream

public interface InputStreamSource {

    /**
     * It means that resources of any form can be converted into input streams. It is better to return a new InputStream every time this method is called
     * See that the subclass {@ link FileSystemResource} meets this requirement
     */
    InputStream getInputStream() throws IOException;

}

The Resource interface provides more convenient methods than URL s

There are corresponding Resource implementations for Resource files from different sources

  • File FileSystemResource
  • classpath resourceclasspathresource
  • url URLResource
  • InputStream resourceinputstreamresource
  • byte array resource

AbstractResource implements most of the methods in the Resource interface. If we want to customize Resource, we can inherit this class instead of directly implementing Resource

Uniform resource loading

org.springframework.core.io.ResourceLoader Is the abstraction of Spring resource loading

public interface ResourceLoader {
    /**
     * Pseudo URL prefix for loading from the class path: "classpath:".
     */
    String CLASSPATH_URL_PREFIX = ResourceUtils.CLASSPATH_URL_PREFIX;
    /**
     * Return a Resource handle according to the Resource path, but do not ensure that the Resource must exist. You need to call the Resource exists method * to judge
     * URL Location resources, such as " file:C / test.dat "
     * ClassPath Location resources, such as " classpath:test.dat "
     * Relative path resources, such as "WEB-INF"/ test.dat ”At this time, the returned Resource instance will vary according to the implementation
     *
     * To be reusable, getInputStream can be called multiple times
     */
    Resource getResource(String location);
    @Nullable
    ClassLoader getClassLoader();

}

The main implementation class is DefaultResourceLoader, and the core method is getResource

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

            // agreed interpretation
            for (ProtocolResolver protocolResolver : getProtocolResolvers()) {
                Resource resource = protocolResolver.resolve(location, this);
                if (resource != null) {
                    return resource;
                }
            }

        // If it starts with /, it creates a ClassPathContextResource, which is actually a ClassPathResource of its subclass and classpath
        if (location.startsWith("/")) {
            return getResourceByPath(location);
        } else if (location.startsWith(CLASSPATH_URL_PREFIX)) {
            // Create ClassPathResource if it starts with classpath
            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);
                return (ResourceUtils.isFileURL(url) ? new FileUrlResource(url) : new UrlResource(url));
            } catch (MalformedURLException ex) {
                // No URL -> resolve as resource path.
                return getResourceByPath(location);
            }
        }
    }

Where ProtocolResolver

@FunctionalInterface
public interface ProtocolResolver {

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

It is mainly used to process user-defined resource protocol. We can interpret our customized resource protocol by implementing this interface. We only need to add the implementation class object to DefaultResourceLoader

    public void addProtocolResolver(ProtocolResolver resolver) {
        Assert.notNull(resolver, "ProtocolResolver must not be null");
        this.protocolResolvers.add(resolver);
    }

ResourcePatternResolver

The Resource[] getResources(String locationPattern) method is added based on the ResourceLoader interface, which supports multiple Resource instances to be returned each time according to the specified path matching pattern.

@Override
    public Resource[] getResources(String locationPattern) throws IOException {
        Assert.notNull(locationPattern, "Location pattern must not be null");
        // classpath*:
        if (locationPattern.startsWith(CLASSPATH_ALL_URL_PREFIX)) {
            // a class path resource (multiple resources for same name possible)
            // Wildcards in path
            if (getPathMatcher().isPattern(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()))) {
                // a class path resource pattern
                return findPathMatchingResources(locationPattern);
            }
            else {
                // all class path resources with the given name
                return findAllClassPathResources(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()));
            }
        }
        else {
            // Generally only look for a pattern after a prefix here,
            // and on Tomcat only after the "*/" separator for its "war:" protocol.
            int prefixEnd = (locationPattern.startsWith("war:") ? locationPattern.indexOf("*/") + 1 :
                    locationPattern.indexOf(':') + 1);
            if (getPathMatcher().isPattern(locationPattern.substring(prefixEnd))) {
                // a file pattern
                return findPathMatchingResources(locationPattern);
            }
            else {
                // a single resource with the given name
                return new Resource[] {getResourceLoader().getResource(locationPattern)};
            }
        }
    }

Design patterns involved

During the interview

Interviewer: have you seen the source code of any framework

I've seen the source code of Spring

Interviewer: what design patterns do you use in Spring

Vegetable chicken me: Er... Seems to have... I forgot

Responsibility chain model

In the section of resource definition and resource loading, we can see the ProtocolResolver in DefaultResourceLoader,

private final Set<ProtocolResolver> protocolResolvers = new LinkedHashSet<>(4);

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

            // agreed interpretation
            for (ProtocolResolver protocolResolver : getProtocolResolvers()) {
                Resource resource = protocolResolver.resolve(location, this);
                if (resource != null) {
                    return resource;
                }
            }
      .....
      .....

A request to load resources will be interpreted as a Resource by one of the protocolresolvers or no ProtocolResolver can handle the request and return a Resource (which will be finally resolved into Resource by default). This process actually uses the responsibility chain mode, but it is not a pure responsibility chain mode.

In Dr. Yan Hong's Book JAVA and model, this is how to describe the Chain of Responsibility model

The model of responsibility chain is an object behavior model. In the responsibility chain model, many objects are connected by each object's reference to its next family to form a chain. The request is passed through the chain until an object in the chain decides to process the request. The client issuing the request does not know which object in the chain will finally process the request, which enables the system to reorganize and allocate responsibilities dynamically without affecting the client.

Pure and impure responsibility chain model

  • Pure responsibility chain mode: either process the request or not process the request at all and pass the request to the next processor; impure responsibility chain mode: a processor processes part of the request's business, and then passes the rest to the next processor for further processing
  • Pure responsibility chain mode: a request must be processed by a request processor; impure responsibility chain mode: the request may not be processed by any processor

For how to store the reference of the next handler, of course, you can store the reference of the next handler in the current handler, but it is more commonly used to store all the handlers with array or list, and then make chain call to process the request, such as servlet filter.

Strategic model

Different resource loaders correspond to different resource loading strategies

At org.springframework.context.support.GenericApplicationContext Class

public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry {

    private final DefaultListableBeanFactory beanFactory;

    @Nullable
    private ResourceLoader resourceLoader;
    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }

    @Override
    public Resource getResource(String location) {
        if (this.resourceLoader != null) {
            return this.resourceLoader.getResource(location);
        }
    // Finally call getResource of DefaultResourceLoader
        return super.getResource(location);
    }

Isn't that the strategic model

Related articles

Spring album

Tags: Java Spring Tomcat REST

Posted on Fri, 05 Jun 2020 23:20:24 -0700 by phpfreakjav