文章目录
一、Redis
1.1 缓存策略
添加缓存能够有效降低用户访问物理设备的访问频次.能够有效的提升用户查询的速度.并且缓存中的数据就是数据库中的数据
如何实现缓存策略?
采用KEY-VALUE结构实现数据的存储.
开发语言最好使用C.更加贴近硬件.
缓存的运行环境最好在内存中.
缓存中的数据如何持久化 保存到磁盘中.
实现缓存数据的动态更新 LRU算法(T) LFU算法(次数)
1.2 Redis 安装
上传安装文件
redis-5.0.4.tar.gz
到linux上解压redis
tar -xvf redis-5.0.4.tar.gz
编译安装redis (在redis根目录中执行指令)
(1)编译:
make
(2)安装:
make install
redis 启动校验 :
redis-server
修改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查询商品分类
- 修改
ItemCatController
的findItemCatByParentId
方法
@RequestMapping("/list")
public List<EasyUITree> findItemCatByParentId(@RequestParam(value = "id",defaultValue = "0") Long parentId){
//先查询缓存,如果缓存中没有数据,则查询数据库
return itemCatService.findItemCatByParentId(parentId);
}
- 在
ItemCatServiceImpl
的findItemCatByParentId
上加上CacheFind
注解 ,进行AOP控制
随手笔记
1.@Autowired 与@Resource的区别
@Autowired
按byType自动注入 ;@Resource
默认默认按name注入,可以通过name和type属性进行选择性注入
@Autowired
属于spring的 ;@Resource
属于J2EE的
@Autowired
一定要找到匹配的Bean,否则抛异常。 默认值就是true ;
@Resource
默认按名称装配,当找不到名称匹配的bean再按类型装配.在微服务项目中 , @Resource注入的值可能为
null
,所以最好用@Autowired在字段上使用@Resource注解,不用写setter方法
2.Redis 命令
- 启动命令
redis-server redis.conf
- 进入客户端
redis-cli
或 带端口号redis-cli -p 6379
退出:ctrl+c
;exit
;quit
- 关闭redis
redis-cli shutdown
或redis-cli -p 6379 shutdown
3.工具API封装JSON转化
实现对象(以及List集合) 和 JSON数据的转化
private static final ObjectMapper MAPPER = new ObjectMapper();
- 将对象转换为JSON:
MAPPER .writeValueAsString(对象\list)
- 将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.通知种类
环绕通知 : 功能最为强大 控制目标方法是否执行
前置通知 : 目标方法执行之前执行
后置通知 : 目标方法执行之后执行
异常通知 : 当目标方法抛出异常时使用
最终通知 : 无论何时,最终都会执行的通知