😀 这里写文章的前言: 一个简单的开头,简述这篇文章讨论的问题、目标、人物、背景是什么?并简述你给出的答案。
可以说说你的故事:阻碍、努力、结果成果,意外与转折。
📝 SpringBoot 之 run 方法启动 SpringBoot 无配置式启动,大大的减轻了以前需要配置很多xml中的bean/aop/事物等. 只需要使用@SpringBootApplication 和 SpringApplication.run() 方法就可以轻松的跑起来了. 想要看怎么这么神奇,就需要看其run() 方法的执行操作.
run() 方法又包含了之前分析的 refresh 方法,但是这里的refresh 方法是要比之前的 单独分析 Spring 的 refresh方法的内容要多得多.
run方法整体 我们先看下 run 方法整体的流程. 其实也不难看出来, 也是new对象,调用方法. 但是人家可不仅仅是这二种就轻松搞定了的. 而且里面采用了大量的设计模式等知识.
从run 方法整体来看, 先是启动 StopWatch,然后获取出SpringApplicationRunListener对应的Listener,最后封装到SpringApplicationRunListeners这个Object类中, 接着调用这个类的 starting 方法,也就是启动方法.
再往下就是创建 DefaultApplicationArguments,准备环境,然后往System.setProperty给key为”spring.beaninfo.ignore” 设置ignore.toString() 方法进去. 打印Banner(也就是我们看到的SpringBoot,用很多杠杆给拼接起来的)
创建一个AnnotationConfigApplicationContext的Context(我个人觉得context是对beanFactory的一种扩展吧).
接着在 获取 SpringBootExceptionReporter 对应的信息(从META-INF/spring.factiories中获取),返回的是一个集合对象.
再就是为context进行准备.
接着就是调用refresh 方法,这个方法会加载beanPostProcssor,初始化bean等操作.
afterRefresh 就是创建完容器之后执行一些额外的操作
stopWatch.stop 就是打印启动计时等信息
再就是执行 listeners.started() 方法, org.springframework.boot.context.event.EventPublishingRunListener#started,也就是new一个ApplicationStartedEvent给传播出去,然后监听器拿到这个事件后,执行相应的逻辑处理关系.
callRunners 获取出 ApplicationRunner和CommandLineRunner,从beanFactory中获取出来,然后调用其run方法
接着就是执行 listeners.running()方法:
org.springframework.boot.context.event.EventPublishingRunListener#running,其内部就是new一个ApplicationReadyEvent事件给传播出去,然后相应的监听器拿到这个事件,就会去执行其内部的逻辑代码.
return 一个 context回去,也就是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); 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; }
最后是给 java.awt.headless 设置了默认值 headless = true, 所以也就是给 SYSTEM_PROPERTY_JAVA_AWT_HEADLESS 在System中设置上了true. 所以这个方法也没有特别重要的地方讲解
private void configureHeadlessProperty () { System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, System.getProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this .headless))); }
run 中 getRunListeners() 方法 方法依次往下执行的. 可以看出这个方法, 根据 SpringApplicationRunListener.class 从 META-INF/spring.factories 中获取并且实例化获取出来的结果. 然后放入到 SpringApplicationRunListeners 这个对象中. 从 META-INF/spring.factories中获取出来的数据,是有一个cache缓存的,也就是使用的Map.
private SpringApplicationRunListeners getRunListeners (String[] args) { Class<?>[] types = new Class <?>[] { SpringApplication.class, String[].class }; return new SpringApplicationRunListeners (logger, getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this , args)); } private <T> Collection<T> getSpringFactoriesInstances (Class<T> type, Class<?>[] parameterTypes, Object... args) { ClassLoader classLoader = getClassLoader(); Set<String> names = new LinkedHashSet <>(SpringFactoriesLoader.loadFactoryNames(type, classLoader)); List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); AnnotationAwareOrderComparator.sort(instances); return instances; } 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 factoryTypeName = ((String) entry.getKey()).trim(); for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) { 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); } }
实战一把 参考 org.springframework.boot.context.event.EventPublishingRunListener
先写一个类 :
public class GavinYangEventPublishingRunListener implements SpringApplicationRunListener , Ordered { private Logger LOGGER = LoggerFactory.getLogger(GavinYangEventPublishingRunListener.class); private SpringApplication application; private String[] args; public GavinYangEventPublishingRunListener () { } public GavinYangEventPublishingRunListener (SpringApplication application, String[] args) { this .application = application; this .args = args; System.out.println("调用到了 private final String[] args; 有参数构造方法" ); } @Override public int getOrder () { return 0 ; } @Override public void starting () { LOGGER.info("GavinYangEventPublishingRunListener 调用 starting 方法成功" ); } @Override public void started (ConfigurableApplicationContext context) { LOGGER.info("GavinYangEventPublishingRunListener 调用到了 started方法" ); } @Override public void running (ConfigurableApplicationContext context) { LOGGER.info("GavinYangEventPublishingRunListener 调用 running 方法成功" ); } }
然后在META-INF/spring.factories中配置下:
# Run Listenersorg.springframework.boot.SpringApplicationRunListener=\com.iyang.bootsourceread.listener.GavinYangEventPublishingRunListener
控制台的log输出顺序: 可以看到打印的参数顺序, 也就是先反射调用实例化, 然后接着调用 starting 方法,接着就是started 方法, 最后就是 running方法. 可以看到执行的一次顺序.
调用到了 private final String[] args; 有参数构造方法 15:47:56.498 [main] INFO com.iyang.bootsourceread.listener.GavinYangEventPublishingRunListener - GavinYangEventPublishingRunListener 调用 starting 方法成功
2020-07-02 15:47:57.069 INFO 4063 — [ main] .b.l.GavinYangEventPublishingRunListener : GavinYangEventPublishingRunListener 调用到了 started方法 2020-07-02 15:47:57.070 INFO 4063 — [ main] .b.l.GavinYangEventPublishingRunListener : GavinYangEventPublishingRunListener 调用 running 方法成功
具体的执行,可自行debug进去看,是怎么具体走到哪步的. 与EventPublishingRunListener不同的是, 我们这里是没有new什么事件丢给监听器的. 就是简单的打印了几句log.
run 中 starting() 方法 org.springframework.boot.SpringApplicationRunListeners#starting
该方法就是直接走 SpringApplicationRunListeners中的starting方法. getRunListeners方法最后返回的是SpringApplicationRunListeners,该类中是包装了 我们SpringApplicationRunListener.class对应的Listener的.
然后这里就会迭代每个 listener 并且会调用到 starting 放. 这个结果在上面的方法小实战中,是可以很明显的体会到的.
void starting() { for (SpringApplicationRunListener listener : this.listeners) { listener.starting(); } }
run 中 prepareEnvironment() 方法 可以看到这该方法最后处理的都是和 Environment有关的.
private ConfigurableEnvironment prepareEnvironment (SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) { ConfigurableEnvironment environment = getOrCreateEnvironment(); configureEnvironment(environment, applicationArguments.getSourceArgs()); ConfigurationPropertySources.attach(environment); listeners.environmentPrepared(environment); bindToSpringApplication(environment); if (!this .isCustomEnvironment) { environment = new EnvironmentConverter (getClassLoader()).convertEnvironmentIfNecessary(environment, deduceEnvironmentClass()); } ConfigurationPropertySources.attach(environment); return environment; }
run 中 createApplicationContext() 方法 创建 ApplicationCntext, 该方法可以看到 根据 this.webApplicationType来创建 ApplicationContext的class的,然后使用BeanUtils.instantiateClass() 方法来实例化这个bean. 这里还是很好理解的,就是创建一个 ApplicationContext. 最后返回这个 context给赋值到 context 这个参数.
protected ConfigurableApplicationContext createApplicationContext () { Class<?> contextClass = this .applicationContextClass; if (contextClass == null ) { try { switch (this .webApplicationType) { case SERVLET: contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS); break ; case REACTIVE: contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS); break ; default : contextClass = Class.forName(DEFAULT_CONTEXT_CLASS); } } catch (ClassNotFoundException ex) { throw new IllegalStateException ( "Unable create a default ApplicationContext, please specify an ApplicationContextClass" , ex); } } return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass); }
run 中 getSpringFactoriesInstances(SpringBootExceptionReporter.class…) 方法 之前阅读到的 getRunListeners方法,其内部也是走了 getSpringFactoriesInstances 这个方法,然后这里也是根据SpringBootExceptionReporter.class来获取出相应的集合数据.
所以这里直接说,返回的集合中,如果没有进行扩展的话,那么这里就只有 FailureAnalyzers这一个.
org.springframework.boot.diagnostics.FailureAnalyzers
如果是有异常的话,就会放入到 SpringBootExceptionReporter 这个里,由其子类给打印出来. Reporter这个意思还是很好理解的.
private <T> Collection<T> getSpringFactoriesInstances (Class<T> type, Class<?>[] parameterTypes, Object... args) { ClassLoader classLoader = getClassLoader(); Set<String> names = new LinkedHashSet <>(SpringFactoriesLoader.loadFactoryNames(type, classLoader)); List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); AnnotationAwareOrderComparator.sort(instances); return instances; }
run 中 prepareContext() 方法 可以看到这个方法基本都是一直在操作 context,先是给context设置environment.
然后判断internalConfigurationBeanNameGenerator要不要注册到 beanFactory中去. 如果this.resourceLoader不是null的话,就会分别设置ResourceLoader/ClassLoader(看具体的值),给ConversionService set 到beanFactory中去.
调用 ApplicationContextInitializer 的 initialize 方法.
listeners.contextPrepared(context) : 广播一个 ApplicationContextInitializedEvent 出去
注册一个springApplicationArguments到 beanFactory中去. printedBanner 不是null的话,也注册到beanFactory中去. 如果beanFactory是DefaultListableBeanFactory的话,就this.allowBeanDefinitionOverriding赋值为false
如果是有lazyInitialization,就会添加一个LazyInitializationBeanFactoryPostProcessor到context中去.
getAllSources() 这里获取出来的就是只有我们的启动类,对获取出来的集合进行非空判断.
最后广播一个 ApplicationPreparedEvent 出去. 广播出去之前会有对listener都添加到 context中,如果是ApplicationContextAware的话,就会再调用一个 setApplicationContext 方法.
private void prepareContext (ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) { context.setEnvironment(environment); postProcessApplicationContext(context); applyInitializers(context); listeners.contextPrepared(context); if (this .logStartupInfo) { logStartupInfo(context.getParent() == null ); logStartupProfileInfo(context); } ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); beanFactory.registerSingleton("springApplicationArguments" , applicationArguments); if (printedBanner != null ) { beanFactory.registerSingleton("springBootBanner" , printedBanner); } if (beanFactory instanceof DefaultListableBeanFactory) { ((DefaultListableBeanFactory) beanFactory) .setAllowBeanDefinitionOverriding(this .allowBeanDefinitionOverriding); } if (this .lazyInitialization) { context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor ()); } Set<Object> sources = getAllSources(); Assert.notEmpty(sources, "Sources must not be empty" ); load(context, sources.toArray(new Object [0 ])); listeners.contextLoaded(context); }
run.refreshContext() 方法 最后是走到了 org.springframework.context.support.AbstractApplicationContext#refresh 这里,也就是走到refresh方法.
run -> listeners.started(context) 方法 这里可以看到是广播了一个 ApplicationStartedEvent 事件出去.
于是后面又广播了一个 AvailabilityChangeEvent 事件出去.
void started (ConfigurableApplicationContext context) { for (SpringApplicationRunListener listener : this .listeners) { listener.started(context); } } ---------------------- org.springframework.boot.context.event.EventPublishingRunListener#started @Override public void started (ConfigurableApplicationContext context) { context.publishEvent(new ApplicationStartedEvent (this .application, this .args, context)); AvailabilityChangeEvent.publish(context, LivenessState.CORRECT); }
run.callRunners() 方法 该方法可以看到是, 分别获取出 ApplicationRunner 和 CommandLineRunner, 然后调用callRunner方法,该方法其内部也是去调用的run方法.
private void callRunners (ApplicationContext context, ApplicationArguments args) { List<Object> runners = new ArrayList <>(); runners.addAll(context.getBeansOfType(ApplicationRunner.class).values()); runners.addAll(context.getBeansOfType(CommandLineRunner.class).values()); AnnotationAwareOrderComparator.sort(runners); for (Object runner : new LinkedHashSet <>(runners)) { if (runner instanceof ApplicationRunner) { callRunner((ApplicationRunner) runner, args); } if (runner instanceof CommandLineRunner) { callRunner((CommandLineRunner) runner, args); } } }
run -> listeners.running(context) 方法 可以看到 running是分别广播了二个事件出去. 然后相应的监听器分别获取到这个event,并且进行相应的逻辑处理.
走完该方法,如果是没有出现任何异常的话,就会返回一个context,所以该run方法也就是走到这里就算走完了.
void running (ConfigurableApplicationContext context) { for (SpringApplicationRunListener listener : this .listeners) { listener.running(context); } } @Override public void running (ConfigurableApplicationContext context) { context.publishEvent(new ApplicationReadyEvent (this .application, this .args, context)); AvailabilityChangeEvent.publish(context, ReadinessState.ACCEPTING_TRAFFIC); }
🤗 总结归纳 📎 参考文章
💡 有关文章的问题,欢迎您在底部评论区留言,一起交流~