Spring Boot教程(29) – RabbitMQ必备基础

RabbitMQ是使用最广泛的开源消息队列中间件之一,它是用Erlang写的,并实现了高级消息队列协议AMQP。消息队列可以在应用间交换消息,实现解耦、异步处理、削峰、缓冲等目的,应用场景还是比较丰富的。除了RabbitMQ,还有其他的消息队列可用,比如Kafka、Java写的ActiveMQ和阿里出品的RocketMQ等,现在网上有很多他们的对比,你可以具体分析分析那种可能更适合你。

RabbitMQ现在属于Pivotal公司,而Spring框架也是Pivotal出品,所以两者搭配使用也是顺理成章,Spring Boot也有AMQP Starter以提供支持。在打开IDE演示代码如何编写之前,先说说消息队列以及RabbitMQ具体是怎么工作的。

(更多…)

继续阅读 →

Spring Boot教程(28) – 简单读写Redis

Redis是最流行的key-value数据库,它最常用的功能就是缓存。就算你不是搞后端的,也会不断看到Redis相关的新闻和技术文章等,因为它性能强劲、功能丰富、生态完备,各种“吊打面试”的系列文章肯定少不了它。

如果仅仅是CRUD工程师来使用Redis的话,那需要掌握的东西寥寥几句话就可以概括,简单说就是80%的情况下你只需要用到20%的知识。我想更复杂的地方就是如何针对不同的业务进行数据结构的设计,这个就需要实践和经验了。如果你是在大厂,数据足够多,访问量足够大,可能会需要搭建Redis集群,这也是有现成工具和方案的。

可惜作为一个toB项目的CRUD的工程师,还没有机会将Redis的作用充分发挥出来,只能在这里啰嗦下它的基本使用。轮子丰富的Java生态和Spring全家桶,老早已经为你准备好了工具,Spring Data Redis可以帮你高效使用Redis,而且Spring Boot也提供了starter可以一键接入Redis,“0配置”就可以写代码。在介绍它之前,先复习下Redis的基本概念和命令,然后再来说说它的API的使用。

(更多…)

继续阅读 →

Spring Boot教程(27) – 优雅地实现重试逻辑

Spring家族中有一个小项目是Spring Retry,他用来方便地实现重试机制。你的程序在调用远程API的时候,可能因为网络抖动等原因,导致调用失败,这种失败是偶发的,出现了多试几下就行。如果你遇到需要重试多次的情况,倘若只是朴素地写个for循环N次,请求成功就跳出循环返回结果,未免也太粗糙了。Spring已经为这一通用需求造好了轮子。

Spring Retry现在被Spring BatchSpring Integration等项目引用,在Spring Boot项目中,我一般直接引入spring-boot-start-batch来间接引入Spring Retry(Spring Batch是个批处理工具库,你也许会用得到其中的类,我们有机会再具体研究下),另外它还要配合AOP Starter才能使用,后者你的项目里一般都有。引入Spring Retry后,可以用加注解声明的方式,或者编码的方式来使用。

(更多…)

继续阅读 →

Spring Boot教程(26) – 如何发送邮件

发送邮件的需求很常见,找回密码啦,推广产品啦等等。在演示如何写代码之前,我先来说说,常见的邮件发送方式有哪些,以及我们应该如何做选择。

大体上来说,发送的方式有两种,一种是使用SMTP协议,连上SMTP服务器发送邮件,一种是使用第三方的邮件发送服务,调调API就行。我们先来说说前者。

SMTP是“简单邮件传输协议”的简称,是用来邮件的,与之对应的是用来邮件的IMAP和POP3协议,后俩协议暂且不提。现在各大邮件服务商比如Gmail,网易邮箱,QQ邮箱等都有提供SMTP服务器方便你发送邮件,比如你本地的邮件客户端,在添加你的邮箱的时候就需要提供SMTP服务器的地址和端口。同时还需要提供的,就是你的邮箱和密码,这个密码通常不是你的登陆密码,而是一个授权码,类似于token的东西,你需要在后台生成,在账户出现安全问题的时候这个授权码是有办法revoke的。有了SMTP服务的地址、端口、邮箱和密码,你就能在邮件客户端上或者编写代码来发送邮件了。

(更多…)

继续阅读 →

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),不懂什么是代理,不妨看看我之前的文章

(更多…)

继续阅读 →