Spring Import External Configuration File via less thanimportgreater than Tag

  • 2021-09-24 22:34:44
  • OfStack

Catalog sample
Principle
DefaultBeanDefinitionDocumentReader
parseDefaultElementimportBeanDefinitionResource Summary

Example

Let's first look at how configuration files are imported into external configuration files. First define an spring-import configuration file as follows:


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">
   <import resource="spring-config.xml"/>
   <bean id="innerPerson" class="com.john.aop.Person">
   </bean>
</beans>

We see that it defines a tag and declares the imported resource file as spring-config. xml with the attribute resource. Let's look at the spring-config. xml configuration file again:


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">
   <bean id="outerPerson" class="com.john.aop.Person">
          <property name="firstName" value="john"/>
            <property name="lastName" value="wonder"/>
   </bean>
   <bean id="ChineseFemaleSinger" class="com.john.beanFactory.Singer" abstract="true" >
      <property name="country" value=" China "/>
      <property name="gender" value=" Female "/>
   </bean>
</beans>

We can then instantiate an BeanFactory to load the spring-import configuration file.


public static void main(String[] args) {
   ApplicationContext context=new ClassPathXmlApplicationContext("spring-import.xml");
   Person outerPerson=(Person)context.getBean("outerPerson");
   System.out.println(outerPerson);
}

If there is no problem, we can get outerPerson, bean and print it.


Person [0, john wonder]

Principle

Let's analyze how Spring parses the import tag and loads the imported configuration file through the source code. First, let's look at the DefaultBeanDefinitionDocumentReader class:

DefaultBeanDefinitionDocumentReader

We can see that one IMPORT_ELEMENT variable of public static final is defined in the class:


public static final String IMPORT_ELEMENT = "import";

Then we can search which side is using this variable and navigate to the parseDefaultElement function:

parseDefaultElement


private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
   //todo   Right  import  Parsing of tags  2020-11-17
   if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
      importBeanDefinitionResource(ele);
   }
}

Here is the source code we are looking for to import the external configuration file and load the Bean definition. Let's focus on the importBeanDefinitionResource function again:

importBeanDefinitionResource


/**
 * Parse an "import" element and load the bean definitions
 * from the given resource into the bean factory.
 */
protected void importBeanDefinitionResource(Element ele) {
   ////  Get  resource  Attribute value of 
   String location = ele.getAttribute(RESOURCE_ATTRIBUTE);
   ////  If it is empty, exit directly 
   if (!StringUtils.hasText(location)) {
      getReaderContext().error("Resource location must not be empty", ele);
      return;
   }
   // Resolve system properties: e.g. "${user.dir}"
   // //  Resolve system properties in a format such as   : "${user.dir}"
   location = getReaderContext().getEnvironment().resolveRequiredPlaceholders(location);
   Set<Resource> actualResources = new LinkedHashSet<>(4);
   // Discover whether the location is an absolute or relative URI
   ////  Judge  location  Is it a relative path or an absolute path 
   boolean absoluteLocation = false;
   try {
      // With  classpath*:  Or  classpath:  Begins with an absolute path 
      absoluteLocation = ResourcePatternUtils.isUrl(location) || ResourceUtils.toURI(location).isAbsolute();
   }
   catch (URISyntaxException ex) {
      // cannot convert to an URI, considering the location relative
      // unless it is the well-known Spring prefix "classpath*:"
   }

   // Absolute or relative?
   // Absolute path   Will also call the loadBeanDefinitions
   if (absoluteLocation) {
      try {
         int importCount = getReaderContext().getReader().loadBeanDefinitions(location, actualResources);
         if (logger.isTraceEnabled()) {
            logger.trace("Imported " + importCount + " bean definitions from URL location [" + location + "]");
         }
      }
      catch (BeanDefinitionStoreException ex) {
         getReaderContext().error(
               "Failed to import bean definitions from URL location [" + location + "]", ele, ex);
      }
   }
   else {
      // If it is a relative path, the absolute path is calculated first to get it  Resource And then parse 
      // No URL -> considering resource location as relative to the current file.
      try {
         int importCount;
         Resource relativeResource = getReaderContext().getResource().createRelative(location);

         // If the relative path   This resource exists   Then load this bean  Definition 
         if (relativeResource.exists()) {
            importCount = getReaderContext().getReader().loadBeanDefinitions(relativeResource);
            actualResources.add(relativeResource);
         }
         else {
            String baseLocation = getReaderContext().getResource().getURL().toString();
            //todo import Node   Internally, it will call loadBeanDefinitions  Operation  2020-10-17
            importCount = getReaderContext().getReader().loadBeanDefinitions(
                  StringUtils.applyRelativePath(baseLocation, location), actualResources);
         }
         if (logger.isTraceEnabled()) {
            logger.trace("Imported " + importCount + " bean definitions from relative location [" + location + "]");
         }
      }
   }
}

Let's analyze how this code flows:

1. First parse the resource tag to its attribute value and interpret whether the string value is empty.

2. Next, it parses the placeholders in the string path through the current context, which we analyzed in the previous article.

2. Next, judge whether it is an absolute path by calling ResourcePatternUtils. isUrl (location) ResourceUtils. toURI (location). isAbsolute (); To judge, emphasize: classpath*: or classpath: is the absolute path at the beginning, or one instance of URI can be generated as the absolute path, or isAbsolute of URI can be used to judge

3. If it is an absolute path, we get XmlBeanDefinitionReader through getReaderContext (). getReader () and call its loadBeanDefinitions (String location, @ Nullable Set < Resource > actualResources) function

4. If it is not an absolute path, then we try to generate a path relative to the current resource (which is important), and then load BeanDefinitions in this configuration file through the loadBeanDefinitions method. Here is a detail that needs our attention, that is, why does it try to judge whether resources exist or not? That is, if it exists, call the loadBeanDefinitions (Resource resource) method directly, which means that a single resource file must be loaded here, as the method comment says:


/**
 * Load bean definitions from the specified XML file.
 * @param resource the resource descriptor for the XML file
 * @return the number of bean definitions found
 * @throws BeanDefinitionStoreException in case of loading or parsing errors
 */
@Override
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
   //load Register in BeanDefinition
   return loadBeanDefinitions(new EncodedResource(resource));
}

Loads the Bean definition from the specified xml file. If it does not exist, then loadBeanDefinitions (String location, @ Nullable Set will be called just like absolute path 1 < Resource > actualResources) function, let's look at the definition of this function:


/**
 * Load bean definitions from the specified resource location.
 * <p>The location can also be a location pattern, provided that the
 * ResourceLoader of this bean definition reader is a ResourcePatternResolver.
 * @param location the resource location, to be loaded with the ResourceLoader
 * (or ResourcePatternResolver) of this bean definition reader
 * @param actualResources a Set to be filled with the actual Resource objects
 * that have been resolved during the loading process( To populate the actual resource object that has been resolved during loading * Collection of ). May be {@code null}
 * to indicate that the caller is not interested in those Resource objects.
 */
public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {

Clearly explained, this location refers to loading BeanDefinitions from the specified resource path.

Summarize

It can be seen from the source code that importing configuration files from outside gives you the opportunity to load each single 1 configuration file extension through a total configuration file.

That's Spring through < import > For more information about Spring importing external configuration files, please pay attention to other related articles on this site!


Related articles: