SpringBoot中动态注册接口

news2024/10/6 8:38:25

1. 说明

  • 接口注册,使用RequestMappingHandlerMapping来实现
  • mybatis中动态执行sql使用github上的SqlMapper工具类实现

2. 核心代码片段

        以下代码为spring动态注册接口代码示例

@Autowired
private RequestMappingHandlerMapping requestMappingHandlerMapping;

public boolean register2Spring(String path) {
        RequestMappingInfo requestMappingInfo = RequestMappingInfo.paths(path)
                .methods(RequestMethod.POST)
                .produces(MediaType.APPLICATION_JSON_VALUE)
                .options(requestMappingHandlerMapping.getBuilderConfiguration())
                .build();

        Method method = ReflectionUtils.findMethod(getClass(), "handler",
                HttpServletRequest.class, HttpServletResponse.class,
                Map.class, Map.class, Map.class);

        boolean status = true;
        try {
            requestMappingHandlerMapping.registerMapping(requestMappingInfo, this, method);
            LOGGER.info("【接口注册成功】{}", path);
        } catch (Exception e) {
            status = false;
            LOGGER.error("【注册接口异常】动态映射失败", e.getMessage());
        }

        return status;
    }

3. 源码

3.1 核心代码

3.1.1 ApiServiceHandler

        handler中register职责如下:

  • 注册到数据库中
  • 注册接口到spring容器中
import com.alibaba.fastjson2.JSONObject;
import com.google.common.net.HttpHeaders;
import com.hz.pro.artifact.bean.CommonException;
import com.hz.pro.artifact.bean.Response;
import com.hz.pro.artifact.dynamic.bean.ServiceDto;
import com.hz.pro.artifact.dynamic.mapper.main.ApiServiceMapper;
import com.hz.pro.artifact.utils.SqlMapper;
import org.apache.ibatis.session.SqlSessionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;

/**
 * @author pp_lan
 * @date 2024/1/4
 */
@Service
public class ApiServiceHandler {

    private static final Logger LOGGER = LoggerFactory.getLogger(ApiServiceHandler.class);

    @Autowired
    private RequestMappingHandlerMapping requestMappingHandlerMapping;

    @Autowired
    @Qualifier("sqlSessionFactory")
    private SqlSessionFactory sqlSessionFactory;

    @Autowired
    private ApiServiceMapper apiServiceMapper;

    public void initialRegister() {
        List<ServiceDto> apis = findApis();
        for (ServiceDto api : apis) {
            try {
                register2Spring(api.getPath());
            } catch (Exception e) {
                LOGGER.error("[接口注册失败]{}", api.getPath(), e.getMessage());
            }
        }
    }

    /**
     * 注册到spring,并添加到数据库中
     *
     * @param path
     * @param sql
     * @return
     */
    public boolean register(String path, String sql) {
        boolean status = this.registerApiOfSql(path, sql);
        if (status) {
            status = register2Spring(path);
        }
        return status;
    }

    /**
     * 注册到容器
     *
     * @param path
     * @return
     */
    public boolean register2Spring(String path) {
        RequestMappingInfo requestMappingInfo = RequestMappingInfo.paths(path)
                .methods(RequestMethod.POST)
                .produces(MediaType.APPLICATION_JSON_VALUE)
                .options(requestMappingHandlerMapping.getBuilderConfiguration())
                .build();

        Method method = ReflectionUtils.findMethod(getClass(), "handler",
                HttpServletRequest.class, HttpServletResponse.class,
                Map.class, Map.class, Map.class);

        boolean status = true;
        try {
            requestMappingHandlerMapping.registerMapping(requestMappingInfo, this, method);
            LOGGER.info("【接口注册成功】{}", path);
        } catch (Exception e) {
            status = false;
            LOGGER.error("【注册接口异常】动态映射失败", e.getMessage());
        }

        return status;
    }

    @ResponseBody
    public Response handler(HttpServletRequest request, HttpServletResponse response,
                            @PathVariable(required = false) Map<String, Object> pathVariable,
                            @RequestParam(required = false) Map<String, Object> requestParam,
                            @RequestBody(required = false) Map<String, Object> requestBody) {
        String header = request.getHeader(HttpHeaders.CONTENT_TYPE);

        // 参数处理
        JSONObject params;
        if (header != null && header.contains(MediaType.APPLICATION_JSON_VALUE)) {
            params = new JSONObject(requestBody);
        } else {
            params = new JSONObject(requestParam);
        }

        // 执行查询
        try (SqlMapper sqlMapper = new SqlMapper(sqlSessionFactory)) {

            String path = request.getRequestURI();
            String sql = apiServiceMapper.findSqlByPath(path);

            List<Map<String, Object>> result = sqlMapper.selectList(sql, params);
            return Response.ok(result);
        } catch (Exception e) {
            throw new CommonException("【公共查询异常】", e);
        }

    }

    /**
     * 查询所有在用接口
     *
     * @return
     */
    public List<ServiceDto> findApis() {
        return apiServiceMapper.findApis();
    }

    /**
     * 注册接口
     *
     * @param path
     * @param sql
     * @return
     */
    public boolean registerApiOfSql(String path, String sql) {

        try {
            return apiServiceMapper.insertApiSql(path, sql) > 0;
        } catch (Exception e) {
            throw new CommonException("【注册接口异常】插入sql配置失败", e);
        }
    }

}

3.1.2 DynamicController

        手动注册接口执行/dynamic/register方法,便可以完成接口注册。

import com.hz.pro.artifact.bean.Response;
import com.hz.pro.artifact.dynamic.bean.ApiReq;
import com.hz.pro.artifact.dynamic.service.ApiServiceHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author pp_lan
 * @date 2024/1/2
 */
@RestController
@RequestMapping("/dynamic")
public class DynamicController {

    @Autowired
    private ApiServiceHandler apiServiceHandler;

    /**
     * 注册接口
     *
     * @param apiReq
     * @return
     */
    @PostMapping("register")
    public Response register(@RequestBody @Validated ApiReq apiReq) {
        boolean registerStatus = apiServiceHandler.register(apiReq.getPath(), apiReq.getSql());
        return registerStatus ? Response.ok("接口注册成功") : Response.error("接口注册失败");
    }
}

3.2 依赖类

3.2.1 SqlMapper

        此为github上有开源工具类,最新代码请移步github。以下为其源码:

import org.apache.ibatis.builder.StaticSqlSource;
import org.apache.ibatis.exceptions.TooManyResultsException;
import org.apache.ibatis.mapping.*;
import org.apache.ibatis.scripting.LanguageDriver;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * MyBatis执行sql工具,在写SQL的时候建议使用参数形式的可以是${}或#{}
 *
 * 不建议将参数直接拼到字符串中,当大量这么使用的时候由于缓存MappedStatement而占用更多的内存
 *
 * @author liuzh
 * @since 2015-03-10
 */
public class SqlMapper implements AutoCloseable {
    private final MSUtils msUtils;
    private final SqlSession sqlSession;

    /**
     * 构造方法,默认缓存MappedStatement
     *
     * @param sqlSession
     */
    public SqlMapper(SqlSession sqlSession) {
        this.sqlSession = sqlSession;
        this.msUtils = new MSUtils(sqlSession.getConfiguration());
    }

    public SqlMapper(SqlSessionFactory sqlSessionFactory) {
        this.sqlSession = sqlSessionFactory.openSession();
        this.msUtils = new MSUtils(sqlSession.getConfiguration());
    }

    /**
     * 获取List中最多只有一个的数据
     *
     * @param list List结果
     * @param <T>  泛型类型
     * @return
     */
    private <T> T getOne(List<T> list) {
        if (list.size() == 1) {
            return list.get(0);
        } else if (list.size() > 1) {
            throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
        } else {
            return null;
        }
    }

    /**
     * 查询返回一个结果,多个结果时抛出异常
     *
     * @param sql 执行的sql
     * @return
     */
    public Map<String, Object> selectOne(String sql) {
        List<Map<String, Object>> list = selectList(sql);
        return getOne(list);
    }

    /**
     * 查询返回一个结果,多个结果时抛出异常
     *
     * @param sql   执行的sql
     * @param value 参数
     * @return
     */
    public Map<String, Object> selectOne(String sql, Object value) {
        List<Map<String, Object>> list = selectList(sql, value);
        return getOne(list);
    }

    /**
     * 查询返回一个结果,多个结果时抛出异常
     *
     * @param sql        执行的sql
     * @param resultType 返回的结果类型
     * @param <T>        泛型类型
     * @return
     */
    public <T> T selectOne(String sql, Class<T> resultType) {
        List<T> list = selectList(sql, resultType);
        return getOne(list);
    }

    /**
     * 查询返回一个结果,多个结果时抛出异常
     *
     * @param sql        执行的sql
     * @param value      参数
     * @param resultType 返回的结果类型
     * @param <T>        泛型类型
     * @return
     */
    public <T> T selectOne(String sql, Object value, Class<T> resultType) {
        List<T> list = selectList(sql, value, resultType);
        return getOne(list);
    }

    /**
     * 查询返回List<Map<String, Object>>
     *
     * @param sql 执行的sql
     * @return
     */
    public List<Map<String, Object>> selectList(String sql) {
        String msId = msUtils.select(sql);
        return sqlSession.selectList(msId);
    }

    /**
     * 查询返回List<Map<String, Object>>
     *
     * @param sql   执行的sql
     * @param value 参数
     * @return
     */
    public List<Map<String, Object>> selectList(String sql, Object value) {
        Class<?> parameterType = value != null ? value.getClass() : null;
        String msId = msUtils.selectDynamic(sql, parameterType);
        return sqlSession.selectList(msId, value);
    }

    /**
     * 查询返回指定的结果类型
     *
     * @param sql        执行的sql
     * @param resultType 返回的结果类型
     * @param <T>        泛型类型
     * @return
     */
    public <T> List<T> selectList(String sql, Class<T> resultType) {
        String msId;
        if (resultType == null) {
            msId = msUtils.select(sql);
        } else {
            msId = msUtils.select(sql, resultType);
        }
        return sqlSession.selectList(msId);
    }

    /**
     * 查询返回指定的结果类型
     *
     * @param sql        执行的sql
     * @param value      参数
     * @param resultType 返回的结果类型
     * @param <T>        泛型类型
     * @return
     */
    public <T> List<T> selectList(String sql, Object value, Class<T> resultType) {
        String msId;
        Class<?> parameterType = value != null ? value.getClass() : null;
        if (resultType == null) {
            msId = msUtils.selectDynamic(sql, parameterType);
        } else {
            msId = msUtils.selectDynamic(sql, parameterType, resultType);
        }
        return sqlSession.selectList(msId, value);
    }

    /**
     * 插入数据
     *
     * @param sql 执行的sql
     * @return
     */
    public int insert(String sql) {
        String msId = msUtils.insert(sql);
        return sqlSession.insert(msId);
    }

    /**
     * 插入数据
     *
     * @param sql   执行的sql
     * @param value 参数
     * @return
     */
    public int insert(String sql, Object value) {
        Class<?> parameterType = value != null ? value.getClass() : null;
        String msId = msUtils.insertDynamic(sql, parameterType);
        return sqlSession.insert(msId, value);
    }

    /**
     * 更新数据
     *
     * @param sql 执行的sql
     * @return
     */
    public int update(String sql) {
        String msId = msUtils.update(sql);
        return sqlSession.update(msId);
    }

    /**
     * 更新数据
     *
     * @param sql   执行的sql
     * @param value 参数
     * @return
     */
    public int update(String sql, Object value) {
        Class<?> parameterType = value != null ? value.getClass() : null;
        String msId = msUtils.updateDynamic(sql, parameterType);
        return sqlSession.update(msId, value);
    }

    /**
     * 删除数据
     *
     * @param sql 执行的sql
     * @return
     */
    public int delete(String sql) {
        String msId = msUtils.delete(sql);
        return sqlSession.delete(msId);
    }

    /**
     * 删除数据
     *
     * @param sql   执行的sql
     * @param value 参数
     * @return
     */
    public int delete(String sql, Object value) {
        Class<?> parameterType = value != null ? value.getClass() : null;
        String msId = msUtils.deleteDynamic(sql, parameterType);
        return sqlSession.delete(msId, value);
    }

    @Override
    public void close() throws Exception {
        this.sqlSession.close();
    }

    private class MSUtils {
        private Configuration configuration;
        private LanguageDriver languageDriver;

        private MSUtils(Configuration configuration) {
            this.configuration = configuration;
            languageDriver = configuration.getDefaultScriptingLanuageInstance();
        }

        /**
         * 创建MSID
         *
         * @param sql 执行的sql
         * @param sql 执行的sqlCommandType
         * @return
         */
        private String newMsId(String sql, SqlCommandType sqlCommandType) {
            StringBuilder msIdBuilder = new StringBuilder(sqlCommandType.toString());
            msIdBuilder.append(".").append(sql.hashCode());
            return msIdBuilder.toString();
        }

        /**
         * 是否已经存在该ID
         *
         * @param msId
         * @return
         */
        private boolean hasMappedStatement(String msId) {
            return configuration.hasStatement(msId, false);
        }

        /**
         * 创建一个查询的MS
         *
         * @param msId
         * @param sqlSource  执行的sqlSource
         * @param resultType 返回的结果类型
         */
        private void newSelectMappedStatement(String msId, SqlSource sqlSource, final Class<?> resultType) {
            MappedStatement ms = new MappedStatement.Builder(configuration, msId, sqlSource, SqlCommandType.SELECT)
                    .resultMaps(new ArrayList<ResultMap>() {
                        {
                            add(new ResultMap.Builder(configuration, "defaultResultMap", resultType, new ArrayList<ResultMapping>(0)).build());
                        }
                    })
                    .build();
            //缓存
            configuration.addMappedStatement(ms);
        }

        /**
         * 创建一个简单的MS
         *
         * @param msId
         * @param sqlSource      执行的sqlSource
         * @param sqlCommandType 执行的sqlCommandType
         */
        private void newUpdateMappedStatement(String msId, SqlSource sqlSource, SqlCommandType sqlCommandType) {
            MappedStatement ms = new MappedStatement.Builder(configuration, msId, sqlSource, sqlCommandType)
                    .resultMaps(new ArrayList<ResultMap>() {
                        {
                            add(new ResultMap.Builder(configuration, "defaultResultMap", int.class, new ArrayList<ResultMapping>(0)).build());
                        }
                    })
                    .build();
            //缓存
            configuration.addMappedStatement(ms);
        }

        private String select(String sql) {
            String msId = newMsId(sql, SqlCommandType.SELECT);
            if (hasMappedStatement(msId)) {
                return msId;
            }
            StaticSqlSource sqlSource = new StaticSqlSource(configuration, sql);
            newSelectMappedStatement(msId, sqlSource, Map.class);
            return msId;
        }

        private String selectDynamic(String sql, Class<?> parameterType) {
            String msId = newMsId(sql + parameterType, SqlCommandType.SELECT);
            if (hasMappedStatement(msId)) {
                return msId;
            }
            SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, parameterType);
            newSelectMappedStatement(msId, sqlSource, Map.class);
            return msId;
        }

        private String select(String sql, Class<?> resultType) {
            String msId = newMsId(resultType + sql, SqlCommandType.SELECT);
            if (hasMappedStatement(msId)) {
                return msId;
            }
            StaticSqlSource sqlSource = new StaticSqlSource(configuration, sql);
            newSelectMappedStatement(msId, sqlSource, resultType);
            return msId;
        }

        private String selectDynamic(String sql, Class<?> parameterType, Class<?> resultType) {
            String msId = newMsId(resultType + sql + parameterType, SqlCommandType.SELECT);
            if (hasMappedStatement(msId)) {
                return msId;
            }
            SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, parameterType);
            newSelectMappedStatement(msId, sqlSource, resultType);
            return msId;
        }

        private String insert(String sql) {
            String msId = newMsId(sql, SqlCommandType.INSERT);
            if (hasMappedStatement(msId)) {
                return msId;
            }
            StaticSqlSource sqlSource = new StaticSqlSource(configuration, sql);
            newUpdateMappedStatement(msId, sqlSource, SqlCommandType.INSERT);
            return msId;
        }

        private String insertDynamic(String sql, Class<?> parameterType) {
            String msId = newMsId(sql + parameterType, SqlCommandType.INSERT);
            if (hasMappedStatement(msId)) {
                return msId;
            }
            SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, parameterType);
            newUpdateMappedStatement(msId, sqlSource, SqlCommandType.INSERT);
            return msId;
        }

        private String update(String sql) {
            String msId = newMsId(sql, SqlCommandType.UPDATE);
            if (hasMappedStatement(msId)) {
                return msId;
            }
            StaticSqlSource sqlSource = new StaticSqlSource(configuration, sql);
            newUpdateMappedStatement(msId, sqlSource, SqlCommandType.UPDATE);
            return msId;
        }

        private String updateDynamic(String sql, Class<?> parameterType) {
            String msId = newMsId(sql + parameterType, SqlCommandType.UPDATE);
            if (hasMappedStatement(msId)) {
                return msId;
            }
            SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, parameterType);
            newUpdateMappedStatement(msId, sqlSource, SqlCommandType.UPDATE);
            return msId;
        }

        private String delete(String sql) {
            String msId = newMsId(sql, SqlCommandType.DELETE);
            if (hasMappedStatement(msId)) {
                return msId;
            }
            StaticSqlSource sqlSource = new StaticSqlSource(configuration, sql);
            newUpdateMappedStatement(msId, sqlSource, SqlCommandType.DELETE);
            return msId;
        }

        private String deleteDynamic(String sql, Class<?> parameterType) {
            String msId = newMsId(sql + parameterType, SqlCommandType.DELETE);
            if (hasMappedStatement(msId)) {
                return msId;
            }
            SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, parameterType);
            newUpdateMappedStatement(msId, sqlSource, SqlCommandType.DELETE);
            return msId;
        }
    }
}

3.2.2 ApiServiceMapper

import com.hz.pro.artifact.dynamic.bean.ServiceDto;
import org.apache.ibatis.annotations.Param;

import java.util.List;

/**
 * @author pp_lan
 * @date 2024/1/5
 */
public interface ApiServiceMapper {

    List<ServiceDto> findApis();

    String findSqlByPath(@Param("path") String path);

    int insertApiSql(@Param("path") String path, @Param("sqlContent") String sqlContent);
}

3.2.3 ApiServiceMapper.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.hz.pro.artifact.dynamic.mapper.main.ApiServiceMapper">

    <select id="findApis" resultType="com.hz.pro.artifact.dynamic.bean.ServiceDto">
        select path, sql_content from t_api_sql where in_use = 1
    </select>

    <select id="findSqlByPath" resultType="java.lang.String">
        select sql_content from t_api_sql where path = #{path} and in_use = 1
    </select>

    <insert id="insertApiSql">
        INSERT INTO t_api_sql VALUES(#{path}, #{sqlContent}, 1)
    </insert>


</mapper>

4 效果

4.1 注册

4.2 查询

5. 其他

       上述实现步骤已完成接口的注册、查询功能。但是存在一个问题,重启后接口便不存在了,需要重新初始化。后续可以使用监听读取数据库中接口配置进行接口的初始化。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

手机上连网络转接app,电脑连接手机,共用网络转接app的办法

方法一&#xff0c;&#xff08;不推荐&#xff09; 因为太简单了所以写一下 电脑安装MuMu模拟器&#xff0c;之后安装网络转接app&#xff0c;这个模拟器设置了从电脑上安装app和&#xff0c;安卓与电脑同步文件夹功能&#xff0c;实现文件共享。所以直接用就可以了。 方法二…

彻底认识Unity ui设计中Space - Overlay、Screen Space - Camera和World Space三种模式

文章目录 简述Screen Space - Overlay优点缺点 Screen Space - Camera优点缺点 World Space优点缺点 简述 用Unity中开发了很久&#xff0c;但是对unity UI管理中Canvas组件的Render Mode有三种主要类型&#xff1a;Screen Space - Overlay、Screen Space - Camera和World Spa…

OR-3150:IGBT驱动光耦,可替代HCPL3150

具有MOSFET高输入阻抗和GTR低导通压降特性提供隔离反馈 高隔离电压 1.5A输出电流 工业温度范围&#xff1a;–40C 至 110C 宽工作 VCC 范围 特征 VCM 1500V 时最小共模抑制 &#xff08;CMR&#xff09; 为 35 kV/μs 最大低电平输出电压 &#xff08;VOL&#xff09; 1.0…

ERROR:SyntaxError: Non-ASCII character ‘\xc3‘ in file

报错信息&#xff1a; SyntaxError: Non-ASCII character ‘\xc3’ in file /home/user/ROSpy-LeaderFollower/src/follow_/src/scripts/tb3_flw.py on line 46, but no encoding declared; see http://python.org/dev/peps/pep-0263/ for details 解决办法&#xff1a; pyt…

数据的创建、调用、修改、删除存储过程,以及第一类丢失更新(回滚丢失)和 第二类丢失更新(覆盖丢失/两次更新问题)

数据的创建存储过程、调用存储过程、修改存储过程、删除存储过程&#xff0c;以及第一类丢失更新&#xff08;回滚丢失&#xff09;和 第二类丢失更新&#xff08;覆盖丢失/两次更新问题&#xff09; 文章目录 一、创建存储的语法二、调用存储过程三、修改存储过程四、删除存储…

码云Gitee复制 GitHub 项目

码云提供了直接复制 GitHub 项目的功能&#xff0c;方便我们做项目的迁移和下载。 1.新建仓库 2.导入仓库 3.强制同步 如果 GitHub 项目更新了以后&#xff0c;在码云项目端可以手动重新同步&#xff0c;进行更新&#xff01;

Vue3-36-路由-路由的元数据信息 meta

什么是 meta 简单的理解&#xff0c;meta 就是路由对象 的一个属性对象&#xff0c; 可以 通过这个 属性给 路由对象添加 一些必要的属性值&#xff0c; 在使用路由对象时可以获取到这个属性型对象&#xff0c;从而进行一些其他的逻辑判断。 meta 这个非常的简单&#xff0c;就…

将yolov8的检测框从正框修改为旋转框需要做那些修改?

将yolov8项目修改为yolov8_obb项目需要修改模型结构(增加角度预测)、dataloader(使其支持dota格式数据)、修改TaskAlignedAssigner(使其支持带角度的bbox)、修改loss(新增对角度的训练)、修改metric(将hbb指标titile修改为obb)、修改绘图代码(使其能绘制旋转框)。 …

Flink窗口与WaterMark

本文目录 窗口的生命周期Window Assigners窗口函数&#xff08;Window Functions&#xff09;TriggersEvictorsAllowed Lateness 窗口 窗口&#xff08;Window&#xff09;是处理无界流的关键所在。窗口可以将数据流装入大小有限的“桶”中&#xff0c;再对每个“桶”加以处理。…

K8S陈述式资源管理(1)

命令行: kubectl命令行工具 优点: 90%以上的场景都可以满足对资源的增&#xff0c;删&#xff0c;查比较方便&#xff0c;对改不是很友好 缺点:命令比较冗长&#xff0c;复杂&#xff0c;难记声明式 声明式&#xff1a;K8S当中的yaml文件来实现资源管理 GUI&#xff1a;图形…

box-shadow参数学习及渲染过程研究

参数定义 CSS 的 box-shadow 属性用于在元素的框架周围添加阴影效果。它可以接受多个由逗号分隔的阴影效果&#xff0c;每个阴影效果由以下几部分组成&#xff1a; h-offset&#xff1a;水平阴影的位置。正值将阴影向右移动&#xff0c;负值将阴影向左移动。v-offset&#xf…

vue-video-player播放hls视频流

需求 最近需要接入海康视频摄像头&#xff0c;然后把视频的画面接入到自己的网站系统中。以前对接过rtsp固定IP的显示视频&#xff0c;这次的不一样&#xff0c;没有了固定IP。海康的解决办法是&#xff0c;摄像头通过配置服务器到萤石云平台&#xff0c;然后购买企业版账号和…

【unity小技巧】实现没有动画的FPS武器摇摆和摆动效果

文章目录 前言开始完结 前言 添加程序摇摆和摆动是为任何FPS游戏添加一些细节的非常简单的方法。但是并不是所以的模型动画都会配有武器摆动动画效果&#xff0c;在本文中&#xff0c;将实现如何使用一些简单的代码实现武器摇摆和摆动效果&#xff0c;这比设置动画来尝试实现类…

调整几行代码,接口吞吐提升 10 倍,性能调优妙啊!

景 分析过程 总结 背景 公司的一个ToB系统,因为客户使用的也不多,没啥并发要求,就一直没有经过压测。这两天来了一个“大客户”,对并发量提出了要求:核心接口与几个重点使用场景单节点吞吐量要满足最低500/s的要求。 当时一想,500/s吞吐量还不简单。Tomcat按照100个线程…

小心JDK20 ZipOutputStream

Oracle 團隊竟然這麽粗心&#xff0c;編譯JDK 20 時ZipOutputStream沒有編譯成功就發佈了。 所以這個20版本不可以使用ZipOutputStream。 GZIPInputStream 只能做最後的壓縮&#xff0c;不能添加多個附件ZipEntry。 下一個版本21不存在這個問題。 try(var zipOut new ZipOu…

C++之STL库简介

目录 一、STL&#xff08;Standard Template Library&#xff0c;标准模板库&#xff09; 二、容器&#xff08;Containers&#xff09; 1.vector&#xff08;动态数组&#xff09; 2.list&#xff08;双向链表&#xff09; 3.deque&#xff08;双端队列&#xff09; 4.st…

怎么做表单二维码来获取用户数据?扫码填表的制作方法

​怎么用二维码来收集其他人的信息&#xff0c;比如用户反馈、信息采集、问卷调查等等&#xff0c;都是现在表单二维码的常见应用方式。那么如果我们想制作一个表单二维码用来采集其他人员的反馈信息&#xff0c;用二维码生成器来制作的步骤有哪些呢&#xff1f;下面来教大家在…

Redis高级特性和应用(慢查询、Pipeline、事务、Lua)

Redis的慢查询 许多存储系统(例如 MySQL)提供慢查询日志帮助开发和运维人员定位系统存在的慢操作。所谓慢查询日志就是系统在命令执行前后计算每条命令的执行时间,当超过预设阀值,就将这条命令的相关信息(例如:发生时间,耗时,命令的详细信息)记录下来,Redis也提供了类似…

高压放大器输出接法及其注意事项

高压放大器应用场景非常广泛&#xff0c;非常适用于半导体高压驱动、TFT产业高压驱动、各种高压工程等应用&#xff1b;也很适用当作音频信号产生器或函数波形产生器的波形放大使用。使用场景广泛&#xff0c;放大器的输出接法也多种&#xff0c;对于不同的放大器也有对应的输出…

Linux vi/vim 教程

文章目录 【 1. vi/vim 的三种模式 】1.1 命令模式1.2 输入模式1.3 底线命令模式 【 2. 实例 】【 3. vim 的其他命令 】 所有的 Unix Like 系统都会内建 vi 文本编辑器&#xff0c;其他的文本编辑器则不一定会存在。目前我们使用比较多的是 vim 编辑器。vim 从 vi 发展出来&am…