Spring Boot source code - auto assembly - AutoConfigurationImportSelector

Posted by Quilmes on Fri, 26 Jun 2020 10:19:45 +0200

catalog

1, process

1,getAutoConfigurationMetadata

2,getAutoConfigurationEntry

1) , get the exclude and excludeName property values of the EnableAutoConfiguration annotation

2) , get spring.factories Configuration value of EnableAutoConfiguration in

3) , removedduplicates

4) , get the classes that need to be excluded (not automatically assembled)

5) , checkExcludedClasses (check items to be excluded)

6) . exclude

6) , send AutoConfigurationImportEvent event

7) , create AutoConfigurationEntry object return

2, selectImports

Continue to the previous article, know the execution time and call order, continue to analyze the logic of the two methods, and how to realize automatic assembly. Take a look at its class structure (properties) autoconfigu before you start rationImportSelector.AutoConfigurationGroup

private static class AutoConfigurationGroup
			implements DeferredImportSelector.Group, BeanClassLoaderAware, BeanFactoryAware, ResourceLoaderAware {
    // Store annotation object relationships
    private final Map<String, AnnotationMetadata> entries = new LinkedHashMap<>();
    // Store auto assembled data
    private final List<AutoConfigurationEntry> autoConfigurationEntries = new ArrayList<>();

    private ClassLoader beanClassLoader;

    private BeanFactory beanFactory;

    private ResourceLoader resourceLoader;
    // Store automatically assembled, post processor, default order value and other information
    private AutoConfigurationMetadata autoConfigurationMetadata;
}

1, process

@Override
public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
    // Assertion judgment, definitely true
    Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector, // Omit some codes);
    // Transition up, calling the method of the parent class
    AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
            .getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata);
    // Add to autoConfigurationEntries property
    this.autoConfigurationEntries.add(autoConfigurationEntry);
    // Add bean information to entries (map < string, annotationmetadata >)
    for (String importClassName : autoConfigurationEntry.getConfigurations()) {
        this.entries.putIfAbsent(importClassName, annotationMetadata);
    }
}

Get the return result of AutoConfigurationEntry type, and assemble the data into autoConfigurationEntries and entries of AutoConfigurationGroup. The main method is in getAutoConfigurationMetadata, but autoConfigurationMetadata data is loaded before calling.

1,getAutoConfigurationMetadata

private AutoConfigurationMetadata getAutoConfigurationMetadata() {
    if (this.autoConfigurationMetadata == null) {
        this.autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
    }
    return this.autoConfigurationMetadata;
}
static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader) {
    // String PATH = "META-INF/spring-autoconfigure-metadata.properties";
    return loadMetadata(classLoader, PATH);
}

static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader, String path) {
    try {
        Enumeration<URL> urls = (classLoader != null) ? classLoader.getResources(path)
                : ClassLoader.getSystemResources(path);
        Properties properties = new Properties();
        while (urls.hasMoreElements()) {
            properties.putAll(PropertiesLoaderUtils.loadProperties(new UrlResource(urls.nextElement())));
        }
        return loadMetadata(properties);
    }
    catch (IOException ex) {
        throw new IllegalArgumentException("Unable to load @ConditionalOnClass location [" + path + "]", ex);
    }
}

The file path of the query is "meta-inf / spring autoconfigure"- metadata.Properties ", there are 488 key and value corresponding values in total. And load its value as property type data. Take a look at the general contents of the configuration file:

org...RestTemplateAutoConfiguration.AutoConfigureAfter=org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration

org...CassandraReactiveDataAutoConfiguration.ConditionalOnClass=com.datastax.driver.core.Cluster,reactor.core.publisher.Flux,org.springframework.data.cassandra.core.ReactiveCassandraTemplate

org...JerseyAutoConfiguration.AutoConfigureOrder=-2147483648

There are roughly three types (follow-up spring.factories Automatic assembly class in, condition judgment, filter and use):

1),... XXAutoConfiguration.AutoConfigureAfter Who's the type of auto assembled postprocessor

2),... XXAutoConfiguration.ConditionalOnClass Whose automatic assembly conditions

3),... XXAutoConfiguration.AutoConfigureorder Whose auto assemble sort field value

 

Continue to see the last added type (it's better to add the type of PropertiesAutoConfigurationMetadata, which is stored in the Properties type of Spring, which is a Hashtable):

static AutoConfigurationMetadata loadMetadata(Properties properties) {
    return new PropertiesAutoConfigurationMetadata(properties);
}

 

2,getAutoConfigurationEntry

protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
                                                           AnnotationMetadata annotationMetadata) {
    if (!isEnabled(annotationMetadata)) {
        return EMPTY_ENTRY;
    }
    AnnotationAttributes attributes = getAttributes(annotationMetadata);
    List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
    configurations = removeDuplicates(configurations);
    Set<String> exclusions = getExclusions(annotationMetadata, attributes);
    checkExcludedClasses(configurations, exclusions);
    configurations.removeAll(exclusions);
    configurations = filter(configurations, autoConfigurationMetadata);
    fireAutoConfigurationImportEvents(configurations, exclusions);
    return new AutoConfigurationEntry(configurations, exclusions);
}

1) , get the exclude and excludeName property values of the EnableAutoConfiguration annotation

Only by default, @ EnableAutoConfiguration is directly used by @ SpringBootApplication, so the obtained property values are all default values (empty).

protected AnnotationAttributes getAttributes(AnnotationMetadata metadata) {
    // org.springframework.boot.autoconfigure.EnableAutoConfiguration
    String name = getAnnotationClass().getName();
    AnnotationAttributes attributes = AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(name, true));
    Assert.notNull(attributes, () -> "No auto-configuration attributes found. Is " + metadata.getClassName()
            + " annotated with " + ClassUtils.getShortName(name) + "?");
    return attributes;
}

2) , get spring.factories Configuration value of EnableAutoConfiguration in

You need to view the loaded type before calling:

protected Class<?> getSpringFactoriesLoaderFactoryClass() {
    return EnableAutoConfiguration.class;
}
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
            getBeanClassLoader());
    Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
            + "are using a custom packaging, make sure that file is correct.");
    return configurations;
}

When starting the Spring Boot project, meta-inf will be loaded after analysis/ spring.factories All configuration information in. In the case of correct process, it will be applied in spring

ation.run Method, when loading the SpringApplicationRunListener type, the information in the properties will be loaded. One of the items is roughly as follows:

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
. . . . . . 

 

3) , removedduplicates

protected final <T> List<T> removeDuplicates(List<T> list) {
    return new ArrayList<>(new LinkedHashSet<>(list));
}

Although it's relatively simple, it's a way we can learn from when we write a project.

4) , get the classes that need to be excluded (not automatically assembled)

protected Set<String> getExclusions(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    Set<String> excluded = new LinkedHashSet<>();
    excluded.addAll(asList(attributes, "exclude"));
    excluded.addAll(Arrays.asList(attributes.getStringArray("excludeName")));
    excluded.addAll(getExcludeAutoConfigurationsProperty());
    return excluded;
}

Not only the configuration value on @ EnableConfiguration loaded by getAttributes, but also the value loaded in Environment in getExcludeAutoConfigurationsProperty. Of course, all values are empty by default.

5) , checkExcludedClasses (check items to be excluded)

Loop through the excluded items. If there is no item to be excluded in the auto assembly item, the assembly checks the exception IllegalStateException and throws it. So you can't just write exclusions.

6) . exclude

// Remove the exclusions according to the above exclusion set
configurations.removeAll(exclusions);
// According to the loaded spring autoconfigure- metadata.properties Conditions, excluding
configurations = filter(configurations, autoConfigurationMetadata);
private List<String> filter(List<String> configurations, AutoConfigurationMetadata 
    autoConfigurationMetadata) {

    long startTime = System.nanoTime();
    String[] candidates = StringUtils.toStringArray(configurations);
    boolean[] skip = new boolean[candidates.length];
    boolean skipped = false;
    for (AutoConfigurationImportFilter filter : getAutoConfigurationImportFilters()) {
        invokeAwareMethods(filter);
        boolean[] match = filter.match(candidates, autoConfigurationMetadata);
        for (int i = 0; i < match.length; i++) {
            if (!match[i]) {
                skip[i] = true;
                candidates[i] = null;
                skipped = true;
            }
        }
    }
    if (!skipped) {
        return configurations;
    }
    List<String> result = new ArrayList<>(candidates.length);
    for (int i = 0; i < candidates.length; i++) {
        if (!skip[i]) {
            result.add(candidates[i]);
        }
    }
    if (logger.isTraceEnabled()) {
        int numberFiltered = configurations.size() - result.size();
        logger.trace("Filtered " + numberFiltered + " auto configuration class in "
                + TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime) + " ms");
    }
    return new ArrayList<>(result);
}

1) , get first spring.factories AutoConfigurationImportFilter type in.

# Auto Configuration Import Filters
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnBeanCondition,\
org.springframework.boot.autoconfigure.condition.OnClassCondition,\
org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition

2) , traverse the above three types, and call back the three types of BeanClassLoaderAware, BeanFactoryAware, EnvironmentAware and ResourceLoaderAware interfaces to assign values.

3) , combined with spring autoconfigure- metadata.properties The specific type loaded in is determined by calling the match method.

4) . the type of final automatic assembly after assembly and filtering. Return.

 

6) , send AutoConfigurationImportEvent event

private void fireAutoConfigurationImportEvents(List<String> configurations, Set<String> exclusions) {
    List<AutoConfigurationImportListener> listeners = getAutoConfigurationImportListeners();
    if (!listeners.isEmpty()) {
        AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this, configurations, exclusions);
        for (AutoConfigurationImportListener listener : listeners) {
            invokeAwareMethods(listener);
            listener.onAutoConfigurationImportEvent(event);
        }
    }
}

Get the event listening list, traverse the callback Aware interface, and send the AutoConfigurationImportEvent event event.

The monitor list is also automatically assembled spring.factories AutoConfigurationImportListener type in.

7) , create AutoConfigurationEntry object return

Create an AutoConfigurationEntry type object return based on the type list to be automatically assembled and the object list to be excluded to prepare for the subsequent selectImports callback.

 

2, selectImports

@Override
public Iterable<Entry> selectImports() {
    if (this.autoConfigurationEntries.isEmpty()) {
        return Collections.emptyList();
    }
    Set<String> allExclusions = this.autoConfigurationEntries.stream()
            .map(AutoConfigurationEntry::getExclusions).flatMap(Collection::stream)
            .collect(Collectors.toSet());
    Set<String> processedConfigurations = this.autoConfigurationEntries.stream()
            .map(AutoConfigurationEntry::getConfigurations).flatMap(Collection::stream)
            .collect(Collectors.toCollection(LinkedHashSet::new));
    processedConfigurations.removeAll(allExclusions);

    return sortAutoConfigurations(processedConfigurations, getAutoConfigurationMetadata()).stream()
            .map((importClassName) -> new Entry(this.entries.get(importClassName), importClassName))
            .collect(Collectors.toList());
}

1) , get the HashSet to be excluded from list < autoconfigurationentry > autoconfigurationentries

2) , from List < autoconfigurationentry > autoconfigurationentries, get the auto assembled List and convert it to LinkedHashSet

3) . remove the items to be excluded from the automatic assembly list

4) Sort the auto assembly items and assemble them into de ferredImportSelector.Group.Entry Type, return.

 

 

Topics: Spring