Spring Boot source code analysis (a)
sschrodinger
2019/05/28
Spring boot Introduction
Spring boot configuration using convention is larger than the idea of the default configuration of the application, reducing the amount of configuration time.
Spring Boot comprising the following characteristics:
- Create a Spring application can run independently
- Directly embedded Tomcat or Jetty server, do not need to deploy the WAR file
- Providing recommendations on the basis POM file to simplify the configuration of Apache Maven
- Possible to automatically configure the Spring framework in accordance with project dependencies
- It provides functionality that can be used directly in production environments, such as performance, application information and application health check
- No code generation, and no XML configuration file
By Spring Boot, create a new Spring application becomes very easy, and create a Spring application conform to the universal best practices. It requires only a few simple steps you can create a Web application. For creating spring boot, see spring official starter .
Spring boot loader
Because Spring boot built Jetty or Tomcat server, does not need to be deployed directly War file, the program starting spring boot for a normal main function, the main function of the following categories:
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
Seemingly ordinary procedure is no different, in fact, he was the most important step through annotations @SpringBootApplication
and methods SpringApplication.run()
completed.
In fact, all the dependencies in this step can be completed injection, the main steps are spring read all the dependence META-INF/spring.factories
file indicates which dependence can be automatically loaded, and in accordance with ImportSelector
load-dependent class selector which is loaded into IoC container .
META-INF/spring.factories
Read the file
The main function is only one way, i.e. SpringApplication.run()
, as follows:
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
run
The method is just a static method call is run(new Class<?>[] { primarySource }, args)
method. as follows:
public static ConfigurableApplicationContext run(Class<?>[] primarySources,
String[] args) {
return new SpringApplication(primarySources).run(args);
}
Wherein the configuration file read operation occurs in the SpringApplication
instantiation process class. Examples of the code is as follows:
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
this.webApplicationType = WebApplicationType.deduceFromClasspath();
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
In getSpringFactoriesInstances(ApplicationContextInitializer.class))
the spring spring.factories read the file.
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}
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
}
This method keeps all META-INF/spring.factories
caches SpringFactoriesLoader
class read read when needed.
to sum up
SpringFactoriesLoader
Static method in class implements read the file dependent, reading the names of all the fully qualified name of the class configuration
@SpringBootApplication comment
@SpringBootApplication
It is a complex annotation, as follows:
@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 {
// ...
}
Notes on behalf of a configuration notes, as follows:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {}
With this comment, we can put the @SpringBootApplication
notes as modified class configuration class to use, that is in a class DemoApplication
by @Bean
annotations modified code will be injected into the unified management by the IoC container Spring.
Annotations where three such packages automatically scans the various components is injected into the vessel allowed IoC management, so we can type DemoApplication
on the increase, such as @Controller
annotations, which as a Controller.
The most important thing is @EnableAutoConfiguration
comments.
In Spring, in fact, the same strain of ideas and ways of doing things annotated at the beginning of Enable, a quick overview that, with the support of @Import collected and registered bean definitions related to specific scene .
@EnableAutoConfiguration comment
@EnableAutoConfiguration
It is defined as follows:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage // 注解一
@Import(AutoConfigurationImportSelector.class) // 注解二
public @interface EnableAutoConfiguration {
// ...
}
@Import
@Import
Annotations can import three types:
-
@Configuration
Notes modified class -
ImportSelector
OrImportBeanDefinitionRegistrar
implementation class - Common component class, that is, from
@Componet
notes modified class.
ImportSelector
Interface is defined as follows:
public interface ImportSelector {
// 返回哪些类需要被创建
String[] selectImports(AnnotationMetadata importingClassMetadata);
}
The main role of the interface which is introduced in accordance with given selection criteria (usually one or more annotations attributes) @Configuration
class.
note
- If the subclass interface achieves the following four interface functions will first perform the following four interfaces:
- EnvironmentAware
- BeanFactoryAware
- BeanClassLoaderAware
- ResourceLoaderAware
- If you want all of
@Configuration
the import of such class are imported and then, using its sub-interfacesDeferredImportSelector
ImportSelector
Class BeanFactory.refresh()
calls the method, it should be specific to the BeanFactory.refresh()
function invokeBeanFactoryPostProcessors
, in particular see [the Spring source analysis (b) Core], invokeBeanFactoryPostProcessors
the main role is to register all PostProcessors
, i.e. bean post-processor, call before sigleton instantiated.
The main methods are as follows:
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
// ...
}
The following is PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors()
the code for the method, part of the code as follows:
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);
}
}
invokeBeanDefinitionRegistryPostProcessors
Postprocessor function class is defined for each application separately @Configure
resolved in this function. as follows:
// 从注册表中的配置类派生更多的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);
}
Entered the most crucial of class ConfigurationClassPostProcessor
, this class of users to register all @Configure
and @Bean
. His processConfigBeanDefinitions
function is as follows:
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();
}
}
Note that process
in the this.deferredImportSelectorHandler.process()
method:
public void parse(Set<BeanDefinitionHolder> configCandidates) {
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
try {
if (bd instanceof AnnotatedBeanDefinition) {
parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
}
else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
}
else {
parse(bd.getBeanClassName(), holder.getBeanName());
}
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
}
}
this.deferredImportSelectorHandler.process();
}
This is to achieve a processing deferredImportSelector
method interface. The method reads all class names meet the requirements and added to IoC framework.
This method reads a static class variables SpringFactoriesLoader.cache
to get all you need to load the fully qualified name of the class. Method stacks as follows:
selectImports:10, RoleImportSelector (com.example.demo.config)
/^\
process:889, ConfigurationClassParser$DefaultDeferredImportSelectorGroup (org.springframework.context.annotation)
/^\
getImports:875, ConfigurationClassParser$DeferredImportSelectorGrouping (org.springframework.context.annotation)
/^\
processGroupImports:801, ConfigurationClassParser$DeferredImportSelectorGroupingHandler (org.springframework.context.annotation)
/^\
process:771, ConfigurationClassParser$DeferredImportSelectorHandler (org.springframework.context.annotation)
/^\
parse:185, ConfigurationClassParser (org.springframework.context.annotation)
/^\
processConfigBeanDefinitions:315, ConfigurationClassPostProcessor (org.springframework.context.annotation)
/^\
postProcessBeanDefinitionRegistry:232, ConfigurationClassPostProcessor (org.springframework.context.annotation)
/^\
invokeBeanDefinitionRegistryPostProcessors:275, PostProcessorRegistrationDelegate (org.springframework.context.support)
/^\
invokeBeanFactoryPostProcessors:95, PostProcessorRegistrationDelegate (org.springframework.context.support)
/^\
invokeBeanFactoryPostProcessors:705, AbstractApplicationContext (org.springframework.context.support)
/^\
According to @EnableAutoConfiguration
the full class name org.springframework.boot.autoconfigure.EnableAutoConfiguration
as Key lookup, obtain a set of corresponding @Configuration
classes.
I.e., all of the search from the classpath META-INF/spring.factories
configuration files, and wherein org.springframework.boot.autoconfigure.EnableutoConfiguration
the corresponding configuration item corresponding instantiated as marked by reflection (Java Refletion) @Configuration
in the form of JavaConfig IoC container configuration class, and then aggregated and loaded onto a IoC container.
Reproduced in: https: //www.jianshu.com/p/e7a33e9eec0e