De ce să folosiți spring.factories pentru autoconfigurarea Spring Boot în loc de adnotări? (Programare, Primăvară Boot)

Chuck C a intrebat.

În documentație se menționează:

Dezvoltarea autoconfigurării și utilizarea condițiilor

Dacă lucrați într-o companie care dezvoltă biblioteci partajate sau dacă lucrați la o bibliotecă open-source sau comercială, este posibil să doriți să vă dezvoltați propria autoconfigurare. Clasele de autoconfigurare pot fi grupate în jars externe și pot fi preluate în continuare de Spring Boot.

Dacă am adnotări pentru orice altceva (chiar și adnotările @AutoConfigureAfter sau @AutoConfigureBefore),

De ce să menținem un fișier de proprietăți pentru a indica o clasă cu o adnotare?

Comentarii

3 răspunsuri
Stephane Nicoll

Pentru că nu vom scana lumea pentru a afla ce clase de autoconfigurare există în proiectul dvs. În primul rând, o autoconfigurare este doar o clasă obișnuită de @Configuration clasă.

Modul în care este găsită o componentă Spring este prin intermediul unei declarații explicite sau al scanării componentelor, dar trebuie să cunoaștem lista de clase de autoconfigurare cu mult înainte de a începe efectiv contextul.

Comentarii

  • Puteți să-mi spuneți, vă rog, ce clasă va scana spring.factories și va face instanțierea? –  > Por FeeLGooD.
  • org.springframework.boot.autoconfigure.AutoConfigurationImportSelector și prieteni. Nu că aceasta este o componentă internă a Spring Boot cu care nu ar trebui să vă ocupați. –  > Por Stephane Nicoll.
ridox

Când aplicația SpringBoot pornește, nu va scana toate clasele din borcane, Deci, SpringBoot starter ar trebui să specifice ce clase sunt auto-configurate. De exemplu, în spring-boot-2.0.4.RELEASE, se inițializează astfel:

@SpringBootApplication
public class MyApplication {
    public static void main(String[] args) {
       //1. method run will call the construtor below
        SpringApplication.run(MyApplication.class, args);
    }
}

    public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
        this.resourceLoader = resourceLoader;
        Assert.notNull(primarySources, "PrimarySources must not be null");
        this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
        this.webApplicationType = deduceWebApplicationType();
        //2. find all the classes whose key is ApplicationContextInitializer in spring.factories and initialize them 
        setInitializers((Collection) getSpringFactoriesInstances(
                ApplicationContextInitializer.class));
        setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
        this.mainApplicationClass = deduceMainApplicationClass();
    }

...
    private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
            Class<?>[] parameterTypes, Object... args) {
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        // Use names and ensure unique to protect against duplicates
        Set<String> names = new LinkedHashSet<>(
                //3. use current thread classcloader to load resources in the classpath
                SpringFactoriesLoader.loadFactoryNames(type, classLoader));
        List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
                classLoader, args, names);
        AnnotationAwareOrderComparator.sort(instances);
        return instances;
    }

//SpringFactoriesLoader.java
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
        String factoryClassName = factoryClass.getName();
        // 3.1  first find the configuration file
        return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
    }

private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
        ...
        try {
            Enumeration<URL> urls = (classLoader != null ?
            //  public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"; 
            //4. spring.factories file is defined here
                    classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
                    ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
            result = new LinkedMultiValueMap<>();
            ...
    }

Arvind Kumar

toate intrările din spring.factories sunt încărcate prin metoda de mai jos –

org.springframework.boot.autoconfigure.ImportAutoConfigurationImportSelector#loadFactoryNames

protected Collection<String> loadFactoryNames(Class<?> source) {
        return SpringFactoriesLoader.loadFactoryNames(source, getClass().getClassLoader());
    }

SpringFactoriesLoader aparține bibliotecii spring-core, a se vedea captura de ecran de mai jos