vertx和springboot整合案例

項目結構:
在這裏插入圖片描述
依賴:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.4.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.citydo</groupId>
    <artifactId>vertxspringboot</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>vertxspringboot</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <vertex.version>3.9.1</vertex.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-tomcat</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>io.vertx</groupId>
            <artifactId>vertx-core</artifactId>
            <version>${vertex.version}</version>
        </dependency>
        <dependency>
            <groupId>io.vertx</groupId>
            <artifactId>vertx-web</artifactId>
            <version>${vertex.version}</version>
        </dependency>
        <dependency>
            <groupId>io.vertx</groupId>
            <artifactId>vertx-codegen</artifactId>
            <version>${vertex.version}</version>
        </dependency>
        <dependency>
            <groupId>io.vertx</groupId>
            <artifactId>vertx-web-client</artifactId>
            <version>${vertex.version}</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

配置:

spring.datasource.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.jpa.properties.hibernate.hbm2ddl.auto=update

啓動:

package com.citydo.vertxspringboot;


import com.citydo.vertxspringboot.verticle.JpaProductVerticle;
import com.citydo.vertxspringboot.verticle.ProductServerVerticle;
import io.vertx.core.Vertx;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import javax.annotation.PostConstruct;

@Slf4j
@SpringBootApplication
public class VertxspringbootApplication {

    @Autowired
    private ProductServerVerticle productServerVerticle;

    @Autowired
    private JpaProductVerticle jpaProductVerticle;

    public static void main(String[] args) {
        SpringApplication.run(VertxspringbootApplication.class, args);
    }

    @PostConstruct
    public void deployVerticle() {
        Vertx vertx = Vertx.vertx();
        vertx.deployVerticle(productServerVerticle);
        vertx.deployVerticle(jpaProductVerticle);
        vertx.exceptionHandler(throwable -> log.error("exception happened: {}", throwable.toString()));
        log.info("verticle deployed!!");
    }

}

package com.citydo.vertxspringboot.verticle;

import com.citydo.vertxspringboot.utils.Constants;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.eventbus.EventBus;
import io.vertx.core.http.HttpServerResponse;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.RoutingContext;
import io.vertx.ext.web.handler.BodyHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;


/**
 * @author nick
 */
@Slf4j
@Component
public class ProductServerVerticle extends AbstractVerticle {

    @Override
    public void start() throws Exception {
        super.start();
        Router router = Router.router(vertx);
        router.route().handler(BodyHandler.create());
        router.get("/products/:productID").handler(this::handleGetProduct);
        router.put("/products/:productID").handler(this::handleAddProduct);
        router.get("/products/").handler(this::handleListProducts);
        router.delete("/products/:productID").handler(this::handleDeleteProduct);
        router.patch("/products/:productID").handler(this::handlePatchProduct);
        vertx.createHttpServer().requestHandler(router).listen(8080);
//                .exceptionHandler(
//                throwable -> log.error("HTTPServer error happened: {}", throwable.toString())
//        );
    }

    private void getProductFailureHandler(RoutingContext failureRoutingContext){
        HttpServerResponse response = failureRoutingContext.response();
        response.end("something bad happened!");
    }

    private void handlePatchProduct(RoutingContext routingContext){
        String productId = routingContext.request().getParam("productID");
        log.info("productId used to patch product from request param: {}", productId);
        if (productId == null) {
            log.error("GET: the required productId is null!");
            sendError(400, routingContext.response());
            return;
        }
        JsonObject product = routingContext.getBodyAsJson();
        vertx.eventBus().<JsonObject>send(Constants.PATCH_PRODUCT_ADDRESS, product, asyncResult -> {
            log.info("result is: {}", asyncResult.result().body());
            if (asyncResult.succeeded()) {
                log.info("handle PATCH_PRODUCT_ADDRESS success!!");
                routingContext.response()
                        .putHeader("content-type", "application/json")
                        .setStatusCode(200)
                        .end(String.valueOf(asyncResult.result().body()));
            } else {
                log.info("handle PATCH_PRODUCT_ADDRESS failed!!");
                routingContext.response().setStatusCode(500).end();
            }
        });
    }

    private void handleDeleteProduct(RoutingContext routingContext){
        Integer productId = Integer.valueOf(routingContext.request().getParam("productID"));
        log.info("productId from request param: {}", productId);
        vertx.eventBus()
                .<Integer>send(Constants.DELETE_PRODUCT_ADDRESS, productId, result -> {
                    log.info("result is: {}", result.result().body());
                    if (result.succeeded()) {
                        log.info("handle DELETE_PRODUCT_ADDRESS success!!");
                        routingContext.response()
                                .putHeader("content-type", "application/json")
                                .setStatusCode(200)
                                .end(String.valueOf(result.result().body()));
                    } else {
                        log.info("handle DELETE_PRODUCT_ADDRESS failed!!");
                        routingContext.response().setStatusCode(500).end();
                    }
                });
    }

    private void handleGetProduct(RoutingContext routingContext) {
        String productId = routingContext.request().getParam("productID");
        if (productId == null) {
            log.error("GET: the required productId is null!");
            sendError(400, routingContext.response());
            return;
        }
        vertx.eventBus().<String>send(Constants.GET_ONE_PRODUCT_ADDRESS, productId, asyncResult -> {
            log.info("Got one product by productId: {}", asyncResult);
            if (asyncResult.succeeded()) {
                String body = asyncResult.result().body();
                if ("null".equals(body)){
                    log.info("No product found by the given productId: {}", productId);
                    routingContext.response().setStatusCode(404).end();
                }else {
                    log.info("handle GET_ONE_PRODUCT_ADDRESS success!!");
                    routingContext.response()
                            .putHeader("content-type", "application/json")
                            .setStatusCode(200)
                            .end(body);
                }

            } else {
                log.info("handle GET_ONE_PRODUCT_ADDRESS failed!!");
                routingContext.response().setStatusCode(500).end();
            }
        });
    }

    private void handleAddProduct(RoutingContext routingContext) {
        String productID = routingContext.request().getParam("productID");
        if (productID == null) {
            log.error("PUT: the productId is null!");
            sendError(400, routingContext.response());
            return;
        }
        JsonObject product = routingContext.getBodyAsJson();
        vertx.eventBus().<JsonObject> send(Constants.ADD_PRODUCT_ADDRESS, product, asyncResult -> {
            log.info("added one product: {}", asyncResult);
            if (asyncResult.succeeded()) {
                log.info("handle ADD_PRODUCT_ADDRESS success!!");
                routingContext.response()
                        .putHeader("content-type", "application/json")
                        .setStatusCode(200)
                        .end(asyncResult.result().body().toString());
            } else {
                log.info("handle ADD_PRODUCT_ADDRESS failed!!");
                routingContext.response().setStatusCode(500).end();
            }
        });
    }

    private void handleListProducts(RoutingContext routingContext) {
        EventBus eventBus = vertx.eventBus();
        eventBus.<String>send(Constants.ALL_PRODUCTS_ADDRESS, "", asyncResult -> {
            log.info("result is: {}", asyncResult.result().body());
            if (asyncResult.succeeded()) {
                log.info("handle  ALL_PRODUCTS_ADDRESS success!!");
                routingContext.response()
                        .putHeader("content-type", "application/json")
                        .setStatusCode(200)
                        .end(asyncResult.result().body());
            } else {
                log.info("handle ALL_PRODUCTS_ADDRESS failed!!");
                routingContext.response().setStatusCode(500).end();
            }
        });
        log.info("ALL_PRODUCTS_ADDRESS Event already sent!");
    }

    private void sendError(int statusCode, HttpServerResponse response) {
        response.setStatusCode(statusCode).end();
    }
}

package com.citydo.vertxspringboot.verticle;

import com.citydo.vertxspringboot.dto.ProductResult;
import com.citydo.vertxspringboot.service.ProductService;
import com.citydo.vertxspringboot.utils.Constants;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.Handler;
import io.vertx.core.eventbus.EventBus;
import io.vertx.core.eventbus.Message;
import io.vertx.core.json.Json;
import io.vertx.core.json.JsonObject;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * @author nick
 */
@Slf4j
@Component
public class JpaProductVerticle extends AbstractVerticle {

    private final static ObjectMapper objectMapper = Json.mapper;

    @Autowired
    private ProductService productService;

    private Handler<Message<String>> allProductsHandler(ProductService productService) {
        return msg -> vertx.<String>executeBlocking(future -> {
                    try {
                        future.complete(objectMapper.writeValueAsString(productService.getAllProduct()));
                        log.info("got all products from blocking...");
                    } catch (JsonProcessingException e) {
                        log.error("Failed to serialize result");
                        future.fail(e);
                    }
                },
                result -> {
                    if (result.succeeded()) {
                        msg.reply(result.result());
                    } else {
                        msg.reply(result.cause().toString());
                    }
                });
    }

    private Handler<Message<String>> getOneProductHandler(ProductService productService) {
        return msg -> vertx.<String>executeBlocking(future -> {
                    try {
                        Integer productId = Integer.valueOf(msg.body());
                        log.info("productId from sender: {}", productId);
                        val productDTO = productService.findProductById(productId);
                        future.complete(objectMapper.writeValueAsString(productDTO));
                        log.info("got one product from blocking...");
                    } catch (Exception e) {
                        log.error("Failed to serialize result");
                        future.fail(e);
                    }
                },
                result -> {
                    if (result.succeeded()) {
                        msg.reply(result.result());
                    } else {
                        msg.reply(result.cause().toString());
                    }
                });
    }

    @Override
    public void start() throws Exception {
        super.start();
        EventBus eventBus = vertx.eventBus();
        eventBus.<String>consumer(Constants.ALL_PRODUCTS_ADDRESS).handler(allProductsHandler(productService));
        eventBus.<String>consumer(Constants.GET_ONE_PRODUCT_ADDRESS).handler(getOneProductHandler(productService));
        eventBus.<JsonObject>consumer(Constants.ADD_PRODUCT_ADDRESS).handler(addProductHandler(productService));
        eventBus.<Integer>consumer(Constants.DELETE_PRODUCT_ADDRESS).handler(deleteProductHandler(productService));
        eventBus.<JsonObject>consumer(Constants.PATCH_PRODUCT_ADDRESS).handler(patchProductHandler(productService));
    }

    private Handler<Message<Integer>> deleteProductHandler(ProductService productService) {

        return message -> vertx.<Integer>executeBlocking(
                future -> {
                    try {
                        Integer productId = message.body();
                        log.info("productId from sender: {}", productId);
                        future.complete(productService.deleteProduct(productId));
                        log.info("deleted one product from blocking...");
                    } catch (Exception e) {
                        log.error("Failed to serialize result");
                        future.fail(e);
                    }
                },
                result -> {
                    if (result.succeeded()) {
                        message.reply(result.result());
                    } else {
                        message.reply(result.cause().toString());
                    }
                }
        );
    }

    private Handler<Message<JsonObject>> patchProductHandler(ProductService productService) {

        return message -> vertx.<JsonObject>executeBlocking(
                future -> {
                    try {
                        JsonObject product = message.body();
                        log.info("product to be patched from sender: {}", product);
                        future.complete(JsonObject.mapFrom(productService.patchProduct(product.mapTo(ProductResult.class))));
                    } catch (Exception e) {
                        log.error("Failed to serialize result");
                        future.fail(e);
                    }
                },
                result -> {
                    if (result.succeeded()) {
                        message.reply(result.result());
                    } else {
                        message.reply(result.cause().toString());
                    }
                }
        );
    }

    private Handler<Message<JsonObject>> addProductHandler(ProductService productService) {

        return message -> vertx.<JsonObject>executeBlocking(
                future -> {
                    try {
                        JsonObject product = message.body();
                        log.info("product from sender: {}", product);
                        future.complete(JsonObject.mapFrom(productService.addProduct(product.mapTo(ProductResult.class))));
                        log.info("got one product from blocking...");
                    } catch (Exception e) {
                        log.error("Failed to serialize result");
                        future.fail(e);
                    }
                },
                result -> {
                    if (result.succeeded()) {
                        message.reply(result.result());
                    } else {
                        message.reply(result.cause().toString());
                    }
                }
        );
    }

    private Handler<Message<String>> getAllService() {
        return msg -> vertx.<String>executeBlocking(future -> {
            log.info("try to get json.....");
            try {
                log.info("get json success..");
                future.complete(new JsonObject().put("name", "wade").toString());
            } catch (Exception e) {
                log.info("Failed to serialize result");
                future.fail(e);
            }
        }, result -> {
            if (result.succeeded()) {
                msg.reply(result.result());
            } else {
                msg.reply(result.cause()
                        .toString());
            }
        });
    }
}

package com.citydo.vertxspringboot.utils;

public class Constants {

    public static final String ALL_PRODUCTS_ADDRESS = "all-products-address";

    public static final String GET_ONE_PRODUCT_ADDRESS = "get-one-product-address";

    public static final String ADD_PRODUCT_ADDRESS = "add-product-address";

    public static final String DELETE_PRODUCT_ADDRESS = "delete-product-address";

    public static final String PATCH_PRODUCT_ADDRESS = "patch-product-address";
}

package com.citydo.vertxspringboot.service;

import com.citydo.vertxspringboot.dto.ProductResult;
import com.citydo.vertxspringboot.entity.Product;
import com.citydo.vertxspringboot.repository.ProductRepository;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.transaction.Transactional;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @author nick
 */
@Slf4j
@Service
@Transactional
public class ProductService {

    @Autowired
    private ProductRepository productRepository;

    /**
     * 獲取全部數據
     * @return
     */
    public List<ProductResult> getAllProduct() {
        val entityList = productRepository.findAll();
        if (CollectionUtils.isEmpty(entityList)){
            return Collections.EMPTY_LIST;
        }
        return entityList.stream().map(Product::getProductResult).collect(Collectors.toList());
    }

    /**
     * 查詢單個數據Constants
     * @param productId
     * @return
     */
    public ProductResult findProductById(Integer productId){
        if (StringUtils.isEmpty(productId)){
            return null;
        }
        val entity = productRepository.findById(productId);
        return Objects.isNull(entity) ? null :  entity.map(Product::getProductResult).get();
    }

    /**
     * 添加
     * @param productResult
     * @return
     */
    public ProductResult addProduct(ProductResult productResult){
        Product toBeSavedEntity = new Product();
        BeanUtils.copyProperties(productResult, toBeSavedEntity);
        val savedEntity = productRepository.save(toBeSavedEntity);
        return savedEntity.getProductResult();
    }

    /**
     * 刪除數據
     * @param productId
     * @return
     */
    public Integer deleteProduct(Integer productId){
        productRepository.deleteById(productId);
        return productId;
    }


    public ProductResult patchProduct(ProductResult productResult) {
        val entity = productRepository.findById(productResult.getProductId()).get();
        entity.setProductName(productResult.getProductName());
        entity.setDescription(productResult.getDescription());
        val saved = productRepository.save(entity);
        return saved.getProductResult();
    }

}

package com.citydo.vertxspringboot.repository;

import com.citydo.vertxspringboot.entity.Product;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface ProductRepository extends JpaRepository<Product, Integer> {

}

package com.citydo.vertxspringboot.entity;


import com.citydo.vertxspringboot.dto.ProductResult;
import lombok.Data;
import lombok.val;
import org.springframework.beans.BeanUtils;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;


/**
 * @author nick
 */
@Data
@Entity
@Table(name = "product")
public class Product {

    @Id
    @Column(name = "product_id")
    private Integer productId;

    @Column(name = "product_name")
    private String productName;

    @Column(name = "description")
    private String description;

    /**
     * 將數據對象轉成返給前端對象
     * @return
     */
    public ProductResult getProductResult(){
        val productDTO = new ProductResult();
        BeanUtils.copyProperties(this, productDTO);
        return productDTO;
    }

}

package com.citydo.vertxspringboot.dto;

import lombok.Data;

@Data
public class ProductResult {

    private Integer productId;

    private String description;

    private String productName;
}

sql:

-- ----------------------------
-- Table structure for product
-- ----------------------------
DROP TABLE IF EXISTS `product`;
CREATE TABLE `product` (
  `product_id` int(11) NOT NULL,
  `product_name` varchar(45) NOT NULL,
  `description` varchar(45) DEFAULT NULL,
  PRIMARY KEY (`product_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of product
-- ----------------------------
INSERT INTO `product` VALUES ('1', '張三', '測試中');
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章