Spring Boot启动过程源码分析
来源:互联网 发布:柯洁评论黑嘉嘉 知乎 编辑:程序博客网 时间:2024/06/05 00:13
转载自:http://blog.csdn.net/dm_vincent/article/details/76735888
http://blog.csdn.net/dm_vincent/article/details/77151122
Spring Boot的启动始于:
然后设置初始化器,会从类路径的META-INF/spring.factories处读取相应配置文件,读取配置文件中Key为:org.springframework.context.ApplicationContextInitializer的value。以spring-boot这个包为例,它的META-INF/spring.factories部分定义如下所示:
之后设置监听器,同上读取配置文件中Key为:org.springframework.context. ApplicationListener的value,然后加载实例化后,设置在listeners中。
然后设置mainApplicationClass:
完成了实例化,下面开始调用run方法:
上下文后置处理afterRefresh()方法:
基于上面的了解,我们就可以在Spring Boot启动添加自定义的初始化器、监听器等。
添加定制化的初始化器:
添加定制化的监听器:
添加定制化的后置Runners:
启动打印Banner:
扩展的方式也很简单,看源代码就清楚该怎么扩展了:
http://blog.csdn.net/dm_vincent/article/details/77151122
Spring Boot的启动始于:
SpringApplication.run(DemoApplication.class, args);相应实现:
public static ConfigurableApplicationContext run(Object source, String... args) {return run(new Object[] { source }, args);}
public static ConfigurableApplicationContext run(Object[] sources, String[] args) {return new SpringApplication(sources).run(args);}它实际上会构造一个SpringApplication的实例,然后运行它的run方法:
public SpringApplication(Object... sources) {initialize(sources);}
private void initialize(Object[] sources) {if (sources != null && sources.length > 0) {this.sources.addAll(Arrays.asList(sources));}this.webEnvironment = deduceWebEnvironment();setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));this.mainApplicationClass = deduceMainApplicationClass();}deduceWebEnvironment()是推断应用类型是Standard还是Web,方法是在当类路径中找是否存在"javax.servlet.Servlet","org.springframework.web.context.ConfigurableWebApplicationContext"的类来判断的。
然后设置初始化器,会从类路径的META-INF/spring.factories处读取相应配置文件,读取配置文件中Key为:org.springframework.context.ApplicationContextInitializer的value。以spring-boot这个包为例,它的META-INF/spring.factories部分定义如下所示:
# Application Context Initializersorg.springframework.context.ApplicationContextInitializer=\org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\org.springframework.boot.context.ContextIdApplicationContextInitializer,\org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\org.springframework.boot.context.embedded.ServerPortInfoApplicationContextInitializer因此这几个类名会实例化出来放在SpringApplication 的initializers中。
之后设置监听器,同上读取配置文件中Key为:org.springframework.context. ApplicationListener的value,然后加载实例化后,设置在listeners中。
# Application Listenersorg.springframework.context.ApplicationListener=\org.springframework.boot.ClearCachesApplicationListener,\org.springframework.boot.builder.ParentContextCloserApplicationListener,\org.springframework.boot.context.FileEncodingApplicationListener,\org.springframework.boot.context.config.AnsiOutputApplicationListener,\org.springframework.boot.context.config.ConfigFileApplicationListener,\org.springframework.boot.context.config.DelegatingApplicationListener,\org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener,\org.springframework.boot.logging.ClasspathLoggingApplicationListener,\org.springframework.boot.logging.LoggingApplicationListener
然后设置mainApplicationClass:
private Class<?> deduceMainApplicationClass() {try {StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();for (StackTraceElement stackTraceElement : stackTrace) {if ("main".equals(stackTraceElement.getMethodName())) {return Class.forName(stackTraceElement.getClassName());}}}catch (ClassNotFoundException ex) {// Swallow and continue}return null;}它通过构造一个运行时异常,通过异常栈中方法名为main的栈帧来得到入口类的名字。至此,对于SpringApplication实例的初始化过程就结束了。
完成了实例化,下面开始调用run方法:
public ConfigurableApplicationContext run(String... args) { // 计时工具StopWatch stopWatch = new StopWatch();stopWatch.start();ConfigurableApplicationContext context = null;FailureAnalyzers analyzers = null; // 设置java.awt.headless系统属性为true - 没有图形化界面configureHeadlessProperty();SpringApplicationRunListeners listeners = getRunListeners(args);listeners.starting();try { ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); ConfigurableEnvironment environment = prepareEnvironment(listeners,applicationArguments); // Banner打印器,就是启动Spring Boot的时候打印在console上的 Banner printedBanner = printBanner(environment); // 创建Spring上下文 context = createApplicationContext(); analyzers = new FailureAnalyzers(context); // Spring上下文前置处理 prepareContext(context, environment, listeners, applicationArguments,printedBanner); refreshContext(context); // Spring上下文后置处理 afterRefresh(context, applicationArguments); listeners.finished(context, null); stopWatch.stop(); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass) .logStarted(getApplicationLog(), stopWatch); } return context;}catch (Throwable ex) {handleRunFailure(context, listeners, analyzers, ex);throw new IllegalStateException(ex);}}createApplicationContext()即创建Spring的主要组件ApplicationContext,对于Web应用,上ApplicationContext就是AnnotationConfigEmbeddedWebApplicationContext。refreshContext(context)即刷新ApplicationContext:
private void refreshContext(ConfigurableApplicationContext context) {refresh(context);if (this.registerShutdownHook) {try {context.registerShutdownHook();}catch (AccessControlException ex) {// Not allowed in some environments.}}}最终会调用到我们熟知的AbstractApplicationContext的refresh()方法。
上下文后置处理afterRefresh()方法:
protected void afterRefresh(ConfigurableApplicationContext context,ApplicationArguments args) {callRunners(context, args);}private void callRunners(ApplicationContext context, ApplicationArguments args) {List<Object> runners = new ArrayList<Object>();runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());AnnotationAwareOrderComparator.sort(runners);for (Object runner : new LinkedHashSet<Object>(runners)) {if (runner instanceof ApplicationRunner) {callRunner((ApplicationRunner) runner, args);}if (runner instanceof CommandLineRunner) {callRunner((CommandLineRunner) runner, args);}}}所谓的后置操作,就是在容器完成刷新后,依次调用注册的Runners。Runners可以是两个接口的实现类:org.springframework.boot.ApplicationRunner与org.springframework.boot.CommandLineRunner。
基于上面的了解,我们就可以在Spring Boot启动添加自定义的初始化器、监听器等。
添加定制化的初始化器:
public static void main(String[] args) { // 创建SpringApplication的实例 SpringApplication app = new SpringApplication(Application.class); // 添加定制的初始化器 app.addInitializers(new CustomApplicationContextInitializer()); // 执行SpringApplication实例的run方法 app.run(args);}
public class CustomApplicationContextInitializer implements ApplicationContextInitializer { private static final Logger LOGGER = LoggerFactory.getLogger(CustomApplicationContextInitializer.class); @Override public void initialize(ConfigurableApplicationContext applicationContext) { LOGGER.info("自定义的初始化器的initialize方法被执行了"); }}
添加定制化的监听器:
app. addListeners(new CustomApplicationListener());
public class CustomApplicationListener implements ApplicationListener { private static final Logger LOGGER = LoggerFactory.getLogger(CustomApplicationListener.class); public void onApplicationEvent(ApplicationEvent event) { // 监听ApplicationStartingEvent if (event instanceof ApplicationStartedEvent) { logInfo("ApplicationStartedEvent listened"); } // 监听ApplicationEnvironmentPreparedEvent else if (event instanceof ApplicationEnvironmentPreparedEvent) { logInfo("ApplicationEnvironmentPreparedEvent listened"); } // 监听ApplicationPreparedEvent else if (event instanceof ApplicationPreparedEvent) { logInfo("ApplicationPreparedEvent listened"); } // 监听ApplicationReadyEvent else if (event instanceof ApplicationReadyEvent) { logInfo("ApplicationReadyEvent listened"); } // 监听ApplicationFailedEvent else if (event instanceof ApplicationFailedEvent) { logInfo("ApplicationFailedEvent listened"); } } private void logInfo(String log) { if (LOGGER.isInfoEnabled()) { LOGGER.info(log); } }}
添加定制化的后置Runners:
@Component@Order(2)public class CustomApplicationRunner implements ApplicationRunner { private static final Logger LOGGER = LoggerFactory.getLogger(CustomApplicationRunner.class); @Override public void run(ApplicationArguments arg) { if (LOGGER.isInfoEnabled()) { LOGGER.info("自定义ApplicationRunner运行了"); } }}
@Component@Order(1)public class CustomCommandLineRunner implements CommandLineRunner { private static final Logger LOGGER = LoggerFactory.getLogger(CustomCommandLineRunner.class); @Override public void run(String... strings) throws Exception { if (LOGGER.isInfoEnabled()) { LOGGER.info("自定义CommandLineRunner运行了"); } }}自定义Runner的添加方式和上述的初始化器以及监听器有点不同,就是Runners直接通过@Component注解添加即可,借助于包扫描机制,它们会被注册到容器中。至于它们运行的顺序,则可以通过上述的@Order注解来完成标注,数值越小的Runner优先级越高,因为源码中有AnnotationAwareOrderComparator.sort(runners),因此上面代码中,CustomCommandLineRunner将先于CustomApplicationRunner被执行。
启动打印Banner:
扩展的方式也很简单,看源代码就清楚该怎么扩展了:
private Banner printBanner(ConfigurableEnvironment environment) {if (this.bannerMode == Banner.Mode.OFF) {return null;}ResourceLoader resourceLoader = this.resourceLoader != null ? this.resourceLoader: new DefaultResourceLoader(getClassLoader());SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter(resourceLoader, this.banner);if (this.bannerMode == Mode.LOG) {return bannerPrinter.print(environment, this.mainApplicationClass, logger);}return bannerPrinter.print(environment, this.mainApplicationClass, System.out);}
阅读全文
0 0
- Spring Boot启动过程源码分析
- [Spring Boot] 1. Spring Boot启动过程源码分析
- Spring Boot启动过程源码分析(二)事件监听器
- U-Boot的启动过程源码分析
- u-boot启动过程分析(源码)
- U-Boot启动过程源码分析(3)-启动Linux
- spring boot启动过程
- spring源码分析-应用启动过程
- Spring Boot 源码分析
- U-Boot 启动过程和源码分析(第一阶段)
- U-Boot 启动过程和源码分析(第二阶段)
- U-Boot启动过程源码分析(1)-第一阶段
- U-Boot启动过程源码分析(2)-第二阶段
- Spring boot源码分析-SpringApplication启动(1)
- 【Spring MVC】Spring MVC启动过程源码分析
- 【Spring启动过程分析】(2)源码分析
- Spring源码-启动过程
- u-boot启动过程分析
- Angular2 创建与使用Observable
- 数据结构实验之二叉树二:遍历二叉树
- MyBatis的foreach语句详解
- 2017多校6 1003 Inversion
- 排序之冒泡排序
- Spring Boot启动过程源码分析
- Ruby操作MongoDB数据库(进阶十二)--GridFS
- 【POJ3252】 Round Numbers
- Accurate Single Stage Detector Using Recurrent Rolling Convolution
- UVA-10881 Piotr's Ants
- 微信开发——3、微信接入(javaweb)
- SQL查询效率:100w数据查询只需要1秒钟
- [P1349]广义斐波那契数列
- 一起写atom插件(1)——写个简单的插件