SpringBoot启动 SpringBoot因为内置了tomcat或jetty服务器,不需要直接部署War文件,所以SpringBoot的程序起点是一个普通的主函数。主函数如下:
1 2 3 4 5 6 @SpringBootApplication public class SpringbootStudyApplication { public static void main (String[] args) { SpringApplication.run(SpringbootStudyApplication.class, args); } }
整个SpringBoot的启动过程其实都是通过@SpringBootApplication注解和SpringApplication.run方法来实现的。
整个启动的过程可以概括为:
读取所有依赖的META-INF/spring.factories文件,该文件指明了哪些依赖可以被自动加载。
根据importSelector类选择加载哪些依赖,使用conditionOn系列注解排除掉不需要的配置文件
将剩余的配置文件所代表的bean加载到IOC容器中。
比如spring-boot-2.1.8RELEASE.jar中的spring.factories文件的内容是整个样子的(节选):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 # PropertySource Loaders org.springframework.boot.env.PropertySourceLoader=\ org.springframework.boot.env.PropertiesPropertySourceLoader,\ org.springframework.boot.env.YamlPropertySourceLoader # Run Listeners org.springframework.boot.SpringApplicationRunListener=\ org.springframework.boot.context.event.EventPublishingRunListener # Error Reporters org.springframework.boot.SpringBootExceptionReporter=\ org.springframework.boot.diagnostics.FailureAnalyzers # Application Context Initializers org.springframework.context.ApplicationContextInitializer=\ org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\ org.springframework.boot.context.ContextIdApplicationContextInitializer,\ org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\ org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
这个文件中的内容最终会被解析为Map<K,List<V>>这种格式。键和值都是一个类的全限定名。
跟踪源码,探索原理 我们从这段代码开始跟踪SpringApplication.run(SpringbootStudyApplication.class, args);
这段代码经过重重调用最终来到了:
1 2 3 public static ConfigurableApplicationContext run (Class<?>[] primarySources, String[] args) { return new SpringApplication(primarySources).run(args); }
这个方法完成了SpringApplication的实例化。具体的实例化过程如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 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 = WebApplicationType.deduceFromClasspath(); //将初始化器放到数组中 setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); //把初始化的监听器加入到数组中 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); //获得主类 this .mainApplicationClass = deduceMainApplicationClass(); }
整个Application的实例化过程中,下面这两行代码比较关键。
1 2 setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
方法的具体实现如下:
1 2 3 4 5 6 7 8 9 10 private <T> Collection<T> getSpringFactoriesInstances (Class<T> type, Class<?>[] parameterTypes, Object... args) { ClassLoader classLoader = getClassLoader(); // Use names and ensure unique to protect against duplicates //获取FactoryClass的全限定名 Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader)); //直接利用反射实例化对象 List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); AnnotationAwareOrderComparator.sort(instances); return instances; }
这里的loadFactoryNames方法,其实就是从我们之前提到的spring.factories读取数据,然后以Map的形式进行存储的,loadFactoryNames就是从这个Map`中取数据(类的全限定名)。
读取spring.factories的具体实现:
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 private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) { MultiValueMap<String, String> result = cache.get(classLoader); if (result != null ) { return result; } try { Enumeration<URL> urls = (classLoader != null ? // FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories" 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 properties = PropertiesLoaderUtils.loadProperties(resource); for (Map.Entry<?, ?> entry : properties.entrySet()) { String factoryClassName = ((String) entry.getKey()).trim(); 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); } // output // like: // org.springframework.boot.autoconfigure.EnableAutoConfiguration -> {LinkedList@1446} size = 118 // | // |- org.springframework.boot.autoconfigure.EnableAutoConfiguration }
拿到需要加载的类的全限定名之后,就通过反射进行实例化,然后返回。在Application的构造器中,拿到这些对象后,存入到List中。
1 2 private List<ApplicationContextInitializer<?>> initializers; private List<ApplicationListener<?>> listeners;
到这个地方,我们已经拿到了所有的依赖类,那么SpringBoot是如何进行自动配置的呢?
其实前面我们看到的源码都是SpingApplication的实例化,整个实例化过程就完成了依赖类信息,而run方法其实就是完成装配的。具体的看下面的分析:
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 public ConfigurableApplicationContext run (String... args) { StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null ; Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>(); configureHeadlessProperty(); SpringApplicationRunListeners listeners = getRunListeners(args); listeners.starting(); try { ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); configureIgnoreBeanInfo(environment); Banner printedBanner = printBanner(environment); context = createApplicationContext(); exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[] { ConfigurableApplicationContext.class }, context); prepareContext(context, environment, listeners, applicationArguments, printedBanner); //关键代码 refreshContext(context); afterRefresh(context, applicationArguments); stopWatch.stop(); if (this .logStartupInfo) { new StartupInfoLogger(this .mainApplicationClass).logStarted(getApplicationLog(), stopWatch); } listeners.started(context); 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方法中最关键的就是refreshContext(context);。它实际上是调用了refresh方法,这个方法对应读过Spring源码的同学不会陌生。而我们bean的装配过程实际上就是由它完成的。
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 public void refresh () throws BeansException, IllegalStateException { synchronized (this .startupShutdownMonitor) { // Prepare this context for refreshing. prepareRefresh(); // Tell the subclass to refresh the internal bean factory. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. prepareBeanFactory(beanFactory); try { // Allows post-processing of the bean factory in context subclasses. postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context. invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation. registerBeanPostProcessors(beanFactory); // Initialize message source for this context. initMessageSource(); // Initialize event multicaster for this context. initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. onRefresh(); // Check for listener beans and register them. registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. finishRefresh(); } catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); } // Destroy already created singletons to avoid dangling resources. destroyBeans(); // Reset 'active' flag. cancelRefresh(ex); // Propagate exception to caller. throw ex; } finally { // Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore... resetCommonCaches(); } } }
其中invokeBeanFactoryPostProcessors会解析@import注解,并根据@import的属性进行下一步操作。
1 2 3 4 protected void invokeBeanFactoryPostProcessors (ConfigurableListableBeanFactory beanFactory) { PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors()); // 省略 }
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 static void invokeBeanFactoryPostProcessors ( ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) { // Invoke BeanDefinitionRegistryPostProcessors first, if any. Set<String> processedBeans = new HashSet<>(); if (beanFactory instanceof BeanDefinitionRegistry) { BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory; List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>(); List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>(); // 记录是否是定义类的 Processor 或者普通的 Processor // Do not initialize FactoryBeans here: We need to leave all regular beans // uninitialized to let the bean factory post-processors apply to them! // Separate between BeanDefinitionRegistryPostProcessors that implement // PriorityOrdered, Ordered, and the rest. List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>(); // ... // 应用 Bean 定义类的后置处理器 invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry); // ... } private static void invokeBeanDefinitionRegistryPostProcessors ( Collection<? extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry) { for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) { postProcessor.postProcessBeanDefinitionRegistry(registry); } }
invokerBeandefintionRegistryPostProcessors函数对每一个定义类的后置处理器分别进行应用,@Configure的解析就在这个函数中。
1 2 3 4 5 6 7 // 从注册表中的配置类派生更多的bean定义 public void postProcessBeanDefinitionRegistry (BeanDefinitionRegistry registry) { // ... this .registriesPostProcessed.add(registryId); // Build and validate a configuration model based on the registry of Configuration classes. processConfigBeanDefinitions(registry); }
进入最关键的类ConfigurationClassPostProcessor,这个类用户来注册所有的@Configure和@Bean, 它的processConfigbeanDefinitions函数如下:
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 public void processConfigBeanDefinitions (BeanDefinitionRegistry registry) { List<BeanDefinitionHolder> configCandidates = new ArrayList<>(); String[] candidateNames = registry.getBeanDefinitionNames(); // 记录所有候选的未加载的配置 // Return immediately if no @Configuration classes were found if (configCandidates.isEmpty()) { return ; } // 按照 Ordered 对配置进行排序 // 加载自定义 bean 名命策略 if (this .environment == null ) { this .environment = new StandardEnvironment(); } // Parse each @Configuration class ConfigurationClassParser parser = new ConfigurationClassParser( this .metadataReaderFactory, this .problemReporter, this .environment, this .resourceLoader, this .componentScanBeanNameGenerator, registry); Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates); Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size()); do { // 解译候选集 parser.parse(candidates); parser.validate(); Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses()); configClasses.removeAll(alreadyParsed); // Read the model and create bean definitions based on its content this .reader.loadBeanDefinitions(configClasses); alreadyParsed.addAll(configClasses); // ... } while (!candidates.isEmpty()); // Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) { sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry()); } if (this .metadataReaderFactory instanceof CachingMetadataReaderFactory) { // Clear cache in externally provided MetadataReaderFactory; this is a no-op // for a shared cache since it'll be cleared by the ApplicationContext. ((CachingMetadataReaderFactory) this .metadataReaderFactory).clearCache(); } }
在解释候选集parser.parse(candidates)中,会调用suorceClass=doProcessConfigurationClass(configClass,sourceClass)方法依次解析注解,得到所有的候选集。该方法顺次解析@PropertySource,@componentScan,@Import,@importResource,@Bean父类。
解析完成之后,会找到所有以 @PropertySource、@ComponentScan、@Import、@ImportResource、@Bean 注解的类及其对象,如果有 DeferredImportSelector,会将其加入到 deferredImportSelectorHandler 中,并调用 this.deferredImportSelectorHandler.process() 对这些 DeferredImportSelector 进行处理。
实际上,在 spring boot 中,容器初始化的时候,主要就是对 AutoConfigurationImportSelector 进行处理。
Spring 会将 AutoConfigurationImportSelector 封装成一个 AutoConfigurationGroup,用于处理。最终会调用 AutoConfigurationGroup 的 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 26 27 28 29 30 31 32 @Override public void process (AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) { // 主要通过该函数找到所有需要自动配置的类 AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector) .getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata); this .autoConfigurationEntries.add(autoConfigurationEntry); for (String importClassName : autoConfigurationEntry.getConfigurations()) { this .entries.putIfAbsent(importClassName, annotationMetadata); } } 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); } protected List<String> getCandidateConfigurations (AnnotationMetadata metadata, AnnotationAttributes attributes) { List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()); return configurations; }
如上,我们可以看到 process 最终调用了我们非常熟悉的函数 SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());,该方法以 EnableAutoConfiguration 类为键(org.springframework.boot.autoconfigure.EnableAutoConfiguration),取得所有的值。
在该函数中,还会调用 configurations = filter(configurations, autoConfigurationMetadata) 方法,将不需要的候选集全部排除。(该方法内部使用 AutoConfigurationImportFilter 的实现类排除)。
我们看一个常见的 configuration,即 org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,这个类中有大量的 @Bean 注解的方法,用来产生 bean,如下:
1 2 3 4 5 6 7 8 @Bean @Override public RequestMappingHandlerAdapter requestMappingHandlerAdapter () { RequestMappingHandlerAdapter adapter = super .requestMappingHandlerAdapter(); adapter.setIgnoreDefaultModelOnRedirect( this .mvcProperties == null || this .mvcProperties.isIgnoreDefaultModelOnRedirect()); return adapter; }
spring 通过读取所有所需要的 AutoConfiguration,就可以加载默认的上下文容器,实现自动注入。
SpringBoot常用注解简介
@Configuration
作用于类,用于定义配置类,可替换xml配置文件,被注解的类内部包含有一个或多个被@Bean注解的方法,这些方法会被AnnotationConfigApplicationContext或AnncationConfigWebApplicationContext类进行扫描,并用于构建bean定义,初始化spring容器。
@ComponentScan
该注解会扫描@Controller,@Service,@Repository,@component注解到类到spring容器中。
@SpringBootApplication
该注解包含了@ComponentScan注解,所以在使用中我们可以通过@SpringBootApplication注解的scanBasepackages属性进行配置。
@Conditional
该注解作用于类,它可以根据代码中的条件装载不同的bean,在设置注解之前类需要实现Condition接口,然后对该实现接口的类设置是否装载条件。
@Import
通过导入的方式实现吧实例加入spring容器中,可以在需要时间没有被spring管理的类导入至Spring容器中。
@ImportResource
和@Import类似,区别就是该注解导入的是配置文件。
Component
该注解是一个元注解,意思是可以注解其它类注解,如@Controller @Service @Repository。带此注解的类被看作组件,当使用基于注解的配置和类路径扫描的时候,这些类就会被实例化。
@SpringBootApplication
这个注解是Spring Boot最核心的注解,用在SpringBoot的主类上,标识这是一个Spring Boot应用。用来开启Spring Boot的各项能力,实际上这个注解@Configuration,@EnableAutoConfiguration,@ComponentScan三个注解的组合.由于这些注解一般都是一起使用的。
@EnableAutoConfiguration
允许Spring Boot自动配置注解,开启这个注解之后,Spring Boot就能根据当前类路径下的包或者类来配置Spring Bean。配置信息是从META-INF/spring.factories加载的。