Configuration is XML, and Spring abstracts access to physical resources as resources.

The family of the Resource

Resource is an interface, inheritedInputStreamSource, defines the basic operations of a resource (all read operations) InputStreamSourceThere’s only one waygetInputStream

Basically, different classes of implementations are defined based on different resources.

  • ServletContextResourceLoad resources in a path relative to the root of the Web application, and support access as a stream or URL, or as a file if the WAR package is decompressed
  • ClassPathResourceIt is used to access resources in the class loading path. For Web applications, resource files in the WEB-INF/classes directory can be automatically loaded without using an absolute path
  • FileSystemResourceUsed to access File system resources, the advantage is not obvious, Java’s File class can also do this

EncodedResource

The main implementation of resource file encoding processing, its specific logic ingetReader After we set the encoding property for the resource, Spring uses the corresponding encoding as the encoding for the input stream

AbstractResource

It provides most of the default public implementations of the Resource methods. If you want to customize a Resource, it is not recommended to inherit directly from the Resource interface, but rather from the abstract class.

WritableResource

FileSystemResource in order to achieve write operations, inheritedWritableResource, which has methods to return instances of the output stream

The correct Resource is automatically selected based on the Resource address

Powerful way to load resources:

  • Automatically identifies resource address prefixes such as “classpath:” and “file:”
  • Support automatic resolution of Ant style wildcard resource addresses

Ant: Path matching expression used to match urIs

  • ? Matches any single character
  • * Matches 0 or any number of characters
  • ** matches 0 or more directories

ResourceLoader

Implement different Resource loading strategies to return specific types of resources as needed:

It’s an interface, Resource getResource(String location); The method automatically returns a Resource instance (the three concrete implementation classes mentioned earlier) based on the location passed in.

Also providesClassLoader getClassLoader();Method to expose the class loaderDefaultResourceLoader. Java providesResourceLoaderThe key to the realization ofgetResourcemethods

// Get the concrete implementation class instance of Resource
@Override
public Resource getResource(String location) {
   Assert.notNull(location, "Location must not be null");
   // ProtocolResolver User-defined protocol resource resolution policy
   // If there is one, parse the resource instance based on the location.
   for (ProtocolResolver protocolResolver : getProtocolResolvers()) {
      Resource resource = protocolResolver.resolve(location, this);
      if(resource ! =null) {
         returnresource; }}// If it starts with /, the ClassPathContextResource construct returns
   if (location.startsWith("/")) {
      return getResourceByPath(location);
   }
   // If it starts with classpath:, a resource of type ClassPathResource is constructed and returned, and the current ClassLoader is obtained by getClassLoader() when the resource is constructed
   else if (location.startsWith(CLASSPATH_URL_PREFIX)) {
      return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());
   }
   else {
      // Construct the URL and try to locate the resource through it.
      // Check whether it is FileURL, if it is, construct a resource of type FileUrlResource, otherwise construct UrlResource.
      // If a MalformedURLException is thrown during loading,
      // Delegate getResourceByPath to load the resource location
      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 ClassPathContextResource
         returngetResourceByPath(location); }}}Copy the code

The policy pattern is used, Resource is the policy interface, and many of the implementation subclasses are policy classes. DefaultResourceLoader determines which specific policy (implementation class) is returned. Policy mode requires the user to know the policy clearly, that is, what Resource is used for what Resource. Factory mode users do not need to know the details.

ResourcePatternResolver provides multiple Resource instances based on the path matching pattern.

Resource[] getResources(String locationPattern) throws IOException;
Copy the code

A protocol prefix is added, which is implemented by subclasses

String CLASSPATH_ALL_URL_PREFIX = "classpath*:";
Copy the code

You can see in the inheritance table that ApplicationContext also indirectly implements the ResourceLoader interface

AbstractApplicationContext inherited DefaultResourceLoader, and there are getResourcePatternResolver method, This instance is used to generate PathMatchingResourcePatternResolver.

The method is used in the constructor of AbstractApplicationContext, Means AbstractApplicationContext can call ResourcePatternResolver getResources method, according to the path to return more than one Resource instance.

To sum up, the realization of AbstractApplicationContext can support ResourceLoader and ResourcePatternResolver, this is also why senior container support uniform resource loading. Entrusted to the PathMatchingResourcePatternResolver and DefaultResourceLoader to execute.

Users of ResourceLoader BeanDefinitionReader

A user of sharp objects

  • Read BeanDefinition
  • BeanDefinitionRegistry

BeanDefinitionReader uses ResourceLoader or ResourcePatternResolver to parse configuration information into beanDefinitions, Finally, beanDefinitionDefinition is registered in the container with the registration interface of BeanDefinitionRegistry.

BeanDefinitionReader defines a series of interfaces for loading BeanDefinitions, for a single configuration file:

int loadBeanDefinitions(String location) throws BeanDefinitionStoreException;
Copy the code

For multiple profiles:

int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException;
Copy the code

For a single Resource instance:

int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException;
Copy the code

For multiple Resource instances:

int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException;
Copy the code

The ultimate goal is to translate the configuration of a configuration file into beandefinitions

There are other ways:

/** * Get the BeanDefinitionRegistry object. The main purpose of this class is to register beanDefinitions in the BeanDefinition registry *@return* /
BeanDefinitionRegistry getRegistry(a);
Copy the code
/** * the Bean name generator, which generates a name for the anonymous Bean, that is, id *@return* /
BeanNameGenerator getBeanNameGenerator(a);
Copy the code

BeanDefinitionReader architecture

AbstractBeanDefinitionReaderImplements common processing logic, one of the main methods isloadBeanDefinitionsIf it is ResourcePatternResolver, it means that multiple resources need to be loaded. Else, it means that only a single resource is loaded, which will be called eventuallyloadBeanDefinitionsMethod to do further loading.

public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
   // Get Resource loaders. The main function is to get Resource objects according to the path and class loaders
   ResourceLoader resourceLoader = getResourceLoader();
   // Check whether the resource loader is empty
   if (resourceLoader == null) {
      throw new BeanDefinitionStoreException(
            "Cannot load bean definitions from location [" + location + "]: no ResourceLoader available");
   }

   // ResourcePatternResolver for loading multiple files or file resources that can load Ant style paths
   if (resourceLoader instanceof ResourcePatternResolver) {
      // Resource pattern matching available.
      try {
         Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
         int count = loadBeanDefinitions(resources);
         if(actualResources ! =null) {
            Collections.addAll(actualResources, resources);
         }
         if (logger.isTraceEnabled()) {
            logger.trace("Loaded " + count + " bean definitions from location pattern [" + location + "]");
         }
         return count;
      }
      catch (IOException ex) {
         throw new BeanDefinitionStoreException(
               "Could not resolve bean definition resource pattern [" + location + "]", ex); }}else {
      // Can only load single resources by absolute URL.
      // Load a single file resource
      // Load directly with ResouceLoader
      Resource resource = resourceLoader.getResource(location);
      int count = loadBeanDefinitions(resource);
      if(actualResources ! =null) {
         actualResources.add(resource);
      }
      if (logger.isTraceEnabled()) {
         logger.trace("Loaded " + count + " bean definitions from location [" + location + "]");
      }
      returncount; }}Copy the code

XmlBeanDefinitionReader

A method to read bean definitions from an XML file

@Override
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
   return loadBeanDefinitions(new EncodedResource(resource));
}
Copy the code

The loadBeanDefinitions if logic is calledYou get a Resource instance based on location.


The loadBeanDefinitions method first specifies the encoding to parse the XML resource:

new EncodedResource(resource)
Copy the code

Then you execute loadBeanDefinitions of XmlBeanDefinitionReader. This method returns an int indicating the number of bean Definitions that were loaded and registered in the container.

To support multithreading load, load is a time-consuming process, the method USES resourcesCurrentlyBeingLoaded inside, is a ThreadLocal type of local variables to store resources being loaded. A ThreadLocal variable can only be modified by the current thread. The key is the thread number and the value is the thread resource.

public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
   Assert.notNull(encodedResource, "EncodedResource must not be null");
   if (logger.isTraceEnabled()) {
      logger.trace("Loading XML bean definitions from " + encodedResource);
   }
   // Get the currently loaded resource from the local thread variable
   Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
   // If there is no resource being loaded in the local thread variable, add it to it
   if (currentResources == null) {
      currentResources = new HashSet<>(4);
      this.resourcesCurrentlyBeingLoaded.set(currentResources);
   }
   // If encodedResource fails to be added to currentResources, the resource already exists but has not been loaded
   if(! currentResources.add(encodedResource)) {throw new BeanDefinitionStoreException(
            "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
   }
   try {
      // Get the input stream of the file
      InputStream inputStream = encodedResource.getResource().getInputStream();
      try {
         // Encapsulates an InputSource, which specifies the input stream and encoding format
         InputSource inputSource = new InputSource(inputStream);
         // If there is any code, add it to InputSource
         if(encodedResource.getEncoding() ! =null) {
            inputSource.setEncoding(encodedResource.getEncoding());
         }
         // Call a method of the same kind to continue parsing
         return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
      }
      finally {
         // Close the input streaminputStream.close(); }}catch (IOException ex) {
      throw new BeanDefinitionStoreException(
            "IOException parsing XML document from " + encodedResource.getResource(), ex);
   }
   finally {
      currentResources.remove(encodedResource);
      if (currentResources.isEmpty()) {
         this.resourcesCurrentlyBeingLoaded.remove(); }}}Copy the code

We called doLoadBeanDefinitions to parse out the attributes and register them with the container to fulfill our requirements.