1. Redis
1.1 Redis入门案例
1.1.1 导入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.1.2 在test包下创建测试类以及相关测试方法
1.运行测试
public class RedisTest {
/**
* 如果报错:
* 1. 防火墙
* 2. redis配置文件 3处
* 3. redis启动方式:redis-server redis.conf
*/
@Test
public void testString(){
String host = "192.168.126.129";
/*
* 默认端口号:
* 8066:mycat
* 3306:mybatis
* 6379:redis
* 80:nginx
* */
int port = 6379;
Jedis jedis = new Jedis(host,port);
jedis.set("redis", "你好Redis");
System.out.println
("获取redis中的数据:"+jedis.get("redis"));
}
2.判断数据是否存在,如果存在 则删除,不存在 则赋值
@Test
public void test01(){
Jedis jedis = new Jedis("192.168.126.129", 6379);
if(jedis.exists("redis")){
jedis.del("redis");
System.out.println("已删除");
}else{
jedis.set("hello", "你好Redis");
System.out.println(jedis.get("hello"));
}
}
3.如果数据存在 则不做任何操作,如果数据不存在,则赋值
@Test
public void test02(){
Jedis jedis = new Jedis("192.168.126.129", 6379);
jedis.flushAll();
// jedis.set("redis", "aaa");
jedis.setnx("redis", "bbb");
System.out.println(jedis.get("redis"));
}
4.添加数据,并且设定超时时间 10s
/*
存在bug问题:如果在超时时间内发生意外情况,数据会一直存在
设定超时时间,应该注意原子性(atomic)问题
*/
@Test
public void test03() throws InterruptedException {
Jedis jedis = new Jedis("192.168.126.129", 6379);
// jedis.set("a", "设定超时时间");
// jedis.expire("a", 10);
jedis.setex("a", 10, "设定超时时间");
Thread.sleep(2000);
System.out.println("剩余时间:"+jedis.ttl("a")+"秒");
}
5.如果数据存在,则不修改,反之修改,同时设定超时10s,并且为原子性操作
@Test
public void test04(){
Jedis jedis = new Jedis("192.168.126.129", 6379);
SetParams setParams = new SetParams();
setParams.nx().ex(10);
jedis.set("b", "赋值操作", setParams);
System.out.println(jedis.get("b")+","+"剩余时间:"+jedis.ttl("b")+"秒");
}
1.2 Springboot整合redis
1.2.1 配置公共的redis配置文件
redis.host=192.168.126.129
redis.port=6379
1.2.2 编辑配置类
@Configuration //标识该类为配置类 一般和@Bean注解联用
@PropertySource("classpath:/redis.properties")
public class RedisConfig {
@Value("${redis.host}")
private String host;
@Value("${redis.port}")
private Integer port;
@Bean
public Jedis jedis(){
return new Jedis(host,port);
}
}
1.2.3 启动测试类测试
@SpringBootTest(classes = RedisConfig.class)
public class RedisTest {
@Autowired
private Jedis jedis;
@Test
public void testRedis(){
//利用容器动态获取对象之后操作redis
jedis.set("redis", "整合测试");
System.out.println(jedis.get("redis"));
}
}
1.3 Json对象转化API——ObjectMapper
public class TestObjectMapper {
/**
*
* 1. 通过测试类 实现对象与Json之间的转化
*
* */
@Test
public void test01() throws JsonProcessingException {
ItemDesc itemDesc = new ItemDesc();
itemDesc.setItemId(100L).setItemDesc("转化测试");
ObjectMapper objectMapper = new ObjectMapper();
//将对象转化为Json
String json = objectMapper.writeValueAsString(itemDesc);
System.out.println(json);
//将Json转化为对象
ItemDesc itemDesc2 = objectMapper.readValue(json, ItemDesc.class);
System.out.println(itemDesc2);
}
}
1.4 封装ObjectMapperUtil
public class ObjectMapperUtil {
private static final ObjectMapper MAPPER = new ObjectMapper();
//将对象转化为Json
public static String toJson(Object obj){
try {
return MAPPER.writeValueAsString(obj);
} catch (JsonProcessingException e) {
//将检查异常,转化为运行时异常 之后被全局异常处理机制捕获处理
e.printStackTrace();//或者日志打印...
throw new RuntimeException(e);
}
}
//将Json转化为对象 用户指定什么样的类型,返回什么样的对象——使用泛型
public static <T> T toObj(String json,Class<T> target){
try {
return MAPPER.readValue(json, target);
} catch (JsonProcessingException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
}
- 调用封装工具类,实现对象转Json,Json转对象
public class TestObjectMapper {
/**
* 调用封装工具类,实现对象转Json,Json转对象
* */
@Test
public void testObject(){
ItemDesc itemDesc = new ItemDesc().setItemId(100L).setItemDesc("aaa");
String json = ObjectMapperUtil.toJson(itemDesc);
System.out.println(json);
ItemDesc itemDesc1 = ObjectMapperUtil.toObj(json, ItemDesc.class);
System.out.println(itemDesc1);
}
}
2. 商品分类缓存的实现
2.1 在controller层改写控制方法
@RequestMapping("list")
public List<EasyUITree> findItemCatLost(Long id){
/*
long parentId = (id==null?0:id);
return itemCatService.findItemCatList(parentId);
*/
long parentId = (id==null?0:id);
return itemCatService.findItemCatCache(parentId);
}
2.2 在service层编译具体代码实现
/**
* 缓存处理业务逻辑:
* 1.第一次查询先查缓存
* 2.结果:
* 有结果:说明用户不是第一次查,直接从缓存中获取数据
* 无结果: 说明用户是第一次查询,先查询数据库,保存到缓存中
* 3.Redis.set(key,value):
* key:parentId
* value:
* @param parentId
* @return
*/
@Override
public List<EasyUITree> findItemCatCache(long parentId) {
Long startTime = System.currentTimeMillis();
String key = "ITEM_CAT_PARENTID::"+parentId;
List<EasyUITree> treeList = new ArrayList<>();
if(jedis.exists(key)){
//数据存在
String json = jedis.get(key);
treeList = ObjectMapperUtil.toObj(json, treeList.getClass());
Long endTime = System.currentTimeMillis();
System.out.println("查询缓存的时间:"+(endTime-startTime)+"毫秒");
}else {
//表示数据不存在,需要查询数据库
treeList = findItemCatList(parentId);
//将结果保存到缓存中
String json = ObjectMapperUtil.toJson(treeList);
jedis.set(key,json);
Long endTime = System.currentTimeMillis();
System.out.println("查询数据库的时间:"+(endTime-startTime)+"毫秒");
}
return treeList;
}
3. AOP实现商品分类缓存
3.1 为什么使用AOP?
- 将缓存代码直接写到业务中,只对某个业务有效,如果其他业务需要缓存,则需要重新编译
- 如果将缓存代码直接写死在业务层,如果后期代码需要更新,代码耦合性太高,不便于扩展
3.2 AOP作用
1.面向切面编程,一种设计思想,是对面向对象编程的补充和完善。
2.在不改变原有代码的条件下,对方法进行额外的功能扩展。
3.AOP构造要素= 切入点表达式+通知方法
3.3 常见切入点表达式
1.Bean(“bean的Id”) – 默认是类变量名首字母小写
2.within(“包名.类名”)
3.execution(“返回值类型 包名.类名.方法名(参数列表)”)
4.annotation(“包名.注解名")
3.4 通知方法
-
before 目标方法执行前执行
-
afterReturning 目标方法成功执行后执行
-
afterThrowing 目标方法异常执行时执行
-
after 程序最后执行
上述四大通知类型一般用来记录程序的执行状态,为监控系统提供数据的支持,只监控不处理 -
around 目标方法执行前后都要执行
环绕通知是通知类型中功能最强大的,可以控制目标方法是否执行(控制业务是否操作)
3.5 AOP简单案例
- 恢复controller
- service层设置属性
- 编写AOP入门方法