AOP实现Redis缓存服务及Redis分片

springboot AOP

AOP的作用

名称 :面向切面编程
一句话总结:在不改变原有代码的条件下,对功能进行拓展
公式 : AOP=切入点表达式+通知方法
专业术语:

  1. 连接点:在执行正常的业务过程中满足了切入点表达式进入切面的点.(织入),多个
  2. 通知:在切面中执行的具体的业务(拓展)方法
  3. 切入点:能够进入切面的一个判断 if判断 一个
  4. 目标方法:将要执行的真实的业务逻辑

关于通知说明

  1. 前置通知;目标方法执行之前执行
  2. 后置通知:目标方法执行之后执行
  3. 异常通知:目标方法执行之后抛出的异常时执行
  4. 最终通知:不管什么时候都要执行的通知方法
    说明:上述的四大通知类型 不能控制目标方法是否执行.一般使用上述的四大通知类型,都是用来记录程序的执行状态
  5. 环绕通知:在目标方法执前后都要执行的通知方法.控制目标方法是否执行,并且环绕通知的功能最为强大.

切入点表达式说明

  1. . bean(bean的id)类名首字母小写 匹配一个类
  2. .within(包名.类名) 按包路径匹配类,匹配多个类
    上述表达式是粗粒度的控制,按类匹配
  3. .execution(返回值类型 包名.类名.方法名(参数列表))
  4. @annotation(包名:注解名)按注解进行拦截


@Component  //将对象交给spring容器管理
@Aspect     //标识我是一个切面
public class CacheAOP {

    /**
     * AOP = 切入点表达式 + 通知方法.
     *
     * 拦截需求:
     *  1.要求拦截itemCatServiceImpl的bean
     *  2.拦截com.jt.service下的所有的类
     *  3.拦截com.jt.service下的所有类及方法
     *  3.1拦截com.jt.service的所有的类.返回值为int类型的.并且add开头
     *  的方法.并且参数一个 为String类型
     */
    //@Pointcut(value = "bean(itemCatServiceImpl)")
    //@Pointcut("within(com.jt.service..*)")
    //拦截com.jt.service下的所有类的所有方法的任意参数类型
    //@Pointcut("execution(int com.jt.service..*.add*(String))")
    @Pointcut("execution(* com.jt.service..*.*(..))")
    public void pointcut(){

    }

    //定义前置通知
    @Before("pointcut()")
    public void before(){
        System.out.println("我是前置通知");
    }
}



实现redist缓存

需求分析

  1. 自定义注解CacheFind 主要被注解标识的方法,则开启缓存的实现
  2. 为了将来区分业务,需要在注解中标识key属性,使使用者自行填写
  3. 为了用户提供数据超时功能

自定义注解

@Retention(RetentionPolicy.RUNTIME) //该注解什么时候有效
@Target({ElementType.METHOD})       //对方法有效
public @interface CacheFind {
    String key();               //该属性为必须添加
    int seconds() default 0;    //设定超时时间 默认不超时
}

编辑cacheAOP


@Component  //将对象交给spring容器管理
@Aspect     //标识我是一个切面
public class CacheAOP {

    //1.注入缓存redis对象
    @Autowired
    private Jedis jedis;

    /**
     * 拦截@CacheFind注解标识的方法.
     * 通知选择: 缓存的实现应该选用环绕通知
     * 步骤:
     *  1.动态生成key  用户填写的key+用户提交的参数
     */
    @Around("@annotation(cacheFind)")
    public Object around(ProceedingJoinPoint joinPoint, CacheFind cacheFind){

        //1.如何获取用户在注解中填写的内容呢???  如何获取注解对象....
        String key = cacheFind.key();   //前缀  ITEM_CAT_PARENTID
        //2.如何获取目标对象的参数呢???
        Object[] array = joinPoint.getArgs();
        key += "::"+Arrays.toString(array); // "ITEM_CAT_PARENTID::[0]"

        //3.从redis中获取数据
        Object result = null;
        if(jedis.exists(key)){
            //需要获取json数据之后,直接转化为对象返回!!
            String json = jedis.get(key);
            //如何获取返回值类型
            MethodSignature methodSignature =
                            (MethodSignature) joinPoint.getSignature();
            Class targetClass = methodSignature.getReturnType();
            result = ObjectMapperUtil.toObject(json,targetClass);
            System.out.println("AOP实现缓存的查询!!!");
        }else{
            //key不存在,应该查询数据库
            try {
                result = joinPoint.proceed();    //执行目标方法,获取返回值结果
                String json = ObjectMapperUtil.toJSON(result);
                if(cacheFind.seconds()>0){       //判断是否需要超时时间
                    jedis.setex(key, cacheFind.seconds(), json);
                }else{
                    jedis.set(key,json);
                }
                System.out.println("AOP执行数据库操作!!!");
            } catch (Throwable throwable) {
                throwable.printStackTrace();
                throw new RuntimeException(throwable);
            }
        }
        return result;
    }

    /**
     * AOP = 切入点表达式 + 通知方法.
     *
     * 拦截需求:
     *  1.要求拦截itemCatServiceImpl的bean
     *  2.拦截com.jt.service下的所有的类
     *  3.拦截com.jt.service下的所有类及方法
     *  3.1拦截com.jt.service的所有的类.返回值为int类型的.并且add开头
     *  的方法.并且参数一个 为String类型
     */
    //@Pointcut(value = "bean(itemCatServiceImpl)")
    //@Pointcut("within(com.jt.service..*)")
    //拦截com.jt.service下的所有类的所有方法的任意参数类型
    //@Pointcut("execution(int com.jt.service..*.add*(String))")
   /* @Pointcut("execution(int com.jt.service..*.add*(String))")
    public void pointcut(){

    }

    //定义前置通知
    @Before("pointcut()")
    public void before(){
        System.out.println("我是前置通知");
    }*/
}



关于环绕通知的参数的说明

问题一:连接点必须位于通知的参数的第一位
在这里插入图片描述
否则会报错
在这里插入图片描述
问题二:其他四大通知了类型是否可以添加ProceedingJoinPoint对象
答案:ProceedingJointPoint只能添加到环绕通知中
报错如下
在这里插入图片描述

关于JoinPoint方法的说明

 /**
     * 要求: 拦截注解方法
     * 打印:
     *      1.打印目标对象的类型
     *      2.打印方法的参数
     *      3.获取目标对象的名称及方法的名称
     * @param joinPoint
     */
    @Before("@annotation(com.jt.anno.CacheFind)")
    public void before(JoinPoint joinPoint){

        Object target = joinPoint.getTarget();  //获取目标对象
        Object[] args = joinPoint.getArgs();    //获取方法参数的
        String targetName =
                joinPoint.getSignature().getDeclaringTypeName(); //获取目标对象的名称
        //获取目标对象的类型
        Class targetClass = joinPoint.getSignature().getDeclaringType();
        //获取目标方法的名称
        String methodName = joinPoint.getSignature().getName();
        System.out.println(target);
        System.out.println(args);
        System.out.println(targetName);
        System.out.println(targetClass);
        System.out.println(methodName);

    }

商品列表分类实现缓存的处理

说明:在业务方法中添加缓存的注解

/**
     * 分析业务: 通过itemCatId获取商品分类的名称
     * 1.url地址: url:"/item/cat/queryItemName",
     * 2.参数: {itemCatId:val},
     * 3.返回值: 商品分类名称  String
     */
    @RequestMapping("/queryItemName")
    @CacheFind(key="ITEM_CAT_NAME")
    public String findItemCatName(Long itemCatId){

        return itemCatService.findItemCatNameById(itemCatId);
    }


3. Redis分片机制

需求数据

如果需要在Redis中进行海量的数据存储,如果只有一台Redis显然不能实现该功能,如果通过扩大内存的方式也不能达到要求,因为时间都浪费在寻址中,如何有效的存储海量数据呢?

Redis分片说明

说明:一般采用多台的Redis,分别保存用户的数据,从而实现内存数据的扩容
对于用户而言:将Redis分片当做一个整体,用户不在乎数据到底储存到哪里,只在乎能不能存.
分片主要的作用:实现内存的扩容
在这里插入图片描述

Redis分片准备

  1. 创建目录

说明在Redis更目录创建一个shards目录
在这里插入图片描述

  1. 分片搭建策略 说明:由于Redis启动时根据配置文件运行的,所有如果需要准备三台Redis,则需要复制三分配置文件Redis.conf
    端口号依次为6379/6380/6381 复制配置文件
    在这里插入图片描述

  2. 修改端口号 根据配置文件名称,动态修改对应的端口即可

在这里插入图片描述
在这里插入图片描述

  1. 启动Redis
 redis-server  6379.conf
 redis-server  6380.conf
 redis-server  6381.conf


在这里插入图片描述
Redis分片入门案例

 /**
     * 测试Redis分片机制
     * 业务思路:
     *      用户需要通过API来操作3台redis.用户无需关心数据如何存储,
     *      只需要了解数据能否存储即可.
     * 
     *      
     */
    @Test
    public void testShards(){
        List<JedisShardInfo> list = new ArrayList<>();
        list.add(new JedisShardInfo("192.168.126.129", 6379));
        list.add(new JedisShardInfo("192.168.126.129", 6380));
        list.add(new JedisShardInfo("192.168.126.129", 6381));
        ShardedJedis shardedJedis = new ShardedJedis(list);
        shardedJedis.set("2005", "redis分片学习");
        System.out.println(shardedJedis.get("2005"));
    }


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值