Spring Cloud【Finchley】实战-02订单微服务

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS PostgreSQL,集群系列 2核4GB
简介: Spring Cloud【Finchley】实战-02订单微服务

Spring Cloud【Finchley】专栏

如果还没有系统的学过Spring Cloud ,先到我的专栏去逛逛吧

Spring Cloud 【Finchley】手札


概述

这里我们简单的说下业务相关的需求,重点是体会微服务这种理念是如何落地的。


数据模型-订单微服务

通常来讲,微服务都是分数据库的。这里我们新建个数据库给订单微服务 ,数据库实例名 o2o-order

-- ----------------------------
-- Table structure for order
-- ----------------------------
-- 订单
create table `artisan_order` (
    `order_id` varchar(32) not null,
    `buyer_name` varchar(32) not null comment '买家名字',
    `buyer_phone` varchar(32) not null comment '买家电话',
    `buyer_address` varchar(128) not null comment '买家地址',
    `buyer_openid` varchar(64) not null comment '买家微信openid',
    `order_amount` decimal(8,2) not null comment '订单总金额',
    `order_status` tinyint(3) not null default '0' comment '订单状态, 默认为新下单',
    `pay_status` tinyint(3) not null default '0' comment '支付状态, 默认未支付',
    `create_time` timestamp not null default current_timestamp comment '创建时间',
    `update_time` timestamp not null default current_timestamp on update current_timestamp comment '修改时间',
    primary key (`order_id`),
    key `idx_buyer_openid` (`buyer_openid`)
);
-- ----------------------------
-- Table structure for order_detail
-- ----------------------------
-- 订单详情
create table `order_detail` (
    `detail_id` varchar(32) not null,
    `order_id` varchar(32) not null,
    `product_id` varchar(32) not null,
    `product_name` varchar(64) not null comment '商品名称',
    `product_price` decimal(8,2) not null comment '当前价格,单位分',
    `product_quantity` int not null comment '数量',
    `product_icon` varchar(512) comment '小图',
    `create_time` timestamp not null default current_timestamp comment '创建时间',
    `update_time` timestamp not null default current_timestamp on update current_timestamp comment '修改时间',
    primary key (`detail_id`),
    key `idx_order_id` (`order_id`)
);

订单与订单详情是一对多的关系,一个订单中可能包含多个订单详情,比如我下一个订单,这个订单中买了1杯奶茶、2杯可乐等。

order_detail中不仅设计了product_id,同时也冗余了 product_name product_price product_icon等,主要是考虑到有些促销活动这些字段会经常更改这些因素。


API

请求:

POST方式 /order/create

内容:

"name": "xxx",
    "phone": "xxxx",
    "address": "xxxx",
    "openid": "xxxx", //用户的微信openid
    "items": [
        {
            "productId": "xxxxxx",
            "productQuantity": 2   //购买数量
        }
    ]

后端尽量少依赖前端传递的数据,为了安全起见,产品相关的数据,只传递了一个productId和productQuantity,而没有将价格、描述等等一并传递,不传递就不会被篡改,也减少了交互数据的大小。

返回:

{
  "code": 0,
  "msg": "成功",
  "data": {
      "orderId": "123456" 
  }
}

业务逻辑分析

  1. 校验前台入参
  2. 查询商品信息(调用商品微服务
  3. 计算订单总价
  4. 扣减库存(调用商品微服务
  5. 订单入库

搭建订单微服务

依赖及配置文件

pom.xml

<?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 http://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.0.3.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.artisan</groupId>
    <artifactId>artisan_order</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>artisan_order</name>
    <description>Order</description>
    <properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Finchley.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

application.yml

这里我们连接到 order微服务的数据库。 我这里本地环境,就新建了个数据库实例。

server:
  port: 8081
spring:
  application:
    name: artisan-order
  # datasource
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/o2o-order?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&useSSL=false
    username: root
    password: root
  #jpa
  jpa:
    show-sql: true
# Eureka
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/

将微服务注册到注册中心

application.yml中配置了Eureka的信息后,我们在启动类增加@EnableEurekaClient即可

启动注册中心微服务,启动该服务

访问 http://localhost:8761/

注册成功


实体类

Order

package com.artisan.order.domain;
import lombok.Data;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import java.math.BigDecimal;
import java.util.Date;
@Data
// 必不可少
@Entity
@Table(name = "artisan_order")
public class Order {
    /**
     * 订单id.
     */
    @Id
    private String orderId;
    /**
     * 买家名字.
     */
    private String buyerName;
    /**
     * 买家手机号.
     */
    private String buyerPhone;
    /**
     * 买家地址.
     */
    private String buyerAddress;
    /**
     * 买家微信Openid.
     */
    private String buyerOpenid;
    /**
     * 订单总金额.
     */
    private BigDecimal orderAmount;
    /**
     * 订单状态, 默认为0新下单.
     */
    private Integer orderStatus;
    /**
     * 支付状态, 默认为0未支付.
     */
    private Integer payStatus;
    /**
     * 创建时间.
     */
    private Date createTime;
    /**
     * 更新时间.
     */
    private Date updateTime;
}

OrderDetail

package com.artisan.order.domain;
import lombok.Data;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import java.math.BigDecimal;
@Data
// 必不可少
@Entity
// 如果实体类是OrderDetail,表名是order_detail,则这个注解可省略
@Table(name = "order_detail")
public class OrderDetail {
    // 必不可少
    @Id
    private String detailId;
    /**
     * 订单id.
     */
    private String orderId;
    /**
     * 商品id.
     */
    private String productId;
    /**
     * 商品名称.
     */
    private String productName;
    /**
     * 商品单价.
     */
    private BigDecimal productPrice;
    /**
     * 商品数量.
     */
    private Integer productQuantity;
    /**
     * 商品小图.
     */
    private String productIcon;
}

Dao层

创建订单无非就是往这两个表里写入数据。直接利用jpa提供的save方法即可。

OrderRepository

空实现 ,利用jpa本身提供的save方法

package com.artisan.order.repository;
import com.artisan.order.domain.Order;
import org.springframework.data.jpa.repository.JpaRepository;
// JpaRepository<Order,String>  第一个是要操作的对象,第二个是实体类中标注的@Id的字段的类型 (主键类型)
public interface OrderRepository extends JpaRepository<Order,String> {
}

OrderDetailRepository

空实现 ,利用jpa本身提供的save方法

package com.artisan.order.repository;
import com.artisan.order.domain.OrderDetail;
import org.springframework.data.jpa.repository.JpaRepository;
public interface OrderDetailRepository extends JpaRepository<OrderDetail ,String> {
}

单元测试

OrderRepositoryTest

package com.artisan.order.repository;
import com.artisan.order.ArtisanOrderApplicationTests;
import com.artisan.order.domain.Order;
import com.artisan.order.enums.OrderStatusEnum;
import com.artisan.order.enums.PayStatusEnum;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
@Component
public class OrderRepositoryTest  extends ArtisanOrderApplicationTests {
    @Autowired
    private OrderRepository orderRepository;
    @Test
    public void testSave(){
        Order order = new Order();
        order.setOrderId("1222");
        order.setBuyerName("artisan");
        order.setBuyerPhone("123445664");
        order.setBuyerAddress("Artisan Tech");
        order.setBuyerOpenid("11112233");
        order.setOrderAmount(new BigDecimal(3.9));
        order.setOrderStatus(OrderStatusEnum.NEW.getCode());
        order.setPayStatus(PayStatusEnum.WAIT.getCode());
        Order result = orderRepository.save(order);
        Assert.assertNotNull(result);
    }
}

数据库记录


package com.artisan.order.repository;
import com.artisan.order.ArtisanOrderApplicationTests;
import com.artisan.order.domain.OrderDetail;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
@Component
public class OrderDetailRepositoryTest extends ArtisanOrderApplicationTests {
    @Autowired
    private OrderDetailRepository orderDetailRepository;
    @Test
    public void testSave() {
        OrderDetail orderDetail = new OrderDetail();
        orderDetail.setDetailId("1111");
        orderDetail.setOrderId("111111");
        orderDetail.setProductIcon("http://xxx.com");
        orderDetail.setProductId("22222");
        orderDetail.setProductName("拿铁");
        orderDetail.setProductPrice(new BigDecimal(0.01));
        orderDetail.setProductQuantity(2);
        OrderDetail result = orderDetailRepository.save(orderDetail);
        Assert.assertTrue(result != null);
    }
}

单元测试 通过。


Service层

分析下,我们要往artisan_order 和 order_detail中写入数据,肯定要传入Order和OrderDetail实体类,类似于 createOrder(Order order , OrderDetail orderDetail) ,根据业务规则,一个Order中可能有多个OrderDetail, 所以入参OrderDetail 必须是个集合,并且返回结果也不好定义。 因此我们将这俩合并一下,封装成DTO来使用,作为入参和返回结果。


Order 和OrderDetail 合并为一个DTO对象

下图中类上少儿个注解 @Data,注意补上


OrderService接口和实现类

package com.artisan.order.service;
import com.artisan.order.dto.OrderDTO;
public interface OrderService {
    OrderDTO createOrder(OrderDTO orderDTO);
}

我们来分析下前端的请求

"name": "xxx",
    "phone": "xxxx",
    "address": "xxxx",
    "openid": "xxxx", //用户的微信openid
    "items": [
        {
            "productId": "xxxxxx",
            "productQuantity": 2   //购买数量
        }
    ]

结合业务逻辑

  • 校验前台入参
  • 查询商品信息(调用商品微服务)
  • 计算订单总价
  • 扣减库存(调用商品微服务)
  • 订单入库

逐一分析下目前的可行性

  • 参数校验,我们放在Controller层校验,所以Service层这里不写
  • 调用微服务的,我们目前还不具备,没法做
  • 计算订单总价,前台入参仅仅传了ProductId, 而Product的数据需要调用商品微服务,目前没法做
  • 订单入库,其实分两部分,第一个是artisan_order表,第二个是Order_detail表。 order_detail表包含了Product的内容,目前也是做不了。

综合分析,目前在Service层能做的仅仅是 入库artisan_order表

那在实现类里,我们先实现部分吧

package com.artisan.order.service.impl;
import com.artisan.order.domain.Order;
import com.artisan.order.dto.OrderDTO;
import com.artisan.order.enums.OrderStatusEnum;
import com.artisan.order.enums.PayStatusEnum;
import com.artisan.order.repository.OrderDetailRepository;
import com.artisan.order.repository.OrderRepository;
import com.artisan.order.service.OrderService;
import com.artisan.order.utils.KeyUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
@Service
public class OrderServiceImpl implements OrderService {
    @Autowired
    OrderRepository orderRepository;
    @Autowired
    OrderDetailRepository orderDetailRepository;
    @Override
    public OrderDTO createOrder(OrderDTO orderDTO) {
        // TODO 查询商品信息(调用商品微服务)
        // TODO  计算订单总价
        // TODO  扣减库存(调用商品微服务)
        //订单入库
        Order order = new Order();
        orderDTO.setOrderId(KeyUtil.genUniqueKey());
        // 复制属性
        BeanUtils.copyProperties(orderDTO, order);
        // 设置其他属性
        order.setOrderAmount(new BigDecimal("100")); // TODO 后需要修改
        order.setOrderStatus(OrderStatusEnum.NEW.getCode());
        order.setPayStatus(PayStatusEnum.WAIT.getCode());
        orderRepository.save(order);
        return orderDTO;
    }
}

Controller层

这里仅列出关键代码,其余请参考github

package com.artisan.order.form;
import lombok.Data;
import org.hibernate.validator.constraints.NotEmpty;
import java.util.List;
@Data
public class OrderForm {
    /**
     * 对应
     *
     * {
     *     "name": "xxx",
     *     "phone": "xxxx",
     *     "address": "xxxx",
     *     "openid": "xxxx", //用户的微信openid
     *     "items": [
     *         {
     *             "productId": "xxxxxx",
     *             "productQuantity": 2   //购买数量
     *         }
     *     ]
     * }
     *
     *
     *
     */
    /**
     * 买家姓名
     */
    @NotEmpty(message = "姓名必填")
    private String name;
    /**
     * 买家手机号
     */
    @NotEmpty(message = "手机号必填")
    private String phone;
    /**
     * 买家地址
     */
    @NotEmpty(message = "地址必填")
    private String address;
    /**
     * 买家微信openid
     */
    @NotEmpty(message = "openid必填")
    private String openid;
    /**
     * 购物车
     */
    @NotEmpty(message = "购物车不能为空")
    private String items;
}
package com.artisan.order.converter;
import com.artisan.order.domain.OrderDetail;
import com.artisan.order.dto.OrderDTO;
import com.artisan.order.enums.ResultEnum;
import com.artisan.order.exception.OrderException;
import com.artisan.order.form.OrderForm;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import lombok.extern.slf4j.Slf4j;
import java.util.ArrayList;
import java.util.List;
@Slf4j
public class OrderForm2OrderDTOConverter {
    public static OrderDTO convert(OrderForm orderForm) {
        Gson gson = new Gson();
        OrderDTO orderDTO = new OrderDTO();
        orderDTO.setBuyerName(orderForm.getName());
        orderDTO.setBuyerPhone(orderForm.getPhone());
        orderDTO.setBuyerAddress(orderForm.getAddress());
        orderDTO.setBuyerOpenid(orderForm.getOpenid());
        List<OrderDetail> orderDetailList = new ArrayList<>();
        try {
            // fromJson 从Json相关对象到Java实体的方法 ,转换成列表类型
            orderDetailList = gson.fromJson(orderForm.getItems(),
                    new TypeToken<List<OrderDetail>>() {
                    }.getType());
        }catch(Exception e){
            log.error("【json转换】错误, string={}", orderForm.getItems());
            throw new OrderException(ResultEnum.PARAM_ERROR);
        }
        orderDTO.setOrderDetailList(orderDetailList);
        return orderDTO;
    }
}
package com.artisan.order.controller;
import com.artisan.order.converter.OrderForm2OrderDTOConverter;
import com.artisan.order.dto.OrderDTO;
import com.artisan.order.enums.ResultEnum;
import com.artisan.order.exception.OrderException;
import com.artisan.order.form.OrderForm;
import com.artisan.order.service.OrderService;
import com.artisan.order.vo.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/order")
@Slf4j
public class OrderController {
    @Autowired
    OrderService orderService;
    @PostMapping("/create")
    public Result create(@Valid  OrderForm orderForm,
                         BindingResult bindingResult) {
        if (bindingResult.hasErrors()){
            log.error("【Create Order】参数不正确, orderForm={}", orderForm);
            throw new OrderException(ResultEnum.PARAM_ERROR.getCode(),
                    bindingResult.getFieldError().getDefaultMessage());
        }
        // orderForm -> orderDTO
        OrderDTO orderDTO = OrderForm2OrderDTOConverter.convert(orderForm);
        if (CollectionUtils.isEmpty(orderDTO.getOrderDetailList())) {
            log.error("【Create Order】购物车信息为空");
            throw new OrderException(ResultEnum.CART_EMPTY);
        }
        OrderDTO result = orderService.createOrder(orderDTO);
        Map<String, String> map = new HashMap<>();
        map.put("orderId", result.getOrderId());
        return Result.success(map);
    }
}

测试

使用PostMan

查看数据库

OK

说明下: x-www-form-urlencoded 这种格式 就是application/x-www-from-urlencoded,会将表单内的数据转换为键值对,比如:name=artisan&phone=123


知识点总结

Gson库

谷歌提供的 JSON – Java Object 相互转换的 Java序列化/反序列化库。

将Json转换为对象

解析json数组
orderDetailList = gson.fromJson(orderForm.getItems(),
                    new TypeToken<List<OrderDetail>>() {
                    }.getType());

更多示例参考


将Java对象转换为Json


Github

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


相关实践学习
如何快速连接云数据库RDS MySQL
本场景介绍如何通过阿里云数据管理服务DMS快速连接云数据库RDS MySQL,然后进行数据表的CRUD操作。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
24天前
|
JSON Java API
利用Spring Cloud Gateway Predicate优化微服务路由策略
Spring Cloud Gateway 的路由配置中,`predicates`​(断言)用于定义哪些请求应该匹配特定的路由规则。 断言是Gateway在进行路由时,根据具体的请求信息如请求路径、请求方法、请求参数等进行匹配的规则。当一个请求的信息符合断言设置的条件时,Gateway就会将该请求路由到对应的服务上。
134 69
利用Spring Cloud Gateway Predicate优化微服务路由策略
|
8天前
|
搜索推荐 NoSQL Java
微服务架构设计与实践:用Spring Cloud实现抖音的推荐系统
本文基于Spring Cloud实现了一个简化的抖音推荐系统,涵盖用户行为管理、视频资源管理、个性化推荐和实时数据处理四大核心功能。通过Eureka进行服务注册与发现,使用Feign实现服务间调用,并借助Redis缓存用户画像,Kafka传递用户行为数据。文章详细介绍了项目搭建、服务创建及配置过程,包括用户服务、视频服务、推荐服务和数据处理服务的开发步骤。最后,通过业务测试验证了系统的功能,并引入Resilience4j实现服务降级,确保系统在部分服务故障时仍能正常运行。此示例旨在帮助读者理解微服务架构的设计思路与实践方法。
52 16
|
11天前
|
监控 JavaScript 数据可视化
建筑施工一体化信息管理平台源码,支持微服务架构,采用Java、Spring Cloud、Vue等技术开发。
智慧工地云平台是专为建筑施工领域打造的一体化信息管理平台,利用大数据、云计算、物联网等技术,实现施工区域各系统数据汇总与可视化管理。平台涵盖人员、设备、物料、环境等关键因素的实时监控与数据分析,提供远程指挥、决策支持等功能,提升工作效率,促进产业信息化发展。系统由PC端、APP移动端及项目、监管、数据屏三大平台组成,支持微服务架构,采用Java、Spring Cloud、Vue等技术开发。
|
16天前
|
Java 关系型数据库 数据库
微服务SpringCloud分布式事务之Seata
SpringCloud+SpringCloudAlibaba的Seata实现分布式事务,步骤超详细,附带视频教程
39 1
|
3月前
|
人工智能 自然语言处理 前端开发
SpringBoot + 通义千问 + 自定义React组件:支持EventStream数据解析的技术实践
【10月更文挑战第7天】在现代Web开发中,集成多种技术栈以实现复杂的功能需求已成为常态。本文将详细介绍如何使用SpringBoot作为后端框架,结合阿里巴巴的通义千问(一个强大的自然语言处理服务),并通过自定义React组件来支持服务器发送事件(SSE, Server-Sent Events)的EventStream数据解析。这一组合不仅能够实现高效的实时通信,还能利用AI技术提升用户体验。
290 2
|
3天前
|
XML JavaScript Java
SpringBoot集成Shiro权限+Jwt认证
本文主要描述如何快速基于SpringBoot 2.5.X版本集成Shiro+JWT框架,让大家快速实现无状态登陆和接口权限认证主体框架,具体业务细节未实现,大家按照实际项目补充。
31 11
|
5天前
|
缓存 安全 Java
Spring Boot 3 集成 Spring Security + JWT
本文详细介绍了如何使用Spring Boot 3和Spring Security集成JWT,实现前后端分离的安全认证概述了从入门到引入数据库,再到使用JWT的完整流程。列举了项目中用到的关键依赖,如MyBatis-Plus、Hutool等。简要提及了系统配置表、部门表、字典表等表结构。使用Hutool-jwt工具类进行JWT校验。配置忽略路径、禁用CSRF、添加JWT校验过滤器等。实现登录接口,返回token等信息。
107 12
|
25天前
|
Java 数据库连接 Maven
最新版 | 深入剖析SpringBoot3源码——分析自动装配原理(面试常考)
自动装配是现在面试中常考的一道面试题。本文基于最新的 SpringBoot 3.3.3 版本的源码来分析自动装配的原理,并在文未说明了SpringBoot2和SpringBoot3的自动装配源码中区别,以及面试回答的拿分核心话术。
最新版 | 深入剖析SpringBoot3源码——分析自动装配原理(面试常考)
|
11天前
|
Java 测试技术 应用服务中间件
Spring Boot 如何测试打包部署
本文介绍了 Spring Boot 项目的开发、调试、打包及投产上线的全流程。主要内容包括: 1. **单元测试**:通过添加 `spring-boot-starter-test` 包,使用 `@RunWith(SpringRunner.class)` 和 `@SpringBootTest` 注解进行测试类开发。 2. **集成测试**:支持热部署,通过添加 `spring-boot-devtools` 实现代码修改后自动重启。 3. **投产上线**:提供两种部署方案,一是打包成 jar 包直接运行,二是打包成 war 包部署到 Tomcat 服务器。
37 10
|
11天前
|
存储 安全 Java
Spring Boot 3 集成Spring AOP实现系统日志记录
本文介绍了如何在Spring Boot 3中集成Spring AOP实现系统日志记录功能。通过定义`SysLog`注解和配置相应的AOP切面,可以在方法执行前后自动记录日志信息,包括操作的开始时间、结束时间、请求参数、返回结果、异常信息等,并将这些信息保存到数据库中。此外,还使用了`ThreadLocal`变量来存储每个线程独立的日志数据,确保线程安全。文中还展示了项目实战中的部分代码片段,以及基于Spring Boot 3 + Vue 3构建的快速开发框架的简介与内置功能列表。此框架结合了当前主流技术栈,提供了用户管理、权限控制、接口文档自动生成等多项实用特性。
52 8