Spring Boot @ EnableAutoConfiguration 해석

7383 단어 SpringBoot
백 엔 드 개발 을 처음 했 을 때 가장 먼저 접 한 것 은 기본 적 인 spring 입 니 다. 양쪽 가방 을 참조 하여 bean 을 제공 하기 위해 xml 에 해당 하 는 가방 을 추가 해 야 합 니 다. 주석 추가 @ComponentScan({ "xxx"}).그 때 는 urgly 라 고 생각 했 지만 더 좋 은 방법 이 있 는 지 연구 하지 않 았 다.
Spring Boot 에 접촉 할 때 까지 두 개의 가방 을 자동 으로 도입 할 수 있 는 bean 을 발견 합 니 다.하지만 이 실현 원 리 를 보지 못 했다.최근 면접 때 까지 물 어 봤 어 요.그래서 논 리 를 실현 하 는 걸 봤 어 요.
사용 자세
원 리 를 말 하기 전에 사용 자 세 를 먼저 말 해라.
프로젝트 A 에서 bean 을 정의 합 니 다.
package com.wangzhi;

import org.springframework.stereotype.Service;

@Service
public class Dog {
}

이 프로젝트 의 resources/META-INF/ 아래 에 spring.factories 라 는 파일 을 만 듭 니 다. 이 파일 의 내용 은 다음 과 같 습 니 다.
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.wangzhi.Dog

그리고 procject B 에서 procject A 의 jar 가방 을 참조 합 니 다.
procject A 코드 는 다음 과 같 습 니 다.
package com.wangzhi.springbootdemo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan;

@EnableAutoConfiguration
public class SpringBootDemoApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(SpringBootDemoApplication.class, args);
        System.out.println(context.getBean(com.wangzhi.Dog.class));
    }

}

인쇄 결과:
com.wangzhi.Dog@3148f668

원리 해석
전체적으로 두 부분 으로 나 뉜 다. 하 나 는 모든 spring.factoriesEnableAutoConfiguration 관련 bean 의 종 류 를 수집 하 는 것 이 고, 다른 하 나 는 얻 은 종 류 를 spring 용기 에 등록 하 는 것 이다.
bean 정의 클래스 수집
spring 용기 가 시 작 될 때 호출 됩 니 다 AutoConfigurationImportSelector#getAutoConfigurationEntry
protected AutoConfigurationEntry getAutoConfigurationEntry(
        AutoConfigurationMetadata autoConfigurationMetadata,
        AnnotationMetadata annotationMetadata) {
    if (!isEnabled(annotationMetadata)) {
        return EMPTY_ENTRY;
    }
    // EnableAutoConfiguration     :exclude,excludeName 
    AnnotationAttributes attributes = getAttributes(annotationMetadata);
    //      Configurations
    List configurations = getCandidateConfigurations(annotationMetadata,
            attributes);
    //   
    configurations = removeDuplicates(configurations);
    //    exclude     
    Set exclusions = getExclusions(annotationMetadata, attributes);
    checkExcludedClasses(configurations, exclusions);
    configurations.removeAll(exclusions);
    configurations = filter(configurations, autoConfigurationMetadata);
    fireAutoConfigurationImportEvents(configurations, exclusions);
    return new AutoConfigurationEntry(configurations, exclusions);
}
getCandidateConfigurations 호출 방법 loadFactoryNames:
public static List loadFactoryNames(Class> factoryClass, @Nullable ClassLoader classLoader) {
        // factoryClassName org.springframework.boot.autoconfigure.EnableAutoConfiguration
		String factoryClassName = factoryClass.getName();
        //          spring.factories   key org.springframework.boot.autoconfigure.EnableAutoConfiguration    
		return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
	}


public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

private static Map> loadSpringFactories(@Nullable ClassLoader classLoader) {
		MultiValueMap result = cache.get(classLoader);
		if (result != null) {
			return result;
		}

		try {
            //      "META-INF/spring.factories"
			Enumeration urls = (classLoader != null ?
					classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
					ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
			result = new LinkedMultiValueMap<>();
			while (urls.hasMoreElements()) {
				URL url = urls.nextElement();
				UrlResource resource = new UrlResource(url);
                //       ,properties   HashMap,      key value
				Properties properties = PropertiesLoaderUtils.loadProperties(resource);
				for (Map.Entry, ?> entry : properties.entrySet()) {
					String factoryClassName = ((String) entry.getKey()).trim();
                    //         ','    value
					for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
						result.add(factoryClassName, factoryName.trim());
					}
				}
			}
			cache.put(classLoader, result);
			return result;
		}
		catch (IOException ex) {
			throw new IllegalArgumentException("Unable to load factories from location [" +
					FACTORIES_RESOURCE_LOCATION + "]", ex);
		}
	}

용기 에 등록
위의 프로 세 스에 서 spring.factories 에서 지정 한 bean 의 클래스 경 로 를 모두 얻 었 습 니 다. processGroupImports 방법 에 서 는 @ import 주해 와 같은 논리 로 용기 에 가 져 옵 니 다.
public void processGroupImports() {
    for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
        // getImports              
        grouping.getImports().forEach(entry -> {
            ConfigurationClass configurationClass = this.configurationClasses.get(
                    entry.getMetadata());
            try {
                //    @Import    
                processImports(configurationClass, asSourceClass(configurationClass),
                        asSourceClasses(entry.getImportClassName()), false);
            }
            catch (BeanDefinitionStoreException ex) {
                throw ex;
            }
            catch (Throwable ex) {
                throw new BeanDefinitionStoreException(
                        "Failed to process import candidates for configuration class [" +
                                configurationClass.getMetadata().getClassName() + "]", ex);
            }
        });
    }
}

private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
			Collection importCandidates, boolean checkForCircularImports) {
	...
    //          
    for (SourceClass candidate : importCandidates) {
       ...
        //  candidate ImportSelector ImportBeanDefinitionRegistrar           ,     
     	// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
						// process it as an @Configuration class
						this.importStack.registerImport(
								currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
		//    @Configuration   			
        processConfigurationClass(candidate.asConfigClass(configClass));
   ...
}
            
    ...
}

첫 번 째 단계 에서 수집 한 bean 류 의 정 의 는 최종 적 으로 Configuration 와 같은 처리 방식 으로 용기 에 등록 되 는 것 을 볼 수 있다.
End @EnableAutoConfiguration 주 해 는 양쪽 가방 bean 을 도입 하 는 원 가 를 간소화 했다.다른 응용 프로그램 에 사용 할 수 있 는 2 자 가방 을 제공 합 니 다. 2 자 가방 에서 대외 적 으로 노출 된 bean 을 spring.factories 에 정의 하면 됩 니 다.필요 하지 않 은 bean 에 대해 서 는 사용 자 용 @EnableAutoConfigurationexclude 속성 에서 제외 할 수 있다.

좋은 웹페이지 즐겨찾기