自用的springboot后端增删改查模板
Springboot后端简易方式快速搭建
前言
快速学了下。和传统的springboot项目相比,没有用service和serviceImpl。比较不合规,但够简单。可以用于快速开发。
前后端分离。前端请另寻。
特别感谢:程序员青戈
1 开始


2 手动导入一些依赖
<!--mysql驱动-->
		<dependency>
   			 <groupId>mysql</groupId>
   			 <artifactId>mysql-connector-java</artifactId>
   			 <scope>runtime</scope>
		</dependency>
<!--lombook 哦对 在项目创建就可以导入了-->
		<dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
<!--mybatis-plus 解放增删改查-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.3.1</version>
        </dependency>
 <!--  JWT Token相关 -->
        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>3.4.0</version>
        </dependency>
<!--  超多好用的工具 强烈推荐 -->
 		<dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.7.3</version>
        </dependency>
<!--  加密工具 -->
		<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
3 建个数据库

4 application.properties
# 数据库驱动:
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# 数据库连接地址
spring.datasource.url=jdbc:mysql://localhost:3306/springboot-vue?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false&serverTimezone=GMT%2b8
# 数据库用户名&密码:
spring.datasource.username=root
spring.datasource.password=123456
# 应用服务 WEB 访问端口
server.port=9090
5 返回Result
public class Result<T> {
    private String code;
    private String msg;
    private T data;
    public String getCode() {
        return code;
    }
    public void setCode(String code) {
        this.code = code;
    }
    public String getMsg() {
        return msg;
    }
    public void setMsg(String msg) {
        this.msg = msg;
    }
    public T getData() {
        return data;
    }
    public void setData(T data) {
        this.data = data;
    }
    public Result() {
    }
    public Result(T data) {
        this.data = data;
    }
    public static Result success() {
        Result result = new Result<>();
        result.setCode("0");
        result.setMsg("成功");
        return result;
    }
    public static <T> Result<T> success(T data) {
        Result<T> result = new Result<>(data);
        result.setCode("0");
        result.setMsg("成功");
        return result;
    }
    public static Result error(String code, String msg) {
        Result result = new Result();
        result.setCode(code);
        result.setMsg(msg);
        return result;
    }
}
6 文件结构预览

7 entity.User
这里写实体。属性和表一一对应
注意看注释!!
@TableName("user")
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class User {
    @TableId(type = IdType.AUTO)
    private Integer id;
    private String username;
    private String password;
    private String nickName;
    private Integer age;
    private String sex;
    private String address;
//    private String avatar;
//    @TableField(exist = false)
//    private List<Integer> roles;
//
//    @TableField(exist = false)
//    private List<Book> bookList;
//
//    @TableField(exist = false)
//    private String token;
//
//    private BigDecimal account;
//
//    @TableField(exist = false)
//    private Set<Permission> permissions;
}
8 mapper.UserMapper
这个BaseMapper自带增删改查。如果有其他需求可以自己写。
public interface UserMapper  extends BaseMapper<User> {
}
9 controller.UserController
可以了解一下restful风格接口。
@RestController
@RequestMapping("/user")
public class UserController {
    @Resource
    UserMapper userMapper;
    @PostMapping
    public Result<?> save(@RequestBody User user){
        userMapper.insert(user);
        return Result.success();
    }
}
10 体验一下
在上面的controller输入:
@GetMapping("/all")
public Result<?> findAll() {
    return Result.success(userMapper.selectList(null));
}
就能看到数据库的json啦 是不是很简单呢
返回的json交给前端耍了。
(一定要找一个好前端啊 就算后端写的比较拉也能高逼格)

以下是写完项目后的一些记录,包含一些重要的配置和代码。
11.一些配置
11.1 允许客户端携带验证信息 AddResponseHeaderFilter
因为本项目将使用cookie,因此必须允许客户端携带验证信息。实现的方法是继承重写spring web的OncePerRequestFilter,对每一个申请都在回复中添加请求头Access-Control-Allow-Credentials为ture。
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Component
public class AddResponseHeaderFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        response.addHeader("Access-Control-Allow-Credentials", "true");
        filterChain.doFilter(request,response);
    }
}
11.2 跨域设置 CorsConfig
前后端分离的跨域设置。允许前端的origin向后端发送请求。
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
@Configuration
public class CorsConfig {
    // 当前跨域请求最大有效时长。这里默认1天
    private static final long MAX_AGE = 24 * 60 * 60;
    private CorsConfiguration buildConfig() {
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.addAllowedOrigin("http://127.0.0.1:5173"); // 1 设置访问源地址
        corsConfiguration.addAllowedOrigin("https://blog-alpha.dev.mxowl.com");
        corsConfiguration.addAllowedHeader("*"); // 2 设置访问源请求头
        corsConfiguration.addAllowedMethod("*"); // 3 设置访问源请求方法
        corsConfiguration.setMaxAge(MAX_AGE);
        return corsConfiguration;
    }
    @Bean
    public CorsFilter corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", buildConfig()); // 4 对接口配置跨域设置
        return new CorsFilter(source);
    }
}
11.3 MybatisPlus配置 MybatisPlusConfig
该处主要配置了分页插件。后续的博客搜索、评论搜索等都会用到分页查询
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MybatisPlusConfig {
    /**
     * 分页插件
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}
11.4 其他配置
在application.java中的配置:引入了spring security的加密。
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
@SpringBootApplication(exclude= SecurityAutoConfiguration.class)
@MapperScan("com.example.QLblog.mapper")
public class SpringbootTestApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringbootTestApplication.class, args);
    }
    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder(){
        return new BCryptPasswordEncoder();
    }
}
我们只要用:
bCryptPasswordEncoder.encode(user.getPassword())
即可加密密码。当然,控制类需装配进来:
@Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;
在application.properties中配置:(隐去了敏感信息)
# 数据库驱动:
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# 数据库用户名&密码:
spring.datasource.username=***
#开发版
#spring.datasource.url=jdbc:mysql://***:3306/qlblog?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false&serverTimezone=GMT%2b8
#spring.datasource.password=***
#发行版
spring.datasource.url=jdbc:mysql://localhost:3306/qlblog?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false&serverTimezone=GMT%2b8
spring.datasource.password=***
# 应用服务 WEB 访问端口
server.port=9090
# 文件上传ip
file.ip=***
#文件上传限额
spring.servlet.multipart.max-file-size=10MB
spring.servlet.multipart.max-request-size=10MB
#连接sql字符集为utf8mb4 可能是多余的
spring.datasource.hikari.connection-init-sql=set names utf8mb4 collate utf8mb4_unicode_ci
spring.datasource.tomcat.init-s-q-l=set names utf8mb4 collate utf8mb4_unicode_ci
12 Cookie相关
12.1 生成Token的工具
之后生成cookie时,会先将用户信息装进token,再装进cookie。本工具类定义了通过user生成token的方法。
import cn.hutool.core.date.DateUtil;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.liuzhiwen.recruitment.common.Result;
import com.liuzhiwen.recruitment.entity.User;
import com.liuzhiwen.recruitment.mapper.UserMapper;
import com.liuzhiwen.recruitment.entity.User;
import com.liuzhiwen.recruitment.mapper.UserMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;
@Slf4j
@Component
public class TokenUtils {
    @Resource
    private UserMapper userMapper;
    private static UserMapper staticUserMapper;
    @PostConstruct
    public void init() {
        staticUserMapper = userMapper;
    }
    /**
     * 生成token
     * @param user
     * @return
     */
    public static String genToken(User user) {
        return JWT.create().withExpiresAt(DateUtil.offsetDay(new Date(), 1)).withAudience(user.getId().toString())
                .sign(Algorithm.HMAC256(user.getPassword()));
    }
    /**
     * 获取token中的用户信息
     * @return
     */
    public static User getUser() {
        try {
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            String token = request.getHeader("token");
            String aud = JWT.decode(token).getAudience().get(0);
            Integer userId = Integer.valueOf(aud);
            return staticUserMapper.selectById(userId);
        } catch (Exception e) {
            log.error("解析token失败", e);
            return null;
        }
    }
}
12.2 基础控制类 BaseController
因为有些操作,我们所有的controller都会使用,因此我创建了BaseController用于存放这些会反复用到的、重要的函数。如:
l 通过token获取user
l 通过cookie获取token,进而获取token
l 通过cookie判断用户登陆状态和信息
其他controller只要继承这玩意就行。
*后端的几乎所有敏感操作都会通过cookie进行身份的验证!*
import com.auth0.jwt.JWT;
import com.liuzhiwen.recruitment.entity.User;
import com.liuzhiwen.recruitment.mapper.UserMapper;
import com.liuzhiwen.recruitment.entity.User;
import com.liuzhiwen.recruitment.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
@RestController
public class BaseController {
    @Resource
    UserMapper userMapper;
    @Autowired
    protected HttpServletRequest request;
    /**
     * 根据token获取用户信息
     * @return user
     */
    public User getUserFromToken() {
        String token = request.getHeader("token");
        String aud = JWT.decode(token).getAudience().get(0);
        Integer userId = Integer.valueOf(aud);
        return userMapper.selectById(userId);
    }
    /**
     * 根据cookie获得token,获得用户信息
     * @return user
     */
    public User getUserFromCookie(){
        String token = null;
        for(int i = 0; i < request.getCookies().length; i++){
            if(request.getCookies()[i].getName().equals("token")){
                token = request.getCookies()[i].getValue();
            }
        }
        if(token!=null){
            String aud = JWT.decode(token).getAudience().get(0);
            Integer userId = Integer.valueOf(aud);
            if(userMapper.selectById(userId)!=null){
                User retUser = userMapper.selectById(userId);
                retUser.setPassword("");
                return retUser;
            }else {
                return null;
            }
        }else {
            return null;
        }
    }
    /**
     * 判断登录状态及用户是否存在
     * @return boolean
     */
    //验证登陆状态
    public boolean verifyLoginStatus(){
        if (request.getCookies() == null) {
            return false;
        }
        String token = null;
        for (int i = 0; i < request.getCookies().length; i++) {
            if (request.getCookies()[i].getName().equals("token")) {
                token = request.getCookies()[i].getValue();
            }
        }
        if (token == null) return false;
        return getUserFromCookie() != null;
    }
    /*验证登录模板:
        if(!verifyLoginStatus()){
            return Result.error("-1","登录状态有误,请重新登陆!");
        }
     */
}
12.3 组装cookie及注销
//登录 post /login
    @PostMapping("/login")
    public Result<?> login(@RequestBody User userParam, HttpServletResponse response) {
        User userPwd = userMapper.selectPwdByName(userParam.getUsername());
        if(userPwd==null){
            return Result.error("-1","用户名错误");
        }
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("username", userParam.getUsername());
        queryWrapper.eq("password", userPwd.getPassword());
        User res = userMapper.selectOne(queryWrapper);
        // 判断密码是否正确
        if (!bCryptPasswordEncoder.matches(userParam.getPassword(), userPwd.getPassword())) {
            return Result.error("-1", "密码错误");
        }
        if (res == null) {
            return Result.error("-1", "用户名或密码错误");
        }
        // 生成token
        String token = TokenUtils.genToken(res);
        //res.setToken(token);
        //组装cookie
        final ResponseCookie responseCookie = ResponseCookie
                .from("token", token)
                //.secure(true)
                .httpOnly(true)
                .path("/")
                .maxAge(60 * 60 * 24 * 7)   //7天有效期
                .sameSite("Lax")
                .build();
        response.addHeader(HttpHeaders.SET_COOKIE, responseCookie.toString());
        res.setPassword("");
        return Result.success(res);
    }
//注销
    @PostMapping("/logout")
    public Result<?> logOut(HttpServletResponse response){
        if(request.getCookies()==null){
            return Result.error("-1","未登录");
        }
        final ResponseCookie responseCookie = ResponseCookie
                .from("token", "")
                //.secure(true)
                .httpOnly(true)
                .path("/")
                .maxAge(0)
                .sameSite("Lax")
                .build();
        response.addHeader(HttpHeaders.SET_COOKIE, responseCookie.toString());
        return Result.success();
    }
13 文件上传
13.1 普通文件上传
@RestController
@RequestMapping("/file")
public class FileController extends BaseController {
    @Value("${server.port}")
    private String port;
    @Value("${file.ip}")
    private String ip;
    /**
     * 上传接口
     *
     * @param file
     * @return
     * @throws IOException
     */
    @PostMapping("/upload")
    public Result<?> upload(MultipartFile file) throws IOException {
        if (file == null) {
            return Result.error("-1", "文件为空");
        }
        String fileType = file.getContentType();
        if(fileType==null){
            return Result.error("-1", "未知文件格式");
        }
        if (!fileType.contains("image/")) {
            return Result.error("-1", "文件格式上传错误");
        }
        if(!verifyLoginStatus()){
            return Result.error("-1","登录状态有误,请重新登陆!");
        }
        String originalFilename = file.getOriginalFilename();  // 获取源文件的名称
        // 定义文件的唯一标识(前缀)
        String fileUUID = IdUtil.fastSimpleUUID();
        String rootFilePath = System.getProperty("user.dir") + "/files/" + fileUUID + "_" + originalFilename;  // 获取上传的路径
        File rootFile = new File(rootFilePath);
        if (!rootFile.getParentFile().exists()) {
            rootFile.getParentFile().mkdirs();
        }
        FileUtil.writeBytes(file.getBytes(), rootFilePath);  // 把文件写入到上传的路径
        return Result.success("/file/" + fileUUID);  // 返回结果 url
    }
    /**
     * 下载接口
     *
     * @param fileUUID
     * @param response
     */
    @GetMapping("/{fileUUID}")
    public Result<?> getFiles(@PathVariable String fileUUID, HttpServletResponse response) {
        OutputStream os;  // 新建一个输出流对象
        String basePath = System.getProperty("user.dir") + "/files/";  // 定于文件上传的根路径
        List<String> fileNames = FileUtil.listFileNames(basePath);  // 获取所有的文件名称
        String fileName = fileNames.stream().filter(name -> name.contains(fileUUID)).findAny().orElse("");  // 找到跟参数一致的文件
        try {
            if (StrUtil.isNotEmpty(fileName)) {
                response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
                response.setContentType("application/octet-stream");
                response.setHeader("cache-control","max-age=5184000");
                byte[] bytes = FileUtil.readBytes(basePath + fileName);  // 通过文件的路径读取文件字节流
                os = response.getOutputStream();   // 通过输出流返回文件
                os.write(bytes);
                os.flush();
                os.close();
            }
        } catch (Exception e) {
            return Result.error("-1","文件下载失败");
        }
        return Result.success();
    }
}
13.2 头像上传
此类上传需记录url到数据库
 @PostMapping("/upload_avatar")
    public Result<?> upload(MultipartFile file) throws IOException {
        if (file == null) {
            return Result.error("-1", "文件为空");
        }
        String fileType = file.getContentType();
        if (fileType == null) {
            return Result.error("-1", "未知文件格式");
        }
        if (!fileType.contains("image/")) {
            return Result.error("-1", "文件格式上传错误");
        }
        if (!verifyLoginStatus()) {
            return Result.error("-1", "登录状态有误,请重新登陆!");
        }
        String originalFilename = file.getOriginalFilename();  // 获取源文件的名称
        // 定义文件的唯一标识(前缀)
        String fileUUID = IdUtil.fastSimpleUUID();
        String rootFilePath = System.getProperty("user.dir") + "/files/" + fileUUID + "_" + originalFilename;  // 获取上传的路径
        File rootFile = new File(rootFilePath);
        if (!rootFile.getParentFile().exists()) {
            rootFile.getParentFile().mkdirs();
        }
        FileUtil.writeBytes(file.getBytes(), rootFilePath);  // 把文件写入到上传的路径
        User user = getUserFromCookie();
        Profile profile = profileMapper.selectById(user.getId());
        profile.setAvatar("/file/" + fileUUID);
        profileMapper.updateById(profile);
        return Result.success("/file/" + fileUUID);  // 返回结果 url
    }
14 运行配置configuration
14.1 Application
@SpringBootApplication(exclude= SecurityAutoConfiguration.class)
@MapperScan("com.example.QLblog.mapper")
public class SpringbootTestApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringbootTestApplication.class, args);
    }
    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder(){
        return new BCryptPasswordEncoder();
    }
}
14.2 Edit configurations

15 2024/1/8更新
15.1 Service的加入
这次增加了service(但是没有ServiceImply)所以直接把service类写成class就行,然后把controller里的逻辑搬到service里就行,controller只负责路由。如图所示:


需要注意的是因为业务逻辑的抽离,basecontroller不再有用,将其中的函数搬到baseservice里,由service继承。

15.2 数据库与实体类命名相关
这个之前忘说了。数据库两个单词要用下划线隔开,而实体类用小驼峰。实体类用下划线的话搜不到的。
如:数据库中表项:user_name 实体类就要: userName
等毕设做的差不多了会将本文重制为更完善的模板。现在就暂时缝缝补补
自用的springboot后端增删改查模板的更多相关文章
- spring--boot数据库增删改查
		
spring--boot数据库增删改查 数据库配置:(必须配置),我写的文件是yml的,和properties是相同的 1 spring: 2 datasource: 3 driver-class-n ...
 - 【Mybatis】简单的mybatis增删改查模板
		
简单的mybatis增删改查模板: <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE map ...
 - SpringBoot+Mybatis增删改查实战
		
简介 SpringBoot和Mybatis是啥请自行百度,作者这里也是花了几天时间入门了这个框架用来完成任务,并且也算符合要求的完成了任务,期间也各种百度但是没找到自己想要的那种简单易懂的教程,所以踩 ...
 - MyBatis增删改查模板
		
1. 首先,和Spring整合一下 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns=& ...
 - springboot&mybatis 增删改查系列(二)
		
数据库篇 我的数据库名为data0525,数据表名为user,其中有五列uid,uname,upass,usex,umessage.uid为主键并且自动生成,由于是练习表,所以并没有考虑设计的合理性. ...
 - 使用IDEA搭建SpringBoot进行增删改查
		
功能环境:java1.8以上 .IntellJIDEA First: 创建项目,请根据项目图一步一步完成建立. 二.配置数据库 三.创建实体对象建表或对应存在表,根据需要加入相应注解 四.创建应用 ...
 - springboot&mybatis 增删改查系列(一)
		
创建父项目 首先,我们需要创建一个Maven项目. 在这个项目的pom文件中加入以下几个依赖: <!-- spring boot --> <parent> <groupI ...
 - SpringBoot JPA + H2增删改查示例
		
下面的例子是基于SpringBoot JPA以及H2数据库来实现的,下面就开始搭建项目吧. 首先看下项目的整体结构: 具体操作步骤: 打开IDEA,创建一个新的Spring Initializr项目, ...
 - Django中ORM对数据库的增删改查
		
Django中ORM对数据库数据的增删改查 模板语言 {% for line in press %} {% line.name %} {% endfor %} {% if 条件 %}{% else % ...
 - springboot+layui实现PC端用户的增删改查 & 整合mui实现app端的自动登录和用户的上拉加载 & HBuilder打包app并在手机端下载安装
		
springboot整合web开发的各个组件在前面已经有详细的介绍,下面是用springboot整合layui实现了基本的增删改查. 同时在学习mui开发app,也就用mui实现了一个简单的自动登录和 ...
 
随机推荐
- Mono GC
			
1.虽然是stw但mark阶段可以concurrent 2.并行mark就需要写屏障 3.unity的gc也不是扫描整个堆内存 https://schani.wordpress.com/2012/12 ...
 - 牛逼,这款开源聊天应用竟能一键召唤多个AI助手,跨平台通话神器!
			
嗨,大家好,我是小华同学,关注我们获得"最新.最全.最优质"开源项目和高效工作学习方法 JiwuChat是一款基于Tauri2和Nuxt3构建的轻量化多平台即时通讯工具,仅约8MB ...
 - Nacos源码—2.Nacos服务注册发现分析二
			
大纲 5.服务发现-服务之间的调用请求链路分析 6.服务端如何维护不健康的微服务实例 7.服务下线时涉及的处理 8.服务注册发现总结 5.服务发现-服务之间的调用请求链路分析 (1)微服务通过Naco ...
 - uni-app小程序登录后…
			
前情 最近新接了一个全新项目,是类似商城的小程序项目,我负责从0开始搭建小程序,我选用的技术栈是uni-app技术栈,其中就有一个用户登录功能,小程序部分页面是需要登录才可以查看的,对于未登录的用户需 ...
 - C# AggreateException
			
在 C# 中,AggregateException 是一种特殊类型的异常,它允许在多个异步任务中捕获并组合多个异常.当在一个异步任务中同时执行多个子任务时,如果其中任何一个子任务抛出了异常,那么父任务 ...
 - 自签名证书工具cfssl详解
			
概述 GitHub地址:https://github.com/cloudflare/cfssl 官方地址:https://pkg.cfssl.org CFSSL(CloudFlare's PKI an ...
 - Redis集群的三种姿势
			
一.Redis主从复制 主机数据更新后根据配置和策略,自动同步到备机的master/slaver机制,Master以写为主,Slave以读为主. 1.如何实现 新建三个配置文件,分别命名为redis_ ...
 - vivo Pulsar 万亿级消息处理实践(2)-从0到1建设 Pulsar 指标监控链路
			
作者:vivo 互联网大数据团队- You Shuo 本文是<vivo Pulsar万亿级消息处理实践>系列文章第2篇,Pulsar支持上报分区粒度指标,Kafka则没有分区粒度的指标,所 ...
 - 特殊恢复:最简单的BBED修改ASM的数据块的方法
			
我们的文章会在微信公众号Oracle恢复实录和博客网站同步更新 ,欢迎关注收藏,也欢迎大家转载,但是请在文章开始地方标注文章出处,谢谢! 由于博客中有大量代码,通过页面浏览效果更佳. 前天在客户现场遇 ...
 - go 进阶训练营 微服务可用性(中)笔记
			
过载保护 令牌桶算法 存放固定容量令牌的桶,按照固定速率往桶里添加令牌 https://pkg.go.dev/golang.org/x/time/rate 漏桶算法 作为计量工具(The Leaky ...