Spring Boot教程(25) – 状态码和错误码的一种最佳实践

前两天在V2EX上看到一个帖子,有人吐槽公司项目所有的接口请求都返回200状态码,再在body里加入code进行业务区分的做法不合理,帖子引起了程序员们激烈的讨论。左耳朵耗子(陈皓)之前也发微博讨论过这个问题,不同意见的两方人数都挺多的,各有各的道理。

“200一把梭”的人会采用类似如下的JSON响应:

{ 
   "code":123,
   "message":"OK",
   "data":{ ... }
}

即所有的请求状态码都是200,业务根据body中的code字段判断请求的状态,需要时会从data字段中拿出数据。这种方式的使用者,大概理由是,业务的状态码足够多,如果对应成HTTP状态码根本不够用。

而另一堆人对于这种做法十分反对,认为这相当于抛弃了HTTP协议的状态码,抛弃了普遍认可的共识。

(更多…)

继续阅读 →

Spring Boot教程(24) – 用RestTemplate访问外部服务

日常开发中难免会向应用外部发起HTTP请求,比如访问云存储平台的API、调用微信或支付宝的API、抓取网页等等。Spring框架提供了RestTemplate来完成这一需求。RestTemplate提供了很多方法,方便你发起GET和POST等请求。下图是个简单的调用,去获取网页的HTML代码:

getForEntity方法会发起GET请求,然后返回一个ResponseEntity对象,它把HTTP请求的响应抽象成一个对象。通过ResponseEntity,你可以获取到此次响应的状态码、首部(Header)和主体(Body)。有的时候你仅仅需要Body部分,那就可以使用getForObject方法。

(更多…)

继续阅读 →

Spring Boot教程(23) – 容器中对象的命名和查找

Spring容器中的对象(Bean)都有自己的标识符(identifier),多数情况下一个Bean只对应一个标识符,你也可以给Bean指定多个标识符。另外在网上的教程里你可能看到id或者name的概念,其实他们和标识符指的是同一种东西。一个对象如果有多个标识符,还会有一个别名(alias)的概念,它是一种相对的叫法,你挑一个标识符出来,剩下的标识符都叫别名。我通常就把Bean的标识符称为Bean的名字。

(更多…)

继续阅读 →

Spring Boot教程(22) – 容器中对象的生命周期

你可能在学习其他框架的时候,听到过“生命周期”的概念,通常,框架会提供给你回调方法,在生命周期的关键点,框架会调用这些回调方法。比如你给你的小程序或者安卓应用编写页面的时候,会有类似onHide()、onShow()这种方法,分别在显示(show)页面或者离开(hide)页面的时候调用到,他们正属于页面生命周期的一部分。

在Spring容器中的对象(Bean),也有着自己的生命周期,只不过比较简单一点,因为只有两个关键点。第一个关键点是Bean的所有依赖都已经装配好的时候,另一个是容器即将销毁的时候。你可能会问,容器什么时候销毁?如果你不是自己维护ApplicationContext的话,而是框架自动维护的话,比如使用Spring Boot写web运用,那么你程序停止运行的时候,就是容器销毁的时候。这个时候容器中的对象会根据自己的情况,来释放一些资源,比如关闭它跟外部系统的一些连接等。

(更多…)

继续阅读 →

Spring Boot教程(21) – 默认线程池

之前我们简要说过@Async@Scheduled的用法,这俩注解会帮你完成异步任务和定时任务的需求。不知道你有没有想过,这些异步任务和定时任务都是在哪个线程执行的?Spring Boot肯定在背后做了很多工作,本文就来说说框架都为我们做了什么。

首先肯定是有线程池的。Spring Boot已经帮你创建并配置好了,还配了两个,一个供@Async使用,一个供@Scheduled使用。

Spring将异步任务和定时任务的执行,抽象出了两个接口,TaskExecutorTaskScheduler。我们先来说说TaskExecutor

(更多…)

继续阅读 →

Spring Boot教程(20) – 用AspectJ实现AOP内部调用

你一定用过@Transactional注解吧,它加在方法上可以实现声明式事务。第一次接触到它的时候,我感觉这种设计挺完美的。后来发现由于它是基于Spring AOP的代理实现的,所以有个坑——不支持内部调用的。

比如你的@Service类,里面有个A方法,调用了带有@Transactional注解的B方法,那么你在调用A方法,执行到B方法的时候是没有事务关联的。因为A调用B的时候,并不是通过代理类,而事务相关逻辑是放在代理类的。

如果你还不懂面向切面编程(AOP),不懂什么是代理,不妨看看我之前的文章

(更多…)

继续阅读 →

Spring Boot教程(19) – 缓存入门

缓存是啥?某些重复的操作太耗时,不如把结果存起来,下次需要直接拿出来。

咱说个场景吧,比如你的系统里需要生成报表,然而这个报表需要做各种SQL查询和计算,总计要个10秒才能运行结束,如果每次请求都来个10秒,频繁看数据的老板可是要发飙了。你可以把报表缓存起来,只有第一次生成的时候慢一点,以后生成都可以瞬间完成。

(更多…)

继续阅读 →

Spring Boot教程(18) – 管理日志

日志用来记录你程序运行的中的一些关键信息,方便你调试以及后期上线的时候查找问题。典型的用法就是应用出现5xx错误了之后,上日志里找找哪里抛出了异常,异常调用栈是什么。

混乱的日志框架

其实Java语言本身有着一套日志工具,在java.util.logging包下,简称JUL。JUL大家好像不怎么用,易用性不行,性能也没跟得上。大家都用Log4j,使用得也比较广泛,本身属于Apache软件基金会(ASF)。Log4j的作者Ceki后来搞了个Logback,弥补了Log4j的缺点。ASF后来也把Log4j升级成了Log4j2,前者于15年停止更新,后者对于Logback和Log4j也是取其精华,弃之糟粕(PS:Ceki和ASF绝对有过节)。所以目前可用的日志实现也就是JUL、Log4j2和Logback

(更多…)

继续阅读 →

Spring Boot教程(17) – 快速理解AOP

AOP,Aspect-oriented Programming,面向切面编程,是你接触Spring之后比较难理解的概念。网上也有很多文章来介绍它,但是我老是看不懂,上来就一堆术语,头大。在我详细解释AOP之前,先用一句话说明他的典型用法:

把对象修改一下或者包装起来,在它方法执行的前后,额外运行点代码

很多时候你不知不觉就使用了AOP:在@Component类中,你在方法上加了@Transactional注解之后,方法执行前会开始事务,方法执行之后结束事务,方法发生异常之后回退事务;加上@Async注解之后,方法最终被调用的时候是异步的;加上@Cacheable注解之后,方法的返回值会被缓存起来,下次调用的时候直接返回缓存值。这些都是框架提供给你的,你只要加个注解声明一下就能用。

同时你也可以编写AOP代码来实现自己需求,比如在方法执行之前开始计时,在方法结束之后停止计时,来得到方法的运行时间;比如在业务的关键地方加上log;比如权限控制、懒加载等等。使用AOP的特点就是侵入性比较小,你的业务代码不用动,降低耦合度,方便团队分工。

(更多…)

继续阅读 →

Spring Boot教程(16) – 属性配置

Spring Boot的特点就是自动配置,开箱即用。它按照业界一些常见的做法去配置项目,如果你代码跑起来之后,感觉够用了,就不用再操心。如果感觉不够用,就需要自己定义一些属性来满足你的需求。使用Spring Initializr或者IDE创建项目之后,在src/main/resources目录下会有个application.properties文件,它就是用来保存你的自定义配置的,每条配置使用”key=value”的形式,大概长这样:

相信你肯定用过了,网上各种教程基本开头都会教你配置一些常见的属性,Spring Boot文档也列出了大部分的属性以及其默认值,我就不再赘述了。这次我们聊点别的。

(更多…)

继续阅读 →