本文為實戰SpringCloud響應式微服務系列教程第十章,本章給出響應式RESTful服務完整代碼示例。建議沒有之前基礎的童鞋,先看之前的章節,章節目錄放在文末。
1.搭建響應式RESTful服務。
在前面章節中我們講了如何使用 Spring Initializer初始化響應式web應用,本節中就不再做過多介紹(請回顧第九章內容)。
在學習本章內容之前需要了解mongodb以及redis,mongodb以及redis可查閱相關資料進行全面了解,並在本地環境搭建mongodb和redis。
2.application.yml文件配置
server:
port: 9801
spring:
application:
name: advert
data:
mongodb:
uri: mongodb://localhost:27017/db_advert
http:
encoding:
force: true
charset: UTF-8
enabled: true
redis:
host: 127.0.0.1
password: 123456
logback:
level: info
以上配置代碼是我們目前學習的響應式RESTful服務的全部配置,配置比較簡單,spring.data.mongodb.uri: mongodb://localhost:27017/db_advert和spring.data.redis是我們服務的核心配置,我們知道傳統的數據庫是不支持響應式數據讀取的,所以這里使用mongodb和redis代替。
3.集成響應式的MongoDB
springboot 本身提供了cassandra/couchbase/mongodb/redis這幾個NoSQL數據庫的響應式驅動:
阻塞式的spring-boot-starter-data-mongodb 改為響應式的mongodb依賴spring-boot-starter-data-mongodb-reactive:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-mongodb-reactive</artifactId> </dependency>
(1)編寫實體類:
/** * 廣告投放 */ @Document(collection="advert")//集合名 @Data @Builder @NoArgsConstructor @AllArgsConstructor public class Advert implements Serializable { private static final long serialVersionUID = -8985545025018238754L; /** * 主鍵 */ @Id private String id; /** * 內容 */ private String content; /** * 發布人 */ private Long userId; /** * 創建時間 */ private Date creatData; /** * 圖片地址 */ private String imgUrl; /** * 視頻地址 */ private String videoUrl; /** * 廣告類型(視頻圖片) */ private String advertType; /** * 今日投放地區 */ private String launchArea; /** * 投放時長(小時為單位) */ private int durationTime; /** * 廣告類型 */ private int classify; /** * 計費方式 */ private int billingMode; /** * 展示位置(首頁輪播,其他輪播,首頁其他位置,其他) */ private int displayPosition; /** * 廣告主題 */ private String advertTitle; /** * 是否需要自定義展示頁面 */ private int isCustom; /** * 索引關鍵詞 */ private String keyWords; }
其中@document把一個java類聲明為mongodb的文檔,可以通過collection參數指定這個類對應的文檔,標注在實體類上,類似於hibernate的entity注解。其他注解均為lombok的注解。
(2)編寫repository接口
import com.shmc.advert.model.po.Advert; import org.springframework.data.mongodb.repository.ReactiveMongoRepository; import org.springframework.data.mongodb.repository.Tailable; import org.springframework.stereotype.Repository; import reactor.core.publisher.Flux; @Repository public interface AdvertRepository extends ReactiveMongoRepository<Advert,Long> { @Tailable Flux<Advert> findBy(); }
其中@Repository是org.springframework.stereotype.Repository的注解,這個大家應該都很熟悉了不做解釋。
從以上代碼中我們可以清楚看到AdvertRepository 繼承了ReactiveMongoRepository,ReactiveMongoRepository正是我們依賴的maven響應式mongodb-reactive中的類,其中@Tailable注解,該注解類似於Linux中的tail ,可以將DB的變化以響應式流的方式獲取到並推送給前端。
除了可以繼承ReactiveMongoRepository之外我們還可以通過注入MongoTemplate來操作mongodb,但是MongoTemplate做不到實時監控和主動推送。如:
@Autowired
MongoTemplate mongoTemplate;
(3)編寫Service接口以及實現類
Service接口:
import com.shmc.advert.model.po.Advert; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; public interface AdvertService { Mono<Advert> saveAdvert(Advert advert); Mono<Advert> findById(String id); Flux<Advert> findAll(); Flux<Advert> findByAll(); }
Service實現類:
@Service public class AdvertServiceImpl implements AdvertService { @Autowired MongoTemplate mongoTemplate; @Autowired private AdvertRepository advertRepository; @Override public Mono<Advert> saveAdvert(Advert advert){ advert.setId(new IdWorker().nextId()); advert.setCreatData(new Date()); mongoTemplate.insert(advert); return Mono.just(advert); } @Override public Mono<Advert> findById(String id){ Query query = new Query(Criteria.where("id").is(id)); return Mono.just(mongoTemplate.findOne(query,Advert.class)); } @Override public Flux<Advert> findAll(){ return advertRepository.findAll(); } @Override public Flux<Advert> findByAll(){ return advertRepository.findBy(); } }
(4)編寫Controller
import com.shmc.advert.model.po.Advert; import com.shmc.advert.service.AdvertService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.*; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import java.time.Duration; @RestController @RequestMapping("/advert") public class AdvertController { @Autowired private AdvertService advertService; @PostMapping("/saveAdvert") public Mono<Advert> saveAdvert(@RequestBody Advert advert){ return advertService.saveAdvert(advert); } @GetMapping("/findById/{id}") public Mono<Advert> findById(@PathVariable String id){ return advertService.findById(id); } /** * 以stream+json流的方式推送到客戶端 * @return */ @GetMapping(value = "/findAllPreSec", produces = MediaType.APPLICATION_STREAM_JSON_VALUE) public Flux<Advert> findAllPreSec() { return advertService.findAll().delayElements(Duration.ofSeconds(1)); } /** * 數據變更 * @return */ @GetMapping(value = "/findByAll", produces = MediaType.APPLICATION_STREAM_JSON_VALUE) public Flux<Advert> findByAll(){ return advertService.findByAll(); } }
至此基於RESTful的響應式服務我們全部完成了,啟動程序訪問“/advert/findAllPreSec"接口和”/advert/findByAll“接口就可以看到響應式的數據推送了。
findAllPreSec這個方法。使用了delayElements使得每隔一秒鍾獲取一條數據發送給客戶端,以“異步響應式流”的方式逐條推送。
這里指定了MediaType是APPLICATION_STREAM_JSON,即application/stream+json格式。
在瀏覽器中就可以看到每隔一秒出現一條記錄。運行程序,訪問findByAll接口,然后測試調用save接口添加advert數據,或者直接通過MongoDB Compass客戶端添加Stu數據,就會看到在頁面中實時看到新添加的數據了。
下一章會吧代碼上傳到gitee,各位看官如有需要請自行下載。
