Spring source code analysis 12: spring boot loading mechanism
1. spring-boot
Included modules
Before analyzing the loading mechanism of springboot, let’s take a look at the officialspring-boot
What modules are included and what are their uses.
Spring boot official warehouse
spring-boot
The core package of springboot, including:spring-framework
Packaging and expansion ofapplication.yaml
Loading mechanism, annotation starting mechanism, etcspring-boot-autoconfigure
:application.yaml
Configuration item definition forspring-boot-actuator
: health inspection, audit, statistics and monitoring of springspring-boot-actuator-autoconfigure
: for spring health inspection, audit, statistics and monitoringapplication.yaml
Configuration item supportspring-boot-cli
: command line execution supportspring-boot-devtools
: automatically refresh the application by listening to changes in class files during developmentspring-boot-starters/*
: springboot starter, the following projects are all dependency definitions without codespring-boot-tools/*
: springboot Kit
2. Application entrance
A packaged application has the following basic directory structure
- /
- BOOT-INF
-Classes # store application class files and resources resource files
-Lib # dependent jar
- META-INF
- MANIFEST. Startup configuration of MF # application
-Org / springframework / boot / loader / # spring boot loader code
We can generally usejava -jar app.jar
To start the application,MANIFEST.MF
Is to configure the startup of the application
# MANIFEST.MF
Manifest-Version: 1.0
Start-Class: com.example.demo.App
Spring-Boot-Classes: BOOT-INF/classes/
Spring-Boot-Lib: BOOT-INF/lib/
Spring-Boot-Version: 2.1.4.RELEASE
Main-Class: org.springframework.boot.loader.JarLauncher
For Java, the configuration here is onlyMain-Class
Is valid, that is, specify the entry class
3. JarLauncher
JarLauncher
The main function of is to start the application through jar
public class JarLauncher extends ExecutableArchiveLauncher {
//Entrance starting method
public static void main(String[] args) throws Exception {
new JarLauncher().launch(args);
}
}
Now let’s carefully decompose this class and take a look at the inheritance relationship first
- Launcher
- ExecutableArchiveLauncher
- JarLauncher
3.1. Launcher
Launcher
The main function of is to realize the basic startup mechanism
public abstract class Launcher {
//Start application
protected void launch(String[] args) throws Exception {
//Get class loader
ClassLoader classLoader = createClassLoader(getClassPathArchivesIterator());
//Get startup class
String launchClass = getMainClass();
//Start
launch(args, launchClass, classLoader);
}
//Start
protected void launch(String[] args, String launchClass, ClassLoader classLoader) throws Exception {
Thread.currentThread().setContextClassLoader(classLoader);
//Create runner, run
createMainMethodRunner(launchClass, args, classLoader).run();
}
//Create runner
protected MainMethodRunner createMainMethodRunner(String mainClass, String[] args, ClassLoader classLoader) {
return new MainMethodRunner(mainClass, args);
}
//Get startup class,由子类实现
protected abstract String getMainClass() throws Exception;
}
Let’s seeMainMethodRunner
How does it work
public class MainMethodRunner {
private final String mainClassName;
private final String[] args;
public MainMethodRunner(String mainClass, String[] args) {
this.mainClassName = mainClass;
this.args = (args != null) ? args.clone() : null;
}
public void run() throws Exception {
Class<?> mainClass = Class.forName(this.mainClassName, false, Thread.currentThread().getContextClassLoader());
//Get the main method of mainclass
Method mainMethod = mainClass.getDeclaredMethod("main", String[].class);
mainMethod.setAccessible(true);
//Call the main method
mainMethod.invoke(null, new Object[] { this.args });
}
}
3.2. ExecutableArchiveLauncher
ExecutableArchiveLauncher
The main function of is to execute archive files (jar, war)
public abstract class ExecutableArchiveLauncher extends Launcher {
//Startup class
private static final String START_CLASS_ATTRIBUTE = "Start-Class";
//Get startup class
@Override
protected String getMainClass() throws Exception {
//Get ` manifest. In archive file (jar, war) MF ` file
Manifest manifest = this.archive.getManifest();
String mainClass = null;
if (manifest != null) {
//Gets the class specified by start class as the start class
mainClass = manifest.getMainAttributes().getValue(START_CLASS_ATTRIBUTE);
}
if (mainClass == null) {
//Startup class不能为空
throw new IllegalStateException("No 'Start-Class' manifest entry specified in " + this);
}
return mainClass;
}
}
3.3. JarLauncher
JarLauncher
The main function of is to execute the jar file and specify the classpath
public class JarLauncher extends ExecutableArchiveLauncher {
//Can I load the following files
static final EntryFilter NESTED_ARCHIVE_ENTRY_FILTER = (entry) -> {
if (entry.isDirectory()) {
return entry.getName().equals("BOOT-INF/classes/");
}
return entry.getName().startsWith("BOOT-INF/lib/");
};
//"Boot-inf / classes /" and "boot-inf / lib / *. Jar" are loaded as Classpaths
@Override
protected boolean isNestedArchive(Archive.Entry entry) {
return NESTED_ARCHIVE_ENTRY_FILTER.matches(entry);
}
}
4. Start the application
Let’s look at a basic startup
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
SpringApplication
ofrun
Method to start a springboot application
public class SpringApplication {
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
//Specify the startup main source and operation parameters
return new SpringApplication(primarySources).run(args);
}
}
5. SpringApplication.run
public class SpringApplication {
public SpringApplication(Class<?>... primarySources) {
this(null, primarySources);
}
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
//Resource loader
this.resourceLoader = resourceLoader;
//Start primary source
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
//Get spring Components defined in the factories file that implement the bootstrapper interface
this.bootstrappers = new ArrayList<>(getSpringFactoriesInstances(Bootstrapper.class));
//Get spring The component defined in the factories file that implements the applicationcontextinitializer interface
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
//Get spring The component defined in the factories file that implements the applicationlistener interface
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
//Get the entry class file, such as com example. demo. App. class
this.mainApplicationClass = deduceMainApplicationClass();
}
//Run project
public ConfigurableApplicationContext run(String... args) {
//Create startup context
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
//Application context
ConfigurableApplicationContext context = null;
// ... Code omission
//Get spring Components defined in the factories file that implement the springapplicationrunlistener interface
SpringApplicationRunListeners listeners = getRunListeners(args);
//Start listener
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
//Application parameters
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//Prepare environment objects
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
//Initialize spring beaninfo. Ignore configuration
configureIgnoreBeanInfo(environment);
//When creating an application context, the annotation configservletwebserver ApplicationContext is used by default
context = createApplicationContext();
//Prepare application context
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
//Call the refresh method of context to refresh the context data
refreshContext(context);
// ... Code omission
//Start of application listening function call
listeners.started(context);
//Run application
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
// ... Code omission
}
try {
//Call the listening function of application running
listeners.running(context);
}
catch (Throwable ex) {
// ... Code omission
}
return context;
}
//Create startup context
private DefaultBootstrapContext createBootstrapContext() {
//The bootstrap default method in each component is used to initialize bootstrap default
DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();
this.bootstrappers.forEach((initializer) -> initializer.intitialize(bootstrapContext));
return bootstrapContext;
}
}
public class SpringApplication {
//Prepare environment objects
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
//Create a standard environment for spring Web
ConfigurableEnvironment environment = getOrCreateEnvironment();
//Configuration environment
configureEnvironment(environment, applicationArguments.getSourceArgs());
// ... Code omission
//Configure additionalprofiles
configureAdditionalProfiles(environment);
//Binding environment and Application
bindToSpringApplication(environment);
// ... Code omission
return environment;
}
//Configuration environment
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
//Add data conversion and formatting services
if (this.addConversionService) {
ConversionService conversionService = ApplicationConversionService.getSharedInstance();
environment.setConversionService((ConfigurableConversionService) conversionService);
}
//Configure the property source of the command line propertysources
configurePropertySources(environment, args);
}
}
public class SpringApplication {
//Prepare application context
private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
//Setting environment
context.setEnvironment(environment);
//Add bean name generator, resource loader and data conversion service
postProcessApplicationContext(context);
//Call the initialize method of the component in initializers
applyInitializers(context);
// ... Code omission
//Get bean factory
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
//Register springapplicationarguments singleton
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
// ... Code omission
//Get all sources
Set<Object> sources = getAllSources();
//Load resources under these sources
load(context, sources.toArray(new Object[0]));
// ... Code omission
}
//Load resources under these sources
protected void load(ApplicationContext context, Object[] sources) {
//Create beandefinitionloader
BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);
// ... Code omission
//Load resources
loader.load();
}
}
public class SpringApplication {
//Run application
private void callRunners(ApplicationContext context, ApplicationArguments args) {
List<Object> runners = new ArrayList<>();
//Application runner
runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
//Command line runner
runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
for (Object runner : new LinkedHashSet<>(runners)) {
if (runner instanceof ApplicationRunner) {
//Call applicationrunner Run method
callRunner((ApplicationRunner) runner, args);
}
if (runner instanceof CommandLineRunner) {
//Call commandlinerunner Run method
callRunner((CommandLineRunner) runner, args);
}
}
}
}
In this section, there are several points that need to be analyzed slowly
AnnotationConfigServletWebServerApplicationContext
How to manage applications and dataBeanDefinitionLoader.load
How to load resources
6. AnnotationConfigServletWebServerApplicationContext
AnnotationConfigServletWebServerApplicationContext
Is the default application context used by springboot
Let’s look at inheritance first
- GenericWebApplicationContext
- ServletWebServerApplicationContext
- AnnotationConfigServletWebServerApplicationContext
GenericWebApplicationContext
staySpring source code analysis II: context component (webapplicationcontext)We have already talked about it. Let’s have a look firstServletWebServerApplicationContext
6.1. ServletWebServerApplicationContext
ServletWebServerApplicationContext
Its main function is to start itself as an application service
public class ServletWebServerApplicationContext extends GenericWebApplicationContext
implements ConfigurableWebServerApplicationContext {
//Refresh application
@Override
protected void onRefresh() {
super.onRefresh();
//When refreshing, re create an application service
createWebServer();
}
//Create an application service
private void createWebServer() {
WebServer webServer = this.webServer;
ServletContext servletContext = getServletContext();
//Both application service and context are empty
if (webServer == null && servletContext == null) {
//Get the bean of servletwebserverfactory, with built-in implementation of tomcat, jetty, undertow and netty
ServletWebServerFactory factory = getWebServerFactory();
//When the application service is initialized, call self initialize
this.webServer = factory.getWebServer(getSelfInitializer());
// ... Code omission
}
//If there is a context object, call self initialize to initialize it
else if (servletContext != null) {
try {
getSelfInitializer().onStartup(servletContext);
}
catch (ServletException ex) {
// ... Code omission
}
}
// ... Code omission
}
}
public class ServletWebServerApplicationContext extends GenericWebApplicationContext
implements ConfigurableWebServerApplicationContext {
//Initialize application
private void selfInitialize(ServletContext servletContext) throws ServletException {
// ... Code omission
//Get the beans of all servletcontextinitializers and call the onstartup method of these components
for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
beans.onStartup(servletContext);
}
}
}
6.2. AnnotationConfigServletWebServerApplicationContext
AnnotationConfigServletWebServerApplicationContext
The main function of is to automatically scan the beans defined by annotations
public class AnnotationConfigServletWebServerApplicationContext extends ServletWebServerApplicationContext
implements AnnotationConfigRegistry {
//Annotation bean definition reader
private final AnnotatedBeanDefinitionReader reader;
//Classpath bean definition scanner
private final ClassPathBeanDefinitionScanner scanner;
//Registered annotation class component
private final Set<Class<?>> annotatedClasses = new LinkedHashSet<>();
//Packages to be scanned
private String[] basePackages;
public AnnotationConfigServletWebServerApplicationContext() {
//Initialize reader and scanner
this.reader = new AnnotatedBeanDefinitionReader(this);
this.scanner = new ClassPathBeanDefinitionScanner(this);
}
public AnnotationConfigServletWebServerApplicationContext(DefaultListableBeanFactory beanFactory) {
super(beanFactory);
//Initialize reader and scanner
this.reader = new AnnotatedBeanDefinitionReader(this);
this.scanner = new ClassPathBeanDefinitionScanner(this);
}
public AnnotationConfigServletWebServerApplicationContext(Class<?>... annotatedClasses) {
this();
//Register the bean and refresh the application context data
register(annotatedClasses);
refresh();
}
public AnnotationConfigServletWebServerApplicationContext(String... basePackages) {
this();
//Scan the package and refresh the application context data
scan(basePackages);
refresh();
}
}
public class AnnotationConfigServletWebServerApplicationContext extends ServletWebServerApplicationContext
implements AnnotationConfigRegistry {
//Register bean
@Override
public final void register(Class<?>... annotatedClasses) {
this.annotatedClasses.addAll(Arrays.asList(annotatedClasses));
}
//Scan package
@Override
public final void scan(String... basePackages) {
this.basePackages = basePackages;
}
//After the application context is refreshed, beans and scanning packages are loaded automatically
@Override
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
super.postProcessBeanFactory(beanFactory);
if (this.basePackages != null && this.basePackages.length > 0) {
this.scanner.scan(this.basePackages);
}
if (!this.annotatedClasses.isEmpty()) {
this.reader.register(ClassUtils.toClassArray(this.annotatedClasses));
}
}
}
7. BeanDefinitionLoader.load
In the above code, resource loading is a classBeanDefinitionLoader
Completed
class BeanDefinitionLoader {
//Load resources
void load() {
//Traversal loading
for (Object source : this.sources) {
load(source);
}
}
//Load a single resource
private void load(Object source) {
//Load resources under class
if (source instanceof Class<?>) {
loadClass((Class<?>) source);
return;
}
//Load resources of resource
if (source instanceof Resource) {
loadResource((Resource) source);
return;
}
//Load resources under package
if (source instanceof Package) {
loadPackage((Package) source);
return;
}
//Load resources represented by characters
if (source instanceof CharSequence) {
loadCharSequence((CharSequence) source);
return;
}
//Others cannot be loaded
throw new IllegalArgumentException("Invalid source type " + source.getClass());
}
}
class BeanDefinitionLoader {
//Load resources under class
private void loadClass(Class<?> source) {
//Loading annotations under classes
this.annotatedReader.register(source);
}
//Load resources of resource
private void loadResource(Resource source) {
//Load XML of resource
this.xmlReader.loadBeanDefinitions(source);
}
//Load resources under package
private void loadPackage(Package source) {
//Comments under scan package
this.scanner.scan(source.getName());
}
//Load resources represented by characters
private void loadCharSequence(CharSequence source) {
//Replace placeholder ${}
String resolvedSource = this.scanner.getEnvironment().resolvePlaceholders(source.toString());
//Load as a class. If successful, return
try {
load(ClassUtils.forName(resolvedSource, null));
return;
}
catch (IllegalArgumentException | ClassNotFoundException ex) {}
//Load as a resource. If successful, return
if (loadAsResources(resolvedSource)) {
return;
}
//Load as a package. If successful, return
Package packageResource = findPackage(resolvedSource);
if (packageResource != null) {
load(packageResource);
return;
}
//Others cannot be loaded
throw new IllegalArgumentException("Invalid source '" + resolvedSource + "'");
}
}
follow-up
More blogs, seehttps://github.com/senntyou/blogs
Author:Deep (@ senntyou)
Copyright notice: Free Reprint – non commercial – non derivative – keep signature(Creative sharing 3.0 License)