Springboot深度集成Redis

SpringBoot项目里集成Guacamole,实现浏览器远程桌面(含WebSocket连接与录屏配置) 本文详细介绍了如何在SpringBoot项目中集成Apache Guacamole,实现高性能的浏览器远程桌面功能,包括WebSocket连接优化、动态参数传递和会话录制等高级特性。通过Docker化部署和SpringBoot配置,开发者可以快速构建企业级远程桌面解决方案,提升远程访问的安全性和效率。 阅读详情

前言

之前没怎么用过Redis,对Springboot集成Redis不是非常熟悉。在动手集成的时候又发现集成的有Jedis和Lettuce两种客户端。想改设置都不知道如何下手。索性就研究了一番。

在 springboot 1.5.x版本的默认的客户端是 Jedis实现的,springboot 2.x版本中默认客户端是用 lettuce实现的。

Jedis和 Lettuce是 Java操作 Redis的客户端。在 Spring Boot 1.x 版本默认使用的是 jedis,而在 Spring Boot 2.x 版本默认使用的就是Lettuce。关于 Jedis跟 Lettuce的区别如下:

  • Jedis在实现上是直接连接的redis server,如果在多线程环境下是非线程安全的,这个时候只有使用连接池,为每个Jedis实例增加物理连接
  • Lettuce的连接是基于Netty的,连接实例(StatefulRedisConnection)可以在多个线程间并发访问,应为StatefulRedisConnection是线程安全的,所以一个连接实例(StatefulRedisConnection)就可以满足多线程环境下的并发访问,当然这个也是可伸缩的设计,一个连接实例不够的情况也可以按需增加连接实例。

集成配置

通过研究,我们发现Lettuce的性能会更好一些。所以下面在集成时会以Lettuce客户端的API进行讲解。

导入依赖

除了Springboot必要的依赖,还需要导入如下两个依赖。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
</dependency>

配置文件

注意lettuce客户端的配置时以spring.redis.lettuce开头的。jedis客户端的配置是以spring.redis.jedis开头的。

# redis基本配置
spring.redis.database=9
spring.redis.host=192.168.0.102
spring.redis.port=33332
spring.redis.timeout=360000

# lettuce连接池配置(下面四项是默认项)
spring.redis.lettuce.pool.max-active=8
spring.redis.lettuce.pool.max-wait=-1ms
spring.redis.lettuce.pool.max-idle=8
spring.redis.lettuce.pool.min-idle=0

自定义RedisTemplate

默认情况下的模板只能支持 RedisTemplate<String,String>,只能存入字符串,很多时候,我们需要自定义 RedisTemplate ,设置序列化器,这样我们可以很方便的操作实例对象。

@Configuration
public class RedisConfig {
    //配置数据库,默认使用的是0号数据库,如果使用其他数据库可以指定
    @Value("${spring.redis.database}")
    private int database;
    
    @Bean
    public RedisTemplate<String, Serializable> redisTemplate(LettuceConnectionFactory connectionFactory) {
        //设置数据库,默认为0
        connectionFactory.setDatabase(database);
        RedisTemplate<String, Serializable> redisTemplate = new RedisTemplate<>();
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        redisTemplate.setConnectionFactory(connectionFactory);
        return redisTemplate;
    }
    
    //下面几个对象不是必须的但是可以方便对不同数据类型进行操作
    //hash类型的数据操作
    @Bean
    public HashOperations<String, String, Object> hashOperations(RedisTemplate<String, Serializable> redisTemplate) {
        return redisTemplate.opsForHash();
    }

    //字符串类型数据操作
    @Bean
    public ValueOperations<String, Serializable> valueOperations(RedisTemplate<String, Serializable> redisTemplate) {
        return redisTemplate.opsForValue();
    }

    //链表类型的数据操作
    @Bean
    public ListOperations<String, Serializable> listOperations(RedisTemplate<String, Serializable> redisTemplate) {
        return redisTemplate.opsForList();
    }

    //无序集合类型的数据操作
    @Bean
    public SetOperations<String, Serializable> setOperations(RedisTemplate<String, Serializable> redisTemplate) {
        return redisTemplate.opsForSet();
    }

    //有序集合类型的数据操作
    @Bean
    public ZSetOperations<String, Serializable> zSetOperations(RedisTemplate<String, Serializable> redisTemplate) {
        return redisTemplate.opsForZSet();
    }
}

多数据库集成配置

切换数据库要解决的问题

在上一节集成配置当中简单介绍了指定Redis数据库的配置。但是通常而言,如果仅仅使用一个redis数据库,使用默认的0号数据库就可以。但是如果一个项目中用到了多个数据库就需要考虑数据库的切换问题。

需要考虑的问题主要有如下几个:

  • DB切换时直接用一个redisTemplate是否可以?
  • DB1切换到DB2后,其他线程如果要继续使用DB1该如何选取?
  • 工具类该如何封装?

要解决第一个问题可以使用如下代码。但是问题是切换数据库是在Factory中进行的。在Factory中切换数据库以后需要给redisTemplate重新设置,然后reset Connection。

如果同一项目使用多个数据库频繁进行切换Connection,性能可能会存在问题。因此,虽然下面这种方式可以,但是并不推荐。

//不推荐使用该方式
@Autowired
private StringRedisTemplate redisTemplate;

public void setDataBase(int num) {
    LettuceConnectionFactory connectionFactory = (LettuceConnectionFactory) redisTemplate.getConnectionFactory();
    if (connectionFactory != null && num != connectionFactory.getDatabase()) {
        //切换DB
        connectionFactory.setDatabase(num);
        //是否允许多个线程操作共用同一个缓存连接,默认 true,false 时每个操作都将开辟新的连接
        connectionFactory.setShareNativeConnection(false);
        this.redisTemplate.setConnectionFactory(connectionFactory);
        connectionFactory.resetConnection();
    }
}

既然多个数据库使用同一个redisTemplate可能存在性能问题,那么最好的方式就是为不同的数据库创建不同的redisTemplate。

多redisTemplate难点

  1. 如何通过factory创建多个restTemplate?
  2. 如何维护Bean的生命周期?yml如何规划
  3. redis工具类该如何定义?

Java代码配置

package it.aspirin.learnredis.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.beans.factory.config.ConstructorArgumentValues;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Supplier;

/**
 * @description: 根据yml创建redisTemplate并注入到Spring容器
 * @date: Created in 2020/9/27 15:27
 */
@Configuration
public class Knife4jRedisRegister implements EnvironmentAware, ImportBeanDefinitionRegistrar {

    private static final Logger logger = LoggerFactory.getLogger(Knife4jRedisRegister.class);

    @Value("${spring.redis.host}")
    private String host;

    @Value("${spring.redis.host}")
    private Integer port;

    @Value("#{'${spring.redis.databases}'.split(',')}")
    private List<Integer> databases;

    @Value("${spring.redis.lettuce.pool.max-active}")
    private Integer maxActive;

    @Value("${spring.redis.lettuce.pool.max-wait}")
    private Integer maxWait;

    @Value("${spring.redis.lettuce.pool.max-idle}")
    private Integer maxIdle;

    @Value("${spring.redis.lettuce.pool.min-idle}")
    private Integer minIdle;

    private static final Map<String, Object> registerBean = new ConcurrentHashMap<>();

    private Environment environment;

    private Binder binder;

    @Override
    public void setEnvironment(Environment environment) {
        this.environment = environment;
        this.binder = Binder.get(this.environment);
    }

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {

        boolean onPrimary = true;
        System.out.println(databases+" ddddd "+port);
        //根据多个库实例化出多个连接池和Template
        for (Integer database : databases) {
            //单机模式
            RedisStandaloneConfiguration configuration = new RedisStandaloneConfiguration();
            configuration.setHostName(host);
            configuration.setPort(port);
            configuration.setDatabase(database);


            //池配置
            GenericObjectPoolConfig genericObjectPoolConfig = new GenericObjectPoolConfig();

            genericObjectPoolConfig.setMaxIdle(maxIdle);
            genericObjectPoolConfig.setMaxTotal(maxActive);
            genericObjectPoolConfig.setMinIdle(minIdle);
            if (maxWait != null) {
                genericObjectPoolConfig.setMaxWaitMillis(maxWait);
            }
            Supplier<LettuceConnectionFactory> lettuceConnectionFactorySupplier = () -> {
                LettuceConnectionFactory factory = (LettuceConnectionFactory) registerBean.get("LettuceConnectionFactory" + database);
                if (factory != null) {
                    return factory;
                }
                LettucePoolingClientConfiguration.LettucePoolingClientConfigurationBuilder builder = LettucePoolingClientConfiguration.builder();

                LettuceClientConfiguration clientConfiguration = builder.poolConfig(genericObjectPoolConfig).build();
                factory = new LettuceConnectionFactory(configuration, clientConfiguration);
                registerBean.put("LettuceConnectionFactory" + database, factory);
                return factory;
            };

            LettuceConnectionFactory lettuceConnectionFactory = lettuceConnectionFactorySupplier.get();
            BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(LettuceConnectionFactory.class, lettuceConnectionFactorySupplier);
            AbstractBeanDefinition factoryBean = builder.getRawBeanDefinition();
            factoryBean.setPrimary(onPrimary);
            beanDefinitionRegistry.registerBeanDefinition("lettuceConnectionFactory" + database, factoryBean);
            // StringRedisTemplate
            GenericBeanDefinition stringRedisTemplate = new GenericBeanDefinition();
            stringRedisTemplate.setBeanClass(StringRedisTemplate.class);
            ConstructorArgumentValues constructorArgumentValues = new ConstructorArgumentValues();
            constructorArgumentValues.addIndexedArgumentValue(0, lettuceConnectionFactory);
            stringRedisTemplate.setConstructorArgumentValues(constructorArgumentValues);
            stringRedisTemplate.setAutowireMode(AutowireCapableBeanFactory.AUTOWIRE_BY_NAME);
            beanDefinitionRegistry.registerBeanDefinition("stringRedisTemplate" + database, stringRedisTemplate);
            // 定义RedisTemplate对象
            GenericBeanDefinition redisTemplate = new GenericBeanDefinition();
            redisTemplate.setBeanClass(RedisTemplate.class);
            redisTemplate.getPropertyValues().add("connectionFactory", lettuceConnectionFactory);
            redisTemplate.setAutowireMode(AutowireCapableBeanFactory.AUTOWIRE_BY_NAME);
            RedisSerializer stringRedisSerializer = null;
            Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = null;

            // 内置默认序列化(此处若不设置则采用默认的JDK设置,也可以在使用使自定义序列化方式)
            jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
            ObjectMapper om = new ObjectMapper();
            om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
            om.activateDefaultTyping(om.getPolymorphicTypeValidator(), ObjectMapper.DefaultTyping.NON_FINAL);
            jackson2JsonRedisSerializer.setObjectMapper(om);
            stringRedisSerializer = new StringRedisSerializer();
            // key采用String的序列化方式,value采用json序列化方式
            redisTemplate.getPropertyValues().add("keySerializer",stringRedisSerializer);
            redisTemplate.getPropertyValues().add("hashKeySerializer",stringRedisSerializer);
            redisTemplate.getPropertyValues().add("valueSerializer",jackson2JsonRedisSerializer);
            redisTemplate.getPropertyValues().add("hashValueSerializer",jackson2JsonRedisSerializer);

            //注册Bean
            beanDefinitionRegistry.registerBeanDefinition("redisTemplate" + database, redisTemplate);
            logger.info("Registration redis ({}) !", database);
            if (onPrimary) {
                onPrimary = false;
            }
        }
    }
}
package it.aspirin.learnredis.config;

import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;

import java.util.Map;

/**
 * @description: 给工具类提供manager,由先的configuration进行初始化和赋值
 * @date: Created in 2020/9/27 15:27
 */
public class Knife4jRedisManager {

    private Map<String, RedisTemplate> redisTemplateMap;

    private Map<String, StringRedisTemplate> stringRedisTemplateMap;

    public Knife4jRedisManager(Map<String, RedisTemplate> redisTemplateMap ,
                               Map<String, StringRedisTemplate> stringRedisTemplateMap) {
        this.redisTemplateMap = redisTemplateMap;
        this.stringRedisTemplateMap = stringRedisTemplateMap;
    }

    public RedisTemplate redisTemplate(int dbIndex) {
        RedisTemplate redisTemplate = redisTemplateMap.get("redisTemplate" + dbIndex);
        return redisTemplate;
    }

    public StringRedisTemplate stringRedisTemplate(int dbIndex) {
        StringRedisTemplate stringRedisTemplate = stringRedisTemplateMap.get("stringRedisTemplate" + dbIndex);
        stringRedisTemplate.setEnableTransactionSupport(true);
        return stringRedisTemplate;
    }

    public Map<String, RedisTemplate> getRedisTemplateMap() {
        return redisTemplateMap;
    }

    public Map<String, StringRedisTemplate> getStringRedisTemplateMap() {
        return stringRedisTemplateMap;
    }
}
package it.aspirin.learnredis.config;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.env.Environment;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;

import javax.annotation.PostConstruct;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @description: 核心配置,取出spring中的redisTemplate暂存到map中,并初始化redisManager
 * @date: Created in 2020/9/27 15:27
 */
@AutoConfigureBefore({RedisAutoConfiguration.class})
@Import(Knife4jRedisRegister.class)
@EnableCaching
@Configuration
public class Knife4jRedisConfiguration implements EnvironmentAware, ApplicationContextAware {

    private static final Logger logger = LoggerFactory.getLogger(Knife4jRedisConfiguration.class);

    @Value("#{'${spring.redis.databases}'.split(',')}")
    private List<Integer> databases;

    private static String key1 = "redisTemplate";
    private static String key2 = "stringRedisTemplate";

    Map<String, RedisTemplate> redisTemplateMap = new HashMap<>();
    Map<String, StringRedisTemplate> stringRedisTemplateMap = new HashMap<>();
    private Binder binder;
    private Environment environment;

    @Override
    public void setEnvironment(Environment environment) {
        this.environment = environment;
        this.binder = Binder.get(this.environment);
    }

    @PostConstruct
    public Map<String, RedisTemplate> initRedisTemplate() {

        //根据多个库实例化出多个连接池和Template
        if (databases == null || databases.size() == 0) {
            logger.warn("no config property multi databases , default use db0 !!!");
            databases.add(0);
        }

        //根据指定的数据库个数来加载对应的RedisTemplate
        for (Integer database : databases) {
            String key = key1 + database;
            RedisTemplate redisTemplate = applicationContext.getBean(key, RedisTemplate.class);
            if (redisTemplate != null) {
                redisTemplateMap.put(key, redisTemplate);
            }

            key = key2 + database;
            if (stringRedisTemplateMap != null) {
                StringRedisTemplate stringRedisTemplate = applicationContext.getBean(key, StringRedisTemplate.class);
                stringRedisTemplateMap.put(key, stringRedisTemplate);
            }
        }
        if (redisTemplateMap.size() == 0 && stringRedisTemplateMap.size() == 0) {
            throw new RuntimeException("load redisTemplate failure , please check knife4j.redis property config!!!");
        }
        return redisTemplateMap;
    }

    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }


    @Bean
    public Knife4jRedisManager knife4jRedisManager() {
        return new Knife4jRedisManager(redisTemplateMap, stringRedisTemplateMap);
    }
}
package it.aspirin.learnredis.config;

import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * @description: redis工具类基类,集成通用的方法,由`configuration`进行初始化
 * @date: Created in 2020/9/27 11:20
 */
@Component
public class RedisBaseUtil {

    @Resource
    protected Knife4jRedisManager knife4jRedisManager;

    protected int defaultDB = 0;

    public void delete(String key) {
        delete(0,key);
    }
    public void delete(int dbIndex ,String key) {
        knife4jRedisManager.redisTemplate(dbIndex).delete(key);
    }

    public boolean set(String key, Object value) {
        return set(0,key,value);
    }
    public boolean set(int dbIndex ,String key, Object value) {
        try {
            knife4jRedisManager.redisTemplate(dbIndex).opsForValue().set(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    public void delete(Collection<String> keys){
        delete(defaultDB , keys);
    }
    public void delete(int dbIndex ,Collection<String> keys){
        knife4jRedisManager.redisTemplate(dbIndex).delete(keys);
    }

    public Set<String> getKeys(String redisKey) {
        return getKeys(0,redisKey);
    }
    public Set<String> getKeys(int dbIndex ,String redisKey) {
        Set<Object> keys = knife4jRedisManager.redisTemplate(dbIndex).opsForHash().keys(redisKey);
        Set<String> retKeys = new HashSet<>();
        for (Object key : keys) {
            retKeys.add(String.valueOf(key));
        }
        return retKeys;
    }

    /**
     * 每个redis
     * 指定缓存失效时间
     * @param key 键
     * @param time 时间(秒)
     * @return
     */
    public boolean expire(String key, long time) {
        return expire(defaultDB , key,time);
    }
    public boolean expire(int dbIndex ,String key, long time) {
        try {
            if (time > 0) {
                knife4jRedisManager.redisTemplate(dbIndex).expire(key, time, TimeUnit.SECONDS);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
    /**
     * 根据key 获取过期时间
     * @param key 键 不能为null
     * @return 时间(秒) 返回0代表为永久有效
     */
    public long getExpire(int dbIndex , String key) {
        return knife4jRedisManager.redisTemplate(dbIndex).getExpire(key, TimeUnit.SECONDS);
    }
    public long getExpire(String key) {
        return getExpire(defaultDB , key);
    }
    /**
     * 判断key是否存在
     * @param key 键
     * @return true 存在 false不存在
     */
    public boolean hasKey(String key) {
        return hasKey(defaultDB , key);
    }
    public boolean hasKey(int dbIndex ,String key) {
        try {
            return knife4jRedisManager.redisTemplate(dbIndex).hasKey(key);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
}

测试

@RestController
@RequestMapping("/test")
public class RedisTestController {

    @Autowired
    private RedisBaseUtil redisBaseUtil;

    /**
     * 单值操作测试
     * @param key
     * @return
     */
    @GetMapping("/val/{key}")
    public String test(@PathVariable("key") String key){
        redisBaseUtil.set(key , "默认库设置");
        redisBaseUtil.set(1 , key , "指定1库设置值");
        //查看key是否存在
        boolean flag = redisBaseUtil.hasKey(1, key);
        boolean flag1 = redisBaseUtil.hasKey(2, key);
        System.out.println("指定1库获取值:" + flag+" "+flag1);
        return "ok";
    }
}

参考文章

【SpringBoot 3.x 第61节】Spring Boot 3.x 还能这么玩?Testcontainers 深度集成后,我彻底卸载了本地 MySQL 和 Redis! 🏆本文收录于《滚雪球学SpringBoot 3》,专门攻坚指数提升,本年度国内最系统+最专业+最详细(永久更新)。    本专栏致力打造最硬核 SpringBoot3 从零基础到进阶系列学习内容,🚀均为全网独家首发,打造精品专栏,专栏持续更新中…欢迎大家订阅持续学习。 如果想快速定位学习,可以看这篇【SpringBoot3教程导航帖】,你想学习的都被收集在内,快速投入学习!!两不误。    若还想学习更多,可直接前往《滚雪球学SpringBoot(全版本合集)》,涵盖SpringBoot所有版本教学文章 阅读详情

相关推荐

Spring Boot整合MyBatis、Redis、Netty与Logback实战项目

在现代企业级Java应用开发中,数据访问层的设计直接决定了系统的性能、可维护性以及扩展能力。当业务逻辑变得复杂,简单的CRUD操作已无法满足需求时,就需要借助MyBatis强大的自定义SQL能力和高级结果映射机制来实现高效的数据处理。本章将深入剖析如何通过编写复杂的查询语句提升数据库操作的灵活性,并结合实际场景探讨分页优化、执行计划分析等关键技术;同时,全面解析resultMap的定制化字段映射能力,支持一对一、一对多关联查询和基于继承结构的鉴别器使用方式;

weixin_42513870的博客 755

redistemplate set方法_使用RedisTemplate对Redis的常用操作

“点击上方程序大视界,关注后置顶或者星标原创不易,且行且珍惜”01—前言Redis是一个NoSQL(非关系型数据库)数据库之一,key-value存储系统或者说是一个缓存键值对数据库,具有如下特性:基于内存运行,性能高效支持分布式,理论上可以无限扩展key-value存储系统开源的使用ANSI C语言编写、遵守BSD协议、支持网络、可基于内存亦可...

weixin_32841641的博客 1万+

泛微ecology9二次开发-附件上传接口实战:从表单创建到数据库存储解析

本文详细解析了泛微ecology9二次开发中的附件上传接口实战,从测试环境搭建、数据库表结构解析到接口代码实现,提供了全面的开发指南。重点介绍了文件上传的核心逻辑、常见问题排查及高级应用场景,帮助开发者高效实现附件上传功能并优化系统性能。

weixin_30836759的博客 477

【RedisTemplate】Set数据类型的常用操作

引言 上篇博客总结了使用RedisTemplate操作ZSet数据类型的一些常用方法,本篇博客继续总结Set数据类型。 Set数据结构 Redis 的 Set 是 String 类型的无序集合。集合成员是唯一的,这就意味着集合中不能出现重复的数据。 Redis 中集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)。 常用方法 1. 增加集合元素 /** * set中增加元素,支持一次增加多个元素,逗号分隔即可,结果返回添加的个数 * * @param key * @param val

Dream it Possible 1万+

信号输入到底要不要接GND?单端与差分输入的接地选择与抗干扰分析

在电子工程与工业现场中,接地是信号完整性的基石。电压本身是相对值,任何测量都需要一个公共参考点,这就是地(GND)在信号输入中的核心作用。单端输入依赖信号源与接收端共地,以GND为基准还原信号;差分输入则直接测量两根信号线之间的差值,天然不依赖本地地电位,因此能有效抑制共模干扰和地环路噪声。实际选型时,需要根据传感器类型、传输距离、地电位差大小权衡:短距离共地良好的场景可用单端,长距离或高干扰环境应优先考虑差分或隔离方案。本文从接地原理出发,结合实际接线与故障排查经验,帮助工程师在PLC、采集卡和传感器链路

weixin_28703507的博客 311

华为鸿蒙系统也能用!5分钟搞定谷歌三件套安装(附最新APK下载链接)

本文为华为鸿蒙系统用户提供了一份详尽的谷歌三件套(Google Play服务、商店及框架)手动安装指南。文章核心在于无需Root,通过ADB命令获取设备信息,并从可靠来源下载匹配版本的APK,按照正确顺序安装,从而解决海外应用依赖问题,实现5分钟快速部署。

c7d8e9的博客 6448

redis在 java中操作不同数据类型

//list set zSet Hash 删除所有元素的方法 redisTemplate.delete("name"); /** * 注入redisTemplate / /* * 单个值得缓存 boundValueOps */ @Test public void setValue() { redisTemplate.boundValueOps("name").set("zhangsan"...

weixin_43959028的博客 358

(8)(8.19) GPIOs

Ardupilot官网关于《Copter》的翻译 —《(8.19) GPIOs》。

无人机开发(开源Ardupilot) 1万+

Java——redis通过改写部分源码实现动态db-操作数据库的时候可以指定db操作

*** 存放对应库的redisTemplate,用于操作对应的db/*** 当不指定库时默认使用的redisTemplate/*** 当类被加载到容器中属性设置完毕后检查redisTemplates和defaultRedisTemplate是否为空} } /*** 获取要操作的RedisTemplate// 当前要操作的DB Object lookupKey = determineCurrentLookupKey();

weixin_56039103的博客 1232

Vue2 - 详细实现网页 “开启录音并生成音频文件,上传到服务器效果“,浏览器录制外部音频调用麦克风并传到后端,兼容全部浏览器、支持 PC 网页或手机 H5 网页、可本地播放试听(详细示例运行代码)

vue,nuxt,uniapp,网页网站,麦克风,网页录音,MediaRecorder,浏览器录音,移动端h5网页,手机端录音,web网页录音,recorder-core官方文档,Recorder,非https怎么本地录音,录音报错失败,录音后怎么播放音频,如何获取录音mp3文件,解决一直不提示麦克风权限,小白详细教程,实时录音,调用浏览器麦克风录音,浏览器不兼容,wav,mp3,vue怎么实现网页录音呢,vue2网页录音并上传到服务器,web如何实现录制音频,vue网页录音,音频录制播放和上传服务器,vu

🏆 前端领域优质创作者 6003

杰理JL7033C芯片运行SoundFont音色库教程

本教程介绍如何在杰理JL7033C芯片上移植TinySoundFont音色库,实现MIDI播放功能。关键步骤包括:获取TinySoundFont开源代码,创建音色库声音播放数据流,封装核心功能文件。硬件需配备外部PSRAM和NOR FLASH存储音色库,软件使用杰理SDK和工具链。移植后系统可支持40个voice,满足MIDI播放和电子琴演奏需求。教程详细说明了代码移植过程,包括音色库文件处理、内存配置和音频流水线设置。

qq_42014563的博客 610

Redis的基本使用和SpringBoot集成Redis

Redis的基本使用和SpringBoot集成Redis

y516369的博客 3975

AI上推荐 之 协同过滤

㐇

Miracle8070 1万+

Spring Boot与Redis深度集成:实现高性能分布式应用

在当今的分布式系统和微服务架构中,数据的快速访问和高效处理是系统性能的核心。传统的数据库虽然功能强大,但在面对高并发和快速读写需求时,往往显得力不从心。此时,Redis作为一种高性能的内存数据库,能够以极高的速度处理数据,从而有效缓解数据库的压力,提升系统的整体性能。Spring Boot以其简洁的配置和强大的功能,为开发者提供了一个快速开发的平台。

709

45、使用 sqlread 函数将数据从数据库表导入 MATLAB

本文详细介绍了如何使用 MATLAB 中的 `sqlread` 函数从不同类型的数据库(如 Microsoft SQL Server 和 SQLite)中导入数据。文章涵盖了 `sqlread` 的基本语法、输入输出参数、使用示例以及注意事项,并通过多个实例演示了自定义导入选项、元信息检索和数据类型转换等内容。此外,还总结了导入数据库数据的通用流程及最佳实践建议,帮助用户更高效地进行数据分析和处理工作。

bash7scripter的博客 133

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

Redis结合SpringBoot

主要介绍了Redis基本概念、命令行操作、结合springboot进行五种数据类型的crud操作。

A1225438764的博客 700

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

推荐文章:零基础入门,Redis在IDEA中的完美集成之旅

推荐文章:零基础入门,Redis在IDEA中的完美集成之旅 去发现同类优质开源项目:https://gitcode.com/ 在当今高速发展的软件行业中,数据处理效率成为了众多开发者关注的焦点。Redis,这个高性能的键值存储数据库,凭借其出色的速度和灵活性,已成为缓存、消息队列等多种场景下的首选解决方案。对于广大Java开发者而言,能否高效地在开发环境中集成Redis,直接影响到项目开发的流畅度...

gitblog_06582的博客 1031

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

AudioLDM-S与SpringBoot集成实战:企业级音效管理系统

本文介绍了如何在星图GPU平台上自动化部署AudioLDM-S (极速音效生成)镜像,快速构建企业级音效管理系统。通过该平台,用户可轻松实现文本到音频的自动化生成,典型应用于游戏开发、影视制作中的音效快速创作与集成,显著提升内容生产效率。

weixin_33363025的博客 413

SpringBoot 2.7 + 海康SDK 多摄像头布防实战:3步实现车牌抓拍与Redis缓存

本文详细介绍了如何利用SpringBoot 2.7与海康SDK实现多摄像头布防与车牌识别缓存系统。通过3步实战教程,包括SDK初始化、多摄像头管理和车牌数据缓存,帮助开发者高效实现车牌抓拍与Redis缓存,提升智能安防系统的实时性与稳定性。

chuiqi9947的博客 398

SpringBoot项目实战:用阿里云短信SDK实现分布式验证码服务(含Redis防刷策略)

本文详细介绍了如何在SpringBoot项目中集成阿里云短信SDK实现分布式验证码服务,包括阿里云短信服务的深度集成、验证码服务核心架构设计、Redis防刷策略实现等关键内容。通过实战案例和代码示例,帮助开发者构建高可用、安全的短信验证码服务,特别适合电商系统等需要高并发验证的场景。

sand8的博客 196

springboot+langchain4j实战:Day17-----Reranker 深度集成

> 在 Day 16 混合检索的基础上,深入 Reranker 环节的三大升级: > **Redis 结果缓存**(省钱 + 提速)+ **多模型对比**(量化 Reranker 价值)+ **对照实验**(用数据说话)

qq_44909430的博客 392

Phi-3-vision-128k-instruct 黑马点评项目实战:基于SpringBoot+Redis的仿大众点评

本文介绍了如何在星图GPU平台上自动化部署Phi-3-vision-128k-instruct镜像,实现智能代码生成与业务逻辑梳理功能。该镜像在SpringBoot+Redis项目开发中表现卓越,能自动生成高质量Java代码并优化Redis应用方案,特别适用于电商秒杀系统等企业级应用场景。

weixin_36288992的博客 294

企业级PDF解析方案:PDF-Parser-1.0与SpringBoot深度集成

本文介绍了如何在星图GPU平台上自动化部署PDF-Parser-1.0文档理解模型,实现企业级PDF智能解析。该方案通过与SpringBoot深度集成,能够高效处理发票、合同等文档,自动提取文本、表格和关键信息,大幅提升企业文档数字化处理效率。

weixin_33239721的博客 284

PowerPaint-V1 Gradio与Java集成开发:SpringBoot微服务图像处理系统

本文介绍了如何在星图GPU平台上自动化部署🎨 PowerPaint-V1 Gradio镜像,构建基于SpringBoot的微服务图像处理系统。该镜像支持文本引导的图像修复、物体移除和智能扩展等功能,可广泛应用于电商平台的商品图片自动化处理、背景替换和水印去除等场景,显著提升图像处理效率与质量。

weixin_42097508的博客 385

基于YOLO12的智能安防系统实战:SpringBoot后端集成方案

本文介绍了如何在星图GPU平台上自动化部署YOLO12 实时目标检测模型 V1.0镜像,以构建智能安防系统。该方案将AI检测能力无缝集成至SpringBoot后端,实现对视频流的实时分析,典型应用于智慧园区中的人员闯入、车辆违章等异常行为的实时监控与告警。

weixin_36311421的博客 671

AWPortrait-Z与SpringBoot集成开发指南

本文介绍了如何在星图GPU平台上自动化部署AWPortrait-Z镜像(基于Z-Image精心构建的人像美化LoRA,二次开发webui构建by科哥),实现高效的人像美化服务。该镜像可快速集成到SpringBoot应用中,为电商平台和社交应用提供高质量的人像照片自动美化与增强功能。

weixin_42465140的博客 28
上一篇: Springboot集成Redis实现发布订阅功能(Java Lettuce客户端)
下一篇: 通过JSONObject在不强制类型转换的情况下获取带泛型的对象
iFence
博客等级 码龄9年 8051粉丝 111原创
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值