基于springboot Vue3的两种图形验证码工具——vue3-puzzle-vcode纯前端防人机图形滑动验证码和kaptcha图片文字验证码

news2024/11/25 3:52:38

一.vue3-puzzle-vcode

        Vue 纯前端的拼图人机验证、右滑拼图验证

官网:

vue3-puzzle-vcode - npm (npmjs.com)icon-default.png?t=O83Ahttps://www.npmjs.com/package/vue3-puzzle-vcode

1.1基本使用步骤

安装

npm install vue-puzzle-vcode --save

简单例子

点击开始验证按钮弹出验证弹窗

<template>
    <Vcode :show="isShow" @success="onSuccess" @close="onClose"/>
    <button @click="onShow">开始验证</button>
</template>

<script setup>
  import { ref } from "vue";
  import Vcode from "vue3-puzzle-vcode";

  const isShow = ref(false);

  const onShow = () => {
    isShow.value = true;
  };

  const onClose = () => {
    isShow.value = false;
  };

  const onSuccess = () => {
    onClose(); // 验证成功,手动关闭模态框
  };
</script>

注:更多参数信息设置请参考官网文档

二.kaptcha

        kaptcha 是谷歌开源的非常实用的验证码生成工具,基于SimpleCaptcha的开源项目。使用Kaptcha 生成验证码十分简单并且参数可以进行自定义。只需添加jar包配置下就可以使用,通过配置,可以自己定义验证码大小、颜色、显示的字符等等。

官网:code.google.comicon-default.png?t=O83Ahttps://code.google.com/p/kaptcha/

验证码的一般流程

后端:

  • 随机生成图片验证码
  • 结合随机生成的UUID作为Key,验证码值作为Value保存验证码到Redis中
  • 将UUID和验证码图片响应给用户,等用户提交后验证校验码是否有效

前端:

  • 进入登录/注册页面时,获取验证码图片
  • 提供刷新验证码的动作,防止出现用户难以辨识的识别码

2.1基本的使用步骤

导入POM依赖

<!-- Kaptcha 依赖(验证码) -->
        <dependency>
            <groupId>com.github.penggle</groupId>
            <artifactId>kaptcha</artifactId>
            <version>2.3.2</version>
        </dependency>

KaptchaConfig配置类

该配置类用于给验证码的设置提供配置,如宽高,颜色等等

import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.google.code.kaptcha.util.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.Properties;

import static com.google.code.kaptcha.Constants.*;

/**
 * 验证码配置
 */
@Configuration
public class KaptchaConfig {
    @Bean(name = "captchaProducer")
    public DefaultKaptcha getKaptchaBean() {
        DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
        Properties properties = new Properties();
        // 是否有边框 默认为true 我们可以自己设置yes,no
        properties.setProperty(KAPTCHA_BORDER, "no");
        // 验证码文本字符颜色 默认为Color.BLACK
        properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_COLOR, "black");
        // 验证码图片宽度 默认为200
        properties.setProperty(KAPTCHA_IMAGE_WIDTH, "200");
        // 验证码图片高度 默认为50
        properties.setProperty(KAPTCHA_IMAGE_HEIGHT, "60");
        // 验证码文本字符大小 默认为40
        properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_SIZE, "38");
        // KAPTCHA_SESSION_KEY
        properties.setProperty(KAPTCHA_SESSION_CONFIG_KEY, "kaptchaCode");
        // 验证码文本字符长度 默认为5
        properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "6");
        // 验证码文本字体样式 默认为new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize)
        properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_NAMES, "Arial,Courier");
        // 图片样式 水纹com.google.code.kaptcha.impl.WaterRipple 鱼眼com.google.code.kaptcha.impl.FishEyeGimpy 阴影com.google.code.kaptcha.impl.ShadowGimpy
        properties.setProperty(KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.ShadowGimpy");
        Config config = new Config(properties);
        defaultKaptcha.setConfig(config);
        return defaultKaptcha;
    }
}

整合Redis

Redis是一个开源的内存数据库,属于NoSQL数据库的一种。它以高性能、支持丰富的数据结构、持久化特性、复制、集群以及发布/订阅等特性而闻名。

使用Redis给验证码设置有效期

引入依赖

<!-- redis 缓存操作 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

配置yml文件

server:
  port: 8080
  address: 127.0.0.1
spring:
  #  redis配置
  redis:
    host: localhost
    port: 6379
#spring事务管理日志
logging:
  level:
    org.springframework.jdbc.support.JdbcTransactionManager: debug

Redis配置类

package com.hl;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
public class RedisConfig {
    @Autowired
    private RedisConnectionFactory factory;

    @Bean
    public RedisTemplate<String, Object> redisTemplate() {
        //使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值(默认使用JDK的序列化方式)
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>(Object.class);
        ObjectMapper om = new ObjectMapper();
        // 指定要序列化的域,field,get和set,以及修饰符范围,ANY是都有包括private和public
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        // 指定序列化输入的类型,类必须是非final修饰的,final修饰的类,比如String,Integer等会跑出异常
        //om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
        jackson2JsonRedisSerializer.setObjectMapper(om);

        RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
        template.setConnectionFactory(factory);
        template.setKeySerializer(new StringRedisSerializer()); //指定Redis的Key序列化方式
        template.setValueSerializer(jackson2JsonRedisSerializer); //指定Value的序列化方式
        template.setHashKeySerializer(jackson2JsonRedisSerializer); //执行Hash的Key的序列化方式
        template.setHashValueSerializer(jackson2JsonRedisSerializer); //指定Hash的Value的序列化方式
        template.setDefaultSerializer(new StringRedisSerializer());
        template.afterPropertiesSet();
        return template;
    }

    @Bean
    public ValueOperations<String, String> valueOperations(RedisTemplate<String, String> redisTemplate) {
        return redisTemplate.opsForValue();
    }

}

Redis工具类(整合Redis的一些操作)

package com.hl;

import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundSetOperations;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;

/**
 * spring redis 工具类
 *
 **/
@SuppressWarnings(value = { "unchecked", "rawtypes" })
@Component
public class RedisCache
{
    @Autowired
    public RedisTemplate redisTemplate;

    /**
     * 缓存基本的对象,Integer、String、实体类等
     *
     * @param key 缓存的键值
     * @param value 缓存的值
     */
    public <T> void setCacheObject(final String key, final T value)
    {
        redisTemplate.opsForValue().set(key, value);
    }

    /**
     * 缓存基本的对象,Integer、String、实体类等
     *
     * @param key 缓存的键值
     * @param value 缓存的值
     * @param timeout 时间
     * @param timeUnit 时间颗粒度
     */
    public <T> void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit)
    {
        redisTemplate.opsForValue().set(key, value, timeout, timeUnit);
    }

    /**
     * 设置有效时间
     *
     * @param key Redis键
     * @param timeout 超时时间
     * @return true=设置成功;false=设置失败
     */
    public boolean expire(final String key, final long timeout)
    {
        return expire(key, timeout, TimeUnit.SECONDS);
    }

    /**
     * 设置有效时间
     *
     * @param key Redis键
     * @param timeout 超时时间
     * @param unit 时间单位
     * @return true=设置成功;false=设置失败
     */
    public boolean expire(final String key, final long timeout, final TimeUnit unit)
    {
        return redisTemplate.expire(key, timeout, unit);
    }

    /**
     * 获取有效时间
     *
     * @param key Redis键
     * @return 有效时间
     */
    public long getExpire(final String key)
    {
        return redisTemplate.getExpire(key);
    }

    /**
     * 判断 key是否存在
     *
     * @param key 键
     * @return true 存在 false不存在
     */
    public Boolean hasKey(String key)
    {
        return redisTemplate.hasKey(key);
    }

    /**
     * 获得缓存的基本对象。
     *
     * @param key 缓存键值
     * @return 缓存键值对应的数据
     */
    public <T> T getCacheObject(final String key)
    {
        ValueOperations<String, T> operation = redisTemplate.opsForValue();
        return operation.get(key);
    }

    /**
     * 删除单个对象
     *
     * @param key
     */
    public boolean deleteObject(final String key)
    {
        return redisTemplate.delete(key);
    }

    /**
     * 删除集合对象
     *
     * @param collection 多个对象
     * @return
     */
    public boolean deleteObject(final Collection collection)
    {
        return redisTemplate.delete(collection) > 0;
    }

    /**
     * 缓存List数据
     *
     * @param key 缓存的键值
     * @param dataList 待缓存的List数据
     * @return 缓存的对象
     */
    public <T> long setCacheList(final String key, final List<T> dataList)
    {
        Long count = redisTemplate.opsForList().rightPushAll(key, dataList);
        return count == null ? 0 : count;
    }

    /**
     * 获得缓存的list对象
     *
     * @param key 缓存的键值
     * @return 缓存键值对应的数据
     */
    public <T> List<T> getCacheList(final String key)
    {
        return redisTemplate.opsForList().range(key, 0, -1);
    }

    /**
     * 缓存Set
     *
     * @param key 缓存键值
     * @param dataSet 缓存的数据
     * @return 缓存数据的对象
     */
    public <T> BoundSetOperations<String, T> setCacheSet(final String key, final Set<T> dataSet)
    {
        BoundSetOperations<String, T> setOperation = redisTemplate.boundSetOps(key);
        Iterator<T> it = dataSet.iterator();
        while (it.hasNext())
        {
            setOperation.add(it.next());
        }
        return setOperation;
    }

    /**
     * 获得缓存的set
     *
     * @param key
     * @return
     */
    public <T> Set<T> getCacheSet(final String key)
    {
        return redisTemplate.opsForSet().members(key);
    }

    /**
     * 缓存Map
     *
     * @param key
     * @param dataMap
     */
    public <T> void setCacheMap(final String key, final Map<String, T> dataMap)
    {
        if (dataMap != null) {
            redisTemplate.opsForHash().putAll(key, dataMap);
        }
    }

    /**
     * 获得缓存的Map
     *
     * @param key
     * @return
     */
    public <T> Map<String, T> getCacheMap(final String key)
    {
        return redisTemplate.opsForHash().entries(key);
    }

    /**
     * 往Hash中存入数据
     *
     * @param key Redis键
     * @param hKey Hash键
     * @param value 值
     */
    public <T> void setCacheMapValue(final String key, final String hKey, final T value)
    {
        redisTemplate.opsForHash().put(key, hKey, value);
    }

    /**
     * 获取Hash中的数据
     *
     * @param key Redis键
     * @param hKey Hash键
     * @return Hash中的对象
     */
    public <T> T getCacheMapValue(final String key, final String hKey)
    {
        HashOperations<String, String, T> opsForHash = redisTemplate.opsForHash();
        return opsForHash.get(key, hKey);
    }

    /**
     * 获取多个Hash中的数据
     *
     * @param key Redis键
     * @param hKeys Hash键集合
     * @return Hash对象集合
     */
    public <T> List<T> getMultiCacheMapValue(final String key, final Collection<Object> hKeys)
    {
        return redisTemplate.opsForHash().multiGet(key, hKeys);
    }

    /**
     * 删除Hash中的某条数据
     *
     * @param key Redis键
     * @param hKey Hash键
     * @return 是否成功
     */
    public boolean deleteCacheMapValue(final String key, final String hKey)
    {
        return redisTemplate.opsForHash().delete(key, hKey) > 0;
    }

    /**
     * 获得缓存的基本对象列表
     *
     * @param pattern 字符串前缀
     * @return 对象列表
     */
    public Collection<String> keys(final String pattern)
    {
        return redisTemplate.keys(pattern);
    }
}

获取验证码接口流程

后端提供验证码

        我们提供一个get接口来实现该流程

  1. 保存验证码信息,生成UUID并构建验证码键名
  2. 生成验证码(字符or数学)
  3. 将生成的验证码存入Redis缓存,并设置过期时间
  4. 转换流信息写出
  5. 将验证码图片转换为Base64编码格式,将UUID和Base64编码后的图片添加到响应对象并返回
package com.hl;

import com.google.code.kaptcha.Producer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.FastByteArrayOutputStream;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

@Slf4j
@RestController
public class LoginController {

    @Resource(name = "captchaProducer")
    private Producer captchaProducer;
    @Autowired
    private RedisCache redisCache;

    @GetMapping("/captchaImage")
    public Result<Map<String,String>> captchaImage() {
        log.info("获取验证码");
        // 保存验证码信息,生成UUID并构建验证码键名
        String uuid = UUID.randomUUID().toString();
        String verifyKey = "captcha_codes:" + uuid;

        String capStr = null, code = null;
        BufferedImage image = null;

        // 生成验证码
        //数学运算型验证码
//        String capText = captchaProducerMath.createText();
//        capStr = capText.substring(0, capText.lastIndexOf("@"));
//        code = capText.substring(capText.lastIndexOf("@") + 1);
//        image = captchaProducerMath.createImage(capStr);

        //字符型验证码
        capStr = code = captchaProducer.createText();
        image = captchaProducer.createImage(capStr);

        //将生成的验证码存入Redis缓存,并设置过期时间
        redisCache.setCacheObject(verifyKey, code, 2, TimeUnit.MINUTES);
        // 转换流信息写出
        FastByteArrayOutputStream os = new FastByteArrayOutputStream();
        try {
            ImageIO.write(image, "jpg", os);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        //将验证码图片转换为Base64编码格式,将UUID和Base64编码后的图片添加到响应对象并返回
        Map<String, String> result = new HashMap<>();
        result.put("uuid", uuid);
        result.put("img", Base64.encode(os.toByteArray()));
        return Result.success(result);
    }
}
前端接收Base64的验证码图片

        前端发送请求后端接口,获取验证码图片的Base64信息,将其塞入img标签的src属性中。为img标签添加onclick事件,每次点击,就重新请求验证码图片。

import request from '@/utils/request'
export const getCodeImg = () => {
    return request.get('/captchaImage');
}
<script setup>
//引入组件
import { ref } from "vue";
import { ElMessage } from "element-plus";
const userDate = ref({
  username: "",
  password: "",
  captcha: "",
});

// vue3-puzzle-vcode
import Vcode from "vue3-puzzle-vcode";
const isShow = ref(false);
const onShow = () => {
  isShow.value = true;
};
const onClose = () => {
  isShow.value = false;
};
const onSuccess = () => {
  onClose(); // 验证成功,手动关闭模态框
};
const imgs = [
  "https://picsum.photos/200/300?random=1",
  "https://picsum.photos/200/300?random=2",
  "https://picsum.photos/200/300?random=3",
  "https://picsum.photos/200/300?random=4",
  "https://picsum.photos/200/300?random=5",
];

// kaptcha 
import { getCodeImg } from "../api/login";
const codeUrl=ref('')
const getCode = async()=>{
    //调用接口
    let result = await getCodeImg();
    codeUrl.value = "data:image/gif;base64," + result.data.img;
    // loginForm.value.uuid = res.uuid;
}
getCode();

</script>
<template>
  <Vcode :show="isShow" @success="onSuccess" @close="onClose" :imgs="imgs" />
  <el-form
    style="width: 500px; height: 40px; margin: auto; padding-top: 100px"
    red="form"
    :model="userDate"
  >
    <el-form-item label="账号">
      <el-input v-model="userDate.username" placeholder="账号"></el-input>
    </el-form-item>
    <el-form-item label="密码">
      <el-input v-model="userDate.password" placeholder="密码"></el-input>
    </el-form-item>
    <el-form-item label="验证码">
      <el-input v-model="userDate.captcha" placeholder="验证码" class="codeInput"></el-input>
    <!-- kaptcha -->
    <div class="login-code">
      <img :src="codeUrl" @click="getCode" class="login-code-img" />
    </div>
    </el-form-item>
    <el-form-item>
      <el-button type="primary" @click="onShow">登录</el-button>
    </el-form-item>
  </el-form>
</template>

<style lang='scss' scoped>
.login-code {
  width: 33%;
  height: 40px;
  float: right;
  img {
    cursor: pointer;
    vertical-align: middle;
  }
}
.login-code-img {
  height: 40px;
  padding-left: 12px;
}
.codeInput {
  width: 60%;
}
</style>

注: 代码未全部展示,只展示了主要部分

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

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

相关文章

linux从入门到精通-从基础学起,逐步提升,探索linux奥秘(十一)--rpm管理和计划任务

linux从入门到精通-从基础学起&#xff0c;逐步提升&#xff0c;探索linux奥秘&#xff08;十一&#xff09;–rpm管理和计划任务 一、rpm管理&#xff08;重点&#xff09; 1、rpm管理 作用&#xff1a; rpm的作用类似于windows上的电脑管家中“软件管理”、安全卫士里面“…

【机器学习】集成学习|Boosting|随机森林|Adaboost|GBDT梯度提升树|XGBoost 极限梯度提升树 及案例实现

文章目录 集成学习集成学习思想概述集成学习分类Bagging 思想Boosting思想Bagging 和 Boosting 的对比 随机森林算法随机森林实现步骤随机森林算法apiAPI 代码实现 Adaboost 算法实现步骤整体过程实现 算法推导Adaboost 案例 葡萄酒数据 GBDT (梯度提升树)提升树 BDT (Boosting…

WPF 中的 StackPanel 详解

Windows Presentation Foundation&#xff08;WPF&#xff09;是微软开发的一种用于创建桌面客户端应用程序的用户界面框架。WPF 提供了一套丰富的控件和布局能力&#xff0c;使得开发者可以轻松构建出功能强大、视觉优美的用户界面。在 WPF 的布局系统中&#xff0c;StackPane…

Git上传命令汇总

进入企业&#xff0c;每日需要上传执行用例记录到gitlab平台上&#xff0c;本文记录了常用git上传命令&#xff0c; 并用github演示。 1、本地建立分支&#xff0c;克隆远程仓库 在gitlab中&#xff0c;每个人需要创建自己的分支&#xff0c;一般以自己的名字命名&#xff0c;…

新品牌Sesame Street《芝麻街》商标版权双维权,尚未TRO

案件基本情况起诉时间&#xff1a;2024-10-8案件号&#xff1a;24-cv-09713品牌&#xff1a;Sesame Street原告&#xff1a;Sesame Workshop原告律所&#xff1a;TME起诉地&#xff1a;伊利诺伊州北部法院品牌介绍Sesame Street《芝麻街》是美国公共广播协会&#xff08;PBS&…

5个IO控制20个LED灯的方案详解

工程师们经常为了节省一两个IO口想各种方案想到抠脑壳&#xff0c;今天给大家整点活儿&#xff0c;介绍一种超级节省IO口的LED灯控制方案。 5个IO口控制20个LED灯&#xff0c;而且可以对每个LED灯实现单独控制。电路结构如下&#xff1a; 注意一下这种电路网络&#xff0c;其…

ctf.bugku-baby lfi

题目来源&#xff1a;baby lfi - Bugku CTF平台 访问页面&#xff0c; 翻译解析&#xff1a;百度翻译-您的超级翻译伙伴&#xff08;文本、文档翻译&#xff09; (baidu.com) LFI Warmups - 本地文件包含&#xff08;Local File Inclusion&#xff0c;简称LFI&#xff09; H…

【最新华为OD机试E卷-支持在线评测】喊7的次数重排(100分)多语言题解-(Python/C/JavaScript/Java/Cpp)

🍭 大家好这里是春秋招笔试突围 ,一枚热爱算法的程序员 💻 ACM金牌🏅️团队 | 大厂实习经历 | 多年算法竞赛经历 ✨ 本系列打算持续跟新华为OD-E/D卷的多语言AC题解 🧩 大部分包含 Python / C / Javascript / Java / Cpp 多语言代码 👏 感谢大家的订阅➕ 和 喜欢�…

Linux基础-进程的超详细讲解(1)_进程的概念与属性

个人主页&#xff1a;C忠实粉丝 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 C忠实粉丝 原创 Linux基础-进程的超详细讲解(1) 收录于专栏[Linux学习] 本专栏旨在分享学习Linux的一点学习笔记&#xff0c;欢迎大家在评论区交流讨论&#x1f48c; 目录 1. 进…

day-65 鸡蛋掉落-两枚鸡蛋

思路 动态规划&#xff1a;dp[i]表示i楼f确切的值的最小操作次数&#xff0c;对于上一次选择的楼层共有i-1种可能&#xff08;上一次从1楼,2楼…扔下&#xff09;&#xff0c;所以需要在i-1中可能中去最小值 解题过程 对于每一种可能&#xff0c;如dp[10]上一次从5楼扔下&…

ES-入门聚合查询

url 请求地址 http://192.168.1.108:9200/shopping/_search {"aggs": { //聚合操作"price_group":{ //名称,随意起名"terms":{ //分组"field": "price" //分组字段}}} } 查询出来的结果是 查询结果中价格的平均值 {&q…

KubeSphere部署mysql

演示示例使用的是3.4.1&#xff0c;各版本有名字差异 功能是一样的 由于mysql需要做数据持久化所以需要挂载数据 1.创建mysql基础配置 项目中-配置-配置字典 mysql-conf添加键值对 [client] default-character-setutf8mb4 [mysql] default-character-setutf8mb4 [mysqld] …

数据库设计与开发—初识SQLite与DbGate

一、SQLite与DbGate简介 &#xff08;一&#xff09;SQLite[1][3] SQLite 是一个部署最广泛、用 C 语言编写的数据库引擎&#xff0c;属于嵌入式数据库&#xff0c;其作为库被软件开发人员嵌入到应用程序中。 SQLite 的设计允许在不安装数据库管理系统或不需要数据库管理员的情…

QT QML 练习4

效果&#xff1a;鼠标按下Tab建可以选选择标签或者方块之间的切换 这段代码使用了 QtQuick 框架&#xff0c;创建了一个包含两个 Text 元素和两个嵌套 Rectangle 的用户界面。以下是对代码中涉及的主要知识点和实现细节的介绍&#xff1a; 知识点及代码细节介绍 导入 QtQuic…

SpringAOP学习文档

目录 一、概念二、示例代码三、切点1、execution2、within3、this4、target5、args6、annotation7、within8、target9、args10、组合切点表达式11、在Before注解中使用自定义的切入点表达式&#xff0c;以及切入点方法12、获取指定类型的真实对象 四、通知1、Around注解的通知方…

AI赋能,精准防控:AI智能分析网关V4人员徘徊算法的技术优势与应用场景

随着科技的飞速发展&#xff0c;视频监控系统在各个领域的应用越来越广泛&#xff0c;从公共安全到商业管理&#xff0c;再到交通监控等。AI智能分析网关V4作为新一代的视频分析设备&#xff0c;以其强大的智能分析能力和多样化的应用场景&#xff0c;成为市场关注的焦点。本文…

pytorch与卷积神经网络实战笔记

课程视频链接 CNN卷积神经网络算法原理 全神经网络的整体结构 输入层&#xff08;x1, x2, x3…&#xff09;->隐藏层&#xff08;全连接&#xff09;->输出层&#xff0c;整体就类似于一个函数&#xff0c;输入x&#xff0c;经过函数module(x)得到输出y的过程&#xf…

WPF常见容器全方位介绍

Windows Presentation Foundation (WPF) 是微软的一种用于构建Windows桌面应用程序的UI框架。WPF的布局系统基于容器&#xff0c;帮助开发者以灵活、响应的方式组织用户界面 (UI) 元素。本篇文章将详细介绍WPF中几种常见的容器&#xff0c;包括Grid、StackPanel、WrapPanel、Do…

SpringMVC源码-处理器适配器HandlerAdapter

因为定义controller的方式有三种&#xff0c;每种不同的方式调用的方法不同&#xff0c;尤其是注解修饰的 方法名是自定义的 因此需要通过适配器模式来调用方法执行 initStrategies进行适配器的初始化 处理器适配器一共有如下四种: org.springframework.web.servlet.Handl…

springboot系列--web相关知识探索五

一、前言 web相关知识探索四中研究了请求中所带的参数是如何映射到接口参数中的&#xff0c;也即请求参数如何与接口参数绑定。主要有四种、分别是注解方式、Servlet API方式、复杂参数、以及自定义对象参数。web相关知识探索四中主要研究了复杂参数底层绑定原理。本次主要是研…