精灵商场项目(七)--Redis缓存

一、Redis

1.1 缓存策略

添加缓存能够有效降低用户访问物理设备的访问频次.能够有效的提升用户查询的速度.并且缓存中的数据就是数据库中的数据

如何实现缓存策略?

  1. 采用KEY-VALUE结构实现数据的存储.

  2. 开发语言最好使用C.更加贴近硬件.

  3. 缓存的运行环境最好在内存中.

  4. 缓存中的数据如何持久化 保存到磁盘中.

  5. 实现缓存数据的动态更新 LRU算法(T) LFU算法(次数)

1.2 Redis 安装

  1. 上传安装文件 redis-5.0.4.tar.gz 到linux上

  2. 解压redis tar -xvf redis-5.0.4.tar.gz

  3. 编译安装redis (在redis根目录中执行指令)

    (1)编译: make

    (2)安装: make install

  4. redis 启动校验 : redis-server

  5. 修改redis配制文件 : vim redis.conf

    (1)取消IP绑定,将bind 127.0.01注释掉,如图1

    (2)关闭保护模式,将protected-mode设置为no ,如图2

    (3)开启后台启动,将daemonize 设置为yes,如图3

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

1.3 添加jar包

在JL-PARENT项目中添加jar包文件

        <!--spring整合redis -->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-redis</artifactId>
        </dependency>

1.4 Spring 管理 redis

1.4.1 编辑 properties 文件

redis.host=192.168.175.129
redis.port=6379

1.4.2 编辑配置类

实现spring容器管理redis配置类

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.annotation.Scope;
import redis.clients.jedis.Jedis;

@Configuration //标识配置类
@PropertySource("classpath:/properties/redis.properties")
public class RedisConfig {
    @Value("${redis.host}")
    private String host;
    @Value("${redis.port}")
    private Integer port;

    @Bean
    @Scope("prototype")	//设置为多例 当用户使用时创建
    public Jedis jedis() {
        return new Jedis(host, port);
    }
}

1.5 工具API封装JSON转化

需要将json串与对象实现互转

实现对象(以及List集合) 和 JSON数据的转化

在jl-common模块里写工具类

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;

public class ObjectMapperUtil {
    private static final ObjectMapper MAPPER = new ObjectMapper();
    //将对象转换为json
    public static String toJSON(Object obj) {
        String json = null;
        try {
            json = MAPPER.writeValueAsString(obj);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
        return json;
    }

    //将json串转化为对象
    public static <T> T toObject(String json, Class<T> targetClass) {
        T obj = null;
        try {
            obj = MAPPER.readValue(json, targetClass);
        } catch (IOException e) {

            e.printStackTrace();
            throw new RuntimeException(e);
        }
        return obj;
    }
}

二、商品分类缓存实现

页面中需要展现VO对象数据.一般采用Redis保存String类型的数据.如何将VO保存到redis中 :

(1)将vo转化为JSON

(2)将vo 序列化/反序列化 转化为字节数组

2.1 编辑ItemCatController

先查询缓存,如果缓存中没有数据,则查询数据库

	@RequestMapping("/list")
	public List<EasyUITree> findItemCatByParentId(@RequestParam(value = "id",defaultValue = "0") Long parentId){
		return itemCatService.findItemCatCache(parentId);
	}

2.2 编辑ItemCatService

  • 步骤:

    1.先查询缓存 确定key的写法

    2.如果缓存中没有数据.说明用户第一次查询

    ​ 先查询数据库,将数据转化为JSON.保存到redis中

    3.如果缓存中有数据.说明不是第一次查询.从redis中

    ​ 获取数据.需要将json转化为对象

	@Autowired
    private Jedis jedis;

    @SuppressWarnings("unchecked")
    @Override
    public List<EasyUITree> findItemCatCache(Long parentId) {

        List<EasyUITree> treeList = new ArrayList<>();
        String key = "ITEMCAT::" + parentId;
        String json = jedis.get(key);
        //判断数据是否为null
        if (StringUtils.isEmpty(json)) {
            //用户第一次查询
            treeList = findItemCatByParentId(parentId);
            String itemCatJSON = ObjectMapperUtil.toJSON(treeList);
            jedis.set(key, itemCatJSON);
            System.out.println("用户第一次查询数据");
        } else {
            //说明用户不是第一次查询
            treeList = ObjectMapperUtil.toObject(json, treeList.getClass());
            System.out.println("用户查询缓存!!!!");
        }

        return treeList;
    }

三、改进-AOP实现缓存查询

3.1 说明

如果采用上述的方式实现缓存处理.则缓存业务与自己的业务功能紧紧的耦合在一起.后期扩展时不方便.

切面 = 切入点 + 通知

3.2 AOP实现策略

通知类型 : 环绕通知

注解的定义 : 自定义缓存注解,实现缓存标识 , @Cache_Find 对方法生效/运行期起作用

注解属性 : key/seconds

3.3 自定义注解

自定义缓存注解,实现缓存标识

在jl-common模块里定于CacheFind 注解

@Target(ElementType.METHOD)    //对方法生效
@Retention(RetentionPolicy.RUNTIME) //运行时有效
public @interface CacheFind {
    //1.key可以动态获取. 类名.方法名::第一个参数值
    //2.key也可以自己指定
    String key() default "";
    int seconds() default 0;    //用户数据不超时
}

3.4 定义切面

@Component    //将类交给容器管理
@Aspect        //标识切面
public class CacheAspect {
    //当前切面位于common中.必须添加required = false
    @Autowired(required = false)
    private Jedis jedis;

    //如果是环绕通知,则参数必须写ProceedingJoinPoint,必须位于第一位
    @Around("@annotation(cacheFind)")   //直接获取注解的对象
    public Object around(ProceedingJoinPoint joinPoint, CacheFind cacheFind) {
        Object obj = null;
        String key = getKey(joinPoint, cacheFind);
        //1.先查询缓存数据
        String result = jedis.get(key);
        try {
            if (StringUtils.isEmpty(result)) {
                //第一次查询数据
                obj = joinPoint.proceed();    //执行目标方法
                //将数据保存到redis中
                String json = ObjectMapperUtil.toJSON(obj);
                //判断用户是否传递超时时间
                if (cacheFind.seconds() == 0) {
                    jedis.set(key, json);
                } else {
                    jedis.setex(key, cacheFind.seconds(), json);
                }
                System.out.println("执行数据库查询");
            } else {
                //数据不为null,将缓存数据转化为对象
                Class returnType = getType(joinPoint);
                obj = ObjectMapperUtil.toObject(result, returnType);
                System.out.println("执行AOP缓存!!!!");
            }
        } catch (Throwable e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
        return obj;
    }

    /**
     * 目的:获取方法的返回值类型
     * 提示:利用方法对象获取返回值类型
     * @param joinPoint
     * @return
     */
    private Class getType(ProceedingJoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        return signature.getReturnType();
    }

    //判断用户是否传递参数, 如果用户传参使用用户自己的key.
    //如果用户没有指定参数,使用动态生成的.
    private String getKey(ProceedingJoinPoint joinPoint, CacheFind cacheFind) {
        //获取当前方法的名称 类名.方法名
        String className = joinPoint.getSignature().getDeclaringTypeName();
        String methodName = joinPoint.getSignature().getName();
        String key = cacheFind.key();
        if (!StringUtils.isEmpty(key)) { //以用户的数据为准
            return className + "." + methodName + "::" + key;
        } else {//动态拼接参数
            //类名.方法名::第一个参数值
            Object args0 = joinPoint.getArgs()[0];
            return className + "." + methodName + "::" + args0;
        }
    }
}

3.5 实现AOP查询商品分类

  1. 修改ItemCatControllerfindItemCatByParentId方法
@RequestMapping("/list")
	public List<EasyUITree> findItemCatByParentId(@RequestParam(value = "id",defaultValue = "0") Long parentId){
		//先查询缓存,如果缓存中没有数据,则查询数据库
		return itemCatService.findItemCatByParentId(parentId);
	}
  1. ItemCatServiceImplfindItemCatByParentId上加上CacheFind注解 ,进行AOP控制

随手笔记

1.@Autowired 与@Resource的区别

  1. @Autowired按byType自动注入 ; @Resource默认默认按name注入,可以通过name和type属性进行选择性注入

  2. @Autowired属于spring的 ; @Resource属于J2EE的

  3. @Autowired一定要找到匹配的Bean,否则抛异常。 默认值就是true ;

    @Resource默认按名称装配,当找不到名称匹配的bean再按类型装配.

在微服务项目中 , @Resource注入的值可能为null,所以最好用@Autowired

在字段上使用@Resource注解,不用写setter方法

传送门

2.Redis 命令

  1. 启动命令 redis-server redis.conf
  2. 进入客户端 redis-cli 或 带端口号redis-cli -p 6379 退出:ctrl+c ; exit ; quit
  3. 关闭redis redis-cli shutdownredis-cli -p 6379 shutdown

3.工具API封装JSON转化

实现对象(以及List集合) 和 JSON数据的转化

  1. private static final ObjectMapper MAPPER = new ObjectMapper();
  2. 将对象转换为JSON: MAPPER .writeValueAsString(对象\list)
  3. 将JSON还原对象 : MAPPER.readValue(json,对象.class\list.getclass)
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
/**
 * 需要将json串与对象实现互转
 */
public class ObjectMapperUtil {
    private static final ObjectMapper MAPPER = new ObjectMapper();
	//将对象转换为json
    public static String toJSON(Object obj) {
        String json = null;
        try {
            json = MAPPER.writeValueAsString(obj);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
        return json;
    }

    //将json串转化为对象
    public static <T> T toObject(String json, Class<T> targetClass) {
        T obj = null;
        try {
            obj = MAPPER.readValue(json, targetClass);
        } catch (IOException e) {

            e.printStackTrace();
            throw new RuntimeException(e);
        }
        return obj;
    }
}

4.切入点种类

1.bean 按照具体的Bean拦截
2.within(包名.类名) 按照类拦截 粗粒度
3.execution(返回值类型 包名.类名.方法名(参数列表)) 细粒度
4.@annotation(包名.注解名称) 注解形式

5.通知种类

环绕通知 : 功能最为强大 控制目标方法是否执行

前置通知 : 目标方法执行之前执行

后置通知 : 目标方法执行之后执行

异常通知 : 当目标方法抛出异常时使用

最终通知 : 无论何时,最终都会执行的通知

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值