简述
Spring Boot相对Spring的优点主要有两个:
起步依赖-会将很多jar包按照功能合并成stater整体进行版本管理和引用,解决Spring集成其他框架时jar版本管理问题
自动装配-引入相关的jar包后SpringBoot会自动注册一些比较关键的bean,并进行默认配置,不用我们进行特殊配置,解决Spring重量级XML配置问题。比如整合Mybatis时的SqlSessionFactory
SpringBoot启动依靠的是带有main方法的启动类,启动类的内容可以分为两个部分一个是启动类上@SpringBootApplication这个注解;第二部分是main方法里的SpringApplication.run(启动类.class,args)方法。下面探讨的内容是基于Spring Boot 2.1.7-RELEASE
版本。
Spring Boot自动装配原理(从注解@SpringBootApplication入手)
@SpringBootApplication是个组合注解包含四个元注解和@SpringBootConfiguration 、@EnableAutoConfiguration 、@ComponentScan 组成,下面逐个分析。
1 2 3 4 5 6 7 8 9 10 11 12 13 @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) }) public @interface SpringBootApplication {... }
@SpringBootConfiguration
@SpringBootConfiguration
也是一个组合注解,由元注解和@Configuration
构成。@Configuration
是@Component
的一个衍生注解主要作用是标记当前类是个配置类。@SpringBootConfiguration
注解类代码如下:
1 2 3 4 5 6 @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Configuration public @interface SpringBootConfiguration {}
@Configuration
注解类代码如下:
1 2 3 4 5 6 7 8 9 10 11 @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @interface Configuration { @AliasFor( annotation = Component.class ) String value () default "" ; }
@EnableAutoConfiguration
@EnableAutoConfiguration
也是一个组合注解由元注解和@AutoConfigurationPackage
、@Import
注解构成,Spring中有很多Enable开头的注解,其作用大都是借助@Import
来收集并注册特定场景相关的bean。@EnableAutoConfiguration
的主要作用就是借助@Import
来收集并注册所有符合自动装配条件的bean。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import({AutoConfigurationImportSelector.class}) public @interface EnableAutoConfiguration { String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration" ; Class<?>[] exclude() default {}; String[] excludeName() default {}; }
@AutoConfigurationPackage
@SpringBootApplication
扫描启动类当前包及其子包下面的类是由下文的@ComponentScan
注解完成的。非@AutoConfigurationPackage
注解完成的。
1 2 3 4 5 6 7 8 @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @Import({Registrar.class}) public @interface AutoConfigurationPackage {}
Registrar.class实现了ImportBeanDefinitionRegistrar接口会调用registerBeanDefinitions方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 static class Registrar implements ImportBeanDefinitionRegistrar , DeterminableImports { Registrar() { } public void registerBeanDefinitions (AnnotationMetadata metadata, BeanDefinitionRegistry registry) { AutoConfigurationPackages.register(registry, (new AutoConfigurationPackages.PackageImport(metadata)).getPackageName()); } public Set<Object> determineImports (AnnotationMetadata metadata) { return Collections.singleton(new AutoConfigurationPackages.PackageImport(metadata)); } }
@Import({AutoConfigurationImportSelector.class})
AutoConfigurationImportSelector
类是SpringBoot实现自动装配的关键。AutoConfigurationImportSelector
实现了DeferredImportSelector
(DeferredImportSelector接口, 表示为延迟导入)、BeanClassLoaderAware
接口,而DeferredImportSelector
又继承了ImportSelector
接口。其中selectImports 方法会返回一个数组,数组中的类后面都会注册到Spring容器中。
1 2 3 4 5 public class AutoConfigurationImportSelector implements DeferredImportSelector , BeanClassLoaderAware , ResourceLoaderAware , BeanFactoryAware , EnvironmentAware , Ordered { ... }
DeferredImportSelector接口是ImportSelector接口的子接口,该接口会在所有的@Configuration
配置类(不包括自动化配置类,即spring.factories文件中的配置类 )处理完成后运行。DeferredImportSelector 有两个特点:
继承该接口的ImportSelector会在所有@Configuration配置类处理完后运行 。这一点是因为在ConfigurationClassParser#parse(java.util.Set<org.springframework.beans.factory.config.BeanDefinitionHolder>)
方法中直到解析出来其他的候选配置类才会调用this.deferredImportSelectorHandler.process();
来解析DeferredImportSelector
如果定义了一个以上的DeferredImportSelector
则使用Order接口来进行排序。这一点也是在this.deferredImportSelectorHandler.process();
中进行了排序调用。
ImportSelector
接口类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public interface ImportSelector { String[] selectImports(AnnotationMetadata importingClassMetadata);
该接口注释说明:实现该接口的selectImport则返回的String[]的 class字符串 类将被注册为@Configuration,即只要实现该接口返回的类将被注入Spring容器。@see ImportBeanDefinitionRegistrar则说明是使用 ImportBeanDefinitionRegistrar的registerBeanDefinitions方法进行注入。
AutoConfigurationImportSelector.AutoConfigurationGroup
类对selectImports
方法实现
1 2 3 4 5 6 7 8 9 10 11 public String[] selectImports(AnnotationMetadata annotationMetadata) { if (!this .isEnabled(annotationMetadata)) { return NO_IMPORTS; } else { AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this .getAutoConfigurationEntry(annotationMetadata); return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations()); }
该方法的作用就是要返回需要注册到IoC容器中的对象对应的类型的全类路径名称的字符串数组。
下面我们重点对getAutoConfigurationEntry(annotationMetadata)
进行分析。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry (AnnotationMetadata annotationMetadata) { if (!this .isEnabled(annotationMetadata)) { return EMPTY_ENTRY; } else { AnnotationAttributes attributes = this .getAttributes(annotationMetadata); List<String> configurations = this .getCandidateConfigurations(annotationMetadata, attributes); configurations = this .removeDuplicates(configurations); Set<String> exclusions = this .getExclusions(annotationMetadata, attributes); this .checkExcludedClasses(configurations, exclusions); configurations.removeAll(exclusions); configurations = this .getConfigurationClassFilter().filter(configurations); this .fireAutoConfigurationImportEvents(configurations, exclusions); return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions); } }
我们来看一下getCandidateConfigurations(annotationMetadata, attributes)
方法是怎么拿到这些自动配置类的。
1 2 3 4 5 protected List<String> getCandidateConfigurations (AnnotationMetadata metadata, AnnotationAttributes attributes) { List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this .getSpringFactoriesLoaderFactoryClass(), this .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; }
SpringFactoriesLoader.java类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 public static List<String> loadFactoryNames (Class<?> factoryClass, @Nullable ClassLoader classLoader) { String factoryClassName = factoryClass.getName(); return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList()); } private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) { MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader); if (result != null ) { return result; } else { try { Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories" ) : ClassLoader.getSystemResources("META-INF/spring.factories" ); LinkedMultiValueMap result = new LinkedMultiValueMap(); while (urls.hasMoreElements()) { URL url = (URL)urls.nextElement(); UrlResource resource = new UrlResource(url); Properties properties = PropertiesLoaderUtils.loadProperties(resource); Iterator var6 = properties.entrySet().iterator(); while (var6.hasNext()) { Entry<?, ?> entry = (Entry)var6.next(); String factoryClassName = ((String)entry.getKey()).trim(); String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue()); int var10 = var9.length; for (int var11 = 0 ; var11 < var10; ++var11) { String factoryName = var9[var11]; result.add(factoryClassName, factoryName.trim()); } } } cache.put(classLoader, result); return result; } catch (IOException var13) { throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]" , var13); } } }
通过SpringFactoriesLoader
扫描所有的META-INF/spring.factories
中所有路径下的类全路径名(key为org.springframework.boot.autoconfigure.EnableAutoConfiguration
),并通过反射实例化成一个个的配置类并注入到Spring容器中,从而实现了自动装配。
接着我们看下this.getExclusions(annotationMetadata, attributes);
方法的作用。该方法用于排除主类上@SpringBootApplication
注解上排除的自动装配的类。比如我们在该注解上排除我们自定义starter的自动装配的类,@SpringBootApplication(exclude = {com.demo.starter.config.DemoConfig.class})
(当然也可以用excludeName进行排除),那么在后面的configurations.removeAll(exclusions);
方法中将会删除我们的com.demo.starter.config.DemoConfig.class
。
configurations=filter(configurations,autoConfigurationMetadata);
该行代码将会过滤掉不需要装配的类。过滤的逻辑有很多,比如我们常用的@ConditionXXX注解。如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 @ConditionalOnBean :容器中有指定的Bean@ConditionalOnClass :当类路径下有指定的类@ConditionalOnExpression :基于SpEL表达式作为判断条件@ConditionalOnJava :基于JVM版本作为判断条件@ConditionalOnJndi :在JNDI存在的条件下查找指定的位置@ConditionalOnMissingBean :当容器中没有指定Bean的情况下@ConditionalOnMissingClass :当类路径下没有指定的类@ConditionalOnNotWebApplication :当前项目不是Web项目@ConditionalOnProperty :配置文件中指定的属性是否有指定的值@ConditionalOnResource :类路径下是否有指定的资源@ConditionalOnSingleCandidate :当指定Bean在容器中只有一个,或者虽然有多个但是指定首选Bean@ConditionalOnWebApplication :当前项目是Web项目的条件下
@ComponentScan
默认扫描启动类所在包以及子包路径下全部类
小结
实现自动装配这块最核心的注解就是@EnableAutoConfiguration
,其中起作用的子注解为@Import({AutoConfigurationImportSelector.class})
。在AutoConfigurationImportSelector.AutoConfigurationGroup
的selectImports
方法中,通过SpringFactoriesLoader
的loadFactoryNames()
方法扫描到所有META-INF/spring.factories
文件中对应key为org.springframework.boot.autoconfigure.EnableAutoConfiguration
的类全路径名。并通过getExclusions
、.getConfigurationClassFilter().filter(configurations)
进行过滤处理。
决定一个XxxAutoConfiguration是否需要进行装配,由以下几方面控制:
@EnableAutoConfiguration参数控制
spring.autoconfigure.exclude参数控制
spring-autoconfigure-metadata.properties文件中指定的规则控制
XxxAutoConfiguration本身的条件控制,如硬编码的@ConditionalOn
那么selectImports
返回的类全路径名称数组在何时加载、实例化加入到IOC容器呢?在下面的章节最后部分会讲到。
Spring Boot启动过程(从代码run()入手)
SpringBoot项目的mian函数代码
1 2 3 4 5 @SpringBootApplication public class Application { public static void main (String[] args) { SpringApplication.run(Application.class, args); }
我们进入run()
方法看下,其代码在SpringApplication.class文件。
1 2 3 4 5 6 7 8 public static ConfigurableApplicationContext run (Class<?> primarySource, String... args) { return run(new Class[]{primarySource}, args); } public static ConfigurableApplicationContext run (Class<?>[] primarySources, String[] args) { return (new SpringApplication(primarySources)).run(args); }
我们发现其实其首先是创建了SpringApplication的实例,然后调用了SpringApplication的run()方法。
创建SpringApplication实例
接下来我们看下SpringApplication构造函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public SpringApplication (Class<?>... primarySources) { this ((ResourceLoader)null , primarySources); } public SpringApplication (ResourceLoader resourceLoader, Class<?>... primarySources) { this .sources = new LinkedHashSet(); this .bannerMode = Mode.CONSOLE; this .logStartupInfo = true ; this .addCommandLineProperties = true ; this .addConversionService = true ; this .headless = true ; this .registerShutdownHook = true ; this .additionalProfiles = new HashSet(); this .isCustomEnvironment = false ; this .resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null" ); this .primarySources = new LinkedHashSet(Arrays.asList(primarySources)); this .webApplicationType = WebApplicationType.deduceFromClasspath(); this .setInitializers(this .getSpringFactoriesInstances(ApplicationContextInitializer.class)); this .setListeners(this .getSpringFactoriesInstances(ApplicationListener.class)); this .mainApplicationClass = this .deduceMainApplicationClass(); }
在实例化SpringApplication中设置的初始化器和监听器都是在/META-INF/spring.factories 中获取的。
deduceFromClasspath()-推断应用的类型
我们看下WebApplicationType枚举类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 public enum WebApplicationType { NONE, SERVLET, REACTIVE; private static final String[] SERVLET_INDICATOR_CLASSES = new String[]{"javax.servlet.Servlet" , "org.springframework.web.context.ConfigurableWebApplicationContext" }; private static final String WEBMVC_INDICATOR_CLASS = "org.springframework.web.servlet.DispatcherServlet" ; private static final String WEBFLUX_INDICATOR_CLASS = "org.springframework.web.reactive.DispatcherHandler" ; private static final String JERSEY_INDICATOR_CLASS = "org.glassfish.jersey.servlet.ServletContainer" ; private static final String SERVLET_APPLICATION_CONTEXT_CLASS = "org.springframework.web.context.WebApplicationContext" ; private static final String REACTIVE_APPLICATION_CONTEXT_CLASS = "org.springframework.boot.web.reactive.context.ReactiveWebApplicationContext" ; private WebApplicationType () { } static WebApplicationType deduceFromClasspath () { if (ClassUtils.isPresent("org.springframework.web.reactive.DispatcherHandler" , (ClassLoader)null ) && !ClassUtils.isPresent("org.springframework.web.servlet.DispatcherServlet" , (ClassLoader)null ) && !ClassUtils.isPresent("org.glassfish.jersey.servlet.ServletContainer" , (ClassLoader)null )) { return REACTIVE; } else { String[] var0 = SERVLET_INDICATOR_CLASSES; int var1 = var0.length; for (int var2 = 0 ; var2 < var1; ++var2) { String className = var0[var2]; if (!ClassUtils.isPresent(className, (ClassLoader)null )) { return NONE; } } return SERVLET; } } }
NONE: 应用程序不是web应用,也不应该用web服务器去启动
SERVLET: 应用程序应作为基于servlet的web应用程序运行,并应启动嵌入式servlet web(tomcat)服务器。
REACTIVE: 应用程序应作为 reactive web应用程序运行,并应启动嵌入式 reactive web服务器。
run方法逻辑
run方法在SpringApplication.class中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 public ConfigurableApplicationContext run (String... args) { StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null ; Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList(); this .configureHeadlessProperty(); SpringApplicationRunListeners listeners = this .getRunListeners(args); listeners.starting(); Collection exceptionReporters; try { ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); ConfigurableEnvironment environment = this .prepareEnvironment(listeners, applicationArguments); this .configureIgnoreBeanInfo(environment); Banner printedBanner = this .printBanner(environment); context = this .createApplicationContext(); exceptionReporters = this .getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context); this .prepareContext(context, environment, listeners, applicationArguments, printedBanner); this .refreshContext(context); this .afterRefresh(context, applicationArguments); stopWatch.stop(); if (this .logStartupInfo) { (new StartupInfoLogger(this .mainApplicationClass)).logStarted(this .getApplicationLog(), stopWatch); } listeners.started(context); this .callRunners(context, applicationArguments); } catch (Throwable var10) { this .handleRunFailure(context, var10, exceptionReporters, listeners); throw new IllegalStateException(var10); } try { listeners.running(context); return context; } catch (Throwable var9) { this .handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null ); throw new IllegalStateException(var9); } }
run方法步骤一共分为9步:
获取并启动监听器,监听器也是在spring.factories中获取的。
项目运行环境Environment的预配置
创建Spring容器
Spring容器前置处理,这一步主要是在容器刷新之前的准备动作。包含一个非常关键的操作:将启动类注入容器,为后续开启自动化配置奠定基础。
刷新容器
Spring容器后置处理,扩展接口,设计模式中的模板方法,默认为空实现。
向监听器发出结束执行的事件通知
执行Runners
向监听器发布应用上下文就绪事件 上述步骤中,最重要的为第4、5步。
第一步:获取并启动监听器
事件机制在Spring是很重要的一部分内容,通过事件机制我们可以监听Spring容器中正在发生的一些事件,同样也可以自定义监听事件。Spring的事件为Bean和Bean之间的消息传递提供支持。当一个对象处理完某种任务后,通知另外的对象进行某些处理,常用的场景有进行某些操作后发送通知,消息、邮件等情况。
1 2 3 4 private SpringApplicationRunListeners getRunListeners (String[] args) { Class<?>[] types = new Class[]{SpringApplication.class, String[].class}; return new SpringApplicationRunListeners(logger, this .getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this , args)); }
这里也是从META-INF/spring.factories
获取指定的监听器实例
可以看到EventPublishingRunListener
监听器是Spring容器的启动监听器。
第二步:配置项目运行环境
这里的运行环境包括计算机的环境,Java环境,Spring的运行环境,Spring项目的配置(在SpringBoot中就是那个熟悉的application.properties/yml)等等。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 private ConfigurableEnvironment prepareEnvironment (SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) { ConfigurableEnvironment environment = this .getOrCreateEnvironment(); this .configureEnvironment((ConfigurableEnvironment)environment, applicationArguments.getSourceArgs()); listeners.environmentPrepared((ConfigurableEnvironment)environment); this .bindToSpringApplication((ConfigurableEnvironment)environment); if (!this .isCustomEnvironment) { environment = (new EnvironmentConverter(this .getClassLoader())).convertEnvironmentIfNecessary((ConfigurableEnvironment)environment, this .deduceEnvironmentClass()); } ConfigurationPropertySources.attach((Environment)environment); return (ConfigurableEnvironment)environment; }
该方法中主要完成的工作,首先是创建并按照相应的应用类型配置相应的环境,然后根据用户的配置,配置系统环境,然后启动监听器,并加载系统配置文件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 protected void configureEnvironment (ConfigurableEnvironment environment, String[] args) { if (this .addConversionService) { ConversionService conversionService = ApplicationConversionService.getSharedInstance(); environment.setConversionService((ConfigurableConversionService)conversionService); } this .configurePropertySources(environment, args); this .configureProfiles(environment, args); } protected void configurePropertySources (ConfigurableEnvironment environment, String[] args) { MutablePropertySources sources = environment.getPropertySources(); if (this .defaultProperties != null && !this .defaultProperties.isEmpty()) { sources.addLast(new MapPropertySource("defaultProperties" , this .defaultProperties)); } if (this .addCommandLineProperties && args.length > 0 ) { String name = "commandLineArgs" ; if (sources.contains(name)) { PropertySource<?> source = sources.get(name); CompositePropertySource composite = new CompositePropertySource(name); composite.addPropertySource(new SimpleCommandLinePropertySource("springApplicationCommandLineArgs" , args)); composite.addPropertySource(source); sources.replace(name, composite); } else { sources.addFirst(new SimpleCommandLinePropertySource(args)); } } protected void configureProfiles (ConfigurableEnvironment environment, String[] args) { environment.getActiveProfiles(); Set<String> profiles = new LinkedHashSet(this .additionalProfiles); profiles.addAll(Arrays.asList(environment.getActiveProfiles())); environment.setActiveProfiles(StringUtils.toStringArray(profiles)); }
如我们给项目提供了两个配置文件application-dev.yml
和application-prod.yml
。当我们想要在生产环境使用application-prod.yml
配置时,只需要在启动参数增加--spring.profiles.active=prod
。这其中就用到了configurePropertySources(environment, args);
中将args封装成了SimpleCommandLinePropertySource
并加入到了environment中,同时利用configureProfiles(environment, args);
根据启动参数激活了相应的配置文件。
listeners.environmentPrepared((ConfigurableEnvironment)environment)
在SpringApplication实例化对象过程中,this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
会获取并加载META-INF/spring.factories
中key为org.springframework.context.ApplicationListener
下的所有Listener。
在environmentPrepared
方法中,ConfigFileApplicationListener
会从以下几个位置加载配置文件内容
classpath:
file:./
classpath:config/
file:./config/:
第三步:创建Spring容器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 protected ConfigurableApplicationContext createApplicationContext () { Class<?> contextClass = this .applicationContextClass; if (contextClass == null ) { try { switch (this .webApplicationType) { case SERVLET: contextClass = Class.forName("org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext" ); break ; case REACTIVE: contextClass = Class.forName("org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext" ); break ; default : contextClass = Class.forName("org.springframework.context.annotation.AnnotationConfigApplicationContext" ); } } catch (ClassNotFoundException var3) { throw new IllegalStateException("Unable create a default ApplicationContext, please specify an ApplicationContextClass" , var3); } } return (ConfigurableApplicationContext)BeanUtils.instantiateClass(contextClass); }
以web工程为例,即其上下文为AnnotationConfigServletWebServerApplicationContext
,我们先看一下AnnotationConfigServletWebServerApplicationContext的设计。
第四步:Spring容器前置处理(关键)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 private void prepareContext (ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) { context.setEnvironment(environment); this .postProcessApplicationContext(context); this .applyInitializers(context); listeners.contextPrepared(context); if (this .logStartupInfo) { this .logStartupInfo(context.getParent() == null ); this .logStartupProfileInfo(context); } ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); beanFactory.registerSingleton("springApplicationArguments" , applicationArguments); if (printedBanner != null ) { beanFactory.registerSingleton("springBootBanner" , printedBanner); } if (beanFactory instanceof DefaultListableBeanFactory) { ((DefaultListableBeanFactory)beanFactory).setAllowBeanDefinitionOverriding(this .allowBeanDefinitionOverriding); } Set<Object> sources = this .getAllSources(); Assert.notEmpty(sources, "Sources must not be empty" ); this .load(context, sources.toArray(new Object[0 ])); listeners.contextLoaded(context); }
在前置处理中最核心的一步是加载我们的启动类,将启动类注入容器。
第五步:刷新容器(关键)
refresh相关代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 private void refreshContext (ConfigurableApplicationContext context) { this .refresh(context); if (this .registerShutdownHook) { try { context.registerShutdownHook(); } catch (AccessControlException var3) { } } } protected void refresh (ApplicationContext applicationContext) { Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext); ((AbstractApplicationContext)applicationContext).refresh(); } public void refresh () throws BeansException, IllegalStateException { synchronized (this .startupShutdownMonitor) { this .prepareRefresh(); ConfigurableListableBeanFactory beanFactory = this .obtainFreshBeanFactory(); this .prepareBeanFactory(beanFactory); try { this .postProcessBeanFactory(beanFactory); this .invokeBeanFactoryPostProcessors(beanFactory); this .registerBeanPostProcessors(beanFactory); this .initMessageSource(); this .initApplicationEventMulticaster(); this .onRefresh(); this .registerListeners(); this .finishBeanFactoryInitialization(beanFactory); this .finishRefresh(); } catch (BeansException var9) { if (this .logger.isWarnEnabled()) { this .logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9); } this .destroyBeans(); this .cancelRefresh(var9); throw var9; } finally { this .resetCommonCaches(); } } }
invokeBeanFactoryPostProcessors
invokeBeanFactoryPostProcessors()
方法是这期的重点。实例化并调⽤实现了BeanFactoryPostProcessor接⼝的Bean,就是在这一步解析的@SpringBootApplication
这个组合注解,BeanFactoryPostProcessor比较重要的一个ConfigurationClassPostProcessor 在这里调用,用来遍历BeanDefinitionRegistry中现有的BeanDefinition解析@Import
、@Configuration
、@ComponentScan
等注解将注解覆盖到的类也注册到BeanDefinitionRegistry中。
IOC容器初始化过程的三个步骤,也是在invokeBeanFactoryPostProcessors
方法中完成。
BeanDefinition的Resource定位
BeanDefinition的载入
向IoC容器注册BeanDefinition
在SpringBoot中,我们都知道他的包扫描是从主类所在的包开始扫描的,prepareContext()方法中,会先将主类解析成BeanDefinition,然后在refresh()方法的invokeBeanFactoryPostProcessors()方法中解析主类的BeanDefinition获取basePackage的路径。这样就完成了定位的过程。其次SpringBoot的各种starter是通过SPI扩展机制实现的自动装配,SpringBoot的自动装配同样也是在invokeBeanFactoryPostProcessors()方法中实现的。还有一种情况,在SpringBoot中有很多的@EnableXXX注解,细心点进去看的应该就知道其底层是@Import注解,在invokeBeanFactoryPostProcessors()方法中也实现了对该注解指定的配置类的定位加载。
常规的在SpringBoot中有三种实现定位,第一个是主类所在包的,第二个是SPI扩展机制实现的自动装配(比如各种starter),第三种就是@Import注解指定的类。
在第一步中说了三种Resource的定位情况,定位后紧接着就是BeanDefinition的分别载入。所谓的载入就是通过上面的定位得到的basePackage,SpringBoot会将该路径拼接成:classpath*:org/springframework/boot/demo/**/*.class
这样的形式,然后一个叫做PathMatchingResourcePatternResolver的类会将该路径下所有的.class文件都加载进来,然后遍历判断是不是有@Component注解,如果有的话,就是我们要装载的BeanDefinition。
这个过程通过调用上文提到的BeanDefinitionRegister接口的实现来完成。这个注册过程把载入过程中解析得到的BeanDefinition向IoC容器进行注册。通过上文的分析,我们可以看到,在IoC容器中将BeanDefinition注入到一个ConcurrentHashMap中,IoC容器就是通过这个HashMap来持有这些BeanDefinition数据的。比如DefaultListableBeanFactory中的beanDefinitionMap属性。
具体看下代码实现过程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 protected void invokeBeanFactoryPostProcessors (ConfigurableListableBeanFactory beanFactory) { PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors()); ... } public static void invokeBeanFactoryPostProcessors ( ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) { ... invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry); ... } private static void invokeBeanDefinitionRegistryPostProcessors ( Collection<? extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry) { for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) { postProcessor.postProcessBeanDefinitionRegistry(registry); } } @Override public void postProcessBeanDefinitionRegistry (BeanDefinitionRegistry registry) { ... processConfigBeanDefinitions(registry); } public void processConfigBeanDefinitions (BeanDefinitionRegistry registry) { ... Set<BeanDefinitionHolder> candidates = new LinkedHashSet(configCandidates); HashSet alreadyParsed = new HashSet(configCandidates.size()); do { parser.parse(candidates); parser.validate(); Set<ConfigurationClass> configClasses = new LinkedHashSet(parser.getConfigurationClasses()); configClasses.removeAll(alreadyParsed); if (this .reader == null ) { this .reader = new ConfigurationClassBeanDefinitionReader(registry, this .sourceExtractor, this .resourceLoader, this .environment, this .importBeanNameGenerator, parser.getImportRegistry()); } this .reader.loadBeanDefinitions(configClasses); ... } ... }
走到ConfigurationClassParser 类的parse()
方法这里,包扫描及自动装配入口在这个方法里
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public void parse (Set<BeanDefinitionHolder> configCandidates) { Iterator var2 = configCandidates.iterator(); while (var2.hasNext()) { BeanDefinitionHolder holder = (BeanDefinitionHolder)var2.next(); BeanDefinition bd = holder.getBeanDefinition(); try { if (bd instanceof AnnotatedBeanDefinition) { this .parse(((AnnotatedBeanDefinition)bd).getMetadata(), holder.getBeanName()); } else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition)bd).hasBeanClass()) { this .parse(((AbstractBeanDefinition)bd).getBeanClass(), holder.getBeanName()); } else { this .parse(bd.getBeanClassName(), holder.getBeanName()); } } catch (BeanDefinitionStoreException var6) { throw var6; } catch (Throwable var7) { throw new BeanDefinitionStoreException("Failed to parse configuration class [" + bd.getBeanClassName() + "]" , var7); } } this .deferredImportSelectorHandler.process(); }
包扫描方式加载注册Bean
第一步:Resource定位
沿着this.parse(((AnnotatedBeanDefinition)bd).getMetadata(), holder.getBeanName());
追踪
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 protected final void parse (AnnotationMetadata metadata, String beanName) throws IOException { processConfigurationClass(new ConfigurationClass(metadata, beanName)); } protected void processConfigurationClass (ConfigurationClass configClass) throws IOException { if (!this .conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) { ConfigurationClass existingClass = (ConfigurationClass)this .configurationClasses.get(configClass); if (existingClass != null ) { if (configClass.isImported()) { if (existingClass.isImported()) { existingClass.mergeImportedBy(configClass); } return ; } this .configurationClasses.remove(configClass); this .knownSuperclasses.values().removeIf(configClass::equals); } SourceClass sourceClass = asSourceClass(configClass); do { sourceClass = doProcessConfigurationClass(configClass, sourceClass); } while (sourceClass != null ); this .configurationClasses.put(configClass, configClass); } protected final SourceClass doProcessConfigurationClass (ConfigurationClass configClass, SourceClass sourceClass) throws IOException { if (configClass.getMetadata().isAnnotated(Component.class.getName())) { this .processMemberClasses(configClass, sourceClass); } AnnotationAttributes importResource; while (var3.hasNext()) { importResource = (AnnotationAttributes)var3.next(); if (this .environment instanceof ConfigurableEnvironment) { this .processPropertySource(importResource); } else { this .logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() + "]. Reason: Environment must implement ConfigurableEnvironment" ); } } Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable( sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class); if (!componentScans.isEmpty() && !this .conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) { for (AnnotationAttributes componentScan : componentScans) { Set<BeanDefinitionHolder> scannedBeanDefinitions = this .componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName()); for (BeanDefinitionHolder holder : scannedBeanDefinitions) { BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition(); if (bdCand == null ) { bdCand = holder.getBeanDefinition(); } if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this .metadataReaderFactory)) { parse(bdCand.getBeanClassName(), holder.getBeanName()); } } } } processImports(configClass, sourceClass, getImports(sourceClass), true ); AnnotationAttributes importResource = AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class); if (importResource != null ) { String[] resources = importResource.getStringArray("locations" ); Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader" ); for (String resource : resources) { String resolvedResource = this .environment.resolveRequiredPlaceholders(resource); configClass.addImportedResource(resolvedResource, readerClass); } } Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass); for (MethodMetadata methodMetadata : beanMethods) { configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass)); } processInterfaces(configClass, sourceClass); if (sourceClass.getMetadata().hasSuperClass()) { String superclass = sourceClass.getMetadata().getSuperClassName(); if (superclass != null && !superclass.startsWith("java" ) && !this .knownSuperclasses.containsKey(superclass)) { this .knownSuperclasses.put(superclass, configClass); return sourceClass.getSuperClass(); } } return null ; }
上面的doProcessConfigurationClass()
方法是SpringBoot的包扫描的入口方法。
for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(...
获取主类上的@PropertySource注解,解析该注解并将该注解指定的properties配置文件中的值存储到Spring的Environment中,Environment接口提供方法去读取配置文件中的值,参数是properties文件中定义的key值。
Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable( sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
解析主类上的@ComponentScan注解。
parse(bdCand.getBeanClassName(), holder.getBeanName());
会进行递归调用,因为当Spring扫描到需要加载的类会进一步判断每一个类是否满足是@Component/@Configuration
注解的类,如果满足会递归调用parse()方法,查找其相关的类。
processImports(configClass, sourceClass, getImports(sourceClass), true);
通过@Import注解查找到的类同样也会递归查找其相关的类。
我们看下this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
扫描包过程
1 2 3 4 5 6 7 8 9 10 11 12 13 public Set<BeanDefinitionHolder> parse (AnnotationAttributes componentScan, final String declaringClass) { ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this .registry, componentScan.getBoolean("useDefaultFilters" ), this .environment, this .resourceLoader); ... if (basePackages.isEmpty()) { basePackages.add(ClassUtils.getPackageName(declaringClass)); } ... return scanner.doScan(StringUtils.toStringArray(basePackages)); }
接着往下看return scanner.doScan(StringUtils.toStringArray(basePackages));
Spring是如何进行类扫描的,进入doScan()方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 protected Set<BeanDefinitionHolder> doScan (String... basePackages) { Assert.notEmpty(basePackages, "At least one base package must be specified" ); Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>(); for (String basePackage : basePackages) { Set<BeanDefinition> candidates = findCandidateComponents(basePackage); for (BeanDefinition candidate : candidates) { ScopeMetadata scopeMetadata = this .scopeMetadataResolver.resolveScopeMetadata(candidate); candidate.setScope(scopeMetadata.getScopeName()); String beanName = this .beanNameGenerator.generateBeanName(candidate, this .registry); if (candidate instanceof AbstractBeanDefinition) { postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName); } if (candidate instanceof AnnotatedBeanDefinition) { AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate); } if (checkCandidate(beanName, candidate)) { BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName); definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this .registry); beanDefinitions.add(definitionHolder); registerBeanDefinition(definitionHolder, this .registry); } } } return beanDefinitions; }
doScan
中有两个比较重要的方法,Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
从basePackage中扫描类并解析成BeanDefinition,拿到所有符合条件的类后在registerBeanDefinition(definitionHolder, this.registry);
将该类注册进IoC容器。也就是说在这个方法中完成了IoC容器初始化过程的第二、三步,BeanDefinition的载入和BeanDefinition的注册。
第二步:BeanDefinition载入-findCandidateComponents
我们看下findCandidateComponents(basePackage);
代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 public Set<BeanDefinition> findCandidateComponents (String basePackage) { ... else { return scanCandidateComponents(basePackage); } } private Set<BeanDefinition> scanCandidateComponents (String basePackage) { Set<BeanDefinition> candidates = new LinkedHashSet<>(); try { String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + resolveBasePackage(basePackage) + '/' + this .resourcePattern; Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath); boolean traceEnabled = logger.isTraceEnabled(); boolean debugEnabled = logger.isDebugEnabled(); for (Resource resource : resources) { if (traceEnabled) { logger.trace("Scanning " + resource); } if (resource.isReadable()) { try { MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource); if (isCandidateComponent(metadataReader)) { ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader); sbd.setResource(resource); sbd.setSource(resource); if (isCandidateComponent(sbd)) { if (debugEnabled) { logger.debug("Identified candidate component class: " + resource); } candidates.add(sbd); } else { if (debugEnabled) { logger.debug("Ignored because not a concrete top-level class: " + resource); } } } else { if (traceEnabled) { logger.trace("Ignored because not matching any filter: " + resource); } } } catch (Throwable ex) { throw new BeanDefinitionStoreException( "Failed to read candidate component class: " + resource, ex); } } else { if (traceEnabled) { logger.trace("Ignored because not readable: " + resource); } } } } catch (IOException ex) { throw new BeanDefinitionStoreException("I/O failure during classpath scanning" , ex); } return candidates; }
在第13行将basePackage拼接成classpath*:org/springframework/boot/demo/**/*.class
,在第16行的getResources(packageSearchPath);
方法中扫描到了该路径下的所有的类。然后遍历这些Resources,在第27行判断该类是不是 @Component
注解标注的类,并且不是需要排除掉的类。在第29行将扫描到的类,解析成ScannedGenericBeanDefinition
,该类是BeanDefinition接口的实现类。
第三步:BeanDefinition注册
1 2 3 protected void registerBeanDefinition (BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) { BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, registry); }
在ComponentScanAnnotationParser#registerBeanDefinition
完成了BeanDefinition的注册,就完成了IoC容器的初始化过程。此时,在使用的IoC容器DefaultListableFactory中已经建立了整个Bean的配置信息,而这些BeanDefinition已经可以被容器使用了。他们都在BeanbefinitionMap里被检索和使用。容器的作用就是对这些信息进行处理和维护。这些信息是容器简历依赖反转的基础。
自动装配处理-DeferredImportSelector
1、预处理:deferredImportSelectorHandler.handle() 2、真正处理:deferredImportSelectorHandler.process()统一处理此次解析得到的所有DeferredImportSelector 3、ConfigurationClassPostProcessor#processConfigBeanDefinitions()中的loadBeanDefinitions()
加载Bean
在ConfigurationClassParser#parse()
中调用了processImports()
对@Import
注解进行解析。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 private void processImports (ConfigurationClass configClass, ConfigurationClassParser.SourceClass currentSourceClass, Collection<ConfigurationClassParser.SourceClass> importCandidates, boolean checkForCircularImports) { if (!importCandidates.isEmpty()) { if (checkForCircularImports && this .isChainedImportOnStack(configClass)) { this .problemReporter.error(new ConfigurationClassParser.CircularImportProblem(configClass, this .importStack)); } else { this .importStack.push(configClass); try { Iterator var5 = importCandidates.iterator(); while (var5.hasNext()) { ConfigurationClassParser.SourceClass candidate = (ConfigurationClassParser.SourceClass)var5.next(); Class candidateClass; if (candidate.isAssignable(ImportSelector.class)) { candidateClass = candidate.loadClass(); ImportSelector selector = (ImportSelector)BeanUtils.instantiateClass(candidateClass, ImportSelector.class); ParserStrategyUtils.invokeAwareMethods(selector, this .environment, this .resourceLoader, this .registry); if (selector instanceof DeferredImportSelector) { this .deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector)selector); } else { String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata()); Collection<ConfigurationClassParser.SourceClass> importSourceClasses = this .asSourceClasses(importClassNames); this .processImports(configClass, currentSourceClass, importSourceClasses, false ); } } else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) { candidateClass = candidate.loadClass(); ImportBeanDefinitionRegistrar registrar = (ImportBeanDefinitionRegistrar)BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class); ParserStrategyUtils.invokeAwareMethods(registrar, this .environment, this .resourceLoader, this .registry); configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata()); } else { this .importStack.registerImport(currentSourceClass.getMetadata(), candidate.getMetadata().getClassName()); this .processConfigurationClass(candidate.asConfigClass(configClass)); } } } catch (BeanDefinitionStoreException var15) { throw var15; } catch (Throwable var16) { throw new BeanDefinitionStoreException("Failed to process import candidates for configuration class [" + configClass.getMetadata().getClassName() + "]" , var16); } finally { this .importStack.pop(); } } } }
在上面的方法中this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector)selector);
将当前的configClass放到deferredImportSelectorHandler
,留着之后处理。
预处理:deferredImportSelectorHandler.handle()
进入到handle()
方法查看下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 private class DeferredImportSelectorHandler { @Nullable private List<ConfigurationClassParser.DeferredImportSelectorHolder> deferredImportSelectors; private DeferredImportSelectorHandler () { this .deferredImportSelectors = new ArrayList(); } public void handle (ConfigurationClass configClass, DeferredImportSelector importSelector) { ConfigurationClassParser.DeferredImportSelectorHolder holder = new ConfigurationClassParser.DeferredImportSelectorHolder(configClass, importSelector); if (this .deferredImportSelectors == null ) { ConfigurationClassParser.DeferredImportSelectorGroupingHandler handler = ConfigurationClassParser.this .new DeferredImportSelectorGroupingHandler () ; handler.register(holder); handler.processGroupImports(); } else { this .deferredImportSelectors.add(holder); } } }
在ConfigurationClassParser#parse()
中执行完parse(((AnnotatedBeanDefinition)bd).getMetadata(), holder.getBeanName())
后,这时所有的类都已经处理完毕,才执行this.deferredImportSelectorHandler.process();
真正处理:deferredImportSelectorHandler.process()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 private class DeferredImportSelectorHandler { @Nullable private List<ConfigurationClassParser.DeferredImportSelectorHolder> deferredImportSelectors; private DeferredImportSelectorHandler () { this .deferredImportSelectors = new ArrayList(); } public void process () { List<ConfigurationClassParser.DeferredImportSelectorHolder> deferredImports = this .deferredImportSelectors; this .deferredImportSelectors = null ; try { if (deferredImports != null ) { ConfigurationClassParser.DeferredImportSelectorGroupingHandler handler = ConfigurationClassParser.this .new DeferredImportSelectorGroupingHandler () ; deferredImports.sort(ConfigurationClassParser.DEFERRED_IMPORT_COMPARATOR); deferredImports.forEach(handler::register); handler.processGroupImports(); } } finally { this .deferredImportSelectors = new ArrayList(); } } }
DeferredImportSelectorHolder会根据Group进行分组,来区分不同的ImportSelector,比如我们这个项目的AutoConfigurationImportSelector是属于AutoConfigurationGroup类型的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 private class DeferredImportSelectorGroupingHandler { private final Map<Object, ConfigurationClassParser.DeferredImportSelectorGrouping> groupings; private final Map<AnnotationMetadata, ConfigurationClass> configurationClasses; private DeferredImportSelectorGroupingHandler () { this .groupings = new LinkedHashMap(); this .configurationClasses = new HashMap(); } public void register (ConfigurationClassParser.DeferredImportSelectorHolder deferredImport) { Class<? extends Group> group = deferredImport.getImportSelector().getImportGroup(); ConfigurationClassParser.DeferredImportSelectorGrouping grouping = (ConfigurationClassParser.DeferredImportSelectorGrouping)this .groupings.computeIfAbsent(group != null ? group : deferredImport, (key) -> { return new ConfigurationClassParser.DeferredImportSelectorGrouping(this .createGroup(group)); }); grouping.add(deferredImport); this .configurationClasses.put(deferredImport.getConfigurationClass().getMetadata(), deferredImport.getConfigurationClass()); } public void processGroupImports () { Iterator var1 = this .groupings.values().iterator(); while (var1.hasNext()) { ConfigurationClassParser.DeferredImportSelectorGrouping grouping = (ConfigurationClassParser.DeferredImportSelectorGrouping)var1.next(); grouping.getImports().forEach((entry) -> { ConfigurationClass configurationClass = (ConfigurationClass)this .configurationClasses.get(entry.getMetadata()); try { ConfigurationClassParser.this .processImports(configurationClass, ConfigurationClassParser.this .asSourceClass(configurationClass), ConfigurationClassParser.this .asSourceClasses(entry.getImportClassName()), false ); } catch (BeanDefinitionStoreException var4) { throw var4; } catch (Throwable var5) { throw new BeanDefinitionStoreException("Failed to process import candidates for configuration class [" + configurationClass.getMetadata().getClassName() + "]" , var5); } }); } } private Group createGroup (@Nullable Class<? extends Group> type) { Class<? extends Group> effectiveType = type != null ? type : ConfigurationClassParser.DefaultDeferredImportSelectorGroup.class; Group group = (Group)BeanUtils.instantiateClass(effectiveType); ParserStrategyUtils.invokeAwareMethods(group, ConfigurationClassParser.this .environment, ConfigurationClassParser.this .resourceLoader, ConfigurationClassParser.this .registry); return group; } }
在processGroupImports()
中,getImports()
返回的Entry
,Entry保存着要导入的类和导入此类的配置类的注解信息,如下:
1 2 3 4 5 6 class Entry { private final AnnotationMetadata metadata; private final String importClassName;
我们接着看grouping.getImports()
方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 private static class DeferredImportSelectorGrouping { private final Group group; private final List<ConfigurationClassParser.DeferredImportSelectorHolder> deferredImports = new ArrayList(); DeferredImportSelectorGrouping(Group group) { this .group = group; } public void add (ConfigurationClassParser.DeferredImportSelectorHolder deferredImport) { this .deferredImports.add(deferredImport); } public Iterable<Entry> getImports () { Iterator var1 = this .deferredImports.iterator(); while (var1.hasNext()) { ConfigurationClassParser.DeferredImportSelectorHolder deferredImport = (ConfigurationClassParser.DeferredImportSelectorHolder)var1.next(); this .group.process(deferredImport.getConfigurationClass().getMetadata(), deferredImport.getImportSelector()); } return this .group.selectImports(); } }
这里的group对应由deferredImports.forEach(handler::register);
注册进来,对应为AutoConfigurationGroup 。上述方法中调用的process
和selectImports
,其实是调用的是AutoConfigurationGroup#process() 、AutoConfigurationGroup#selectImports() 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 private static class AutoConfigurationGroup implements Group , BeanClassLoaderAware , BeanFactoryAware , ResourceLoaderAware { private final Map<String, AnnotationMetadata> entries = new LinkedHashMap(); private final List<AutoConfigurationImportSelector.AutoConfigurationEntry> autoConfigurationEntries = new ArrayList(); private ClassLoader beanClassLoader; private BeanFactory beanFactory; private ResourceLoader resourceLoader; private AutoConfigurationMetadata autoConfigurationMetadata; private AutoConfigurationGroup () { } public void setBeanClassLoader (ClassLoader classLoader) { this .beanClassLoader = classLoader; } public void setBeanFactory (BeanFactory beanFactory) { this .beanFactory = beanFactory; } public void setResourceLoader (ResourceLoader resourceLoader) { this .resourceLoader = resourceLoader; } public void process (AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) { Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector, () -> { return String.format("Only %s implementations are supported, got %s" , AutoConfigurationImportSelector.class.getSimpleName(), deferredImportSelector.getClass().getName()); }); AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector)deferredImportSelector).getAutoConfigurationEntry(this .getAutoConfigurationMetadata(), annotationMetadata); this .autoConfigurationEntries.add(autoConfigurationEntry); Iterator var4 = autoConfigurationEntry.getConfigurations().iterator(); while (var4.hasNext()) { String importClassName = (String)var4.next(); this .entries.putIfAbsent(importClassName, annotationMetadata); } } public Iterable<Entry> selectImports () { if (this .autoConfigurationEntries.isEmpty()) { return Collections.emptyList(); } else { Set<String> allExclusions = (Set)this .autoConfigurationEntries.stream().map(AutoConfigurationImportSelector.AutoConfigurationEntry::getExclusions).flatMap(Collection::stream).collect(Collectors.toSet()); Set<String> processedConfigurations = (Set)this .autoConfigurationEntries.stream().map(AutoConfigurationImportSelector.AutoConfigurationEntry::getConfigurations).flatMap(Collection::stream).collect(Collectors.toCollection(LinkedHashSet::new )); processedConfigurations.removeAll(allExclusions); return (Iterable)this .sortAutoConfigurations(processedConfigurations, this .getAutoConfigurationMetadata()).stream().map((importClassName) -> { return new Entry((AnnotationMetadata)this .entries.get(importClassName), importClassName); }).collect(Collectors.toList()); } } }
AutoConfigurationGroup#process() 负责加载spring.factories
文件中的配置类,并按条件进行过滤,而AutoConfigurationGroup#selectImports()返回最终的配置实现类集合。
加载Bean
ConfigurationClassPostProcessor#processConfigBeanDefinitions()中的loadBeanDefinitions()
完成配置类bean加载。
小结
综上可以看到DeferredImportSelector
的处理过程并非是直接 调用ImportSelector#selectImports
方法。而是调用DeferredImportSelector.Group#process
和DeferredImportSelector.Group#selectImports
方法来完成引入功能。
参考资料