인터넷의 급속한 발전으로 인해 다양한 구성 요소가 끝없이 등장하고 프레임워크 통합이 필요한 구성 요소가 점점 더 많아지고 있습니다. 각 구성 요소는 Spring 컨테이너와 통합하기 위해 관련 코드를 구현해야 합니다. SpringMVC 프레임워크 구성은 너무 번거롭고 XML 파일에 의존합니다. 타사 구성 요소의 신속한 통합을 촉진하고 구성 파일에 대한 의존도를 줄이기 위해 개발자가 필요하지 않도록 구성에 대한 규칙 이론을 채택한 SpringBoot가 탄생했습니다. 개발을 위해 너무 많은 것을 구성합니다. SpringBoot의 하단에 사용되는 Spring은 기본적으로 N개의 다중 컴포넌트를 자동으로 어셈블리하는 기능을 통합한다. SpringBoot를 사용하는 것은 간단합니다. 메인 클래스에 @SpringBootApplication을 추가하고 SpringApplication.run()을 호출하여 메인 클래스에 전달합니다. 코드는 다음과 같습니다.
@SpringBootApplication public class StartApp { public static void main(String[] args) { SpringApplication.run(StartApp.class); } }
위의 소스코드를 보면 SpringApplication.run()이 SpringBoot의 프로그램 입구임을 알 수 있습니다. 이 기사에서는 이를 SpringApplication.run() 및 @SpringBootApplication 주석의 두 가지 측면에서 분석합니다.
SpringApplication.run(StartApp.class)의 핵심 코드로, 먼저 SpringApplication 클래스를 생성한 후 run 메소드를 실행합니다. 코드는 다음과 같습니다.
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) { return new SpringApplication(primarySources).run(args); }
1. SpringApplication의 구성 메소드 코드는 다음과 같습니다.
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { // 设置资源加载器 this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); // 设置应用主配置类 this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); // 获取web服务器类型 this.webApplicationType = WebApplicationType.deduceFromClasspath(); // 从spring.factories 文件中获取 ApplicationContextInitializer 的实现类 setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); // 从spring.factories 文件中获取 ApplicationListener 监听器的实现类 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); // 设置main启动类 this.mainApplicationClass = deduceMainApplicationClass(); }
구성 메소드의 주요 로직:
1 이후 실행에서 사용될 애플리케이션 기본 구성 클래스를 설정합니다. 이를 BeanDefinitionHolder로 캡슐화하고 컨텍스트 레지스트리 중간에 로드하는 메소드입니다.
2. 특정 웹 서비스 유형을 생성하기 위해 나중에 실행하는 방법에서 사용할 웹 서버 유형을 가져옵니다.
3. spring.factories 파일에서 ApplicationContextInitializer의 구현 클래스를 가져와서 SpringApplication 인스턴스로 설정하세요.
4 spring.factories 파일에서 ApplicationListener 리스너의 구현 클래스를 가져와서 SpringApplication 인스턴스로 설정하세요. 5. 기본 시작 클래스 설정
getSpringFactoriesInstances 메소드의 주요 논리는 인터페이스에 따라 META-INF/spring.factories 파일에서 특정 구현 클래스 문자열을 얻고 문자열을 객체로 인스턴스화하는 것입니다. 코드는 다음과 같습니다.
// 获取类加载器 ClassLoader classLoader = getClassLoader(); // Use names and ensure unique to protect against duplicates // 根据type 从META-INF/spring.factories获取 具体的实现类字符串列表 Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader)); // 实例化具体的实现类 List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); // 排序 AnnotationAwareOrderComparator.sort(instances); return instances;
META-INF/spring.factories 파일의 ApplicationContextInitializer.class에 해당하는 구현 클래스 문자열은
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\ org.springframework.boot.context.ContextIdApplicationContextInitializer,\ org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\ org.springframework.boot.rsocket.context.RSocketPortInfoApplicationContextInitializer,\ org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
META-INF/spring의 ApplicationListener.class에 해당하는 구현 클래스 문자열입니다.
org.springframework.boot.ClearCachesApplicationListener,\ org.springframework.boot.builder.ParentContextCloserApplicationListener,\ org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\ org.springframework.boot.context.FileEncodingApplicationListener,\ org.springframework.boot.context.config.AnsiOutputApplicationListener,\ org.springframework.boot.context.config.ConfigFileApplicationListener,\ org.springframework.boot.context.config.DelegatingApplicationListener,\ org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\ org.springframework.boot.context.logging.LoggingApplicationListener,\ org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener
2.run 메소드의 코드는 다음과 같습니다.
StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>(); // 设置了一个名为 java.awt.headless 的系统属性 // 其实是想设计应用程序,即使没有检测到显示器,也允许其启动 // 对于服务器来说,是不需要显示器的 ,所以要这样设置 configureHeadlessProperty(); // 获取 SpringApplicationRunListener 加载的是 EventPublishingRunListener // 获取启动时的监听器 SpringApplicationRunListeners listeners = getRunListeners(args); // 触发启动事件 listeners.starting(); try { // 构造一个应用程序的参数持有类 ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); // 创建并配置环境 ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); // 配置需要忽略的BeanInfo信息 configureIgnoreBeanInfo(environment); Banner printedBanner = printBanner(environment); // 创建上下文对象 context = createApplicationContext(); // 加载配置的启动异常处理器 exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[] { ConfigurableApplicationContext.class }, context); // 刷新前操作 prepareContext(context, environment, listeners, applicationArguments, printedBanner); // 刷新应用上下文 完成 Spring 容器的初始化 refreshContext(context); // 刷新后操作 afterRefresh(context, applicationArguments); stopWatch.stop(); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch); } // 启动完成事件 listeners.started(context); // 执行 ApplicationRunner 和 CommandLineRunner 实现类 callRunners(context, applicationArguments); } catch (Throwable ex) { // 事件广播启动出错了 handleRunFailure(context, ex, exceptionReporters, listeners); throw new IllegalStateException(ex); } try { // 运行事件 listeners.running(context); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, null); throw new IllegalStateException(ex); } return context;
run 메소드의 주요 로직은 다음과 같습니다.
1 spring.factories에서 SpringApplicationRunListener(청취 이벤트 게시자)의 구현 클래스를 가져옵니다. 파일을 저장하고 컨텍스트 라이프사이클에서 실행합니다. 시작 이벤트 트리거, 시작 완료 이벤트 등과 같은 관련 이벤트입니다.
2. 웹 애플리케이션 컨텍스트 개체를 생성하고 webApplicationType을 기반으로 특정 웹 서비스 유형을 생성합니다.
3. 새로 고치기 전에 기본 구성 클래스 리소스를 BeanDefinitionHolder로 캡슐화하고 이를 컨텍스트 레지스트리에 로드합니다.
4. 애플리케이션 컨텍스트를 새로 고쳐 Spring 컨테이너 초기화를 완료합니다.
5. ApplicationRunner 및 CommandLineRunner 인터페이스를 구현하는 실행 클래스입니다.
2. SpringBoot 자동 조립 원리 분석
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Import { /** * {@link Configuration @Configuration}, {@link ImportSelector}, * {@link ImportBeanDefinitionRegistrar}, or regular component classes to import. */ Class<?>[] value(); }
@Import 주석은 다음과 같습니다. 일반적으로 @Configuration과 함께 사용하면 @Configuration 주석 클래스는 Spring 컨테이너의 초기화 프로세스 중에 구문 분석됩니다(소스 코드는 org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions에 있음). @Import 주석은 주석 클래스를 기반으로 하며 해당 클래스를 기반으로 처리를 위한 관련 인터페이스를 구현할지 여부입니다. 소스 코드 위치: org.springframework.context.annotation.ConfigurationClassParser#processImports; 키 코드는 다음과 같습니다.
try { for (SourceClass candidate : importCandidates) { if (candidate.isAssignable(ImportSelector.class)) { // Candidate class is an ImportSelector -> delegate to it to determine imports Class<?> candidateClass = candidate.loadClass(); ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class, this.environment, this.resourceLoader, this.registry); Predicate<String> selectorFilter = selector.getExclusionFilter(); if (selectorFilter != null) { exclusionFilter = exclusionFilter.or(selectorFilter); } if (selector instanceof DeferredImportSelector) { this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector); } else { String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata()); Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter); processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false); } } else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) { // Candidate class is an ImportBeanDefinitionRegistrar -> // delegate to it to register additional bean definitions Class<?> candidateClass = candidate.loadClass(); ImportBeanDefinitionRegistrar registrar = ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class, this.environment, this.resourceLoader, this.registry); configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata()); } else { // Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar -> // process it as an @Configuration class this.importStack.registerImport( currentSourceClass.getMetadata(), candidate.getMetadata().getClassName()); processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter); } } }
위 코드에서 @Import 값 클래스를 사용하는 세 가지 시나리오가 있음을 알 수 있습니다.
1. ImportSelector.class 인터페이스가 구현된 시나리오 인스턴스 선택기의 selectImports 메소드를 직접 호출하여 인스턴스화할 Bean 객체의 전체 클래스 이름 목록을 반환하고 전체 클래스 이름 문자열 목록을 기반으로 인스턴스 객체를 생성합니다. 그런 다음 현재 processImports 메소드를 재귀적으로 호출합니다. 이 메소드는 결국 ConfigurationClasses 콜렉션에 추가됩니다.configurationClasses 콜렉션의 객체는 BeanDefinitionRegistry 유형의 레지스트리 객체에 등록됩니다. 이 경우 ImportSelector 인터페이스가 구현되고 DeferredImportSelector 인터페이스가 확장되어 BeanDefinition의 지연 주입 기능을 구현합니다. DeferredImportSelector 인터페이스는 ImportSelector 인터페이스를 확장하고 내부 인터페이스 Group을 갖습니다. @Import 주석이 달린 값 클래스가 DeferredImportSelector 인터페이스를 구현하고 인터페이스의 내부 클래스 Group 인터페이스도 구현하는 경우 이 구현 클래스를 연기해야 하는 것으로 보입니다. 지연된 처리가 필요한 경우 ImportSelector 인스턴스 선택기는 DeferredImportSelectorHolder 개체로 결합되고 deferredImportSelectors 컬렉션에 추가됩니다. 처리 논리 소스 코드 위치: org.springframework.context.annotation.ConfigurationClassParser.DeferredImportSelectorHandler#handle;
public void handle(ConfigurationClass configClass, DeferredImportSelector importSelector) { DeferredImportSelectorHolder holder = new DeferredImportSelectorHolder(configClass, importSelector); if (this.deferredImportSelectors == null) { DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler(); handler.register(holder); handler.processGroupImports(); } else { this.deferredImportSelectors.add(holder); } }
DeferredImportSelector 인터페이스 구현 로직은 org.springframework.context.annotation.ConfigurationClassParser#parse 메소드에서 호출됩니다. 구체적인 코드는 this.deferredImportSelectorHandler.process()에 있습니다.
public void process() { List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors; this.deferredImportSelectors = null; try { if (deferredImports != null) { DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler(); deferredImports.sort(DEFERRED_IMPORT_COMPARATOR); deferredImports.forEach(handler::register); // 具体的执行逻辑 handler.processGroupImports(); } } finally { this.deferredImportSelectors = new ArrayList<>(); } }
在processGroupImports()方法中,先通过grouping.getImports()拿到需要自动装配的Group.Entry(封装了全类名)对象集合,然后通过processImports()方法根据Entry类名字符串进行创建SourceClass类(该类可以通过asConfigClass()方法转成ConfigurationClass对象),最终添加到configurationClasses集合中。代码如下,
public void processGroupImports() { for (DeferredImportSelectorGrouping grouping : this.groupings.values()) { Predicate<String> exclusionFilter = grouping.getCandidateFilter(); grouping.getImports().forEach(entry -> { ConfigurationClass configurationClass = this.configurationClasses.get(entry.getMetadata()); try { processImports(configurationClass, asSourceClass(configurationClass, exclusionFilter), Collections.singleton(asSourceClass(entry.getImportClassName(), exclusionFilter)), exclusionFilter, false); } catch (BeanDefinitionStoreException ex) { throw ex; } catch (Throwable ex) { throw new BeanDefinitionStoreException( "Failed to process import candidates for configuration class [" + configurationClass.getMetadata().getClassName() + "]", ex); } }); } }
grouping.getImports()方法中主要执行具体的实现类的process方法和selectImports()方法(如果是AutoConfigurationImportSelector类,则调用org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.AutoConfigurationGroup#process和org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.AutoConfigurationGroup#selectImports,两个方法的具体类容请看2.2.2章节的说明),selectImports返回需要自动装配的Group.Entry对象集合,Entry对象中保存了全类名。代码如下:
public Iterable<Group.Entry> getImports() { for (DeferredImportSelectorHolder deferredImport : this.deferredImports) { this.group.process(deferredImport.getConfigurationClass().getMetadata(), deferredImport.getImportSelector()); } return this.group.selectImports(); }
ImportSelector接口代码代码如下:
public interface ImportSelector { String[] selectImports(AnnotationMetadata importingClassMetadata); @Nullable default Predicate<String> getExclusionFilter() { return null; } }
DeferredImportSelector接口的代码如下:
public interface DeferredImportSelector extends ImportSelector { @Nullable default Class<? extends Group> getImportGroup() { return null; } interface Group { void process(AnnotationMetadata metadata, DeferredImportSelector selector); Iterable<Entry> selectImports(); class Entry { // 省略 } } }
2.实现了 ImportBeanDefinitionRegistrar.class接口的场景;会先创建ImportBeanDefinitionRegistrar 实例类 registrar,再把 registrar 添加到 configClass 的 importBeanDefinitionRegistrars中,接口的registerBeanDefinitions方法的调用是在 org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions方法里的this.reader.loadBeanDefinitions(configClasses)代码中。具体执行语句loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());关键代码如下,
private void loadBeanDefinitionsFromRegistrars(Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> registrars) { registrars.forEach((registrar, metadata) -> registrar.registerBeanDefinitions(metadata, this.registry, this.importBeanNameGenerator)); }
ImportBeanDefinitionRegistrar接口代码如下:
public interface ImportBeanDefinitionRegistrar { default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) { registerBeanDefinitions(importingClassMetadata, registry); } default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { } }
3.没有实现以上两接口的普通类,会直接调用org.springframework.context.annotation.ConfigurationClassParser#processImports里面的processConfigurationClass方法,把当前configClass添加至 configurationClasses 集合中。configurationClasses集合中的对象最终会被注册到BeanDefinitionRegistry类型的 registry 对象中。
@SpringApplication注解主要包括了@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan。代码如下,
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
配置注解,包含了@Configuration注解,表明是配置类。
自动装配注解,主要逻辑是:根据 EnableAutoConfiguration 类型从META-INF/spring.factories 文件加载需要自动装配的类,并注入到Spring容器中。它包括了@AutoConfigurationPackage注解和一个@Import(AutoConfigurationImportSelector.class)注解。代码如下,
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import(AutoConfigurationImportSelector.class)
注册名为 org.springframework.boot.autoconfigure.AutoConfigurationPackages ,BeanClass为BasePackages.class 的GenericBeanDefinition 到 BeanDefinitionRegistry 中,通过@Import 注解实现注入功能,代码如下,
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @Import(AutoConfigurationPackages.Registrar.class) public @interface AutoConfigurationPackage { }
AutoConfigurationPackages.Registrar.class实现了ImportBeanDefinitionRegistrar接口 ,所以在Spring容器初始化的过程中会调用它的registerBeanDefinitions方法把PackageImport类注入到Spring容器中去。代码如下,
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports { @Override public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { register(registry, new PackageImport(metadata).getPackageName()); } @Override public Set<Object> determineImports(AnnotationMetadata metadata) { return Collections.singleton(new PackageImport(metadata)); } }
自动装配关键逻辑,先从META-INF/spring.factories 文件加载类型值为 EnableAutoConfiguration的字符串集合,再通过过滤,生成需要自动装配的类,最后注入到Spring容器中。AutoConfigurationImportSelector实现了DeferredImportSelector接口并且内部也实现了DeferredImportSelector.Group接口,所以在Spring容器初始化的过程中会调用
org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.AutoConfigurationGroup#process方法和#selectImports方法,
process()用来生成需要自动装配的类型,方法的代码如下,
Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector, () -> String.format("Only %s implementations are supported, got %s", AutoConfigurationImportSelector.class.getSimpleName(), deferredImportSelector.getClass().getName())); // 1. getAutoConfigurationMetadata() // 从META-INF/spring-autoconfigure-metadata.properties文件中获取自动装配的元数据, // 里面保存了加载类是否自动装配的条件 , // org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration.ConditionalOnBean // =javax.jms.ConnectionFactory // 2. getAutoConfigurationEntry() // 从 META-INF/spring.factories 文件中获取key为 EnableAutoConfiguration 的配置类字符串类表 并封装成 自动装配类对象 AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector) .getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata); this.autoConfigurationEntries.add(autoConfigurationEntry); // 循环遍历 自动装配类对象 的自动装配类字符串 ,添加到 this.entries for (String importClassName : autoConfigurationEntry.getConfigurations()) { this.entries.putIfAbsent(importClassName, annotationMetadata); }
getAutoConfigurationMetadata() 方法主要逻辑是:从META-INF/spring-autoconfigure-metadata.properties文件中获取自动装配的元数据,里面保存了自动加载类是否符合自动装配的前置条件,比较熟悉的有ConditionalOnClass和ConditionalOnBean,文件相关内容如下:
org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration= org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration.AutoConfigureAfter=org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveDataAutoConfiguration.ConditionalOnClass=com.datastax.driver.core.Cluster,reactor.core.publisher.Flux,org.springframework.data.cassandra.core.ReactiveCassandraTemplate org.springframework.boot.autoconfigure.data.solr.SolrRepositoriesAutoConfiguration.ConditionalOnClass=org.apache.solr.client.solrj.SolrClient,org.springframework.data.solr.repository.SolrRepository org.springframework.boot.autoconfigure.security.oauth3.client.servlet.OAuth3ClientAutoConfiguration.ConditionalOnWebApplication=SERVLET org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration= org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration.AutoConfigureBefore=org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration org.springframework.boot.autoconfigure.jms.artemis.ArtemisXAConnectionFactoryConfiguration= org.springframework.boot.autoconfigure.web.reactive.HttpHandlerAutoConfiguration.ConditionalOnWebApplication=REACTIVE org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryAutoConfiguration.ConditionalOnWebApplication=REACTIVE org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchRepositoriesAutoConfiguration= org.springframework.boot.autoconfigure.security.oauth3.resource.servlet.OAuth3ResourceServerAutoConfiguration.ConditionalOnWebApplication=SERVLET org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration.ConditionalOnWebApplication=SERVLET //省略
getAutoConfigurationEntry()方法 主要逻辑是:从spring.factories 文件中获取key为 EnableAutoConfiguration 的配置类字符串列表并封装成自动装配类AutoConfigurationEntry对象,代码如下,
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return EMPTY_ENTRY; } // 获取注解元数据的属性 AnnotationAttributes attributes = getAttributes(annotationMetadata); // 从spring.factories 文件中获取key为 EnableAutoConfiguration 的配置类字符串列表 List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes); // 去掉重复的 自动装配类字符串 configurations = removeDuplicates(configurations); // 根据注解元数据获取 需要排除的类名 Set<String> exclusions = getExclusions(annotationMetadata, attributes); // 检查排除的类名 checkExcludedClasses(configurations, exclusions); // 根据排除的类名进行排除 configurations.removeAll(exclusions); // 从spring.factories 文件中获取key为 AutoConfigurationImportFilter 的配置对象进行过滤 // 过滤规则从 getAutoConfigurationMetadata() 返回类的数据中获取 configurations = filter(configurations, autoConfigurationMetadata); // 执行导入配置类的监听事件 fireAutoConfigurationImportEvents(configurations, exclusions); // 返回 AutoConfigurationEntry 对象 return new AutoConfigurationEntry(configurations, exclusions); }
getCandidateConfigurations()方法从spring.factories 文件中获取类型为 EnableAutoConfiguration 的配置类字符串列表,代码如下,
// getSpringFactoriesLoaderFactoryClass()方法返回 EnableAutoConfiguration 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;
在META-INF/spring.factories文件中EnableAutoConfiguration .class 对应的实现类字符串为
# Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\ org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\ org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\ org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\ org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\ // 省略
2.selectImports()方法返回排序后的 Entry(需要自动装配的包装实体类) 对象集合,代码如下,
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); // 返回排序后的 Entry 集合 return sortAutoConfigurations(processedConfigurations, getAutoConfigurationMetadata()).stream() .map((importClassName) -> new Entry(this.entries.get(importClassName), importClassName)) .collect(Collectors.toList()); }
注意:@EnableAutoConfiguration 注解的分析过程需要结合@Import注解的过程来看。
구성 요소 검색 주석은 자동 검색 패키지 경로를 구성하는 데 사용됩니다. 구성 경로가 없으면 기본 구성 클래스 네임스페이스 아래의 모든 패키지와 클래스가 검색됩니다.
위 내용은 SpringBoot 시작 코드와 자동 어셈블리 소스 코드는 무엇입니까?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!