2019独角兽企业重金招聘Python工程师标准>>>

  • tag: spring 学习笔记
  • date: 2018-03

spring 是什么?spring 核心是应用组件容器,管理组件生命周期,依赖关系,并提倡面向接口编程实现模块间松耦合。
spring boot 是什么?spring boot 是按特定(约定)方式使用 spring 及相关程序库以简化应用开发的一套框架和工具。
以下统称 spring。
本文使用 spring boot 2.0.0.RELEASE 测试。

ApplicationRunner

spring 广泛应用于 web 应用开发,使用 spring 开发命令行工具、后台服务等通用程序也非常方便。
开发 web 应用时,web 服务器(如 tomcat)启动后即开始监听请求。
开发命令行工具时,只需要实现一个 ApplicationRunner,spring 容器启动后即自动执行之。
如开发一个查看文件大小的示例程序 atest.filesize.App,代码如下:

public class App implements ApplicationRunner {public static void main(String[] args) {SpringApplication app = new SpringApplication(App.class);app.setBannerMode(Banner.Mode.OFF);app.run(args);}@Overridepublic void run(ApplicationArguments args) throws Exception {List<String> fileList = args.getNonOptionArgs();Validate.isTrue(!fileList.isEmpty(), "missing file");Validate.isTrue(fileList.size() == 1, "require only one file, got: %s", fileList);String path = fileList.get(0);File file = new File(path);if (!file.exists()) {throw new FileNotFoundException(path);}long size = file.length();System.out.println(size);}}
  • ApplicationArguments 是 spring boot 解析后的命令行参数。
    如果需要原始命令行参数,可以调用 args.getSourceArgs(),或使用 CommandLineRunner
  • spring 容器生命周期即应用生命周期,spring boot 默认注册了 spring 容器 shutdown hook,jvm 退出时会自动关闭 spring 容器。
    当然也可以手动关闭 spring 容器,这时会自动移除注册的 shutdown hook。

程序退出码

程序退出时通常返回非 0 退出码表示错误(或非正常结束),方便 shell 脚本等自动化检查控制。
命令行下运行应用并查看退出码:

mvn compile dependency:build-classpath -Dmdep.outputFile=target/cp.txt
java -cp "target/classes/:$(cat target/cp.txt)" atest.filesize.App ; echo "exit code: ${?}"

可看到主线程抛出异常时,java 进程默认返回非 0 退出码(默认为 1)。
ApplicationRunner 在主线程中执行,异常堆栈如下:

java.lang.IllegalStateException: Failed to execute ApplicationRunnerat org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:784)at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:771)at org.springframework.boot.SpringApplication.run(SpringApplication.java:335)at atest.filesize.App.main(App.java:18)
Caused by: java.lang.IllegalArgumentException: missing fileat org.apache.commons.lang3.Validate.isTrue(Validate.java:155)at atest.filesize.App.run(App.java:24)at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:781)... 3 common frames omitted

spring boot 主线程中处理异常时,SpringBootExceptionHandler 默认将自己设置为线程 UncaughtExceptionHandler,
其检查发现已经打印了异常日志,因此不再打印异常到 stderr。

程序异常映射为退出码

主线程发生异常时还可以自定义设置退出码:

  • 配置 ExitCodeExceptionMapper 可将主线程产生的异常映射为退出码。
  • 此时还会调用 "getExitCodeFromExitCodeGeneratorException()" 检查异常本身是否为 ExitCodeGenerator。
    但 SpringApplication.exit() 没有这个逻辑,为保持一致性,不建议使用此类异常(?)。
  • 定义好异常和退出码规范,可方便实现自动化检查控制。

将异常映射为退出码,示例代码如下:

public class AppExitCodeExceptionMapper implements ExitCodeExceptionMapper {@Overridepublic int getExitCode(Throwable exception) {return 2;}}
  • 上述简单示例将所有 Throwable 映射为退出码 2。

分析 spring 相关代码。

查看主线程 handleRunFailure 调用栈:

Thread [main](Suspended)SpringApplication.getExitCodeFromMappedException(ConfigurableApplicationContext, Throwable) line: 881    SpringApplication.getExitCodeFromException(ConfigurableApplicationContext, Throwable) line: 866    SpringApplication.handleExitCode(ConfigurableApplicationContext, Throwable) line: 852    SpringApplication.handleRunFailure(ConfigurableApplicationContext, SpringApplicationRunListeners, Collection<SpringBootExceptionReporter>, Throwable) line: 803    SpringApplication.run(String...) line: 338    App.main(String[]) line: 29    

SpringApplication 方法:

    private void handleExitCode(ConfigurableApplicationContext context,Throwable exception) {int exitCode = getExitCodeFromException(context, exception);if (exitCode != 0) {if (context != null) {context.publishEvent(new ExitCodeEvent(context, exitCode));}SpringBootExceptionHandler handler = getSpringBootExceptionHandler();if (handler != null) {handler.registerExitCode(exitCode);}}}private int getExitCodeFromException(ConfigurableApplicationContext context,Throwable exception) {int exitCode = getExitCodeFromMappedException(context, exception);if (exitCode == 0) {exitCode = getExitCodeFromExitCodeGeneratorException(exception);}return exitCode;}private int getExitCodeFromMappedException(ConfigurableApplicationContext context,Throwable exception) {if (context == null || !context.isActive()) {return 0;}ExitCodeGenerators generators = new ExitCodeGenerators();Collection<ExitCodeExceptionMapper> beans = context.getBeansOfType(ExitCodeExceptionMapper.class).values();generators.addAll(exception, beans);return generators.getExitCode();}

ExitCodeGenerators 方法:

    public void add(Throwable exception, ExitCodeExceptionMapper mapper) {Assert.notNull(exception, "Exception must not be null");Assert.notNull(mapper, "Mapper must not be null");add(new MappedExitCodeGenerator(exception, mapper));}

spring boot 获取退出码并注册到 SpringBootExceptionHandler,
其将自己设置为线程 UncaughtExceptionHandler,退出码非 0 时调用 System.exit() 退出进程。

代码 SpringBootExceptionHandler.LoggedExceptionHandlerThreadLocal

        @Overrideprotected SpringBootExceptionHandler initialValue() {SpringBootExceptionHandler handler = new SpringBootExceptionHandler(Thread.currentThread().getUncaughtExceptionHandler());Thread.currentThread().setUncaughtExceptionHandler(handler);return handler;}

代码 SpringBootExceptionHandler

    @Overridepublic void uncaughtException(Thread thread, Throwable ex) {try {if (isPassedToParent(ex) && this.parent != null) {this.parent.uncaughtException(thread, ex);}}finally {this.loggedExceptions.clear();if (this.exitCode != 0) {System.exit(this.exitCode);}}}

这里直接调用 System.exit() 过于粗暴,因此 只有主线程 handleRunFailure 执行了这个逻辑。

多线程应用中工作线程发生异常,可否设置进程退出码呢?

多线程应用结构

先来看看多线程应用结构:

  • 多线程应用默认最后一个非 deamon 线程结束后退出进程。
  • 可以显式控制应用生命周期,显式执行退出,这样就不用关心是否 daemon 线程,简化开发。
  • 退出应用时显式关闭 Spring 容器,线程池也由 spring 容器管理,此时即可退出所有线程。非 daemon 线程可以更优雅的结束,因为 jvm 会等待其结束。
  • 应用退出前需要保持至少一个非 daemon 线程,主线程即可作为这个线程,实现应用主控逻辑,主函数结束即退出应用。
  • 桌面应用、后台服务(如 web 服务器)等需要显式等待应用退出。显式等待应放在主函数主控逻辑之后。即所有 ApplicationRunner 之后,避免阻塞其他 ApplicationRunner。
  • 为简单一致性,将显式退出(关闭容器)操作放在主函数等待结束后。容器在主线程中创建,在主线程中销毁,逻辑更加清晰和一致。
  • Ctrl-C 或 kill 等显式退出进程时,shutdown hook 会关闭容器,但不会等待非 deamon 线程(如主线程)。(会唤醒 sleep ?但不会影响 CountDownLatch.await() ?)

示例程序移动部分逻辑到工作线程,代码如下:

转载于:https://my.oschina.net/u/3611008/blog/1860124

使用 spring boot 开发通用程序相关推荐

  1. 使用Spring Boot开发Restful程序

    一.简介 Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新spring应用的初始搭建以及开发过程.该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的 ...

  2. springboot thymeleaf配置_【程序源代码】Spring Boot 开发笔记web开发实战1

    关键字:<Spring Boot 开发笔记>系列文章 各位亲爱的小伙伴:大家好! <Spring Boot 开发笔记>系列文章 这套笔记和源码是我自己在学习springboot ...

  3. 真的简单,单手用Spring Boot 开发一个微信小程序

    前言   嗨,大家好,现在微信使用的用户很多,作为开发人员也可以建立一个自己的微信小程序,本期与大家分享一下作者建立微信小程序的开发流程. 申请   百度搜索微信公众号平台,然后扫码登录注册一个微信公 ...

  4. 开源oa_圈子哥推荐一款基于 Spring Boot 开发 OA 开源产品,学习/搞外快都是不二选择!...

    点击上方蓝字关注「程序员的技术圈子」 今天圈子哥给大家推荐一套Spring Boot 开发 OA系统,系统功能齐全,不管是用来学习或者搞外快都是不错的选择,clone下来吧! 办公自动化(OA)是面向 ...

  5. 从零搭建一个 Spring Boot 开发环境!Spring Boot+Mybatis+Swagger2 环境搭建

    从零搭建一个 Spring Boot 开发环境!Spring Boot+Mybatis+Swagger2 环境搭建 本文简介 为什么使用Spring Boot 搭建怎样一个环境 开发环境 导入快速启动 ...

  6. 计算机网络即时通信系统设计_天天玩微信,Spring Boot 开发私有即时通信系统了解一下...

    概述 利用Spring Boot作为基础框架,Spring Security作为安全框架,WebSocket作为通信框架,实现点对点聊天和群聊天. 所需依赖 Spring Boot 版本 1.5.3, ...

  7. spring boot 开发soap webservice

    介绍 spring boot web模块提供了RestController实现restful,第一次看到这个名字的时候以为还有SoapController,很可惜没有,对于soap webservic ...

  8. 分布式 Spring Cloud 基于 Spring Boot 开发一整套

    Spring Boot的工程包括:  - Spring IO Platform:用于版本化应用程序的企业级分发.  - Spring Framework:用于事务管理.依赖注入.数据访问.消息传递和W ...

  9. Spring Boot Web应用程序中注册 Servlet 的方法实例

    Spring Boot Web应用程序中注册 Servlet 的方法实例 本文实例工程源代码:https://github.com/KotlinSpringBoot/demo1_add_servlet ...

最新文章

  1. 登顶CLUE榜单,腾讯云小微与腾讯AI Lab联合团队提出基于知识的中文预训练模型...
  2. 赛思互动:初次上线CRM的企业如何将系统用起来、用好?
  3. Android系统移植与驱动开发--第四章
  4. python去重复字符串_python3取出重复3次的字符串保存为3列
  5. Skywalking-11:Skywalking查询协议——案例分析
  6. Linux进程里运行新代码,linux调度器源码分析 - 新进程加入(三)
  7. 云计算年度技术盛典,腾讯Techo Park开发者大会将在京召开
  8. 分享一个在线的HTML5元素在线测验 : HTML5 Element Quiz
  9. markdown与latex:单行式子中连加连乘i放在下面\displaystyle
  10. 解决wineQQ不能输入中文
  11. 分享一组我在ins上收集的程序员最爱桌面壁纸
  12. 利用二维向量的叉乘判断凹凸多边形
  13. 3种团队分组适应项目_分组团队竞赛活动方案
  14. 前端实习面试经验汇总
  15. 12.10.3 冻结窗格
  16. HTML5期末大作业:蘑菇街网站设计——2021蘑菇街首页(1页) HTML+CSS+JavaScript 学生DW网页设计作业成品 web期末作业设计网页_清新淡雅蘑菇街大学生网页设计作业成品
  17. 水贝风机远程控制app
  18. dede修改mysql,织梦教程:如何修改dedecms系统数据库表前缀?
  19. 【学习笔记】计算机时代的统计推断(Bradley Efron and Trevor Hastie 著)
  20. 【linux】Ubuntu 14.04 smba服务器的配置

热门文章

  1. 7个华丽的基于Canvas的HTML5动画
  2. 2014年个人工作总结
  3. [C#]网络编程系列专题二:HTTP协议详解
  4. Fedora 19下Guacamole的安装使用
  5. Fedora 14下安装使用rarlinux
  6. Autools学习总结(一)
  7. 如何写网站的robots.txt和meta name robots的配置
  8. 为 PHP 应用提速、提速、再提速
  9. 在HTML网页中巧用URL
  10. 汇编语言系统调用过程