5. SpringCloudAlibaba 实践笔记:项目微服务开发

设计完毕后,对各服务进行业务实现

用户微服务开发

引入依赖

在 shop-user 的 pom.xml 文件里添加如下 shop-bean 依赖,shop-bean 模块包含着微服务的各种实体类。

<dependencies>
    <dependency>
        <groupId>io.binghe.shop</groupId>
        <artifactId>shop-bean</artifactId>
        <version>1.0.0-SNAPSHOT</version>
    </dependency>
</dependencies>

配置应用

在项目的 resources 目录下创建 application.yml 文件,并在 application.yml 文件中添加如下配置。

server:
  port: 8060
  servlet:
    context-path: /user
spring:
  application:
    name: server-user
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/shop?useSSL=false&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&serverTimezone=Asia/Shanghai
    username: root
    password: root
    platform: mysql
    type: com.alibaba.druid.pool.DruidDataSource
    # 下面为连接池的补充设置,应用到上面所有数据源中
    # 初始化大小,最小,最大
    initialSize: 10
    minIdle: 5
    maxActive: 20
    # 配置获取连接等待超时的时间
    maxWait: 60000
    # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
    timeBetweenEvictionRunsMillis: 3600000
    # 配置一个连接在池中最小生存的时间,单位是毫秒
    minEvictableIdleTimeMillis: 3600000
    validationQuery: select 1 from dual
    testWhileIdle: true
    testOnBorrow: false
    testOnReturn: false
    # 打开PSCache,并且指定每个连接上PSCache的大小
    poolPreparedStatements: true
    maxPoolPreparedStatementPerConnectionSize: 20
    maxOpenPreparedStatements: 20
    # 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
    filters: stat

  http:
    encoding:
      enabled: true
      charset: UTF-8
      force: true

  mybatis-plus:
    global-config:
      db-config:
        id-type: auto
        field-strategy: not-empty
        table-underline: true
        db-type: oracle
        logic-delete-value: 1
        logic-not-delete-value: 0
    mapper-locations: classpath:/mapper/*.xml
    configuration:
      jdbc-type-for-null: 'null'

持久层开发

package com.shop.user.mapper;
public interface UserMapper extends BaseMapper<User> {
    
}

在resources目录下创建mapper目录里创建UserMapper.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="me.chan.shop.user.mapper.UserMapper">
 
</mapper>
  • UserMapper.xml 文件与 UserMapper 接口是一一对应的,UserMapper 中定义的接口方法,都会在 UserMapper.xml 文件中写对应的 SQL 语句。

业务逻辑层开发

创建 UserService 接口

package com.shop.user.service;
public interface UserService {

    /**
     * 根据id获取用户信息
     */
    User getUserById(Long userId);
}

创建 UserServiceImpl 类,实现 UserService 接口

package com.shop.user.service.impl;
@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private UserMapper userMapper;

    @Override
    public User getUserById(Long userId) {
        return userMapper.selectById(userId);
    }
}

接口层开发

创建 UserController 类

package com.shop.user.controller;

@Slf4j
@RestController
public class UserController {

    @Autowired
    private UserService userService;

    @GetMapping(value = "/get/{uid}")
    public User getUser(@PathVariable("uid") Long uid){
        User user = userService.getUserById(uid);
        log.info("获取到的用户信息为:{}", JSONObject.toJSONString(user));
        return user;
    }
}

服务启动类

package com.shop.user;

@SpringBootApplication
@EnableTransactionManagement(proxyTargetClass = true)
@MapperScan(value = { "me.chan.shop.user.mapper" })
public class UserStarter {

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

商品微服务开发

引入依赖

在 shop-product 的 pom.xml 文件里添加如下 shop-bean 依赖,shop-bean 模块包含着微服务的各种实体类。

<dependencies>
    <dependency>
        <groupId>io.binghe.shop</groupId>
        <artifactId>shop-bean</artifactId>
        <version>1.0.0-SNAPSHOT</version>
    </dependency>
</dependencies>

配置应用

在项目的 resources 目录下创建 application.yml 文件,并在 application.yml 文件中添加如下配置。

以下是对上述配置文件添加参数注释后的内容:

```yaml
server:
  # 应用服务端口号
  port: 8070
  servlet:
    # 应用的上下文路径
    context-path: /product
spring:
  application:
    # 应用名称
    name: server-product
  datasource:
    # 数据库驱动类名
    driver-class-name: com.mysql.cj.jdbc.Driver
    # 数据库连接地址
    url: jdbc:mysql://localhost:3306/shop?useSSL=false&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&serverTimezone=Asia/Shanghai
    # 数据库用户名
    username: root
    # 数据库密码
    password: root
    # 数据库平台类型
    platform: mysql
    # 数据源类型
    type: com.alibaba.druid.pool.DruidDataSource
    # 下面为连接池的补充设置,应用到上面所有数据源中
    # 连接池初始化大小
    initialSize: 10
    # 连接池最小空闲连接数
    minIdle: 5
    # 连接池最大活动连接数
    maxActive: 20
    # 配置获取连接等待超时的时间(单位:毫秒)
    maxWait: 60000
    # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接(单位:毫秒)
    timeBetweenEvictionRunsMillis: 3600000
    # 配置一个连接在池中最小生存的时间(单位:毫秒)
    minEvictableIdleTimeMillis: 3600000
    # 用于检测连接是否有效的查询语句
    validationQuery: select 1 from dual
    # 在空闲时是否检测连接
    testWhileIdle: true
    # 在获取连接时是否检测连接
    testOnBorrow: false
    # 在归还连接时是否检测连接
    testOnReturn: false
    # 是否打开PSCache(预编译语句缓存),并指定每个连接上PSCache的大小
    poolPreparedStatements: true
    # 每个连接上PSCache的大小
    maxPoolPreparedStatementPerConnectionSize: 20
    # 最大打开的预编译语句数量
    maxOpenPreparedStatements: 20
    # 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
    filters: stat

  http:
    encoding:
      # 是否启用编码
      enabled: true
      # 编码字符集
      charset: UTF-8
      # 是否强制使用指定编码
      force: true

  mybatis-plus:
    global-config:
      db-config:
        # 实体类中主键的生成策略
        id-type: auto
        # 字段验证策略
        field-strategy: not-empty
        # 是否开启驼峰转下划线
        table-underline: true
        # 数据库类型
        db-type: oracle
        # 逻辑删除值
        logic-delete-value: 1
        # 逻辑未删除值
        logic-not-delete-value: 0
    mapper-locations: classpath:/mapper/*.xml
    configuration:
      # 当属性为null时,映射的JDBC类型
      jdbc-type-for-null: 'null'

持久层开发

创建 ProductMapper 接口

package com.shop.product.mapper;

public interface ProductMapper extends BaseMapper<User> {
    
}

在 resources 目录下创建 mapper 目录里创建 ProductMapper.xml 文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.shop.product.mapper.ProductMapper">
 
</mapper>
  • ProductMapper.xml 文件与 ProductMapper 接口是一一对应的,ProductMapper 中定义的接口方法,都会在 ProductMapper.xml 文件中写对应的 SQL 语句。

业务逻辑层开发

创建 ProductService 接口

package com.shop.product.service;

public interface ProductService{

     /**
     * 根据商品id获取商品信息
     */
    Product getProductById(Long pid);


    /**
     * 扣减商品库存
     */
    int updateProductStockById(Integer count, Long id);
}

创建 ProductServiceImpl 类,实现 ProductService 接口

@Service
package com.shop.product.service.impl;

public class ProductServiceImpl implements ProductService{
    @Autowired
    private ProductMapper productMapper;
    @Override
    public Product getProductById(Long pid) {
        return productMapper.selectById(pid);
    }

    @Override
    public int updateProductStockById(Integer count, Long id) {
        return productMapper.updateProductStockById(count, id);
    }
}

接口层开发

package com.shop.product.controller;

@RestController
@Slf4j
public class ProductController {

    @Autowired
    private ProductService productService;

    @GetMapping(value = "/get/{pid}")
    public Product getProduct(@PathVariable("pid") Long pid){
        Product product = productService.getProductById(pid);
        log.info("获取到的商品信息为:{}", JSONObject.toJSONString(product));
        return product;
    }

    @GetMapping(value = "/update_count/{pid}/{count}")
    public Result<Integer> updateCount(@PathVariable("pid") Long pid, @PathVariable("count") Integer count){
        log.info("更新商品库存传递的参数为: 商品id:{}, 购买数量:{} ", pid, count);
        int updateCount = productService.updateProductStockById(count, pid);
        Result<Integer> result = new Result<>(HttpCode.SUCCESS, "执行成功", updateCount);
        return result;
    }
}

服务启动类

package com.shop.product;

@SpringBootApplication
@MapperScan(value = { "me.chan.shop.product.mapper" })
@EnableTransactionManagement(proxyTargetClass = true)
public class ProductStarter {

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

订单微服务开发

引入依赖

在 shop-order 的 pom.xml 文件里添加如下 shop-bean 依赖,shop-bean 模块包含着微服务的各种实体类。

<dependencies>
    <dependency>
        <groupId>me.chan</groupId>
        <artifactId>shop-bean</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
</dependencies>

配置应用

在项目的 resources 目录下创建 application.yml 文件,并在 application.yml 文件中添加如下配置。

server:
  port: 8080
  servlet:
    context-path: /order
spring:
  application:
    name: server-user
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/shop?useSSL=false&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&serverTimezone=Asia/Shanghai
    username: root
    password: root
    platform: mysql
    type: com.alibaba.druid.pool.DruidDataSource
    # 下面为连接池的补充设置,应用到上面所有数据源中
    # 初始化大小,最小,最大
    initialSize: 10
    minIdle: 5
    maxActive: 20
    # 配置获取连接等待超时的时间
    maxWait: 60000
    # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
    timeBetweenEvictionRunsMillis: 3600000
    # 配置一个连接在池中最小生存的时间,单位是毫秒
    minEvictableIdleTimeMillis: 3600000
    validationQuery: select 1 from dual
    testWhileIdle: true
    testOnBorrow: false
    testOnReturn: false
    # 打开PSCache,并且指定每个连接上PSCache的大小
    poolPreparedStatements: true
    maxPoolPreparedStatementPerConnectionSize: 20
    maxOpenPreparedStatements: 20
    # 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
    filters: stat

  http:
    encoding:
      enabled: true
      charset: UTF-8
      force: true

  mybatis-plus:
    global-config:
      db-config:
        id-type: auto
        field-strategy: not-empty
        table-underline: true
        db-type: oracle
        logic-delete-value: 1
        logic-not-delete-value: 0
    mapper-locations: classpath:/mapper/*.xml
    configuration:
      jdbc-type-for-null: 'null'

持久层开发

创建 OrderMapper 和 OrderItemMapper 接口

public interface OrderMapper extends BaseMapper<Order> {
}

public interface OrderItemMapper extends BaseMapper<OrderItem> {
}

在 resources 目录下创建 mapper 目录里创建 OrderMapper.xml 文件和 OrderItemMapper.xml 文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.shop.order.mapper.OrderMapper">
    
</mapper>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.shop.order.mapper.OrderItemMapper">
        
</mapper>

业务逻辑层开发

创建OrderService接口:

package com.shop.order.service;
public interface OrderService {
    /**
     * 保存订单
     */
    void saveOrder(OrderParams orderParams);
}

创建 OrderServiceImpl 类,实现 OrderService 接口

package com.shop.order.service.impl;

@Service
@Slf4j
public class OrderServiceImpl implements OrderService {

    @Autowired
    private OrderMapper orderMapper;
    @Autowired
    private OrderItemMapper orderItemMapper;
    @Autowired
    private RestTemplate restTemplate;


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveOrder(OrderParams orderParams) {
        if (orderParams.isEmpty()){
            throw new RuntimeException("参数异常: " + JSONObject.toJSONString(orderParams));
        }

        User user = restTemplate.getForObject("http://localhost:8060/user/get/" + orderParams.getUserId(), User.class);
        if (user == null){
            throw new RuntimeException("未获取到用户信息: " + JSONObject.toJSONString(orderParams));
        }
        Product product = restTemplate.getForObject("http://localhost:8070/product/get/" + orderParams.getProductId(), Product.class);
        if (product == null){
            throw new RuntimeException("未获取到商品信息: " + JSONObject.toJSONString(orderParams));
        }
        if (product.getProStock() < orderParams.getCount()){
            throw new RuntimeException("商品库存不足: " + JSONObject.toJSONString(orderParams));
        }
        Order order = new Order();
        order.setAddress(user.getAddress());
        order.setPhone(user.getPhone());
        order.setUserId(user.getId());
        order.setUsername(user.getUsername());
        order.setTotalPrice(product.getProPrice().multiply(BigDecimal.valueOf(orderParams.getCount())));
        orderMapper.insert(order);

        OrderItem orderItem = new OrderItem();
        orderItem.setNumber(orderParams.getCount());
        orderItem.setOrderId(order.getId());
        orderItem.setProId(product.getId());
        orderItem.setProName(product.getProName());
        orderItem.setProPrice(product.getProPrice());
        orderItemMapper.insert(orderItem);

        Result<Integer> result = restTemplate.getForObject("http://localhost:8070/product/update_count/" + orderParams.getProductId() + "/" + orderParams.getCount(), Result.class);
        if (result.getCode() != HttpCode.SUCCESS){
            throw new RuntimeException("库存扣减失败");
        }
        log.info("库存扣减成功");
    }
}

在saveOrder()方法的实现中,实现的主要逻辑如下。

  1. 判断orderParams封装的参数是否为空,如果参数为空,则抛出参数异常。
  2. 通过RestTemplate调用用户微服务获取用户的基本信息,如果获取的用户信息为空,则抛出未获取到用户信息的异常。
  3. 通过RestTemplate调用商品微服务获取商品的基本信息,如果获取的商品信息为空,则抛出未获取到商品信息的异常。
  4. 判断商品的库存是否小于待扣减的商品数量,如果商品的库存小于待扣减的商品数量,则抛出商品库存不足的异常。
  5. 如果orderParams封装的参数不为空,并且获取的用户信息和商品信息不为空,同时商品的库存充足,则创建订单对象保存订单信息,创建订单条目对象,保存订单条目信息。
  6. 调用商品微服务的接口扣减商品库存。

接口层开发

创建 OrderController 类

package com.shop.order.controller;
@Slf4j
@RestController
public class OrderController {
    @Autowired
    private OrderService orderService;

    @PostMapping(value = "/submit_order")
    public String submitOrder(@RequestBody OrderParams orderParams){
        log.info("提交订单时传递的参数:{}", JSONObject.toJSONString(orderParams));
        orderService.saveOrder(orderParams);
        return "success";
    }
}

服务启动类

package com.shop.order;

@SpringBootApplication
@EnableTransactionManagement(proxyTargetClass = true)
@MapperScan(value = { "me.chan.shop.order.mapper" })
public class OrderStarter {
    public static void main(String[] args){
        SpringApplication.run(OrderStarter.class, args);
    }
}

配置类

由于订单服务需要调用用户微服务和商品微服务,所以需要添加 RestTemplate 来进行远程服务调用,所以需要在配置类中添加 RestTemplate Bean,通过依赖注入方式注入到 OrderService,供 OrderService 调用其他微服务。

package com.shop.order.config

@Configuration
public class LoadBalanceConfig {

    @Bean
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
}
posted @ 2024-11-06 22:52  Jacob-Chen  阅读(34)  评论(0)    收藏  举报