Spring Cloud【Finchley】实战-01注册中心及商品微服务(下)

简介: Spring Cloud【Finchley】实战-01注册中心及商品微服务(下)

实体类 ProductCategory


过程同上,这里不赘述了 ,代码如下

domain实体类

package com.artisan.product.domain;
import lombok.Data;
import javax.persistence.*;
import java.util.Date;
@Data
@Table(name = "product_category")
@Entity
public class ProductCategory {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private String categoryId;
    private String categoryName;
    private Integer categoryType;
    private Date createTime;
    private Date updateTime;
}


Dao接口

package com.artisan.product.repository;
import com.artisan.product.domain.ProductCategory;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
public interface ProductCategoryRepository extends JpaRepository<ProductCategory, String> {
    List<ProductCategory> findByCategoryTypeIn(List<Integer> categoryTypeList);
}

单元测试

package com.artisan.product.repository;
import com.artisan.product.domain.ProductCategory;
import com.netflix.discovery.converters.Auto;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.Arrays;
import java.util.List;
import static org.junit.Assert.*;
@RunWith(SpringRunner.class)
@SpringBootTest
public class ProductCategoryRepositoryTest {
    @Autowired
    private ProductCategoryRepository productCategoryRepository;
    @Test
    public void findByCategoryTypeIn() {
        List<ProductCategory> list = productCategoryRepository.findByCategoryTypeIn(Arrays.asList(99, 98, 97));
        Assert.assertEquals(3,list.size());
    }
}


20190322233339862.png

Service层

ProductService 接口

package com.artisan.product.service;
import com.artisan.product.domain.Product;
import java.util.List;
public interface ProductService {
    // 查询上架商品
    List<Product>   getAllUpProduct();
}


ProductService 接口实现类

package com.artisan.product.service.impl;
import com.artisan.product.domain.Product;
import com.artisan.product.enums.ProductStatusEnum;
import com.artisan.product.repository.ProductRepository;
import com.artisan.product.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class ProductServiceImpl implements ProductService {
    @Autowired
    private ProductRepository productRepository;
    @Override
    public List<Product> getAllUpProduct() {
        return productRepository.findByProductStatus(ProductStatusEnum.UP.getCode());
    }
}



ProductStatusEnum

为了方便,将状态封装到了Enum中

package com.artisan.product.enums;
import lombok.Getter;
@Getter
public enum ProductStatusEnum {
    UP(0,"上架"),
    DOWN(1,"下架");
    private int code ;
    private String msg;
    ProductStatusEnum(int code, String msg){
        this.code = code;
        this.msg = msg;
    }
}


对接口进行单元测试

package com.artisan.product.service;
import com.artisan.product.ArtisanProductApplicationTests;
import com.artisan.product.domain.Product;
import com.artisan.product.enums.ProductStatusEnum;
import com.artisan.product.repository.ProductRepository;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
import static org.junit.Assert.*;
@Component
public class ProductServiceTest extends ArtisanProductApplicationTests {
    @Autowired
    private ProductRepository productRepository;
    @Test
    public void getAllUpProduct() {
        List<Product> list =  productRepository.findByProductStatus(ProductStatusEnum.UP.getCode());
        Assert.assertEquals(3,list.size());
    }
}

20190323001136956.png

ProductCategoryService 接口

package com.artisan.product.service;
import com.artisan.product.domain.ProductCategory;
import java.util.List;
public interface ProductCategoryService {
    List<ProductCategory> findByCategoryTypeIn(List<Integer> categoryTypeList);
}


ProductCategoryService 接口实现类

package com.artisan.product.service.impl;
import com.artisan.product.domain.ProductCategory;
import com.artisan.product.repository.ProductCategoryRepository;
import com.artisan.product.service.ProductCategoryService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class ProductCategoryServiceImpl implements ProductCategoryService {
    @Autowired
    private ProductCategoryRepository productCategoryRepository;
    @Override
    public List<ProductCategory> findByCategoryTypeIn(List<Integer> categoryTypeList) {
        return productCategoryRepository.findByCategoryTypeIn(categoryTypeList);
    }
}


单元测试

package com.artisan.product.service.impl;
import com.artisan.product.ArtisanProductApplicationTests;
import com.artisan.product.domain.ProductCategory;
import com.artisan.product.repository.ProductCategoryRepository;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
import static org.junit.Assert.*;
@Component
public class ProductCategoryServiceImplTest extends ArtisanProductApplicationTests {
    @Autowired
    private ProductCategoryRepository productCategoryRepository;
    @Test
    public void findByCategoryTypeIn() {
       List<ProductCategory> list =  productCategoryRepository.findByCategoryTypeIn(Arrays.asList(99,98,97));
        Assert.assertEquals(3,list.size());
    }
}


20190323002005842.png


Controller层


20190322222722642.png


先来观察下,返回给前端的数据

code , msg , 泛型的data 是最外层的数据,那封装下吧 。 可以理解为也是一个VO(View Object)对象,包含3个节点(code msg 泛型的data)

同时data节点 [] ,自然是个数组了,可包含多个{}对象。

20190323005532294.png


按照上图的划分,也把这些信息封装成VO吧。

为了避免引起误解,我们把

20190323010610710.png

改为products .


VO封装

ResultVO 前后台交互的统一格式模板

package com.artisan.product.vo;
import lombok.Getter;
@Getter
public class Result<T> {
    private Integer code ;
    private String msg ;
    private T data;
    /**
     * 成功时候的调用
     * */
    public static <T> Result<T> success(T data){
        return new  Result<T>(data);
    }
    private Result(T data) {
        this.code = 0;
        this.msg = "success";
        this.data = data;
    }
    /**
     * 失败时候的调用
     * */
    public static <T> Result<T> error(ErrorCodeMsg cm){
        return new  Result<T>(cm);
    }
    private Result(ErrorCodeMsg cm) {
        if(cm == null) {
            return;
        }
        this.code = cm.getCode();
        this.msg = cm.getMsg();
    }
}


用到了 ErrorCodeMsg

package com.artisan.product.vo;
import lombok.Getter;
@Getter
public class ErrorCodeMsg {
    private int code;
    private String msg;
    // 异常
    public static ErrorCodeMsg SERVER_ERROR = new ErrorCodeMsg(-1, "服务端异常");
    private ErrorCodeMsg(int code, String msg) {
        this.code = code;
        this.msg = msg;
    }
}


ProductVO :返回给前台的商品信息格式,包含目录信息

package com.artisan.product.vo;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.util.List;
@Data
public class ProductVO {
    //  @JsonProperty注解用于属性上,作用是把该属性的名称序列化为另外一个名称,
    // 如把categoryName属性序列化为name
    // 【这里约定给前台返回的节点名为name, 但是为了方便理解这个name到底是什么的name,在vo中定义了方便理解的属性名】
    @JsonProperty("name")
    private String categoryName;
    @JsonProperty("type")
    private Integer categoryType;
    // 因为这个节点下可能返回多个ProductInfoVO,因此定义一个List集合
    @JsonProperty("products")
    private List<ProductInfoVO> productInfoVOList ;
}

ProductInfoVO 具体产品的数据VO

package com.artisan.product.vo;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.math.BigDecimal;
@Data
public class ProductInfoVO {
    @JsonProperty("id")
    private String productId;
    @JsonProperty("name")
    private String productName;
    @JsonProperty("price")
    private BigDecimal productPrice;
    @JsonProperty("description")
    private String productDescription;
    @JsonProperty("icon")
    private String productIcon;
}


Controller层逻辑


分析约定的前后台交互的JSON格式:

  • 每个ProductVO中我们需要获取产品目录名称及产品目录的category_type ,
    调用ProductCategoryService#categoryService方法即可。categoryService
  • 的入参为categoryTypeList,因此需要调用ProductService#getAllUpProduct获取所有上架商品对应的categoryType.
  • 获取到了后台的数据后,按照约定的格式拼装返回JSON串即可


package com.artisan.product.controller;
import com.artisan.product.domain.Product;
import com.artisan.product.domain.ProductCategory;
import com.artisan.product.service.ProductCategoryService;
import com.artisan.product.service.ProductService;
import com.artisan.product.vo.ProductInfoVO;
import com.artisan.product.vo.ProductVO;
import com.artisan.product.vo.Result;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
@RestController
@RequestMapping("/product")
public class ProductController {
    @Autowired
    private ProductService productService;
    @Autowired
    private ProductCategoryService categoryService;
    @GetMapping("/list")
    private Result list() {
        //1. 查询所有在架的商品
        List<Product> productInfoList = productService.getAllUpProduct();
        //2. 获取类目type列表
        List<Integer> categoryTypeList = productInfoList.stream()
                .map(Product::getCategoryType)
                .collect(Collectors.toList());
        //3. 从数据库查询类目
        List<ProductCategory> categoryList = categoryService.findByCategoryTypeIn(categoryTypeList);
        //4. 构造数据
        List<ProductVO> productVOList = new ArrayList<>();
        for (ProductCategory productCategory : categoryList) {
            ProductVO productVO = new ProductVO();
            // 设置属性
            productVO.setCategoryName(productCategory.getCategoryName());
            productVO.setCategoryType(productCategory.getCategoryType());
            // ProductInfoVO 集合
            List<ProductInfoVO> productInfoVOList = new ArrayList<>();
            for (Product product : productInfoList) {
                // 挂到对应的的categoryType下
                if (product.getCategoryType().equals(productCategory.getCategoryType())) {
                    ProductInfoVO productInfoVO = new ProductInfoVO();
                    // 将属性copy到productInfoVO,避免逐个属性set,更简洁
                    BeanUtils.copyProperties(product, productInfoVO);
                    productInfoVOList.add(productInfoVO);
                }
            }
            productVO.setProductInfoVOList(productInfoVOList);
            productVOList.add(productVO);
        }
        return Result.success(productVOList);
    }
}

启动测试

访问 http://localhost:8080/product/list

{
    "code": 0,
    "msg": "success",
    "data": [
        {
            "name": "热饮",
            "type": 99,
            "products": [
                {
                    "id": "1",
                    "name": "拿铁咖啡",
                    "price": 20.99,
                    "description": "咖啡,提神醒脑",
                    "icon": null
                },
                {
                    "id": "3",
                    "name": "卡布奇诺",
                    "price": 15,
                    "description": "卡布奇诺的香味",
                    "icon": null
                }
            ]
        },
        {
            "name": "酒水",
            "type": 98,
            "products": [
                {
                    "id": "2",
                    "name": "青岛纯生",
                    "price": 7.5,
                    "description": "啤酒",
                    "icon": null
                }
            ]
        }
    ]
}


格式化看下更加直观:

20190323013106955.png


知识点总结

Java8的Stream

 //2. 获取类目type列表
 List<Integer> categoryTypeList = productInfoList.stream()
         .map(Product::getCategoryType)
         .collect(Collectors.toList());


使用Java8中的Stream可以方便的对集合对象进行各种便利、高效的聚合操作,或者大批量数据操作。


20190323100357413.png


map生成的是个一对一映射,相当于for循环


20190323102613338.png


注意:流只能使用一次,使用结束之后,这个流就无法使用了。

点击查看更多示例


Beanutils.copyProperties( )

  // 将属性copy到productInfoVO,避免逐个属性set,更简洁
  BeanUtils.copyProperties(product, productInfoVO);

org.springframework.beans.BeanUtils# copyProperties作用是将一个Bean对象中的数据封装到另一个属性结构相似的Bean对象中。


同时org.apache.commons.beanutils.BeanUtils也有个copyProperties

需要注意的是这俩的copyProperties方法参数位置不同

 org.springframework.beans.BeanUtils#copyProperties(sourceDemo, targetDemo)
 org.apache.commons.beanutils.BeanUtils#copyProperties(targetDemo, sourceDemo)


Github地址


https://github.com/yangshangwei/springcloud-o2o/tree/master/artisan-product


相关文章
|
8月前
|
数据可视化 Java BI
将 Spring 微服务与 BI 工具集成:最佳实践
本文探讨了 Spring 微服务与商业智能(BI)工具集成的潜力与实践。随着微服务架构和数据分析需求的增长,Spring Boot 和 Spring Cloud 提供了构建可扩展、弹性服务的框架,而 BI 工具则增强了数据可视化与实时分析能力。文章介绍了 Spring 微服务的核心概念、BI 工具在企业中的作用,并深入分析了两者集成带来的优势,如实时数据处理、个性化报告、数据聚合与安全保障。同时,文中还总结了集成过程中的最佳实践,包括事件驱动架构、集中配置管理、数据安全控制、模块化设计与持续优化策略,旨在帮助企业构建高效、智能的数据驱动系统。
408 1
将 Spring 微服务与 BI 工具集成:最佳实践
|
8月前
|
Java 数据库 数据安全/隐私保护
Spring 微服务和多租户:处理多个客户端
本文介绍了如何在 Spring Boot 微服务架构中实现多租户。多租户允许单个应用实例为多个客户提供独立服务,尤其适用于 SaaS 应用。文章探讨了多租户的类型、优势与挑战,并详细说明了如何通过 Spring Boot 的灵活配置实现租户隔离、动态租户管理及数据源路由,同时确保数据安全与系统可扩展性。结合微服务的优势,开发者可以构建高效、可维护的多租户系统。
706 127
|
9月前
|
监控 Java API
Spring Boot 3.2 结合 Spring Cloud 微服务架构实操指南 现代分布式应用系统构建实战教程
Spring Boot 3.2 + Spring Cloud 2023.0 微服务架构实践摘要 本文基于Spring Boot 3.2.5和Spring Cloud 2023.0.1最新稳定版本,演示现代微服务架构的构建过程。主要内容包括: 技术栈选择:采用Spring Cloud Netflix Eureka 4.1.0作为服务注册中心,Resilience4j 2.1.0替代Hystrix实现熔断机制,配合OpenFeign和Gateway等组件。 核心实操步骤: 搭建Eureka注册中心服务 构建商品
1338 3
|
8月前
|
存储 安全 Java
管理 Spring 微服务中的分布式会话
在微服务架构中,管理分布式会话是确保用户体验一致性和系统可扩展性的关键挑战。本文探讨了在 Spring 框架下实现分布式会话管理的多种方法,包括集中式会话存储和客户端会话存储(如 Cookie),并分析了它们的优缺点。同时,文章还涵盖了与分布式会话相关的安全考虑,如数据加密、令牌验证、安全 Cookie 政策以及服务间身份验证。此外,文中强调了分布式会话在提升系统可扩展性、增强可用性、实现数据一致性及优化资源利用方面的显著优势。通过合理选择会话管理策略,结合 Spring 提供的强大工具,开发人员可以在保证系统鲁棒性的同时,提供无缝的用户体验。
179 0
|
8月前
|
消息中间件 Java 数据库
Spring 微服务中的数据一致性:最终一致性与强一致性
本文探讨了在Spring微服务中实现数据一致性的策略,重点分析了最终一致性和强一致性的定义、优缺点及适用场景。结合Spring Boot与Spring Cloud框架,介绍了如何根据业务需求选择合适的一致性模型,并提供了实现建议,帮助开发者在分布式系统中确保数据的可靠性与同步性。
531 0
|
7月前
|
Cloud Native Serverless API
微服务架构实战指南:从单体应用到云原生的蜕变之路
🌟蒋星熠Jaxonic,代码为舟的星际旅人。深耕微服务架构,擅以DDD拆分服务、构建高可用通信与治理体系。分享从单体到云原生的实战经验,探索技术演进的无限可能。
微服务架构实战指南:从单体应用到云原生的蜕变之路
|
7月前
|
监控 Cloud Native Java
Spring Boot 3.x 微服务架构实战指南
🌟蒋星熠Jaxonic,技术宇宙中的星际旅人。深耕Spring Boot 3.x与微服务架构,探索云原生、性能优化与高可用系统设计。以代码为笔,在二进制星河中谱写极客诗篇。关注我,共赴技术星辰大海!(238字)
1245 2
Spring Boot 3.x 微服务架构实战指南
|
7月前
|
负载均衡 Java API
《深入理解Spring》Spring Cloud 构建分布式系统的微服务全家桶
Spring Cloud为微服务架构提供一站式解决方案,涵盖服务注册、配置管理、负载均衡、熔断限流等核心功能,助力开发者构建高可用、易扩展的分布式系统,并持续向云原生演进。
|
8月前
|
消息中间件 Java Kafka
消息队列比较:Spring 微服务中的 Kafka 与 RabbitMQ
本文深入解析了 Kafka 和 RabbitMQ 两大主流消息队列在 Spring 微服务中的应用与对比。内容涵盖消息队列的基本原理、Kafka 与 RabbitMQ 的核心概念、各自优势及典型用例,并结合 Spring 生态的集成方式,帮助开发者根据实际需求选择合适的消息中间件,提升系统解耦、可扩展性与可靠性。
558 1
消息队列比较:Spring 微服务中的 Kafka 与 RabbitMQ
|
8月前
|
Prometheus 监控 Java
日志收集和Spring 微服务监控的最佳实践
在微服务架构中,日志记录与监控对系统稳定性、问题排查和性能优化至关重要。本文介绍了在 Spring 微服务中实现高效日志记录与监控的最佳实践,涵盖日志级别选择、结构化日志、集中记录、服务ID跟踪、上下文信息添加、日志轮转,以及使用 Spring Boot Actuator、Micrometer、Prometheus、Grafana、ELK 堆栈等工具进行监控与可视化。通过这些方法,可提升系统的可观测性与运维效率。
738 1
日志收集和Spring 微服务监控的最佳实践

热门文章

最新文章