1.自定义标签

/**
* @author gzy
*  元注解 标识需要缓存分页的方法
*/

@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RedisCache {
    String cacheName() default Constants.REDIS_PAGE_CACHE;
}

2.定义一个切面对使用自定义标签的方法进行处理

/**
* @author gzy
* @version : 1.0
* @date : 2020/1/6 0006
*/
@Aspect
@Component
@Order(3)
public class RedisCacheAspect {

    private org.slf4j.Logger logger = LoggerFactory.getLogger(getClass());
    @Autowired
    @Qualifier("pageCacheRedisTemplate")
    private RedisTemplate<String, Object> redisTemplate;


    /**
     * 定义拦截规则:拦截下面的所有类中,有@RedisCache注解的方法。
     */
    @Pointcut("execution(* com.cfcc.bigdata..service.*.*(..)) && @annotation(com.cfcc.bigdata.common.base.annotation.RedisCache)")
    public void controllerMethodPointcut() {
    }

    @Around("controllerMethodPointcut()") //指定拦截器规则;也可以直接把“execution(* com.xjj.........)”写进这里
    public Object Interceptor(ProceedingJoinPoint pjp) throws Throwable {

        String className = getTargetClassName(pjp);
        TwoTuple<Map<String, String>, EasyUIDatagridParam> methodParams = getMethodParams(pjp);
        Map<String, String> param = methodParams.getVar1();
        EasyUIDatagridParam easyUIDatagridParam = methodParams.getVar2();

        TwoTuple<String, String> methodNameAndCacheName = getMethodNameAndCacheName(pjp);
        String methodName = methodNameAndCacheName.getVar1();
        String redisCacheName = methodNameAndCacheName.getVar2();

        logger.info("请求开始,方法:{}", methodName);

        int page = easyUIDatagridParam.getPage();
        int rows = easyUIDatagridParam.getRows();

        String redisKey = generateRedisKey(redisCacheName, className, methodName, param);

        //从缓存中获取数据
        PageInfo<Object> listPage = getListPage(redisKey, page, rows);
        //如果缓存中的数据集合不为空
        if (listPage.getList() != null) {
            logger.info("redis中 key值:{}", redisKey);
            return listPage;
        }

        //将数据库查询到的数据存储到redis 中,pip.proceed() 这里会执行标签标记的方法并把返回值返回。
        Object dataFromDB = pjp.proceed();

        if (dataFromDB instanceof PageInfo) {
            PageInfo<Object> pageInfo = (PageInfo<Object>)dataFromDB;
            List<Object> list = pageInfo.getList();
            //如果数据库查询到的数据为空直接返回
            if (list != null && list.size() > 0 ) {
                addList(redisKey, list);
            } else {
                return new PageInfo<>(new ArrayList<>());
            }
        }
        //根据条件分页查询
        PageInfo<Object> result = getListPage(redisKey, page, rows);

        return result;

    }


    /**
     * 从 redis list 里面获取数据分页
     *  @param key list 对应的key
     *
     * @param page 当前页
     * @param rows 页面的数据条数
     * @return
     */
    public PageInfo<Object> getListPage(String key, int page, int rows) {

        //如果key 不存在,那么就被看作是空list,并且返回长度为0
        Long size = redisTemplate.opsForList().size(key);
        if (size <= 0) {
            return new PageInfo<>();
        }
        //设置分页参数
        Page param = new Page(page, rows);
        param.setTotal(size);
        int startRow = param.getStartRow();
        int endRow = param.getEndRow();

        List<Object> list = redisTemplate.opsForList().range(key, startRow, endRow > 0 ? endRow - 1 : 0);
        PageInfo<Object> pageInfo = new PageInfo<>(param);
        pageInfo.setSize(list.size());
        pageInfo.setList(list);
        return pageInfo;
    }


    /**
     * redis 中的list是双向的
     * leftPush 先进后出
     * rightPush 先进先出
     *
     * @param key  list 对应 key
     * @param list 需要存储的集合对象
     */
    public void addList(String key, List<Object> list) {
        redisTemplate.opsForList().rightPushAll(key, list);
        redisTemplate.expire(key,30, TimeUnit.MINUTES);
    }

    public String generateRedisKey(String cacheName, String className, String methodName, Map<String, String> params) {

        StringBuilder sb = new StringBuilder();
        sb.append(cacheName);
        sb.append(":");
        sb.append(className);
        sb.append(".");
        sb.append(methodName);
        sb.append("#[");
        for (Iterator<Map.Entry<String, String>> it = params.entrySet().iterator(); it.hasNext(); ) {
            Map.Entry<String, String> param = it.next();
            sb.append("&");
            sb.append(param.getKey());
            sb.append(":");
            sb.append(param.getValue());
        }
        sb.append("]");
        return sb.toString();
    }

    /**
     * 获取方法所在类的方法名
     * @param pjp
     * @return
     */
    public String getTargetClassName(ProceedingJoinPoint pjp) {
        //获取目标方法所在类
        String target = pjp.getTarget().toString();
        String className = target.split("@")[0];
        return className;
    }

    /**
     * 获取方法的参数
     * @param pjp
     * @return 返回参数对应元祖
     **/
    public TwoTuple<Map<String, String>, EasyUIDatagridParam> getMethodParams(ProceedingJoinPoint pjp) {
        Object[] args = pjp.getArgs();
        EasyUIDatagridParam easyUIDatagridParam = (EasyUIDatagridParam) args[0];
        Map<String, String> param = new HashMap<>();
        if (args[1] instanceof List) {
            List<PropertyFilter> filters = (List<PropertyFilter>) args[1];
            for (PropertyFilter filter : filters) {
                param.put(filter.getPropertyName(), filter.getFilterValue());
            }
        }
        TwoTuple<Map<String, String>, EasyUIDatagridParam> result = new TwoTuple<>(param, easyUIDatagridParam);
        return result;
    }

    /**
     * 获取方法名和缓存名
     * @return TwoTupe<方法名, 缓存名>
     */
    public TwoTuple<String, String> getMethodNameAndCacheName(ProceedingJoinPoint pjp) {
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        //获取被拦截的方法
        Method method = signature.getMethod();
        //获取被拦截的方法名
        String methodName = method.getName();

        RedisCache annotation = method.getDeclaredAnnotation(RedisCache.class);
        String redisCacheName = annotation.cacheName();
        TwoTuple<String, String> result = new TwoTuple<>(methodName, redisCacheName);
        return result;
    }
}

3.在目标方法上添加标签

/**
     * 根据通用条件进行分页查询,这里通过生成sql 查询数据库获取返回的数据
     * 使用基于redis 的缓存分页
     * @param param
     *            easyui传来的参数信息
     * @param filters
     *            通用条件
     * @param entityClass
     *            查询的实体对象类型
     * @return
     */
    @Transactional(readOnly = true)
    @RedisCache
    public PageInfo<T> selectPageByFilters(final EasyUIDatagridParam param, final List<PropertyFilter> filters, Class<?> entityClass) {
        PageQueryHelper pageQueryHelper = new PageQueryHelper();
        Example example = pageQueryHelper.generateExampleByFilters(param, filters, entityClass);
        List<T> list = mapper.selectPageByExample(example);
        return new PageInfo<>(list);
    }

总结:这里当访问目标方法时会首先访问切面,切面中会判断当前方法的返回值是否被缓存过,如果没有缓存过,就从数据库中查出数据,并存储在缓存中,如果已经存在了,那么直接返回查询结果即可。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐