个人博客导航页(点击右侧链接即可打开个人博客):大牛带你入门技术栈
SpringBoot自启动源码分析
项目启动代码
public static void main(String[] args) { // springboot项目启动方式 SpringApplication.run(DemoApplication.class, args); }
springApplication.java
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) { // 调用run方法 return run(new Class<?>[] { primarySource }, args); }
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) { // 最终创建了一个SpringApplication对象 return new SpringApplication(primarySources).run(args); }
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { this.resourceLoader = resourceLoader; // 必须传一个类 Assert.notNull(primarySources, "PrimarySources must not be null"); // 用一个Set集合存储我们的启动类 this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); // webApplication的枚举类型 // 主要判断我们启动的类型是什么,一般我们启动的是servlet this.webApplicationType = WebApplicationType.deduceFromClasspath(); // 初始化一些应用上下文 // 加载springboot包下的/META-INF/spring.facotries中的8个类 setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); // 初始化监听器 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); // 通过类加载器启动我们的类 // Class.forName() this.mainApplicationClass = deduceMainApplicationClass(); }
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
我们来看看初始化应用上下文中做了什么
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) { // 获取类加载器,我们传入的ApplicationContextInitializer.class ClassLoader classLoader = getClassLoader(); // Use names and ensure unique to protect against duplicates // 这个方法重点 // 这个方法里会加载spring.factories文件里的东西 Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader)); // 实例化对象 List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); AnnotationAwareOrderComparator.sort(instances); return instances; }
SpringFactoriesLoader.loadFactoryNames(type, classLoader)
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) { // 初始化监听器的时候就会直接取了 MultiValueMap<String, String> result = cache.get(classLoader); if (result != null) { return result; } try { // 扫描我们类路径下META-INF/spring.factories文件 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 factoryTypeName = ((String) entry.getKey()).trim(); for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) { // 添加到map中 // 这里map的映射关系是 接口-> 实现类 result.add(factoryTypeName, factoryImplementationName.trim()); } } } cache.put(classLoader, result); return result; } catch (IOException ex) { throw new IllegalArgumentException("Unable to load factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex); } }
// 这个方法进行实例化 private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args, Set<String> names) { List<T> instances = new ArrayList<>(names.size()); // 获取该类加载器下对应的实现类 // 我们可以手动实现ApplicationInitialzer和Listener for (String name : names) { try { Class<?> instanceClass = ClassUtils.forName(name, classLoader); Assert.isAssignable(type, instanceClass); Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes); T instance = (T) BeanUtils.instantiateClass(constructor, args); instances.add(instance); } catch (Throwable ex) { throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex); } } return instances; }
通过查看源码可知,如果要加载自己的类也可以通过自己建一个/META-INF/spring.factories文件
# PropertySource Loaders com.example.demo.service.NewService=\ com.example.demo.service.NewServiceImpl org.springframework.context.ApplicationContextInitializer=\ com.example.demo.service.TextApplicationInitializer
springApplication对象建好以后就是调用我们的run方法了
public ConfigurableApplicationContext run(String... args) { // 秒表 StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>(); configureHeadlessProperty(); SpringApplicationRunListeners listeners = getRunListeners(args); // 启动监听,SpringApplicationRunListeners listeners.starting(); try { ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); // 配置环境 ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); configureIgnoreBeanInfo(environment); // 打印图标 Banner printedBanner = printBanner(environment); // 默认加载AnnotationConfigServletWebServerApplicationContext.class context = createApplicationContext(); exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[] { ConfigurableApplicationContext.class }, context); // 这里面调用了applyInitializers()方法 执行了 ApplicationContextInitialzer的initialize()方法 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; }
springboot配置
附录
1.WebApplicationType枚举
public enum WebApplicationType { /** * 该应用程序不应作为Web应用程序运行,也不应启动嵌入式Web服务器。 */ NONE, /** * 该应用程序应作为基于Servlet的Web应用程序运行,并应启动嵌入式Servlet Web服务器。 */ SERVLET, /** * 该应用程序应作为反应式Web应用程序运行,并应启动嵌入式反应式Web服务器。 */ 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"; }
2.SpringApplicationBanner
通过类字段可以看到yml文件中配置banner的路径,以及将我们要展示的banner的文件名字取为banner.txt
static final String BANNER_LOCATION_PROPERTY = "spring.banner.location"; static final String BANNER_IMAGE_LOCATION_PROPERTY = "spring.banner.image.location"; static final String DEFAULT_BANNER_LOCATION = "banner.txt"; static final String[] IMAGE_EXTENSION = { "gif", "jpg", "png" };
// 指定Banner.Mode的方式 enum Mode { /** * Disable printing of the banner. */ OFF, /** * Print the banner to System.out. */ CONSOLE, /** * Print the banner to the log file. */ LOG }
附Java/C/C++/机器学习/算法与数据结构/前端/安卓/Python/程序员必读/书籍书单大全:
(点击右侧 即可打开个人博客内有干货):技术干货小栈
=====>>①【Java大牛带你入门到进阶之路】<<====
=====>>②【算法数据结构+acm大牛带你入门到进阶之路】<<===
=====>>③【数据库大牛带你入门到进阶之路】<<=====
=====>>④【Web前端大牛带你入门到进阶之路】<<====
=====>>⑤【机器学习和python大牛带你入门到进阶之路】<<====
=====>>⑥【架构师大牛带你入门到进阶之路】<<=====
=====>>⑦【C++大牛带你入门到进阶之路】<<====
=====>>⑧【ios大牛带你入门到进阶之路】<<====
=====>>⑨【Web安全大牛带你入门到进阶之路】<<=====
=====>>⑩【Linux和操作系统大牛带你入门到进阶之路】<<=====天下没有不劳而获的果实,望各位年轻的朋友,想学技术的朋友,在决心扎入技术道路的路上披荆斩棘,把书弄懂了,再去敲代码,把原理弄懂了,再去实践,将会带给你的人生,你的工作,你的未来一个美梦。