springboot的缓存功能,参数aes加密方式

SKILL开发
开发 未标注权限 许可证不明确
安装这个技能

为Spring Boot项目添加基于AOP + Redis的缓存功能,支持SpEL表达式和AES参数加密。使用@CacheDataAnnotation注解标注方法,自动实现缓存读写。适用于需要快速集成Redis缓存的场景。

一句话选型结论

按资料字段整理

springboot的缓存功能,参数aes加密方式是开发类的 Agent Skill,安装方式未标注,权限级别未标注,许可证不明确,有可直接复制的安装命令。技能本质是给模型读的说明书,效果取决于模型与客户端;装完先用一个小任务验证,再放进正式流程。

分类开发 安装方式未标注 权限级别未标注权限 许可证许可证不明确
累计安装 未标注 本周 未标注
GitHub 星标 未标注 仓库 未标注
下载量 未标注 技能市场下载数
权限级别 未标注权限 安装前请确认风险范围
许可证 许可证不明确 未说明

安装

未标注
在支持 Skills 的客户端里执行,装完问一句「你有哪些技能」验证
skillhub install user_faca7523/springboot-cache-aes
  1. 1
    确认客户端支持

    确认你的客户端支持 Skills(Claude、Codex、Cursor 等),并升级到较新的版本。

  2. 2
    拿到技能文件

    执行上面的命令安装;也可以把仓库里的 SKILL.md 手动放进客户端的 skills 目录。

  3. 3
    刷新技能列表

    重新打开客户端,或刷新技能列表,让新放进来的技能被加载。

  4. 4
    验证是否生效

    问一句「你有哪些技能」,确认列表里出现它,再拿一个小任务试跑。

技能档案

开发
字段来自技能仓库与公开索引,中文说明由本站标注
作者 / 维护方user_faca7523仓库 未标注 · 版本 未标注
宿主客户端未标注技能可被这些客户端加载
安装方式未标注安装包 community
权限级别未标注权限安装前请确认风险范围
许可证未说明许可证不明确,商用前请自行确认条款
仓库可用性available技能仓库当前是否可访问
最近核验未标注上游最后核验时间 · 数据更新 1791618821
正文体量12.4 KBSKILL.md 原文大小,越大说明说明书写得越细

典型使用场景

3 类
按分类整理的常见用法,具体以 SKILL.md 为准
写代码与改 bug

按需求补代码、定位并修掉报错。

读仓库与生成改动

让模型读懂项目结构,再给出可落地的改动。

跑测试与排查故障

按现象排查问题,补测试用例并复跑。

选择建议

装之前先看这几条
按条目字段给出的判断,不替你决定用不用

值得选的地方

  • 有可直接复制的安装命令,不用自己翻文档找入口。

先想清楚的地方

  • 没有标注权限级别,实际能做什么要装好后确认。
  • 许可证不明确,商用或二次分发前先到仓库确认条款。
  • 没有做站内核验,内容以作者仓库为准。
  • 技能只是说明书,实际效果取决于模型与客户端,装完先用小任务试一遍。

更适合

  • 需要在开发场景里给模型补一套做法的人。
  • 愿意先读一遍 SKILL.md、确认它会让模型做什么的人。

不太适合

  • 对许可证要求很严、必须明确可商用的人。
  • 只想装完就自动变强、不打算看说明书的人。

同类对比

6 个同类
同分类里挑最接近的几条,字段以各自详情页为准
技能权限级别安装方式累计安装宿主客户端安装命令
springboot的缓存功能,参数aes加密方式当前 未标注权限 未标注 未标注 未标注 有
技能审查员 浏览器控制 ClawHub 市场 877.9 万 OpenClaw 有
Vercel React 最佳实践 浏览器控制 GitHub 仓库 849.1 万 Codex、Claude、Cursor 有
GitHub 代码协作 联网访问 ClawHub 市场 763.3 万 OpenClaw 有
网页设计指南 联网访问 GitHub 仓库 668.6 万 Codex、Claude、Cursor 有
本体论 只读 ClawHub 市场 663.7 万 OpenClaw 有

SKILL.md 全文

12.4 KB
模型实际读到的说明书原文,来自技能仓库

Spring Boot Cache with AES Encryption

为Spring Boot项目快速集成基于AOP + Redis的缓存功能,支持SpEL表达式动态生成缓存key,并对方法参数进行AES加密保护。

快速开始

1. 创建目录结构

在项目中新建以下文件:

src/main/java/com/xxx/config/
├── cache/
│   ├── CacheDataAnnotation.java      # 缓存注解定义
│   └── CacheDataAnnotationAop.java   # AOP切面实现
└── RedisConfig.java                   # Redis配置类

2. 添加Maven依赖

在 pom.xml 中添加:

<dependencies>
    <!-- Spring Data Redis -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>

    <!-- Spring AOP(通常已包含在starter-web中) -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>

    <!-- Hutool工具类(AES加密) -->
    <dependency>
        <groupId>cn.hutool</groupId>
        <artifactId>hutool-crypto</artifactId>
        <version>5.8.25</version>
    </dependency>
</dependencies>

3. 配置Redis

在 application.yml 中配置:

spring:
  redis:
    host: localhost
    port: 6379
    password: your_password  # 如果没有密码则删除此行
    database: 0
    timeout: 3000ms

4. 复制核心代码

将以下三个文件复制到项目中(记得修改包名):

CacheDataAnnotation.java

package com.xxx.config.cache;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.concurrent.TimeUnit;

/**
 * 缓存注解
 * @author YourName
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CacheDataAnnotation {

    /**
     * 缓存key,优先使用
     */
    String key() default "";

    /**
     * 缓存spElKey,el表达式
     * 使用方式:
     * 1. 获取方法参数: @CacheDataAnnotation(spElKey="#userId+'_info'")
     * 2. 调用静态方法: @CacheDataAnnotation(spElKey="T(com.xxx.Util).encrypt(#userId)")
     */
    String spElKey() default "";

    /**
     * key前缀,默认"haicode:"
     */
    String cacheKeyPrefix() default "haicode:";

    /**
     * 缓存时间,默认60分钟
     */
    long expire() default 60L;

    TimeUnit timeUnit() default TimeUnit.MINUTES;
}

CacheDataAnnotationAop.java

package com.xxx.config.cache;

import cn.hutool.crypto.symmetric.AES;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;

/**
 * 缓存AOP切面
 * @author YourName
 */
@Aspect
@Component
public class CacheDataAnnotationAop {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    // SpEL解析器
    private static final ExpressionParser PARSER = new SpelExpressionParser();

    // AES密钥(建议改为配置项)
    private static final AES AES_UTIL = new AES("1234567890abcdef".getBytes(StandardCharsets.UTF_8));

    @Around("@annotation(cacheDataAnnotation)")
    public Object around(ProceedingJoinPoint joinPoint, CacheDataAnnotation cacheDataAnnotation) throws Throwable {
        String cacheKey = parseKey(joinPoint, cacheDataAnnotation);

        // 尝试从缓存获取
        Object data = redisTemplate.opsForValue().get(cacheKey);
        if (null != data) {
            return data;
        }

        // 执行原方法
        Object returnData = joinPoint.proceed();

        // 存入缓存(null值不缓存)
        if (null != returnData) {
            redisTemplate.opsForValue().set(cacheKey, returnData, 
                cacheDataAnnotation.expire(), cacheDataAnnotation.timeUnit());
        }

        return returnData;
    }

    /**
     * 解析缓存key
     */
    private String parseKey(ProceedingJoinPoint joinPoint, CacheDataAnnotation cacheDataAnnotation) {
        String keyPrefix = cacheDataAnnotation.cacheKeyPrefix();
        String key = cacheDataAnnotation.key();
        String spElKey = cacheDataAnnotation.spElKey();

        if (StringUtils.isNotBlank(key)) {
            return keyPrefix + key;
        }

        if (StringUtils.isNotBlank(spElKey)) {
            String spElKeyValue = parseSpElKey(spElKey, joinPoint);
            return keyPrefix + spElKeyValue;
        }

        // 自动生成key:类名.方法名:参数AES加密
        String methodKey = useMethodKey(joinPoint);
        return keyPrefix + methodKey;
    }

    /**
     * 使用方法和参数生成key
     */
    private String useMethodKey(ProceedingJoinPoint joinPoint) {
        String className = joinPoint.getTarget().getClass().getName();
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
        String methodName = method.getName();
        Object[] args = joinPoint.getArgs();

        StringBuilder argsBuilder = new StringBuilder();
        if (ArrayUtils.isNotEmpty(args)) {
            argsBuilder.append(":");
            for (Object arg : args) {
                // 对参数进行AES加密
                String aesArg = AES_UTIL.encryptHex(arg.toString());
                argsBuilder.append(aesArg);
            }
        }
        return className + "." + methodName + argsBuilder;
    }

    /**
     * 解析SpEL表达式
     */
    private String parseSpElKey(String spElKey, ProceedingJoinPoint joinPoint) {
        StandardEvaluationContext context = new StandardEvaluationContext();
        Object[] args = joinPoint.getArgs();
        String[] parameterNames = getParameterNames(joinPoint);

        for (int i = 0; i < args.length; i++) {
            context.setVariable(parameterNames[i], args[i]);
        }
        return PARSER.parseExpression(spElKey).getValue(context, String.class);
    }

    /**
     * 获取方法参数名
     */
    private String[] getParameterNames(JoinPoint joinPoint) {
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
        return Arrays.stream(method.getParameters())
                .map(Parameter::getName)
                .toArray(String[]::new);
    }
}

RedisConfig.java

package com.xxx.config;

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.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

/**
 * Redis配置类
 * @author YourName
 */
@Configuration
public class RedisConfig {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(connectionFactory);

        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        GenericJackson2JsonRedisSerializer jsonSerializer = new GenericJackson2JsonRedisSerializer();

        template.setKeySerializer(stringRedisSerializer);
        template.setValueSerializer(jsonSerializer);
        template.setHashKeySerializer(stringRedisSerializer);
        template.setHashValueSerializer(jsonSerializer);

        return template;
    }
}

5. 使用示例

示例1:固定key

@Service
public class TokenService {

    @CacheDataAnnotation(key = "huawei:token", expire = 23L, timeUnit = TimeUnit.HOURS)
    public String getToken() {
        // 调用API获取token
        return callApi();
    }
}

示例2:SpEL表达式

@Service
public class DeptService {

    @CacheDataAnnotation(
        spElKey = "'ali:dept:' + #deptId", 
        expire = 1L, 
        timeUnit = TimeUnit.DAYS
    )
    public Department getDeptInfo(String deptId) {
        return queryFromApi(deptId);
    }
}

示例3:多参数SpEL

@Service
public class UserService {

    @CacheDataAnnotation(
        spElKey = "'user:info:' + #userId + ':' + #type",
        expire = 30L,
        timeUnit = TimeUnit.MINUTES
    )
    public UserInfo getUserInfo(Long userId, String type) {
        return queryUser(userId, type);
    }
}

示例4:调用静态方法

@Service
public class SecureService {

    @CacheDataAnnotation(
        spElKey = "'secure:data:' + T(com.xxx.util.EncryptUtil).md5(#sensitiveData)",
        expire = 10L,
        timeUnit = TimeUnit.MINUTES
    )
    public String getSensitiveData(String sensitiveData) {
        return fetchData(sensitiveData);
    }
}

Key生成策略

优先级从高到低:

  1. 固定key:key = "user:info" → haicode:user:info
  2. SpEL表达式:spElKey = "#userId" → haicode:12345
  3. 自动生成:类名.方法名:参数AES加密 → haicode:com.xxx.Service.getUser:aes(...)

注意事项

1. AES密钥配置化(推荐)

当前AES密钥硬编码在代码中,建议改为配置项:

@Value("${cache.aes.key:1234567890abcdef}")
private String aesKey;

private AES getAesUtil() {
    return new AES(aesKey.getBytes(StandardCharsets.UTF_8));
}

2. 返回值可序列化

缓存的对象必须可序列化,建议使用:
- 基本类型及其包装类
- String
- 实现了Serializable接口的对象
- List、Map等集合(元素也需可序列化)

3. 空值不缓存

当前实现中,返回null不会存入缓存。如需缓存null值,需修改AOP逻辑。

4. Redis连接池配置

生产环境建议配置连接池:

spring:
  redis:
    lettuce:
      pool:
        max-active: 8
        max-idle: 8
        min-idle: 0
        max-wait: -1ms

5. 缓存穿透防护

对于可能返回null的场景,建议:
- 缓存空值(设置较短过期时间)
- 使用布隆过滤器
- 参数校验

验证缓存是否生效

创建测试方法:

@Service
public class TestService {

    @CacheDataAnnotation(key = "test:hello", expire = 5L, timeUnit = TimeUnit.MINUTES)
    public String getHello() {
        System.out.println("=== 执行了方法体 ==="); // 第一次会打印,第二次不会
        return "Hello World";
    }
}

调用两次,观察控制台输出和Redis中的数据:

# 查看Redis中的key
redis-cli keys "test:hello"

# 查看value
redis-cli get "test:hello"

常见问题

Q1: 缓存不生效?

检查:
1. Redis服务是否正常运行
2. @EnableAspectJAutoProxy 是否启用(Spring Boot默认启用)
3. 方法是否为public且非static
4. 是否通过Spring容器调用(自调用不触发AOP)

Q2: SpEL表达式报错?

确保:
1. 参数名正确(编译时需保留参数名:-parameters)
2. 表达式语法正确
3. 调用的静态方法存在且可访问

Q3: 缓存数据乱码?

检查 RedisConfig 是否正确配置了序列化器。

性能优化建议

  1. 合理设置过期时间:避免缓存永久有效
  2. 使用合适的key前缀:便于管理和清理
  3. 监控缓存命中率:评估缓存效果
  4. 大对象谨慎缓存:考虑内存占用
  5. 热点数据预热:系统启动时加载常用数据

扩展方向

  • 添加缓存更新策略(主动更新、被动失效)
  • 支持二级缓存(本地缓存 + Redis)
  • 添加缓存统计和监控
  • 支持缓存注解的组合使用

常见问题

5 问
答案由本站依据条目字段整理
springboot的缓存功能,参数aes加密方式怎么安装?

在支持 Skills 的客户端里执行:skillhub install user_faca7523/springboot-cache-aes。安装方式为未标注。装完刷新技能列表或重开客户端。

springboot的缓存功能,参数aes加密方式支持哪些客户端?

资料没有标注宿主客户端,但只要客户端支持 Skills(Claude、Codex、Cursor 等),一般都能加载。

springboot的缓存功能,参数aes加密方式权限高吗,安全吗?

权限级别为未标注权限。安装前先看它会读取或写入哪些内容。

springboot的缓存功能,参数aes加密方式收费吗?

本站不代收费用,也没有标注具体价格。是否收费取决于技能本身和所用的模型或客户端,正式使用前按作者说明确认。

springboot的缓存功能,参数aes加密方式用什么许可证?

许可证标注为 未说明(许可证不明确)。商用或二次分发前请到仓库确认条款原文。

热门专题

按场景挑技能
全部专题 →

技能内容以作者仓库为准;分类、权限级别与许可证状态的中文说明为中文整理,最近核验 未标注。