Redis在 Spring Boot 项目中的完整配置指南

一、基础配置

1.Maven依赖

xml 复制代码
<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>

<!-- Jackson序列化 -->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
</dependency>

2. application.yml基础配置

yaml 复制代码
spring:
  redis:
    # 单节点配置
    host: localhost
    port: 6379
    password: 123456
    database: 0
    
    # 连接池配置
    lettuce:
      pool:
        max-active: 8
        max-idle: 8
        min-idle: 0
        max-wait: -1ms
    timeout: 2000ms
    
    # 集群配置
    # cluster:
    #   nodes: 192.168.1.101:6379,192.168.1.102:6379,192.168.1.103:6379
    #   max-redirects: 3
    
    # 哨兵配置
    # sentinel:
    #   master: mymaster
    #   nodes: 192.168.1.101:26379,192.168.1.102:26379,192.168.1.103:26379
    
    # 主从配置(自定义配置,需要代码支持)

二、高级配置类

1. RedisTemplate配置

java 复制代码
@Configuration
@EnableCaching
public class RedisConfig {

    @Bean
    @SuppressWarnings(value = { "unchecked", "rawtypes" })
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<Object, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(connectionFactory);

        // 使用Jackson2JsonRedisSerializer序列化value
        Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer(Object.class);

        ObjectMapper mapper = new ObjectMapper();
        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        mapper.activateDefaultTyping(
            LaissezFaireSubTypeValidator.instance,
            ObjectMapper.DefaultTyping.NON_FINAL,
            JsonTypeInfo.As.WRAPPER_ARRAY
        );
        serializer.setObjectMapper(mapper);

        // 使用StringRedisSerializer序列化key
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(serializer);
        
        // Hash的key也采用StringRedisSerializer
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setHashValueSerializer(serializer);

        template.afterPropertiesSet();
        return template;
    }
}

2. 自定义序列化配置

java 复制代码
@Configuration
public class RedisSerializationConfig {
    
    /**
     * 通用序列化配置
     */
    @Bean
    public RedisSerializer<Object> redisSerializer() {
        // 创建JSON序列化器
        Jackson2JsonRedisSerializer<Object> serializer = 
            new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(
            PropertyAccessor.ALL, 
            JsonAutoDetect.Visibility.ANY
        );
        objectMapper.activateDefaultTyping(
            objectMapper.getPolymorphicTypeValidator(),
            ObjectMapper.DefaultTyping.NON_FINAL
        );
        serializer.setObjectMapper(objectMapper);
        return serializer;
    }
    
    /**
     * 专门处理LocalDateTime的序列化
     */
    @Bean
    public RedisTemplate<String, Object> redisTemplate(
            RedisConnectionFactory redisConnectionFactory,
            RedisSerializer<Object> redisSerializer) {
        
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);
        
        // Key和HashKey使用String序列化
        StringRedisSerializer stringSerializer = new StringRedisSerializer();
        template.setKeySerializer(stringSerializer);
        template.setHashKeySerializer(stringSerializer);
        
        // Value和HashValue使用JSON序列化
        template.setValueSerializer(redisSerializer);
        template.setHashValueSerializer(redisSerializer);
        
        template.afterPropertiesSet();
        return template;
    }
}

3. 缓存配置

java 复制代码
@Configuration
public class CacheConfig extends CachingConfigurerSupport {
    
    @Bean
    public CacheManager cacheManager(RedisConnectionFactory factory) {
        RedisSerializer<String> redisSerializer = new StringRedisSerializer();
        Jackson2JsonRedisSerializer<Object> jacksonSerializer = 
            new Jackson2JsonRedisSerializer<>(Object.class);
        
        // 配置序列化
        RedisCacheConfiguration config = RedisCacheConfiguration
            .defaultCacheConfig()
            .entryTtl(Duration.ofHours(1))  // 默认缓存1小时
            .serializeKeysWith(
                RedisSerializationContext
                    .SerializationPair
                    .fromSerializer(redisSerializer)
            )
            .serializeValuesWith(
                RedisSerializationContext
                    .SerializationPair
                    .fromSerializer(jacksonSerializer)
            )
            .disableCachingNullValues();  // 不缓存null值
        
        // 针对不同缓存设置不同的过期时间
        Map<String, RedisCacheConfiguration> cacheConfigurations = new HashMap<>();
        cacheConfigurations.put("userCache", 
            config.entryTtl(Duration.ofMinutes(30)));
        cacheConfigurations.put("productCache",
            config.entryTtl(Duration.ofHours(2)));
        
        return RedisCacheManager.builder(factory)
            .cacheDefaults(config)
            .withInitialCacheConfigurations(cacheConfigurations)
            .build();
    }
    
    @Bean
    public KeyGenerator keyGenerator() {
        return (target, method, params) -> {
            StringBuilder sb = new StringBuilder();
            sb.append(target.getClass().getSimpleName());
            sb.append(":");
            sb.append(method.getName());
            for (Object obj : params) {
                if (obj != null) {
                    sb.append(":").append(obj.toString());
                }
            }
            return sb.toString();
        };
    }
}

三、多数据源配置

1. 多Redis实例配置

java 复制代码
@Configuration
public class MultipleRedisConfig {
    
    @Primary
    @Bean(name = "primaryRedisTemplate")
    public RedisTemplate<String, Object> primaryRedisTemplate(
            @Qualifier("primaryRedisConnectionFactory") 
            RedisConnectionFactory factory) {
        return createRedisTemplate(factory);
    }
    
    @Bean(name = "secondaryRedisTemplate")
    public RedisTemplate<String, Object> secondaryRedisTemplate(
            @Qualifier("secondaryRedisConnectionFactory") 
            RedisConnectionFactory factory) {
        return createRedisTemplate(factory);
    }
    
    @Primary
    @Bean(name = "primaryRedisConnectionFactory")
    public RedisConnectionFactory primaryRedisConnectionFactory() {
        RedisStandaloneConfiguration config = 
            new RedisStandaloneConfiguration("127.0.0.1", 6379);
        config.setPassword(RedisPassword.of("password1"));
        config.setDatabase(0);
        
        return new LettuceConnectionFactory(config);
    }
    
    @Bean(name = "secondaryRedisConnectionFactory")
    public RedisConnectionFactory secondaryRedisConnectionFactory() {
        RedisStandaloneConfiguration config = 
            new RedisStandaloneConfiguration("127.0.0.1", 6380);
        config.setPassword(RedisPassword.of("password2"));
        config.setDatabase(0);
        
        return new LettuceConnectionFactory(config);
    }
    
    private RedisTemplate<String, Object> createRedisTemplate(
            RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);
        
        // 序列化配置
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
        
        template.afterPropertiesSet();
        return template;
    }
}

2. 动态数据源切换

java 复制代码
@Component
public class RedisContextHolder {
    private static final ThreadLocal<String> CONTEXT_HOLDER = 
        new ThreadLocal<>();
    
    public static void setDataSource(String dataSource) {
        CONTEXT_HOLDER.set(dataSource);
    }
    
    public static String getDataSource() {
        return CONTEXT_HOLDER.get();
    }
    
    public static void clearDataSource() {
        CONTEXT_HOLDER.remove();
    }
}

@Configuration
public class DynamicRedisConfig {
    
    @Bean
    public RedisTemplate<String, Object> dynamicRedisTemplate(
            RedisConnectionFactory dynamicRedisConnectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(dynamicRedisConnectionFactory);
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        return template;
    }
    
    @Bean
    public RedisConnectionFactory dynamicRedisConnectionFactory(
            @Qualifier("primaryRedisConnectionFactory") 
            RedisConnectionFactory primary,
            @Qualifier("secondaryRedisConnectionFactory") 
            RedisConnectionFactory secondary) {
        
        Map<Object, Object> targetConnectionFactories = new HashMap<>();
        targetConnectionFactories.put("primary", primary);
        targetConnectionFactories.put("secondary", secondary);
        
        DynamicRedisConnectionFactory factory = 
            new DynamicRedisConnectionFactory();
        factory.setDefaultTargetConnectionFactory(primary);
        factory.setTargetConnectionFactories(targetConnectionFactories);
        
        return factory;
    }
}

四、Redis集群配置

1. 集群配置

yaml 复制代码
spring:
  redis:
    cluster:
      nodes:
        - 192.168.1.101:7001
        - 192.168.1.101:7002
        - 192.168.1.102:7001
        - 192.168.1.102:7002
        - 192.168.1.103:7001
        - 192.168.1.103:7002
      max-redirects: 3  # 最大重定向次数
    timeout: 5000ms
    lettuce:
      pool:
        max-active: 8
        max-idle: 8
        min-idle: 0

2. 哨兵配置

yaml 复制代码
spring:
  redis:
    sentinel:
      master: mymaster
      nodes:
        - 192.168.1.101:26379
        - 192.168.1.102:26379
        - 192.168.1.103:26379
    password: yourpassword
    database: 0
    timeout: 3000ms

3. Redis集群配置类

java 复制代码
@Configuration
@ConditionalOnProperty(name = "spring.redis.cluster.nodes")
public class RedisClusterConfig {
    
    @Bean
    public RedisConnectionFactory redisConnectionFactory(
            RedisProperties properties) {
        
        RedisClusterConfiguration clusterConfig = 
            new RedisClusterConfiguration(
                properties.getCluster().getNodes()
            );
        
        if (properties.getPassword() != null) {
            clusterConfig.setPassword(
                RedisPassword.of(properties.getPassword())
            );
        }
        
        LettuceClientConfiguration clientConfig = 
            LettuceClientConfiguration.builder()
                .commandTimeout(properties.getTimeout())
                .clientOptions(ClientOptions.builder()
                    .autoReconnect(true)
                    .disconnectedBehavior(
                        ClientOptions.DisconnectedBehavior.REJECT_COMMANDS)
                    .build())
                .build();
        
        return new LettuceConnectionFactory(clusterConfig, clientConfig);
    }
}

五、高级特性配置

1. 发布订阅配置

java 复制代码
@Configuration
public class RedisPubSubConfig {
    
    @Bean
    public RedisMessageListenerContainer redisContainer(
            RedisConnectionFactory connectionFactory,
            MessageListenerAdapter listenerAdapter) {
        
        RedisMessageListenerContainer container = 
            new RedisMessageListenerContainer();
        container.setConnectionFactory(connectionFactory);
        
        // 订阅频道
        container.addMessageListener(listenerAdapter, 
            new PatternTopic("news.*"));
        container.addMessageListener(listenerAdapter,
            new PatternTopic("logs.*"));
            
        return container;
    }
    
    @Bean
    public MessageListenerAdapter listenerAdapter(
            RedisMessageReceiver receiver) {
        return new MessageListenerAdapter(receiver, "receiveMessage");
    }
    
    @Component
    public static class RedisMessageReceiver {
        public void receiveMessage(String message, String channel) {
            System.out.println("收到消息: " + message + ",频道: " + channel);
        }
    }
}

2. Lua脚本支持

java 复制代码
@Component
public class RedisScriptService {
    
    @Autowired
    private StringRedisTemplate redisTemplate;
    
    // 限流脚本
    private static final String RATE_LIMIT_SCRIPT = 
        "local key = KEYS[1]\n" +
        "local limit = tonumber(ARGV[1])\n" +
        "local window = tonumber(ARGV[2])\n" +
        "local current = redis.call('GET', key)\n" +
        "if current and tonumber(current) >= limit then\n" +
        "    return 0\n" +
        "end\n" +
        "current = redis.call('INCR', key)\n" +
        "if tonumber(current) == 1 then\n" +
        "    redis.call('EXPIRE', key, window)\n" +
        "end\n" +
        "return 1";
    
    private final RedisScript<Long> rateLimitScript = 
        new DefaultRedisScript<>(RATE_LIMIT_SCRIPT, Long.class);
    
    public boolean tryAcquire(String key, int limit, int windowSeconds) {
        Long result = redisTemplate.execute(
            rateLimitScript,
            Collections.singletonList(key),
            String.valueOf(limit),
            String.valueOf(windowSeconds)
        );
        return result != null && result == 1;
    }
}

3. 分布式锁

java 复制代码
@Component
public class RedisDistributedLock {
    
    @Autowired
    private StringRedisTemplate redisTemplate;
    
    private static final String LOCK_SCRIPT = 
        "if redis.call('setnx', KEYS[1], ARGV[1]) == 1 then\n" +
        "    redis.call('pexpire', KEYS[1], ARGV[2])\n" +
        "    return 1\n" +
        "else\n" +
        "    return 0\n" +
        "end";
    
    private static final String UNLOCK_SCRIPT = 
        "if redis.call('get', KEYS[1]) == ARGV[1] then\n" +
        "    return redis.call('del', KEYS[1])\n" +
        "else\n" +
        "    return 0\n" +
        "end";
    
    private final RedisScript<Long> lockScript = 
        new DefaultRedisScript<>(LOCK_SCRIPT, Long.class);
    private final RedisScript<Long> unlockScript = 
        new DefaultRedisScript<>(UNLOCK_SCRIPT, Long.class);
    
    public boolean tryLock(String lockKey, String requestId, long expireMillis) {
        Long result = redisTemplate.execute(
            lockScript,
            Collections.singletonList(lockKey),
            requestId,
            String.valueOf(expireMillis)
        );
        return result != null && result == 1;
    }
    
    public boolean unlock(String lockKey, String requestId) {
        Long result = redisTemplate.execute(
            unlockScript,
            Collections.singletonList(lockKey),
            requestId
        );
        return result != null && result == 1;
    }
}

六、性能优化配置

1. 连接池优化配置

yaml 复制代码
spring:
  redis:
    lettuce:
      pool:
        # 最大连接数(根据业务量调整)
        max-active: 20
        # 最大空闲连接数
        max-idle: 10
        # 最小空闲连接数
        min-idle: 5
        # 最大等待时间(毫秒),-1表示无限等待
        max-wait: 1000ms
      # 关闭超时时间
      shutdown-timeout: 100ms
    # 连接超时时间
    connect-timeout: 2000ms
    # 命令超时时间
    timeout: 5000ms

2. 客户端配置优化

java 复制代码
@Configuration
public class RedisClientOptimizationConfig {
    
    @Bean
    public LettuceConnectionFactory redisConnectionFactory(
            RedisProperties properties) {
        
        RedisStandaloneConfiguration config = 
            new RedisStandaloneConfiguration();
        config.setHostName(properties.getHost());
        config.setPort(properties.getPort());
        config.setPassword(
            RedisPassword.of(properties.getPassword())
        );
        
        // 客户端优化配置
        LettuceClientConfiguration clientConfig = 
            LettuceClientConfiguration.builder()
                .useSsl()  // 启用SSL
                .and()
                .commandTimeout(properties.getTimeout())
                .shutdownTimeout(
                    properties.getLettuce().getShutdownTimeout()
                )
                .clientOptions(
                    ClientOptions.builder()
                        .autoReconnect(true)
                        .publishOnScheduler(true)  // 在调度器上发布
                        .requestQueueSize(1000)  // 请求队列大小
                        .socketOptions(
                            SocketOptions.builder()
                                .keepAlive(true)
                                .connectTimeout(
                                    properties.getConnectTimeout()
                                )
                                .build()
                        )
                        .build()
                )
                .clientResources(
                    DefaultClientResources.builder()
                        .ioThreadPoolSize(4)  // I/O线程数
                        .computationThreadPoolSize(4)  // 计算线程数
                        .build()
                )
                .build();
        
        return new LettuceConnectionFactory(config, clientConfig);
    }
}

七、监控配置

1. 健康检查配置

java 复制代码
@Configuration
public class RedisHealthConfig {
    
    @Bean
    public RedisHealthIndicator redisHealthIndicator(
            RedisConnectionFactory connectionFactory) {
        return new RedisHealthIndicator(connectionFactory);
    }
}

// 自定义健康检查
@Component
public class CustomRedisHealthIndicator 
        implements HealthIndicator {
    
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    
    @Override
    public Health health() {
        try {
            String result = redisTemplate.execute(
                (RedisCallback<String>) connection -> 
                    connection.ping()
            );
            if ("PONG".equals(result)) {
                return Health.up()
                    .withDetail("version", getRedisVersion())
                    .build();
            }
            return Health.down()
                .withDetail("error", "Ping failed")
                .build();
        } catch (Exception e) {
            return Health.down(e).build();
        }
    }
    
    private String getRedisVersion() {
        Properties info = redisTemplate
            .execute((RedisCallback<Properties>) connection -> 
                connection.info("server")
            );
        return info.getProperty("redis_version");
    }
}

2. 指标监控

java 复制代码
@Configuration
public class RedisMetricsConfig {
    
    @Bean
    public MeterBinder redisMetrics(RedisConnectionFactory factory) {
        return new RedisMetrics(factory);
    }
    
    @Bean
    public RedisTemplate<String, String> redisTemplateForMetrics(
            RedisConnectionFactory factory) {
        StringRedisTemplate template = new StringRedisTemplate(factory);
        template.setEnableTransactionSupport(true);
        return template;
    }
}

八、常用工具类

1. Redis工具类

java 复制代码
@Component
public class RedisUtil {
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    // ============================ Common ============================
    
    public Boolean hasKey(String key) {
        try {
            return redisTemplate.hasKey(key);
        } catch (Exception e) {
            return false;
        }
    }
    
    public Boolean delete(String key) {
        return redisTemplate.delete(key);
    }
    
    public Long delete(Collection<String> keys) {
        return redisTemplate.delete(keys);
    }
    
    public Boolean expire(String key, long time, TimeUnit unit) {
        try {
            if (time > 0) {
                return redisTemplate.expire(key, time, unit);
            }
            return false;
        } catch (Exception e) {
            return false;
        }
    }
    
    // ============================ String ============================
    
    public void set(String key, Object value) {
        redisTemplate.opsForValue().set(key, value);
    }
    
    public void set(String key, Object value, long time, TimeUnit unit) {
        if (time > 0) {
            redisTemplate.opsForValue().set(key, value, time, unit);
        } else {
            set(key, value);
        }
    }
    
    public Object get(String key) {
        return key == null ? null : redisTemplate.opsForValue().get(key);
    }
    
    // ============================ Hash ==============================
    
    public void hSet(String key, String hashKey, Object value) {
        redisTemplate.opsForHash().put(key, hashKey, value);
    }
    
    public Object hGet(String key, String hashKey) {
        return redisTemplate.opsForHash().get(key, hashKey);
    }
    
    public Map<Object, Object> hGetAll(String key) {
        return redisTemplate.opsForHash().entries(key);
    }
    
    // ============================ List ==============================
    
    public List<Object> lGet(String key, long start, long end) {
        try {
            return redisTemplate.opsForList().range(key, start, end);
        } catch (Exception e) {
            return Collections.emptyList();
        }
    }
    
    // ============================ Set ===============================
    
    public Set<Object> sMembers(String key) {
        try {
            return redisTemplate.opsForSet().members(key);
        } catch (Exception e) {
            return Collections.emptySet();
        }
    }
    
    // ============================ ZSet ==============================
    
    public Set<Object> zRange(String key, long start, long end) {
        try {
            return redisTemplate.opsForZSet().range(key, start, end);
        } catch (Exception e) {
            return Collections.emptySet();
        }
    }
    
    // ============================ Lock ==============================
    
    public Boolean tryLock(String key, String value, long expireTime) {
        Boolean result = redisTemplate.opsForValue()
            .setIfAbsent(key, value, expireTime, TimeUnit.SECONDS);
        return Boolean.TRUE.equals(result);
    }
    
    public void unlock(String key, String value) {
        Object currentValue = redisTemplate.opsForValue().get(key);
        if (value.equals(currentValue)) {
            redisTemplate.delete(key);
        }
    }
}

九、配置文件示例

1. 生产环境配置(application-prod.yml)

yaml 复制代码
spring:
  redis:
    host: ${REDIS_HOST:redis-cluster.prod.svc.cluster.local}
    port: 6379
    password: ${REDIS_PASSWORD:}
    timeout: 3000ms
    lettuce:
      pool:
        max-active: 20
        max-idle: 10
        min-idle: 5
        max-wait: 1000ms
      shutdown-timeout: 100ms
    # 集群配置
    cluster:
      nodes:
        - redis-node-1:6379
        - redis-node-2:6379
        - redis-node-3:6379
      max-redirects: 3
    # SSL配置
    ssl: true

2. 开发环境配置(application-dev.yml)

yaml 复制代码
spring:
  redis:
    host: localhost
    port: 6379
    password: 
    timeout: 2000ms
    lettuce:
      pool:
        max-active: 8
        max-idle: 8
        min-idle: 0
        max-wait: -1ms
    database: 0

十、常见问题解决

1. 序列化问题

java 复制代码
// 解决LocalDateTime序列化问题
@Bean
public RedisTemplate<String, Object> redisTemplate(
        RedisConnectionFactory factory) {
    RedisTemplate<String, Object> template = new RedisTemplate<>();
    template.setConnectionFactory(factory);
    
    // 解决LocalDateTime序列化
    ObjectMapper mapper = new ObjectMapper();
    mapper.registerModule(new JavaTimeModule());
    mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
    
    Jackson2JsonRedisSerializer<Object> serializer = 
        new Jackson2JsonRedisSerializer<>(mapper, Object.class);
    
    template.setKeySerializer(new StringRedisSerializer());
    template.setValueSerializer(serializer);
    template.setHashKeySerializer(new StringRedisSerializer());
    template.setHashValueSerializer(serializer);
    
    return template;
}

2. 连接超时配置

java 复制代码
@Configuration
public class RedisTimeoutConfig {
    
    @Bean
    public LettuceConnectionFactory redisConnectionFactory() {
        RedisStandaloneConfiguration config = 
            new RedisStandaloneConfiguration();
        
        LettuceClientConfiguration clientConfig = 
            LettuceClientConfiguration.builder()
                .commandTimeout(Duration.ofSeconds(5))
                .shutdownTimeout(Duration.ofSeconds(1))
                .clientOptions(
                    ClientOptions.builder()
                        .socketOptions(
                            SocketOptions.builder()
                                .connectTimeout(Duration.ofSeconds(2))
                                .build()
                        )
                        .build()
                )
                .build();
        
        return new LettuceConnectionFactory(config, clientConfig);
    }
}
相关推荐
KG_LLM图谱增强大模型2 小时前
SciDaSynth:基于大语言模型的科学文献交互式结构化数据提取系统
数据库·人工智能·大模型·知识图谱
凌盛羽2 小时前
用Python非常流行的openpyxl库对Excel(.xlsx格式)文件进行创建、读取、写入、显示等操作
数据库·python·链表·excel
前端小咸鱼一条2 小时前
antdv下拉框树的封装(可懒加载,可级联下级,可单独勾选,可禁用,可搜索)
前端·数据库
千寻技术帮2 小时前
10406_基于Springboot的社交平台系统
spring boot·mysql·毕业设计·源码·文档·社交平台
冉冰学姐2 小时前
SSM社区疫情防控管理系统rgb2a(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面
数据库·管理系统·信息化管理·ssm 框架·社区疫情防控
编程修仙2 小时前
第二篇 SpringBoot项目启动流程
java·spring boot·后端
C++业余爱好者2 小时前
Springboot中的缓存使用
spring boot·后端·缓存
mpHH2 小时前
postgresql源码阅读 search_path
数据库·postgresql
Qiuner2 小时前
Spring Boot 机制五: Bean 生命周期与后置处理器(BeanPostProcessor)源码深度剖析
java·spring boot·后端