【项目实战】从零到一搭建Spring Boot整合Mybatis-plus
前言
2023年想搭建一套属于自己的框架,做一个属于自己想法的项目。这些年工作中一直用公司已有的框架,以前有跟着学习视频搭建过,但自己真正动手搭建时发现问题还是很多,比如没有引入Mybatis-plus包之前,项目api test是成功的,引入Mybatis-plus包后就一直启动不成功,而且异常信息也不抛出,后引入actuator应用健康监测才抛异常信息排查解决。我会下面文中说明为什么引入这个pom作用是什么,pom引入的每个包都有其作用,而不是照搬别人的框架过来,引入不必要的包。
看该文章前需要了解maven pom结构,idea创建一个项目的步骤,spring boot知识,往下阅读默认都具备了。
环境说明:idea2022.3 ,jdk17 ,maven 3.6.3,mysql-8.0.31
一、创建一个maven空项目
项目创建步骤不截图了,默认大家懂了。创建一个空maven项目目的是作为父级引入公用的一些,一般企业级的项目都是这样的结构,当然如果想简单一点,该步骤也是可以省略掉的。
创建出来把一些不要的包,文件都删除掉,目录结构如下图
1、确定spring boot版本号
因为我后面是要实现微服务框架项目,所以在选择Spring Boot版本号是与Spring Cloud是对应的。(最开始我是选择Spring Boot最新版本号3.0.0,但发现在整合Mybatis-Plus 3.5.2出现很多问题,主要是spring boot 3.0自动注入方式改变,原因说明可参考博客:Spring Boot3.0升级,踩坑之旅,附解决方案 :https://www.cnblogs.com/wayn111/p/16950025.html)
访问spring官网https://spring.io,选择projects下 Spring Cloud 右侧Learn ,目前最新版本是2022.0.0GA(上面已有说明不选择最新版),往下找第一个GA版本2021.0.5。
2 、pom整理
关键Spring Boot pom定义如下,完整pom定义详见示例源码。
方便后面Api定义的声明,增加引入Swagger
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.13</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<!-- Spring Cloud -->
<spring.cloud.version>2021.0.5</spring.cloud.version>
<!-- Spring Boot -->
<spring.boot.version>2.6.13</spring.boot.version>
<!-- Swagger -->
<knife4j.spring.boot.version>2.0.8</knife4j.spring.boot.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring.boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring.cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--Swagger-->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-micro-spring-boot-starter</artifactId>
<version>${knife4j.spring.boot.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
二、添加子模块demo-service
1、Spring Boot Api Test
关键pom引入,就可以进行api test,完整pom定义详见示例源码。
添加spring-boot-starter-web是web应用需要的包,Controller等相关,spring-boot-starter-actuator 是Spring Boot应用健康监测,如果应用有异常可以捕获到,供我们排查。
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--actuator-应用健康监测 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--Swagger-->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-micro-spring-boot-starter</artifactId>
</dependency>
</dependencies>
代码层面自己手动加package: com.elephant.demo , 创建启动类DemoApplication,代码如下:
package com.elephant.demo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @author xiufen.huang
* @description:
* @date 2023-01-08-18:38
*/
@Slf4j
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
log.info("========================= elephant-demo-启动成功 ==========================");
}
}
点击试运行,从控制台的日志来看已成功。端口暂时没有设置默认8080。
添加application.yml,设置端口8071
添加测试Controller类:TestController,代码如下:
package com.elephant.demo.controller;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author xiufen.huang
* @description:
* @date 2023-01-08-18:47
*/
@Api(value = "TestController", tags = "测试Controller")
@Slf4j
@RestController
@RequestMapping("/test")
@RequiredArgsConstructor
public class TestController {
@ApiOperation(value = "测试接口")
@GetMapping("/index")
public String test() {
return "ok";
}
}
api test访问 http://127.0.0.1:8071/test/index ,响应成功ok,
2、整合Mybatis-plus
2.1、正常的pom引入说明
数据库采用mysql,mybatis-plus选择版本号3.5.2 ,只需要引入两个包即可。
<properties>
<mybatis.plus.version>3.5.2</mybatis.plus.version>
<mysql.connector.version>8.0.31</mysql.connector.version>
</properties>
<!--Mybatis-plus-start-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis.plus.version}</version>
</dependency>
<!--Mybatis-plus-end-->
<!-- MySql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.connector.version}</version>
</dependency>
2.2、正常的yaml配置
mybatis-plus配置,mapper.xml,实体扫描,打印sql
#mybatis-plus配置
mybatis-plus:
mapper-locations: classpath:com/elephant/demo/**/mapper/*Mapper.xml
#实体扫描,多个package用逗号或者分号分隔
type-aliases-package: com.elephant.demo.**.entity
configuration:
# 驼峰转换 从数据库列名到Java属性驼峰命名的类似映射
map-underscore-to-camel-case: true
# 打印sql
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
数据库连接配置
spring:
application:
# 应用名称
name: elephant-demo
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
# 换成自己的配置
url: jdbc:mysql://127.0.0.1:3306/test
username: root
password: 123456
2.3、测试的建表脚本和数据
我的测试表是订单表,仅针对功能测试使用,不一定按我的,我把建表脚本提供和初始化脚本提供出来。方便如果下载我的demo代码可以初始化后,修改下配置即可运行。在resources/ sql下。
# 创建表语句
CREATE TABLE `ft_order` (
`id` varchar(32) NOT NULL COMMENT '主键',
`create_user` varchar(32) DEFAULT NULL COMMENT '创建人',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_user` varchar(32) DEFAULT NULL COMMENT '更新人',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
`status` int DEFAULT NULL COMMENT '业务状态: 0-正常, 1-已删除',
`is_deleted` bit(1) DEFAULT b'0' COMMENT '是否删除',
`order_no` varchar(50) DEFAULT NULL COMMENT '订单编号',
`customer_name` varchar(100) DEFAULT NULL COMMENT '客户名称',
`customer_email` varchar(100) DEFAULT NULL COMMENT '客户邮箱',
`product_status` int DEFAULT NULL COMMENT '货品状态: 1-备车中,2-出口手续办理中,3-转移待出口(手续办理完成),4-报关完成,5-车辆达到指定港口,6-运输中,7-已抵达,8-确认收货',
`remark` varchar(256) DEFAULT NULL COMMENT '备注',
PRIMARY KEY (`id`),
KEY `idx_customer_name` (`customer_name`) USING BTREE,
KEY `idx_order_no_email_name` (`order_no`,`customer_email`,`customer_name`) USING BTREE COMMENT 'PC后台查询组合索引',
KEY `idx_customer_email_name` (`customer_email`,`customer_name`) USING BTREE
)COMMENT='外贸订单信息';
测试初始化数据
INSERT INTO `ft_order`(`id`, `create_user`, `create_time`, `update_user`, `update_time`, `status`, `is_deleted`, `order_no`, `customer_name`, `customer_email`, `product_status`, `remark`) VALUES ('1001', NULL, '2023-01-03 15:18:46', NULL, '2023-01-03 15:18:49', 0, b'0', 'TEST20230103001', 'Jack', 'test1@test.com', NULL, NULL);
INSERT INTO `ft_order`(`id`, `create_user`, `create_time`, `update_user`, `update_time`, `status`, `is_deleted`, `order_no`, `customer_name`, `customer_email`, `product_status`, `remark`) VALUES ('1002', NULL, '2023-01-04 16:18:49', NULL, '2023-01-04 16:18:49', 0, b'0', 'TEST20230104001', 'Jack', 'test1@test.com', NULL, NULL);
INSERT INTO `ft_order`(`id`, `create_user`, `create_time`, `update_user`, `update_time`, `status`, `is_deleted`, `order_no`, `customer_name`, `customer_email`, `product_status`, `remark`) VALUES ('1003', NULL, '2023-01-04 17:18:49', NULL, '2023-01-04 17:18:49', 0, b'0', 'TEST20230104002', 'Jack', 'test1@test.com', NULL, NULL);
INSERT INTO `ft_order`(`id`, `create_user`, `create_time`, `update_user`, `update_time`, `status`, `is_deleted`, `order_no`, `customer_name`, `customer_email`, `product_status`, `remark`) VALUES ('1004', NULL, '2023-01-04 18:18:49', NULL, '2023-01-04 18:18:49', 0, b'0', 'TEST20230104003', 'Jack', 'test1@test.com', NULL, NULL);
INSERT INTO `ft_order`(`id`, `create_user`, `create_time`, `update_user`, `update_time`, `status`, `is_deleted`, `order_no`, `customer_name`, `customer_email`, `product_status`, `remark`) VALUES ('1005', NULL, '2023-01-04 19:18:49', NULL, '2023-01-04 19:18:49', 0, b'0', 'TEST20230104004', 'Jack', 'test1@test.com', NULL, NULL);
2.4、动态数据源的pom引入说明
如果想采用动态数据源,再引入这个pom包
<properties>
<mybatis.plus.dynamic.version>3.5.2</mybatis.plus.dynamic.version>
</properties>
<!-- 动态数据源-start -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>${mybatis.plus.dynamic.version}</version>
</dependency>
<!-- 动态数据源-end -->
2.5、动态数据源的yaml配置
# 动态数据源
# pom 引入 dynamic-datasource-spring-boot-starter
spring:
application:
# 应用名称
name: elephant-demo
datasource:
dynamic:
primary: master #设置默认的数据源或者数据源组,默认值即为master
strict: false #严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源
datasource:
master:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/test
username: root
password: 123456
slave:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/test
username: root
password: 123456
3、示例代码说明
由于代码比较多不一一贴出来,主要代码贴出来做说明。代码结构做一下说明,是采用4层结构,多了一层业务实现层是为了避免后面业务复杂了,出现循环依赖的情况。具体是用3层还是4层可以按需。
本次示例给出3个:根据主键查询,分页列表查询,保存订单数据。
3.1.Dao层代码
接口继承IService ,实现类继承 ServiceImpl<OrderMapper, Order>。IOrderRepository 代码如下:
/**
* @author xiufen.huang
* @description:
* @date 2022-12-21-15:38
*/
public interface IOrderRepository extends IService<Order> {
/**
* 获取订单信息
* @param orderId 订单id
* @return com.elephant.demo.model.entity.Order
* @author xiufen.huang
* @date 2023-01-19 11:01
*/
Order getById(String orderId);
/**
* 订单分页列表
* @param param 查询参数
* @return com.baomidou.mybatisplus.core.metadata.IPage<com.elephant.demo.model.vo.OrderPageVo>
* @author xiufen.huang
* @date 2023-01-19 11:01
*/
IPage<OrderPageVo> getOrderPage(OrderPageParam param);
/**
* 保存订单数据
* @param param
* @return java.lang.Boolean
* @author xiufen.huang
* @date 2022-12-16 15:50
*/
Boolean saveOrder(OrderCreateParam param);
}
OrderRepositoryImpl 代码如下:
@Slf4j
@Service
public class OrderRepositoryImpl extends ServiceImpl<OrderMapper, Order> implements IOrderRepository {
// 后面分开讲解
}
3.2.单个查询
比如根据主键查询,可以用Mybatis提供封装好的方法selectById() 。具体代码实现如下
@Override
public Order getById(String orderId) {
Order order = this.baseMapper.selectById(orderId);
return order;
}
Service,Controller的代码只需要调用一下即可,实际业务场景再根据需要做一些处理。
3.3.订单分页列表
实现思路:构造分页参数,具体查询实现在Mapper.xml用原生的sql结合动态参数。比如我的场景:根据关键词(订单号/客户名称/客户邮箱)查询订单数据。
OrderMapper 代码如下
public interface OrderMapper extends BaseMapper<Order> {
IPage<OrderPageVo> getOrderPage(Page page, @Param("param") OrderPageParam param);
}
OrderMapper .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.elephant.demo.repository.mapper.OrderMapper">
<select id="getOrderPage" resultType="com.elephant.demo.model.vo.OrderPageVo">
select fto.id,
fto.create_time,
fto.update_time,
fto.`status`,
fto.order_no,
fto.customer_name,
fto.customer_email,
fto.product_status
from ft_order fto
where fto.is_deleted=0
<if test="param.searchWord != null and param.searchWord !=''">
and (
fto.order_no like concat('%',#{param.searchWord},'%')
or fto.customer_email like concat('%',#{param.searchWord},'%')
or fto.customer_name like concat('%',#{param.searchWord},'%')
)
</if>
order by fto.create_time desc
</select>
</mapper>
OrderRepositoryImpl的代码实现就比较简单了,调整下即可,代码如下
@Override
public IPage<OrderPageVo> getOrderPage(OrderPageParam param) {
Page page = new Page(param.getPage().getCurrent(), param.getPage().getSize());
return baseMapper.getOrderPage(page, param);
}
3.4.保存订单数据
实现思路:把提交参数做业务校验后,把对应的字段赋给实体类,然后调用this.save(T entity)方法进行保存,id会自动赋值。OrderRepositoryImpl的代码实现如下:
@Override
public Boolean saveOrder(OrderCreateParam param) {
// 参数赋值
Order order = new Order();
order.setOrderNo(param.getOrderNo());
order.setCustomerName(param.getCustomerName());
order.setCustomerEmail(param.getCustomerEmail());
order.setCreateTime(new Date());
order.setUpdateTime(new Date());
// 订单创建时,默认是0-正常
order.setStatus(0);
// 持久化数据
return this.save(order);
}
4.添加日志打印
实现说明:在src/main/resources下添加日志配置logback-spring.xml,然后application.yml添加配置。
logback-spring.xml各项说明可以阅读博客:https://blog.csdn.net/weixin_43790613/article/details/109428318
4.1.logback-spring.xml配置
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60 seconds" debug="true">
<!--
参考博客:https://blog.csdn.net/weixin_43790613/article/details/109428318
1. scan:程序运行时配置文件被修改,是否重新加载。true=重新加载;false=不重新加载;默认为true;
2. scanPeriod:监测配置文件被修改的时间间隔,scan属性必须设置为true才可生效;默认为1分钟,默认单位是毫秒;
3. debug:是否打印logback程序运行的日志信息。true=打印;false=不打印;默认为false;
-->
<contextName>logback</contextName>
<!-- 路径变量,当前项目所在目录下 -->
<property name="log.path" value="/log/demo/logback.log" />
<!-- 日志格式变量 -->
<property name="logPattern" value="%d{HH:mm:ss.SSS} %contextName [%thread] %-5level %logger{36} %file:%line - %msg%n" />
<!--
1. %d{HH:mm:ss.SSS} 显示的时间
2. [%thread]打印线程号,log4j2使用%t]也可以
3. %-5level日志级别,并且使用5个字符靠左对齐
4. %logger{36}——日志输出者的名字,即类的类名
5. %file 打印类名,也可用%class,打印的全限定类名
6. %line 打印日志所在代码行数
7. %msg——日志消息
8. %n——平台的换行符-->
<!--输出到控制台-->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<!-- <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>ERROR</level>
</filter>-->
<encoder>
<pattern>${logPattern}</pattern>
</encoder>
</appender>
<!--输出到文件-->
<appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}</file>
<!--输出到文件路径一种滚动策略:根据时间制定日志文件的滚动策略,如:按天、按小时、按分钟生成日志文件 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 文件路径,定义了日志的切分方式——把每一天的日志归档到一个文件中,以防止日志填满整个磁盘空间 -->
<fileNamePattern>${log.path}/%d{yyyy-MM-dd}.log.gz</fileNamePattern>
<!-- 日志在磁盘上保留天数 -->
<maxHistory>30</maxHistory>
</rollingPolicy>
<!-- 另一种滚动策略:表示根据日志文件大小,超过制定大小会触发日志滚动; -->
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<maxFileSize>5MB</maxFileSize>
</triggeringPolicy>
<encoder>
<pattern>${logPattern}</pattern>
<!--
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>ERROR</level>
</filter>
ThresholdFilter为系统定义的拦截器,例如我们用ThresholdFilter来过滤掉ERROR级别以下的日志不输出到文件中。如果不用记得注释掉,不然你控制台会发现没日志~
-->
<!-- 设置字符集 -->
<charset>UTF-8</charset>
</encoder>
</appender>
<!-- 把日志异步输出到磁盘文件中,避免每次都进行磁盘IO操作 -->
<appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender">
<discardingThreshold>0</discardingThreshold>
<queueSize>10000</queueSize>
<appender-ref ref="file" />
</appender>
<root level="info">
<appender-ref ref="console" />
<appender-ref ref="ASYNC" />
</root>
</configuration>
4.2.application.yml添加配置
# 日志配置
logging:
config: classpath:logback-spring.xml
三、源码地址
上面步骤说明还是不够全面,如果是自己刚开始搭建,可能还是没什么头绪。
因此我把demo分享出来。https://gitee.com/wuqixiufen2/elephant-demo
【项目实战】从零到一搭建Spring Boot整合Mybatis-plus的更多相关文章
- Spring Boot整合Mybatis并完成CRUD操作
MyBatis 是一款优秀的持久层框架,被各大互联网公司使用,本文使用Spring Boot整合Mybatis,并完成CRUD操作. 为什么要使用Mybatis?我们需要掌握Mybatis吗? 说的官 ...
- Spring Boot 整合mybatis时遇到的mapper接口不能注入的问题
现实情况是这样的,因为在练习spring boot整合mybatis,所以自己新建了个项目做测试,可是在idea里面mapper接口注入报错,后来百度查询了下,把idea的注入等级设置为了warnin ...
- Spring Boot整合Mybatis完成级联一对多CRUD操作
在关系型数据库中,随处可见表之间的连接,对级联的表进行增删改查也是程序员必备的基础技能.关于Spring Boot整合Mybatis在之前已经详细写过,不熟悉的可以回顾Spring Boot整合Myb ...
- spring boot 整合 mybatis 以及原理
同上一篇文章一样,spring boot 整合 mybatis过程中没有看见SqlSessionFactory,sqlsession(sqlsessionTemplate),就连在spring框架整合 ...
- Spring Boot整合Mybatis报错InstantiationException: tk.mybatis.mapper.provider.base.BaseSelectProvider
Spring Boot整合Mybatis时一直报错 后来发现原来主配置类上的MapperScan导错了包 由于我使用了通用Mapper,所以应该导入通用mapper这个包
- Spring Boot整合MyBatis(非注解版)
Spring Boot整合MyBatis(非注解版),开发时采用的时IDEA,JDK1.8 直接上图: 文件夹不存在,创建一个新的路径文件夹 创建完成目录结构如下: 本人第一步习惯先把需要的包结构创建 ...
- Spring Boot系列(三):Spring Boot整合Mybatis源码解析
一.Mybatis回顾 1.MyBatis介绍 Mybatis是一个半ORM框架,它使用简单的 XML 或注解用于配置和原始映射,将接口和Java的POJOs(普通的Java 对象)映射成数据库中的记 ...
- 太妙了!Spring boot 整合 Mybatis Druid,还能配置监控?
Spring boot 整合 Mybatis Druid并配置监控 添加依赖 <!--druid--> <dependency> <groupId>com.alib ...
- Spring Boot 整合 Mybatis 实现 Druid 多数据源详解
摘要: 原创出处:www.bysocket.com 泥瓦匠BYSocket 希望转载,保留摘要,谢谢! “清醒时做事,糊涂时跑步,大怒时睡觉,独处时思考” 本文提纲一.多数据源的应用场景二.运行 sp ...
- Spring boot整合Mybatis
时隔两个月的再来写博客的感觉怎么样呢,只能用“棒”来形容了.闲话少说,直接入正题,之前的博客中有说过,将spring与mybatis整个后开发会更爽,基于现在springboot已经成为整个业界开发主 ...
随机推荐
- 了解 Flutter 开发者们的 IDE 使用情况
作者 / JaYoung Lee, UX Researcher at Google Google 的 Flutter 团队负责构建和维护 Android Studio (基于 IntelliJ-IDE ...
- 0025:2011年NOIp普及组真题——瑞士轮题解
题目链接:https://www.luogu.com.cn/problem/P1309 如果是新手可能马上会想到sort排序,每比一次就排一次,但是这样的时间复杂度有点高,只有60分: 这是因为每次比 ...
- 蓝桥杯赛前复习C++
C标准库常用函数 memset函数:暴力清空 void *memset(void *str, int c, size_t n) str -- 指向要填充的内存块. c -- 要被设置的值.该值以 in ...
- packet Capture 手机抓包工具
packet Capture packet Capture 是一款免root的app, 运行在安卓平台上,用于捕获http/https网络流量嗅探的应用程序 特点: 捕获网络数据包,并记录太慢,使用中 ...
- 一次SpringBoot版本升级,引发的血案
前言 最近项目组升级了SpringBoot版本,由之前的2.0.4升级到最新版本2.7.5,却引出了一个大Bug. 到底是怎么回事呢? 1.案发现场 有一天,项目组的同事反馈给我说,我之前有个接口在新 ...
- C#自定义控件(3)—PanelHead控件
有时候我们会需要这样一种控件效果,上面是标题,下面是另外一个区域,且分别需要设置不同的颜 色等,当然我们可以使用splitContainer控件来制作,也可以直接使用自定义控件来,这样可以减少一 定的 ...
- nacos集群搭建和反向代理
搭建环境 安装ngin https://www.linuxprobe.com/linux-install-nginx.html 配置jdk1.8 https://blog.csdn.net/qq_42 ...
- KubeEdge的云边协同设计原理
1.云端组件与K8s Master的关系 cloudCore和K8s master,非侵入的映射 2.EdgeController详解 -边缘节点管理 -应用状态元数据云边协同 3.DeviceCon ...
- 关于python统计一个列表中每个元素出现的频率
第一种写法: a = ['h','h','e','a','a'] result = {} for i in a: if i not in result: result[i] = 1 else: res ...
- Java 中你绝对没用过的一个关键字?
layout: post categories: Java title: Java 中你绝对没用过的一个关键字? tagline: by 子悠 tags: 子悠 前面的文章给大家介绍了如何自定义一个不 ...