One, foreword
In the last article, we mainly introduced the @SpringBootApplication annotation for automatic configuration, so first let’s review what this annotation does for our SprngBoot project: We can summarize the key steps of automatic configuration and corresponding annotations as follows:
2.@Conditional——–>>>>>> Set automatic configuration condition dependency
3. @ EnableConfigurationProperties with @ ConfigurationProperties – > reads the configuration file is converted into a bean.
4.@EnableAutoConfiguration, @AutoConfigurationPackage, and @import -> implement bean discovery and loading.
Today, we will analyze its startup process through the source code.
This article is based on the 2.0.4.RELEASE version. To read this article, you need to have some basic knowledge of Java and the Spring Framework. If you do not know what Spring Boot is, it is recommended to read the Spring Boot tutorial on the official website first.
Spring Boot entry class
Above is the simplest generic entry class for Spring Boot. The entry class is required to be the first class under the topmost package that contains the main method, using the @SpringBootApplication annotation to enable the SpringBoot feature. Use the SpringApplication.run method to start the Spring Boot project.
First look at this class inside the run method call source:
The second parameter, args, is the application parameter passed to the application.
We instantiate a SpringApplication object with the main resource class and then call the run method of the object, so we analyze the startup source in two steps.
The instantiation process of SpringApplication
Follow the above run to the following method:
Go to SpringApplication and see the following source code:
As you can see from the above source code, the entire instantiation process has seven steps:
1. Resource initialization The resource loader is null
this.resourceLoader = resourceLoader;
Copy the code
2. Assert that the main loading resource class cannot be null; otherwise, an error is reported
Assert.notNull(primarySources, "PrimarySources must not be null");
Copy the code
3. Initialize the main load resource class set and deduplicate it
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
Copy the code
4. Infer the current WEB application type
this.webApplicationType = WebApplicationType.deduceFromClasspath();
Copy the code
Here’s the WebApplicationType method to look at the source code and the associated constructor:
public enum WebApplicationType {
/** * The application should not run as a web application and should not start an * embedded web server. */
NONE,
/** * The application should run as a servlet-based web application and should start an * embedded servlet web server. * /
SERVLET,
/** * The application should run as a reactive web application and should start an * embedded reactive web server. */
REACTIVE;
private static final String[] SERVLET_INDICATOR_CLASSES = { "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";
static WebApplicationType deduceFromClasspath(a) {
if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
for (String className : SERVLET_INDICATOR_CLASSES) {
if(! ClassUtils.isPresent(className,null)) {
returnWebApplicationType.NONE; }}return WebApplicationType.SERVLET;
}
static WebApplicationType deduceFromApplicationContext(Class
applicationContextClass) {
if (isAssignable(SERVLET_APPLICATION_CONTEXT_CLASS, applicationContextClass)) {
return WebApplicationType.SERVLET;
}
if (isAssignable(REACTIVE_APPLICATION_CONTEXT_CLASS, applicationContextClass)) {
return WebApplicationType.REACTIVE;
}
return WebApplicationType.NONE;
}
private static boolean isAssignable(String target, Class
type) {
try {
return ClassUtils.resolveClassName(target, null).isAssignableFrom(type);
}
catch (Throwable ex) {
return false; }}}Copy the code
Spring Boot 2 supports responsive programming. Spring Boot 2 supports responsive programming.
5. Set the application context initializer
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
Copy the code
Entering ApplicationContextInitializer, we can know the role of:
public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {
/**
* Initialize the given application context.
* @param applicationContext the application to configure
*/
void initialize(C applicationContext);
}
Copy the code
Used to initialize the specified Spring application context, such as registering property resources, enabling Profiles, and so on.
Look at setInitializers method source code, is actually initialize a ApplicationContextInitializer application context instance initializer collection.
public void setInitializers(Collection
> initializers) {
this.initializers = new ArrayList<>();
this.initializers.addAll(initializers);
}
Copy the code
Finally, we look at the core method getSpringFactoriesInstances its source code is as follows:
private <T> Collection<T> getSpringFactoriesInstances(Class
type, Class
[] parameterTypes, Object... args)
{
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
// Use names and ensure unique to protect against duplicates
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
Copy the code
Setting up an application context initializer can be done in five steps. Here is the core of instantiation:
5.1) Gets the current thread context classloader
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
Copy the code
5.2) to obtain ApplicationContextInitializer instance name collection and to heavy
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
Copy the code
The loadFactoryNames source code is as follows:
public static List<String> loadFactoryNames(Class
factoryClass, @Nullable ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
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 ?
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); }}Copy the code
According to the class path under the meta-inf/spring. The factories of all configuration file parsing and obtain ApplicationContextInitializer interface class path name.
Spring – the boot – autoconfigure – 2.0.4. RELEASE. The jar! / meta-INF /spring.factories
# Initializers org.springframework.context.ApplicationContextInitializer=\ org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\ org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener # Application Listeners org.springframework.context.ApplicationListener=\ org.springframework.boot.autoconfigure.BackgroundPreinitializer # Auto Configuration Import Listeners org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\ org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener # Auto Configuration Import Filters org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\ org.springframework.boot.autoconfigure.condition.OnClassCondition # Auto Configure ......Copy the code
5.3) Create a list of initializer instances based on the classpath above
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
Copy the code
5.4) Initializer instance list sorting
AnnotationAwareOrderComparator.sort(instances);
Copy the code
5.5) Return the instance object
return instances;
Copy the code
6. Set the listener
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
Copy the code
What does ApplicationListener do? The source code is as follows. (write another application of springboot2 listener when you are free)
@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
/**
* Handle an application event.
* @param event the event to respond to
*/
void onApplicationEvent(E event);
}
Copy the code
This interface extends the Java JDK java.util.EventListener interface and implements observer mode. It is generally used to define the event types of interest, restricted to subclasses of ApplicationEvent. This also inherits the JDK’s java.util.eventobject interface.
Setting the listener calls the same method as setting the initializer, except that the type passed in is different. Setting the listener’s interface type is: GetSpringFactoriesInstances, corresponding spring – the boot – autoconfigure – 2.0.4. The jar! The/meta-INF /spring.factories files are the Application Listeners
You can see from the file that there is currently only one BackgroundPreinitializer listener.
7. Infer the main entry application class
privateClass<? > deduceMainApplicationClass() {try {
StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
for (StackTraceElement stackTraceElement : stackTrace) {
if ("main".equals(stackTraceElement.getMethodName())) {
returnClass.forName(stackTraceElement.getClassName()); }}}catch (ClassNotFoundException ex) {
// Swallow and continue
}
return null;
}
Copy the code
By constructing a runtime exception, iterating through the name of the method in the exception stack, retrieving the stack frame with the method name main, retrieving the name of the entry class and returning that class.
Four,
Today, the main analysis of SpringBoot initialization instances of source code analysis, this chapter temporarily analyzed to: