Redis缓存系列--(五)自定义Redis缓存注解的使用

自定义Redis缓存注解的实现

我们在Spring的框架中,可以使用注解的形式(@EnableCache和@Cacheable)来实现对查询的数据进行Redis的缓存,我们自己其实也可以自定义一个缓存注解来实现redis缓存的功能。

编写自定义缓存注解

首先,我们要自定义一个Redis缓存注解,之后要将该注解标注到对应的方法上去,代码如下:

/**
 * 对方法启用Redis缓存,使其具有方法前判断缓存,方法后如果没有缓存则存储Redis缓存
 * 缓存key的格式为:value::key,具体的数值由用户传递的注解参数来提供
 * @author young
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyRedisCacheable {
    /**
     * Redis缓存key的前缀
     * @return
     */
    String value() default "";

    /**
     * Redis缓存key的后缀
     * @return
     */
    String key() default "";
}

编写具体AOP代理方法类

注解只是用来标注当前的方法要用来做什么,同时传递相应操作所需要的参数信息。具体要在标注的方法前后实现什么功能,还需要通过Spring AOP的面向切面编程来实现对方法前后所需要进行的处理。具体代码如下:

/**
 * MyRedisCacheable注解的具体处理类,实现对标注方法执行前做Redis缓存检查处理,方法执行后执行缓存Redis数据的处理
 * @author young
 */
@Component
@Aspect
public class CacheAspect {
    private Logger logger =  LoggerFactory.getLogger(CacheAspect.class);

    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 设置注解的切入点,即指定切入的方法为
     */
    @Pointcut("@annotation(com.young.redis.aop.MyRedisCacheable)")
    public void cachePointcut(){

    }

    /**
     * 定义需要进行AOP切面编程的切点是哪个,也即标注MyRedisCacheable注释的方法
     * @param proceedingJoinPoint 当前切入方法的上下文
     * @return
     */
    @Around("cachePointcut()")
    public Object doRedisCache(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        Object value = null;
        try {
            //获取切入方法的标签
            MethodSignature signature = (MethodSignature) proceedingJoinPoint.getSignature();
            //通过该标签来获取修饰的方法和方法参数
            Method method = proceedingJoinPoint.getTarget().getClass().
                    getMethod(signature.getName(),signature.getMethod().getParameterTypes());
            //获取当前方法的注解以及注解的参数
            MyRedisCacheable myRedisCacheable = method.getAnnotation(MyRedisCacheable.class);
            //redis key的EL表达式
            String keyEL = myRedisCacheable.key();
            //redis key的前缀
            String prifix = myRedisCacheable.value();

            //解析EL表达式
            ExpressionParser expressionParser = new SpelExpressionParser();
            Expression expression =expressionParser.parseExpression(keyEL);

            //用来组合EL表达式和方法参数的EL上下文对象
            EvaluationContext evaluationContext = new StandardEvaluationContext();

            //获取方法的参数,将参数放入evaluationContext
            Object[] args = proceedingJoinPoint.getArgs();
            DefaultParameterNameDiscoverer defaultParameterNameDiscoverer = new DefaultParameterNameDiscoverer();
            String[] parameterNames = defaultParameterNameDiscoverer.getParameterNames(method);
            for (int i = 0; i < parameterNames.length; i++) {
                evaluationContext.setVariable(parameterNames[i],args[i].toString());
            }

            //使用Expression和EvaluationContext来解析EL表达式
            String cacheKey = prifix + "::" + expression.getValue(evaluationContext).toString();

            //判断缓存是否存在
            value = redisTemplate.opsForValue().get(cacheKey);
            if(value != null){
                logger.info("从缓存中读取到的key:" + cacheKey +",value:" + value);
                return value;
            }

            //不存在则执行数据库方法进行查询
            value = proceedingJoinPoint.proceed();

            //将查询到的内容存到Redis
            redisTemplate.opsForValue().set(cacheKey,value);
        }catch (Throwable throwable){
            throwable.printStackTrace();
        }
        //方法执行后返回查询结果
        return value;
    }
}

编写配置类

无论是在SpringMvc框架还是Springboot框架中,都可以通过编写配置类的方式来配置需要加载的bean对象,具体的配置类代码如下:

/**
 * 通过注解的形式来配置spring中的bean
 * 使用自定义注解方式来实现Redis缓存
 * @author young
 */
@Configuration
@EnableAspectJAutoProxy //开启AOP自动代理
public class AppConfig {

    /**
     * 通过注释来获取properties配置文件中的自定义值,引号里边为EL表达式
     */
    @Value("${spring.redis.host}")
    String host;

    @Value("${spring.redis.port}")
    int port;

    /**
     * 通过RedisConnectionFactory来获取RedisTemplate,从而进行Redis的相关操作(注解方式同样需要)
     * @return
     */
    @Bean
    public RedisConnectionFactory redisConnectionFactory(){
        //配置Redis的主机和端口
        RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
        redisStandaloneConfiguration.setHostName(host);
        redisStandaloneConfiguration.setPort(port);

        //
        RedisConnectionFactory redisConnectionFactory = new JedisConnectionFactory(redisStandaloneConfiguration);
        return redisConnectionFactory;
    }

    /**
     * 加载RedisTemplate bean
     * @param redisConnectionFactory
     * @return
     */
    @Bean
    public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory){
        RedisTemplate redisTemplate = new RedisTemplate();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        //指定Redis序列化的方式
        redisTemplate.setKeySerializer(StringRedisSerializer.UTF_8);
        return redisTemplate;
    }
}

这样,一个自定义Redis缓存注解的实现也就基本完成了,这对于我们了解注解的底层原理也是很有帮助的。

posted @ 2020-11-08 10:03  爪哇洋  阅读(1949)  评论(0编辑  收藏  举报