Springboot 项目中使用redis 的List结构来实现缓存分页分页

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);
    }

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

  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
好的,下面是一个PageInfo分页查询带Redis缓存的业务逻辑层实现类的示例代码(Java语言): ```java public class UserServiceImpl implements UserService { private final UserDao userDao; private final RedisTemplate<String, Object> redisTemplate; public UserServiceImpl(UserDao userDao, RedisTemplate<String, Object> redisTemplate) { this.userDao = userDao; this.redisTemplate = redisTemplate; } @Override public PageInfo<User> getUsers(int pageNum, int pageSize) { String cacheKey = "user_page_" + pageNum + "_" + pageSize; PageInfo<User> pageInfo = (PageInfo<User>) redisTemplate.opsForValue().get(cacheKey); if (pageInfo == null) { PageHelper.startPage(pageNum, pageSize); List<User> userList = userDao.getUsers(); pageInfo = new PageInfo<>(userList); redisTemplate.opsForValue().set(cacheKey, pageInfo, 5, TimeUnit.MINUTES); // 设置5分钟过期 } return pageInfo; } } ``` 在这个示例,我们使用了一个`UserDao`对象来访问数据库,同时也注入了一个`RedisTemplate`对象来进行缓存操作。 `getUsers()`方法接受两个参数,`pageNum`表示要查询的页码,`pageSize`表示每页的记录数。首先,我们根据`pageNum`和`pageSize`构建一个缓存键`cacheKey`,然后尝试从Redis缓存获取用户分页信息。如果缓存没有该分页信息,则调用`PageHelper`对象设置分页信息,并从`UserDao`对象获取用户列表。接着,我们使用获取到的用户列表构建一个`PageInfo`对象,并将其存储到Redis缓存,并设置过期时间为5分钟。最后,我们返回用户分页信息。 使用`Redis`作为缓存可以提高系统的可扩展性和可靠性,可以实现分布式系统的缓存共享。需要注意的是,缓存的过期时间应该根据数据的实时性和数据量大小来进行设置,以避免缓存数据过期或者占用过多内存的问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

独行客-编码爱好者

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值