【云岚到家】-day05-2-预约下单-系统开发

【云岚到家】-day05-2-预约下单-系统开发

  • 1 预约下单系统开发
    • 1.1 订单号生成规则
      • 1.1.1 常见的订单号生成规则
      • 1.1.2 本项目订单号生成规则
      • 1.1.3 代码实现
    • 1.2 下单接口实现
      • 1.2.1 Mapper实现
      • 1.2.2 Service实现
      • 1.2.3 Controller
      • 1.2.4 测试
    • 1.3 下单代码优化
      • 1.3.1 事务方法存在远程调用问题
      • 1.3.2 事务失效问题
        • 1.3.2.1 测试事务失效问题
        • 1.3.2.2 解决事务失效问题
      • 1.3.3 循环依赖问题
        • 1.3.3.1 什么是循环依赖
        • 1.3.3.2 Spring 如何解决循环依赖
        • 1.3.3.3 构造参数注入解决循环依赖问题
        • 1.3.3.4 分析本项目自已注入自己没有循环依赖的原因
      • 1.3.4 Service方法事务失效的原因是什么?
      • 1.3.5 Spring 如何解决循环依赖?


1 预约下单系统开发

我们还是接着day05-1的步骤继续开发

1.1 订单号生成规则

1.1.1 常见的订单号生成规则

  1. 自增数字序列

使用数据库的自增主键或者其他递增的数字序列(比如redis的INCR命令)作为订单号的一部分。例如,订单号可以是"202310280001",其中"20231028"表示日期,"0001"是自增的订单序号。

  1. 时间戳+随机数

将年月日时分秒和一定范围内的随机数组合起来。例如,订单号可以是"20181028124523" + “1234”,其中"20181028124523"表示日期和时间,"1234"是随机生成的数字。

使用时间戳+随机数作为主键有重复的风险。

  1. 订单类型+日期+序号

将订单类型(例如"01"表示普通订单,“02"表示VIP订单等)、日期和序号组合起来。例如,订单号可以是"0101028100001”,其中"01"表示订单类型,"20181028"表示日期,"00001"是序号。

加上订单类型的好处是方便客户服务,根据订单号就可以知道订单的类型。

  1. 分布式唯一ID生成器

使用分布式唯一ID生成器(例如Snowflake雪花算法)生成全局唯一的ID作为订单号。这种方法保证了在分布式系统中生成的订单号的唯一性和有序性。

Snowflake 算法根据机器ID、时间戳、序号等因素生成,保证全局唯一性,它的优势在于生成的 ID 具有趋势递增、唯一性、高效性等特点.

Snowflake 算法对系统时钟的依赖性较强,如果系统时钟发生回拨,可能会导致 ID 生成出现问题。因此,在使用 Snowflake 算法时,需要定时进行时钟同步,确保系统时钟的稳定性。

1.1.2 本项目订单号生成规则

19位:2位年+2位月+2位日+13位序号

例如:2311011000000000001

实现方案:

1、前6位通过当前时间获取,即 DateUtils.getFormatDate(LocalDateTime.now(), "yyMMdd")

2、后13位通过Redis的INCR命令实现,即 redisTemplate.opsForValue().increment

1.1.3 代码实现

定义订单管理的接口,在com.jzo2o.orders.manager.service.impl.OrdersCreateServiceImpl中

@Slf4j
@Service
public class OrdersCreateServiceImpl extends ServiceImpl<OrdersMapper, Orders> implements IOrdersCreateService {

    @Resource
    private RedisTemplate<String, Long> redisTemplate;
    /**
     * 生成订单id 格式:{yyMMdd}{13位id}
     *
     * @return
     */
    private Long generateOrderId() {
        //通过Redis自增序列得到序号
        Long id = redisTemplate.opsForValue().increment(ORDERS_SHARD_KEY_ID_GENERATOR, 1);
        //生成订单号   2位年+2位月+2位日+13位序号
        long orderId = DateUtils.getFormatDate(LocalDateTime.now(), "yyMMdd") * 10000000000000L + id;
        return orderId;
    }
    ...

1.2 下单接口实现

1.2.1 Mapper实现

下单接口向orders表插入一条记录,使用Mybatis-Plus生成的Service类或Mapper接口即可实现,不用单独定义Mapper接口。

1.2.2 Service实现

@Slf4j
@Service
public class OrdersCreateServiceImpl extends ServiceImpl<OrdersMapper, Orders> implements IOrdersCreateService {

    @Resource
    private CustomerClient customerClient;
    @Resource
    private FoundationsClient foundationsClient;

    @Resource
    private RedisTemplate<String, Long> redisTemplate;
    /**
     * 生成订单id 格式:{yyMMdd}{13位id}
     *
     * @return
     */
    private Long generateOrderId() {
        //通过Redis自增序列得到序号
        Long id = redisTemplate.opsForValue().increment(RedisConstants.Lock.ORDERS_SHARD_KEY_ID_GENERATOR, 1);
        //生成订单号   2位年+2位月+2位日+13位序号
        long orderId = DateUtils.getFormatDate(LocalDateTime.now(), "yyMMdd") * 10000000000000L + id;
        return orderId;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public PlaceOrderResDTO placeOrder(PlaceOrderReqDTO placeOrderReqDTO) {
        //1.生成订单
        //1.1 订单基本信息
        //1.2 下单人信息-远程调用jzo2o-customer服务
        AddressBookResDTO customerDetail = customerClient.getDetail(placeOrderReqDTO.getAddressBookId());
        //1.3 订单服务信息-远程调用jzo2o-foundations服务
        ServeAggregationResDTO foundationsDetail = foundationsClient.getDetail(placeOrderReqDTO.getServeId());
        //1.4 生成订单id
        Long orderId = generateOrderId();
        //1.5 订单基础信息
        Orders orders = new Orders();
        //1.5.0 下单人id
        orders.setUserId(UserContext.currentUserId());
        //1.5.1 设置订单id
        orders.setId(orderId);
        //1.5.2 设置服务类型id
        orders.setServeTypeId(foundationsDetail.getServeTypeId());
        //1.5.3 设置服务类型名称
        orders.setServeTypeName(foundationsDetail.getServeTypeName());
        //1.5.4 设置服务项id
        orders.setServeItemId(foundationsDetail.getServeItemId());
        //1.5.5 设置服务项名称
        orders.setServeItemName(foundationsDetail.getServeItemName());
        //1.5.6 设置服务项图片
        orders.setServeItemImg(foundationsDetail.getServeItemImg());
        //1.5.7 设置服务单位
        orders.setUnit(foundationsDetail.getUnit());
        //1.5.8 设置服务id
        orders.setServeId(placeOrderReqDTO.getServeId());
        //1.5.9 设置订单状态
        orders.setOrdersStatus(OrderStatusEnum.NO_PAY.getStatus());
        //1.5.10 设置支付状态
        orders.setPayStatus(OrderPayStatusEnum.NO_PAY.getStatus());
        //1.6 订单价格信息
        //1.6.1 单价
        orders.setPrice(foundationsDetail.getPrice());
        //1.6.1 购买数量
        orders.setPurNum(placeOrderReqDTO.getPurNum());
        //1.6.2 总价
        BigDecimal totalAmount = foundationsDetail.getPrice().multiply(new BigDecimal(placeOrderReqDTO.getPurNum()));
        orders.setTotalAmount(totalAmount);
        //1.6.3 优惠金额
        orders.setDiscountAmount(BigDecimal.ZERO);
        //1.6.4 实付金额
        orders.setRealPayAmount(NumberUtils.sub(orders.getTotalAmount(), orders.getDiscountAmount()));
        //1.7 服务地址信息
        //1.7.1 设置服务cityCode
        orders.setCityCode(foundationsDetail.getCityCode());
        //1.7.2 设置服务地址
        String serveAddr = new StringBuffer()
                .append(customerDetail.getProvince())
                .append(customerDetail.getCity())
                .append(customerDetail.getCounty())
                .append(customerDetail.getAddress())
                .toString();
        orders.setServeAddress(serveAddr);
        //1.7.3 设置服务人电话
        orders.setContactsPhone(customerDetail.getPhone());
        //1.7.4 设置服务人姓名
        orders.setContactsName(customerDetail.getName());
        //1.8 服务其他信息
        //1.8.1 设置服务开始时间
        orders.setServeStartTime(placeOrderReqDTO.getServeStartTime());
        //1.8.2 经纬度
        orders.setLon(customerDetail.getLon());
        orders.setLat(customerDetail.getLat());
        //1.8.3 排序字段
        long sortBy = DateUtils.toEpochMilli(orders.getServeStartTime()) + orders.getId() % 100000;
        orders.setSortBy(sortBy);

        //2 插入数据库
        boolean save = this.save(orders);
        if (!save) {
            throw new DbRuntimeException("下单失败");
        }
        return new PlaceOrderResDTO(orders.getId());
    }
}

如果插入失败要记得回滚 @Transactional(rollbackFor = Exception.class)

1.2.3 Controller

@RestController("consumerOrdersController")
@Api(tags = "用户端-订单相关接口")
@RequestMapping("/consumer/orders")
public class ConsumerOrdersController {

    @Resource
    private IOrdersManagerService ordersManagerService;
    @Resource
    private IOrdersCreateService ordersCreateService;

    @ApiOperation("下单接口")
    @PostMapping("/place")
    public PlaceOrderResDTO place(@RequestBody PlaceOrderReqDTO placeOrderReqDTO) {
        PlaceOrderResDTO orderResDTO = ordersCreateService.placeOrder(placeOrderReqDTO);
        return orderResDTO;
    }

1.2.4 测试

测试流程:

启动jzo2o-foundations服务

启动jzo2o-customer服务。

启动jzo2o-publics服务。

启动jzo2o-gateway服务。

启动jzo2o-orders-manager服务。

打开小程序进行下单。

打开小程序,进入首页,点击一个服务进行预约下单。

预期结果:下单成功向orders表写入一条记录,注意观察数据的正确性。

打开小程序,切换到上海

在这里插入图片描述

点击我们的码农洗车,进入详情页

在这里插入图片描述

点击立即预约后填写上门时间

在这里插入图片描述

点击立即预约,查看控制台和数据库

控制台已经显示插入

在这里插入图片描述

查看数据库

在这里插入图片描述

已经插入,测试成功

1.3 下单代码优化

1.3.1 事务方法存在远程调用问题

在事务方法中存在远程调用是否有问题?

下单方法中远程调用查询地址簿信息和服务信息,远程调用涉及网络传输,如果网络传输时间过长会增加数据库事务的时长,如果并发高会把数据库的链接消耗殆尽,导致系统不能正常工作。

将保存订单的代码移动add方法中,add方法只保存订单,去掉placeOrder方法上的@Transactional注解,在add方法上添加@Transactional注解,优化如下:

在com.jzo2o.orders.manager.service.IOrdersCreateService中添加add方法用于保存订单:

public interface IOrdersCreateService extends IService<Orders> {
    public PlaceOrderResDTO placeOrder(PlaceOrderReqDTO placeOrderReqDTO);

    /**
     * 生成订单
     *
     * @param orders
     */
    void add(Orders orders);

}

实现:

@Transactional(rollbackFor = Exception.class)
public void add(Orders orders) {
    boolean save = this.save(orders);
    if (!save) {
        throw new DbRuntimeException("下单失败");
    }
}

修改service的代码,即com.jzo2o.orders.manager.service.impl.OrdersCreateServiceImpl#placeOrder,修改事务的控制粒度

@Override
public PlaceOrderResDTO placeOrder(PlaceOrderReqDTO placeOrderReqDTO) {
    //1.远程调用获取订单相关信息
    //1.1 下单人信息-远程调用jzo2o-customer服务
    AddressBookResDTO customerDetail = customerClient.getDetail(placeOrderReqDTO.getAddressBookId());
    if (customerDetail == null) {
        throw new BadRequestException("预约地址异常,无法下单");
    }
    //1.2 订单服务信息-远程调用jzo2o-foundations服务
    ServeAggregationResDTO foundationsDetail = foundationsClient.getDetail(placeOrderReqDTO.getServeId());
    if (foundationsDetail == null || foundationsDetail.getSaleStatus() != 2) {
        throw new BadRequestException("服务不可用");
    }

    //2 下单前数据准备
    Orders orders = new Orders();
    //2.1 生成订单id
    Long orderId = generateOrderId();
    //2.2 下单人id
    orders.setUserId(UserContext.currentUserId());
    //2.3 设置订单id
    orders.setId(orderId);
    //2.4 设置服务类型id
    orders.setServeTypeId(foundationsDetail.getServeTypeId());
    //2.5 设置服务类型名称
    orders.setServeTypeName(foundationsDetail.getServeTypeName());
    //2.6 设置服务项id
    orders.setServeItemId(foundationsDetail.getServeItemId());
    //2.7 设置服务项名称
    orders.setServeItemName(foundationsDetail.getServeItemName());
    //2.8 设置服务项图片
    orders.setServeItemImg(foundationsDetail.getServeItemImg());
    //2.9 设置服务单位
    orders.setUnit(foundationsDetail.getUnit());
    //2.10 设置服务id
    orders.setServeId(placeOrderReqDTO.getServeId());
    //2.11 设置订单状态
    orders.setOrdersStatus(OrderStatusEnum.NO_PAY.getStatus());
    //2.12 设置支付状态
    orders.setPayStatus(OrderPayStatusEnum.NO_PAY.getStatus());

    //3 订单价格信息
    //3.1 单价
    orders.setPrice(foundationsDetail.getPrice());
    //3.2 购买数量
    orders.setPurNum(placeOrderReqDTO.getPurNum());
    //3.3 总价
    BigDecimal totalAmount = foundationsDetail.getPrice().multiply(new BigDecimal(placeOrderReqDTO.getPurNum()));
    orders.setTotalAmount(totalAmount);
    //3.4 优惠金额
    orders.setDiscountAmount(BigDecimal.ZERO);
    //3.5 实付金额
    orders.setRealPayAmount(NumberUtils.sub(orders.getTotalAmount(), orders.getDiscountAmount()));

    //4 服务地址信息
    //4.1 设置服务cityCode
    orders.setCityCode(foundationsDetail.getCityCode());
    //4.2 设置服务地址
    String serveAddr = new StringBuffer()
            .append(customerDetail.getProvince())
            .append(customerDetail.getCity())
            .append(customerDetail.getCounty())
            .append(customerDetail.getAddress())
            .toString();
    orders.setServeAddress(serveAddr);
    //4.3 设置服务人电话
    orders.setContactsPhone(customerDetail.getPhone());
    //4.4 设置服务人姓名
    orders.setContactsName(customerDetail.getName());

    //5 服务其他信息
    //5.1 设置服务开始时间
    orders.setServeStartTime(placeOrderReqDTO.getServeStartTime());
    //5.2 经纬度
    orders.setLon(customerDetail.getLon());
    orders.setLat(customerDetail.getLat());
    //5.3 排序字段
    long sortBy = DateUtils.toEpochMilli(orders.getServeStartTime()) + orders.getId() % 100000;
    orders.setSortBy(sortBy);

    //6 插入数据库
    this.add(orders);
    //7 返回结果
    return new PlaceOrderResDTO(orders.getId());
}

1.3.2 事务失效问题

1.3.2.1 测试事务失效问题

现在对优化后的代码进行测试,测试add方法是否可以进行事务控制,在add方法中添加异常代码:

@Transactional(rollbackFor = Exception.class)
public void add(Orders orders) {
    boolean save = this.save(orders);
    if (!save) {
        throw new DbRuntimeException("下单失败");
    }
    //测试事务失效
    int i=1/0;
}

如果事务可以被控制,当抛出异常数据库事务进行回滚,最终保存订单失败。

经过测试发现事务控制失败,当add方法抛出异常数据库事务并没有回滚,订单信息保存成功。

1.3.2.2 解决事务失效问题

这里为什么会事务失效呢?

首先要明白Spring进行事务控制是通过代理对象进行的,在调用add方法之前开启事务,方法执行结束提交事务。

如下图所示:

在这里插入图片描述

跟踪add方法调用代码,如下图:

并不是通过代理对象执行的add方法。

在这里插入图片描述

如果是在placeOrder方法上加@Transactional就可以进行事务控制,暂时先在placeOrder方法上添加@Transactional注解,我们跟踪placeOrder方法调用处的代码,在controller方法中调用的placeOrder方法,打断点,如下图:

的确是通过CGLIB代理对象调用的placeOrder方法。

在这里插入图片描述

执行完成后控制台抛出了异常,事务被控制,订单数据没有保存成功。

为什么调用add方法处不是通过代理对象调用呢?

下图展示了代理对象与原始对象之间的关系图:

在这里插入图片描述

代理对象调用原始对象的placeOrder方法,在placeOrder方法中通过this.add()调用add方法,this就是原始对象本身并不是代理对象。

如何解决呢?

在OrdersCreateServiceImpl注入OrdersCreateServiceImpl的代理对象,通过代理对象去调用add方法.

public class OrdersCreateServiceImpl extends ServiceImpl<OrdersMapper, Orders> implements IOrdersCreateService {

    //将自己的代理对象注入
    @Resource
    private IOrdersCreateService owner;
    
    ...
    
    @Override
public PlaceOrderResDTO placeOrder(PlaceOrderReqDTO placeOrderReqDTO) {
    ...
    //保存订单
    owner.add(orders);

    return new PlaceOrderResDTO(orders.getId());
}

重启订单管理服务继续进行测试,在owner.add(orders);打断点:

在这里插入图片描述

从上图可以看出这次是通过代理对象调用的add方法。

方法执行完成事务控制成功,add方法抛出异常,事务回滚,订单保存失败,符合预期结果。

可以参考我的手写Java系列中事务传播原理的7.6中如何正确的调用代理对象的test2(),【吃透Java手写】2-Spring(下)-AOP-事务及传播原理

1.3.3 循环依赖问题

通过将自己注入自己,使用代理对象调用add方法解决了事务失效问题,这样不会产生循环依赖吗?

1.3.3.1 什么是循环依赖

在 Spring 中,如果一个 bean 尝试将自身引用注入到自身中,通常会引发循环依赖。

首先搞清楚什么是循环依赖:

两个Bean,A依赖B,B依赖A就构成了循环依赖,如下图:

在这里插入图片描述

同样的道理,如果在A中注入A表示A依赖A,也就构成了循环依赖。

1.3.3.2 Spring 如何解决循环依赖

以上图为例说明Spring是如何处理循环依赖问题的?

首先按照常规的流程是:

创建A实例–》初始化A–》注入B–》创建B实例–》初始化B–》注入A

在初始化A时需要注入B,要注入B就需要创建B实例再初始化B,而在初始B时需要注入A,此时A还没有创建完成就陷入死循环。

针对循环依赖的问题Spring会上边的过程调整为下边的流程:

创建A实例–》创建B实例–》在B中注入A–》B初始化—》在A中注入B–》A初始化。

Spring是如何做到呢?

Spring会延迟初始化,B需要注入A,此时Spring会先实例化A,把一个半成品A注入给B,延迟A的初始化。

具体的底层原理是Spring通过三级缓存实现:

1)singletonObjects缓存:这是 Spring 容器用来缓存完全初始化好的单例bean 实例的缓存。当一个 bean 初始化完成后,它会被放入singletonObjects缓存中。这个缓存是单例 bean 的最终缓存,也是 BeanFactory 中保存 bean 的主要缓存。

2)earlySingletonObjects缓存:这个缓存是用来保存被实例化但还未完全初始化的 bean 的引用。当一个 bean 已经被实例化(但还未初始化)时,它会被放入earlySingletonObjects缓存中。

3)singletonFactories缓存:这个缓存保存的是用于创建 bean 实例的 ObjectFactory,用于支持循环依赖的延迟初始化。当一个 bean 被实例化,但尚未完全初始化时,Spring 会在singletonFactories缓存中查找该 bean 的ObjectFactory。这个ObjectFactory会在需要时被调用来完成 bean 的初始化。

Spring 通过这三级缓存的组合,来确保在循环依赖情况下,能够正常初始化 bean。当两个或多个 bean 之间存在循环依赖时,Spring 使用 singletonFactories 缓存来存储 bean 的提供者(ObjectFactory)。当一个 bean 在初始化过程中需要依赖另一个还未初始化的 bean 时,Spring 会调用相应的 ObjectFactory 来获取对应的 bean 实例,这样就实现了循环依赖的延迟初始化。一旦 bean 初始化完成,它就会被移动到singletonObjects缓存中。

举例:

创建A实例–》创建B实例–》在B中注入A–》B初始化—》在A中注入B–》A初始化。

创建A实例(半成品),在earlySingletonObjects放入A半成品。

创建B实例(半成品),在earlySingletonObjects放入B半成品。

在B中注入A,通过singletonFactories拿到A的对象工厂,通过对象工厂拿到A的半成品注入到B中。

B初始化完成,将B从earlySingletonObjects移动到singletonObjects。

A初始化完成,将A从earlySingletonObjects移动到singletonObjects。

1.3.3.3 构造参数注入解决循环依赖问题

虽然Spring可以解决上边通过成员变量注入引发的循环依赖问题,但是通过构造参数注入引发的循环依赖问题是会报错。

在这里插入图片描述

为什么上图中的循环依赖会报错呢?

因为创建C需要调用构造方法,而构造方法需要依赖D,此时C是无法实例化的,上边分析Spring解决循环依赖是通过延迟初始化,当出现循环依赖问题可以注入一个半成品,而这里连半成品都无法创建成功。

如何解决这种通过构造参数注入导致的循环依赖问题呢?

可以在C或D的任意一方注入另一方的代理对象而不是注入原始对象,如下:

假设在C的构造方法中注入D的代理对象可以写为:

在构造参数前加@Lazy注解,表示注入D的代理对象。

public C(@Lazy D d){
...
}
1.3.3.4 分析本项目自已注入自己没有循环依赖的原因

我们在OrdersCreateServiceImpl 中注入的是OrdersCreateServiceImpl 的代理对象,并不是OrdersCreateServiceImpl 本身实例,构不成循环依赖。

即使向OrdersCreateServiceImpl 注入的是本身实例也不会报错,Spring通过三级缓存解决循环依赖,会先向成员变量注入一个半成品实例,而后再完成初始化,过程如下:

创建A实例–>向A注入自己–>完成A初始化

1.3.4 Service方法事务失效的原因是什么?

  1. 在方法中捕获了异常没有抛出去,没有把异常抛给代理对象,代理对象捕捉不到异常没有进行事务回滚
  2. 非事务方法内部调用事务方法,不是通过代理对象去调用
  3. @Transactional标记的方法不是public\
  4. 抛出的异常与rollbackFor指定的异常不匹配,默认rollbackFor指定的异常为RuntimeException
  5. 数据库表不支持事务,比如MySQL的MyISAM
  6. Spring的传播行为导致事务失效,比如:PROPAGATION_NEVER、PROPAGATION_NOT_SUPPORTED
    • PROPAGATION_NOT_SUPPORTED —— 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
    • PROPAGATION_NEVER —— 以非事务方式执行,如果当前存在事务,则抛出异常。

1.3.5 Spring 如何解决循环依赖?

Spring通过三级缓存对Bean延迟初始化解决循环依赖。

具体如下:

  1. singletonObjects缓存:这是 Spring 容器用来缓存完全初始化好的单例 bean 实例的缓存。

  2. earlySingletonObjects缓存:这个缓存是用来保存被实例化但还未完全初始化的 bean (半成品)的引用。

  3. singletonFactories缓存:这个缓存保存的是用于创建 bean 实例的 ObjectFactory,用于支持循环依赖的延迟初始化。

Spring 通过这三级缓存的组合,来确保在循环依赖情况下,能够正常初始化 bean。当一个 bean 在初始化过程中需要依赖另一个还未初始化的 bean 时,Spring 会调用相应的 对象工厂来获取对应的 bean 半成品实例,这样就实现了循环依赖的延迟初始化。一旦 bean 初始化完成,它就会被移动到正式的单例缓存中。

对于通过构造方法注入导致循环依赖的在其中一个类的构造方法中使用@Lazy注解注入一个代理对象即可解决。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/788892.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

自动化一些操作

下拉选择框 from selenium import webdriver from time import sleep # 导包 from selenium.webdriver.support.select import Select driver webdriver.Edge() driver.get(r"D:\WORK\ww\web自动化_day01_课件笔记资料代码\web自动化_day01_课件笔记资料代码\02_其他资料…

Collection接口及遍历集合的方式Iterator接口、增强for循环的介绍和使用

Collection接口 概述&#xff1a;单列集合的顶级接口格式&#xff1a;其中泛型决定了集合中能存储什么类型的数据&#xff0c;可以统一元素类型&#xff0c;泛型中只能写引用数据类型&#xff0c;如果不写&#xff0c;默认Object类型。等号前面的泛型必须写&#xff0c;等号后…

收银系统源代码-收银端UI风格

智慧新零售收银系统是一套线下线上一体化收银系统&#xff0c;给商户提供含线下收银称重、线上商城、精细化会员管理、ERP进销存、丰富营销活动、移动店务助手等一体化的解决方案。 如Windows版收银&#xff08;exe安装包&#xff09;、安卓版收银&#xff08;apk安装包&#…

PID控制与模糊PID控制的比较

一、PID控制器的设计 1.PID控制原理图&#xff1a; PID控制其结构框图如下图所示&#xff1a; 图1&#xff1a;PID控制器结构框图 2.PID控制器传递函数的一般表达式 PID控制器传递函数的一般表达形式为&#xff1a; 其中kp为比例增益&#xff1b;ki为积分增益&#xff1b;k…

学习笔记——动态路由——IS-IS中间系统到中间系统(IS-IS工作过程)

六、IS-IS工作过程 1、第一步&#xff1a;建立邻居关系 IS-IS网络中所有路由器之间实现通信&#xff0c;主要通过以下几个步骤&#xff1a; (1)邻居关系建立&#xff1a; 邻居关系建立主要是通过HELLO包交互并协商各种参数&#xff0c;包括链路类型(level-1/level-2)&#…

详细分析Spring中的@Configuration注解基本知识(附Demo)

目录 前言1. 基本知识2. 详细分析3. Demo3.1 简单Bean配置3.2 属性配置3.3 多条件配置 4. 实战拓展 前言 Java的基本知识推荐阅读&#xff1a; java框架 零基础从入门到精通的学习路线 附开源项目面经等&#xff08;超全&#xff09;Spring框架从入门到学精&#xff08;全&am…

k8s集群如kubeadm init和kube-flannel.yam问题

查看k8s中角色内容kubectl get all (显示pod和server以及delment) 删除应用资源选择删除先删除部署查看部署和pod没了服务还在&#xff0c;但资源和功能以及删除&#xff0c;删除服务kubectl delete 服务名&#xff08;部署名&#xff09;&#xff0c;get pods 获取默认空间的容…

毛细管计算软件

思科普毛细管计算软件 输入部分&#xff1a; 一是制冷剂的选取&#xff0c;含常用制冷剂R134A R600A R407C等 A输入热负荷 B蒸发温度 C冷凝温度 D回气温度 毛细管的选项&#xff0c;根据不同内径对应不同长度的毛细管

详细分析@FunctionalInterface的基本知识(附Demo)

目录 前言1. 基本知识2. Demo 前言 Java的基本知识推荐阅读&#xff1a; java框架 零基础从入门到精通的学习路线 附开源项目面经等&#xff08;超全&#xff09;Spring框架从入门到学精&#xff08;全&#xff09; 1. 基本知识 FunctionalInterface 是 Java 8 引入的一个注…

apache:the requested operation has failed使用httpd -t

Apache24\bin cmd 回车 httpd -t 因为我重新压缩了&#xff0c;记住&#xff0c;重新压缩要使用原路径&#xff0c; 因为你安装的 时候使用的是原路径 还是不行就改个端口&#xff0c;切记修改配置文件httpd.conf先把Tomcat停了 Define SRVROOT "F:\Apache\Apache24&q…

从生物学到机械:人眼如何为机器人视觉系统提供无尽灵感?

人眼激发了相机机制的发展&#xff0c;该机制改善了机器人对周围世界的观察和反应方式。 该摄像头系统由马里兰大学&#xff08;UMD&#xff09;计算机科学家领导的团队开发&#xff0c;模仿人眼用于保持清晰稳定的视力的不自主运动。 该团队对相机的原型设计和测试称为…

警惕!焦虑过度的这些症状正在悄悄侵蚀你的生活!

在快节奏的现代社会中&#xff0c;焦虑已成为许多人生活的一部分。适度的焦虑可以激发我们的斗志&#xff0c;推动我们前进。然而&#xff0c;当焦虑过度时&#xff0c;它可能会变成一把双刃剑&#xff0c;对我们的身心健康造成严重威胁。本文将探讨焦虑过度的表现&#xff0c;…

机器视觉/自然语言/生成式人工智能综合应用实验平台-实训平台-教学平台

AIGC是人工智能1.0时代进入2.0时代的重要标志&#xff0c;MIT 科技评论也将Al合成数据列为2022年十大突破性技术之一&#xff0c;甚至将生成性Al(Generative Al) 称为是AI领域过去十年最具前景的进展。同时&#xff0c;AIGC领域岗位需求数量暴涨。高校方面在人工智能专业与机器…

文献笔记|综述|When Large Language Model Meets Optimization

When Large Language Model Meets Optimization 题目&#xff1a;当大型语言模型遇到优化时 作者&#xff1a;Sen Huang , Kaixiang Yang , Sheng Qi and Rui Wang 来源&#xff1a;arXiv 单位&#xff1a;华南理工大学 文章目录 When Large Language Model Meets Optimization…

【android 9】【input】【10.发送按键事件4——View的分发流程】

系列文章目录 可跳转到下面链接查看下表所有内容https://blog.csdn.net/handsomethefirst/article/details/138226266?spm1001.2014.3001.5501文章浏览阅读2次。系列文章大全https://blog.csdn.net/handsomethefirst/article/details/138226266?spm1001.2014.3001.5501 目录…

AGE 可比性、相等性、可排序性和等效性

AGE已经对原始类型&#xff08;布尔值、字符串、整数和浮点数&#xff09;和映射的相等性有了良好的语义。此外&#xff0c;Cypher对整数、浮点数和字符串的可比性和可排序性也有很好的语义。然而&#xff0c;处理不同类型的值与Postgres定义的逻辑和openCypher规范存在偏差&am…

websockt初始化,创建一个webSocket示例

写文思路&#xff1a; 以下主要从几个方面着手写websocket相关&#xff0c;包括以下&#xff1a;什么是webSocket&#xff0c;webSocket的优点和劣势&#xff0c;webSocket工作原理&#xff0c;webSocket握手示例&#xff0c;如何使用webSocket(使用webSocket的一个示例)&#…

【安全设备】堡垒机

一、什么是堡垒机 安全运维审计与风险控制系统即堡垒机&#xff0c;前身为跳板机&#xff0c;跳板机是一个简单的管理设备&#xff0c;但缺乏对运维操作的控制和审计能力。堡垒机是一种特定的网络安全设备&#xff0c;用于在一个网络环境中保护数据和网络不受外部和内部用户的…

k8s record 20240708

一、PaaS 云平台 web界面 资源利用查看 Rancher 5台 CPU 4核 Mem 4g 100g的机器 映射的目录是指docker重启后&#xff0c;数据还在 Rancher可以创建集群也可以托管已有集群 先docker 部署 Rancher&#xff0c;然后通过 Rancher 部署 k8s 想使用 kubectl 还要yum install 安…

深度分析:智算中心建设 - GPU选型

大模型加持AI技术赛道革新发展&#xff0c;“大模型热”愈演愈烈。2024年2月15日&#xff0c;OpenAI首个视频生成模型Sora发布&#xff0c;完美继承DALLE 3的画质和遵循指令能力&#xff0c;能生成长达1分钟全尺寸的高清视频。2024年5月14日&#xff0c;OpenAI发布GPT-4o&#…