diff --git a/kkmall-product/pom.xml b/kkmall-product/pom.xml
index 85725b5..eb2b97e 100644
--- a/kkmall-product/pom.xml
+++ b/kkmall-product/pom.xml
@@ -47,6 +47,17 @@
org.springframework.boot
spring-boot-starter-data-redis
+
+
+ org.redisson
+ redisson
+ 3.12.5
+
+
+
+ org.springframework.boot
+ spring-boot-starter-cache
+
org.springframework.boot
spring-boot-devtools
diff --git a/kkmall-product/src/main/java/name/lkk/kkmall/product/config/MallCacheConfig.java b/kkmall-product/src/main/java/name/lkk/kkmall/product/config/MallCacheConfig.java
new file mode 100644
index 0000000..2218f09
--- /dev/null
+++ b/kkmall-product/src/main/java/name/lkk/kkmall/product/config/MallCacheConfig.java
@@ -0,0 +1,57 @@
+package name.lkk.kkmall.product.config;
+
+import org.springframework.boot.autoconfigure.cache.CacheProperties;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.cache.annotation.EnableCaching;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.redis.cache.RedisCacheConfiguration;
+import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
+import org.springframework.data.redis.serializer.RedisSerializationContext;
+import org.springframework.data.redis.serializer.StringRedisSerializer;
+
+/**
+ * @author: kirklin
+ * @date: 2021/6/24 3:32 下午
+ * @description:Spring Cache配置
+ */
+@Configuration
+@EnableCaching
+@EnableConfigurationProperties(CacheProperties.class)
+public class MallCacheConfig {
+ /**
+ * 配置文件中 TTL设置没用上
+ *
+ * 原来:
+ * @ConfigurationProperties(prefix = "spring.cache")
+ * public class CacheProperties
+ *
+ * 现在要让这个配置文件生效 : @EnableConfigurationProperties(CacheProperties.class)
+ *
+ */
+ @Bean
+ RedisCacheConfiguration redisCacheConfiguration(CacheProperties cacheProperties){
+
+ RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
+
+ // 设置kv的序列化机制
+ config = config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()));
+ config = config.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));
+ CacheProperties.Redis redisProperties = cacheProperties.getRedis();
+
+ // 设置配置
+ if(redisProperties.getTimeToLive() != null){
+ config = config.entryTtl(redisProperties.getTimeToLive());
+ }
+ if(redisProperties.getKeyPrefix() != null){
+ config = config.prefixCacheNameWith(redisProperties.getKeyPrefix());
+ }
+ if(!redisProperties.isCacheNullValues()){
+ config = config.disableCachingNullValues();
+ }
+ if(!redisProperties.isUseKeyPrefix()){
+ config = config.disableKeyPrefix();
+ }
+ return config;
+ }
+}
diff --git a/kkmall-product/src/main/java/name/lkk/kkmall/product/config/MallRedissonConfig.java b/kkmall-product/src/main/java/name/lkk/kkmall/product/config/MallRedissonConfig.java
new file mode 100644
index 0000000..371dfd7
--- /dev/null
+++ b/kkmall-product/src/main/java/name/lkk/kkmall/product/config/MallRedissonConfig.java
@@ -0,0 +1,29 @@
+package name.lkk.kkmall.product.config;
+
+import org.redisson.Redisson;
+import org.redisson.api.RedissonClient;
+import org.redisson.config.Config;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * @author: kirklin
+ * @date: 2021/6/23 8:43 下午
+ * @description:
+ */
+@Configuration
+public class MallRedissonConfig {
+
+ @Value("${spring.redis.host}")
+ private String redisHost;
+ @Value("${spring.redis.port}")
+ private String redisPort;
+
+ @Bean(destroyMethod = "shutdown")
+ public RedissonClient redisson() {
+ Config config = new Config();
+ config.useSingleServer().setAddress("redis://"+redisHost+":"+redisPort);
+ return Redisson.create(config);
+ }
+}
diff --git a/kkmall-product/src/main/java/name/lkk/kkmall/product/service/impl/CategoryServiceImpl.java b/kkmall-product/src/main/java/name/lkk/kkmall/product/service/impl/CategoryServiceImpl.java
index e6a8a18..cebe060 100644
--- a/kkmall-product/src/main/java/name/lkk/kkmall/product/service/impl/CategoryServiceImpl.java
+++ b/kkmall-product/src/main/java/name/lkk/kkmall/product/service/impl/CategoryServiceImpl.java
@@ -14,7 +14,11 @@ import name.lkk.kkmall.product.service.CategoryBrandRelationService;
import name.lkk.kkmall.product.service.CategoryService;
import name.lkk.kkmall.product.vo.Catalog3Vo;
import name.lkk.kkmall.product.vo.Catelog2Vo;
+import org.redisson.api.RLock;
+import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.cache.annotation.CacheEvict;
+import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.core.script.DefaultRedisScript;
@@ -36,6 +40,9 @@ public class CategoryServiceImpl extends ServiceImpl params) {
IPage page = this.page(
@@ -83,7 +90,9 @@ public class CategoryServiceImpl extends ServiceImpl paths = new ArrayList<>();
@@ -92,7 +101,37 @@ public class CategoryServiceImpl extends ServiceImpl findParentPath(Long catlogId, List paths) {
+ // 1、收集当前节点id
+ paths.add(catlogId);
+ CategoryEntity byId = this.getById(catlogId);
+ if (byId.getParentCid() != 0) {
+ findParentPath(byId.getParentCid(), paths);
+ }
+ return paths;
+ }
+ /**
+ * 级联更新所有数据 [分区名默认是就是缓存的前缀] SpringCache: 不加锁
+ *
+ * @CacheEvict: 缓存失效模式 --- 页面一修改 然后就清除这两个缓存
+ * key = "'getLevel1Categorys'" : 记得加单引号 [子解析字符串]
+ *
+ * @Caching: 同时进行多种缓存操作
+ *
+ * @CacheEvict(value = {"category"}, allEntries = true) : 删除这个分区所有数据
+ *
+ * @CachePut: 这次查询操作写入缓存
+ */
+// @Caching(evict = {
+// @CacheEvict(value = {"category"}, key = "'getLevel1Categorys'"),
+// @CacheEvict(value = {"category"}, key = "'getCatelogJson'")
+// })
+ @CacheEvict(value = {"category"}, allEntries = true)
+// @CachePut
@Transactional
@Override
public void updateCascade(CategoryEntity category) {
@@ -100,6 +139,21 @@ public class CategoryServiceImpl extends ServiceImpl getLevel1Categories() {
return baseMapper.selectList(new QueryWrapper().eq("cat_level", 1));
@@ -113,12 +167,43 @@ public class CategoryServiceImpl extends ServiceImpl item.getParentCid().equals(parentCid)).collect(Collectors.toList());
}
- /**
- * 从缓存中查询CatelogJson
- * @return
- */
+
+
+ @Cacheable(value = {"category"}, key = "#root.methodName")
@Override
public Map> getCatelogJson() {
+ List entityList = baseMapper.selectList(null);
+ // 查询所有一级分类
+ List level1 = getCategoryEntities(entityList, 0L);
+ Map> parent_cid = level1.stream().collect(Collectors.toMap(k -> k.getCatId().toString(), v -> {
+ // 拿到每一个一级分类 然后查询他们的二级分类
+ List entities = getCategoryEntities(entityList, v.getCatId());
+ List catelog2Vos = null;
+ if (entities != null) {
+ catelog2Vos = entities.stream().map(l2 -> {
+ Catelog2Vo catelog2Vo = new Catelog2Vo(v.getCatId().toString(), l2.getName(), l2.getCatId().toString(), null);
+ // 找当前二级分类的三级分类
+ List level3 = getCategoryEntities(entityList, l2.getCatId());
+ // 三级分类有数据的情况下
+ if (level3 != null) {
+ List catalog3Vos = level3.stream().map(l3 -> new Catalog3Vo(l3.getCatId().toString(), l3.getName(), l2.getCatId().toString())).collect(Collectors.toList());
+ catelog2Vo.setCatalog3List(catalog3Vos);
+ }
+ return catelog2Vo;
+ }).collect(Collectors.toList());
+ }
+ return catelog2Vos;
+ }));
+ return parent_cid;
+ }
+
+
+ /**
+ * 自定义实现,从缓存中查询CatelogJson
+ * @return
+ */
+// @Override
+ public Map> getCatelogJsonManual() {
/**
* 1.空结果缓存 解决缓存穿透
@@ -140,6 +225,27 @@ public class CategoryServiceImpl extends ServiceImpl> getCatelogJsonFromDBWithRedissonLock() {
+
+ // 这里只要锁的名字一样那锁就是一样的
+ // 关于锁的粒度 具体缓存的是某个数据 例如: 11-号商品 product-11-lock
+ RLock lock = redissonClient.getLock("CatelogJson-lock");
+ lock.lock();
+
+ Map> data;
+ try {
+ data = getCatelogJsonFormDB();
+ } finally {
+ lock.unlock();
+ }
+ return data;
+ }
+
/**
* 分布式锁
*
@@ -262,16 +368,5 @@ public class CategoryServiceImpl extends ServiceImpl findParentPath(Long catlogId, List paths) {
- // 1、收集当前节点id
- paths.add(catlogId);
- CategoryEntity byId = this.getById(catlogId);
- if (byId.getParentCid() != 0) {
- findParentPath(byId.getParentCid(), paths);
- }
- return paths;
- }
+
}
\ No newline at end of file
diff --git a/kkmall-product/src/main/java/name/lkk/kkmall/product/web/IndexController.java b/kkmall-product/src/main/java/name/lkk/kkmall/product/web/IndexController.java
index 33adbef..8e89652 100644
--- a/kkmall-product/src/main/java/name/lkk/kkmall/product/web/IndexController.java
+++ b/kkmall-product/src/main/java/name/lkk/kkmall/product/web/IndexController.java
@@ -3,15 +3,19 @@ package name.lkk.kkmall.product.web;
import name.lkk.kkmall.product.entity.CategoryEntity;
import name.lkk.kkmall.product.service.CategoryService;
import name.lkk.kkmall.product.vo.Catelog2Vo;
+import org.redisson.api.*;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
-import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.List;
import java.util.Map;
+import java.util.UUID;
/**
* @author: kirklin
@@ -24,12 +28,24 @@ public class IndexController {
@Autowired
private CategoryService categoryService;
- @RequestMapping("/hello")
- @ResponseBody
- public String hello(){
- return "Hello";
- }
+ @Autowired
+ private RedissonClient redissonClient;
+ @Autowired
+ private StringRedisTemplate stringRedisTemplate;
+
+
+// @RequestMapping("/hello")
+// @ResponseBody
+// public String hello(){
+// return "Hello";
+// }
+
+ /**
+ * 首页转发
+ * @param model
+ * @return
+ */
@RequestMapping({"/", "index", "/index.html"})
public String indexPage(Model model) {
// 获取一级分类所有缓存
@@ -49,4 +65,122 @@ public class IndexController {
Map> map = categoryService.getCatelogJson();
return map;
}
+
+
+ /**
+ * RLock锁有看门狗机制 会自动帮我们续期,默认三秒自动过期
+ * lock.lock(10,TimeUnit.SECONDS);
+ * 锁的时间一定要大于业务的时间 否则会出现死锁的情况
+ *
+ * 如果我们传递了锁的超时时间就给redis发送超时脚本 默认超时时间就是我们指定的
+ * 如果我们未指定,就使用 30 * 1000 [LockWatchdogTimeout]
+ * 只要占锁成功 就会启动一个定时任务 任务就是重新给锁设置过期时间
+ * 这个时间还是 [LockWatchdogTimeout] 的时间 1/3 看门狗的时间续期一次 续成满时间
+ */
+ @ResponseBody
+ @RequestMapping("/index/hello")
+ public String hello() {
+ RLock lock = redissonClient.getLock("my-lock");
+ // 阻塞式等待
+ lock.lock();
+ try {
+ Thread.sleep(2000);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ } finally {
+ lock.unlock();
+ }
+ return "hello";
+ }
+ //============================================信号量=========================================
+ /**
+ * 尝试获取车位 [信号量]
+ * 信号量:也可以用作限流
+ */
+ @ResponseBody
+ @GetMapping("/index/park")
+ public String park() {
+
+ RSemaphore park = redissonClient.getSemaphore("park");
+ boolean acquire = park.tryAcquire();
+ return "获取车位 =>" + acquire;
+ }
+
+ /**
+ * 尝试获取车位
+ */
+ @ResponseBody
+ @GetMapping("/index/go/park")
+ public String goPark() {
+
+ RSemaphore park = redissonClient.getSemaphore("park");
+ park.release();
+ return "ok => 车位+1";
+ }
+//============================================读写锁=========================================
+ /**
+ * 读写锁-读锁
+ */
+ @GetMapping("/index/read")
+ @ResponseBody
+ public String readValue() {
+ RReadWriteLock lock = redissonClient.getReadWriteLock("rw-lock");
+ RLock rLock = lock.readLock();
+ String s = "";
+ rLock.lock();
+ try {
+ s = stringRedisTemplate.opsForValue().get("writeValue");
+ } catch (Exception e) {
+ e.printStackTrace();
+ } finally {
+ rLock.unlock();
+ }
+ return s;
+ }
+ /**
+ * 读写锁-写锁
+ */
+ @GetMapping("/index/write")
+ @ResponseBody
+ public String writeValue() {
+ RReadWriteLock lock = redissonClient.getReadWriteLock("rw-lock");
+ RLock rLock = lock.writeLock();
+ String s = "";
+ try {
+ rLock.lock();
+ s = UUID.randomUUID().toString();
+ Thread.sleep(3000);
+ stringRedisTemplate.opsForValue().set("writeValue", s);
+ } catch (Exception e) {
+ e.printStackTrace();
+ } finally {
+ rLock.unlock();
+ }
+ return s;
+ }
+//============================================闭锁=========================================
+ /**
+ * 闭锁 只有设定的人全通过才关门
+ */
+ @ResponseBody
+ @GetMapping("/index/lockDoor")
+ public String lockDoor() throws InterruptedException {
+ RCountDownLatch door = redissonClient.getCountDownLatch("door");
+ // 设置这里有5个人
+ door.trySetCount(5);
+ door.await();
+
+ return "5个人全部通过了...";
+ }
+
+ @ResponseBody
+ @GetMapping("/index/go/{id}")
+ public String go(@PathVariable("id") Long id) throws InterruptedException {
+
+ RCountDownLatch door = redissonClient.getCountDownLatch("door");
+ // 每访问一次相当于出去一个人
+ door.countDown();
+ return id + "走了";
+ }
+
}
diff --git a/kkmall-product/src/main/resources/application.yml b/kkmall-product/src/main/resources/application.yml
index 7ada551..dbbcbac 100644
--- a/kkmall-product/src/main/resources/application.yml
+++ b/kkmall-product/src/main/resources/application.yml
@@ -1,8 +1,9 @@
+ipAddr: localhost
spring:
datasource:
#MySQL配置
driverClassName: com.mysql.cj.jdbc.Driver
- url: jdbc:mysql://localhost:3306/kkmall_pms?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
+ url: jdbc:mysql://${ipAddr}:3306/kkmall_pms?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: 66CcFf!!
cloud:
@@ -18,11 +19,26 @@ spring:
static-path-pattern: /static/**
thymeleaf:
cache: true
+ suffix: .html
+ prefix: classpath:/templates/
+
redis:
- host: localhost
+ host: ${ipAddr}
port: 6379
password: linkekun
+ # 设置缓存类型
+ cache:
+ type: redis
+ # 设置存活时间
+ redis:
+ time-to-live: 3600000
+ # 如果指定了前缀就用我们指定的 如果没有就用缓存的名字作为前缀
+ # key-prefix: CACHE_
+ # 是否缓存空值,解决缓存穿透问题
+ cache-null-values: true
+# cache-names:
+
mybatis-plus:
mapper-locations: classpath:/mapper/**/*.xml
global-config:
diff --git a/kkmall-product/src/main/resources/templates/index.html b/kkmall-product/src/main/resources/templates/index.html
index 55cda4d..51f9da3 100644
--- a/kkmall-product/src/main/resources/templates/index.html
+++ b/kkmall-product/src/main/resources/templates/index.html
@@ -93,20 +93,20 @@
@@ -122,7 +122,7 @@