前面我们实现了用户进入页面增加浏览量,评论时增加评论数等操作,但是当项目第一次启动时,Redis中没有统计数据,导致查询攻略时拿到的统计数据(从Redis中获取)和数据库不一致。因此,在项目启动时,从数据库中查询文章的统计数据,将其写入Redis中。
我们需要在项目初始化完成后,执行统计数据初始化操作,可以通过继承 ApplicationListener 监听器实现
ApplicationEvent以及Listener是Spring为我们提供的一个事件监听、订阅的实现,内部实现原理是观察者设计模式,设计初衷也是为了系统业务逻辑之间的解耦,提高可扩展性以及可维护性。
在模块:trip-article-server中新建com.swx.article.listener
包,创建 RedisStatDataInitListener 类,实现ApplicationListener 接口,重写其中的 onApplicationEvent 方法,当上下文event.getApplicationContext()
为AnnotationConfigServletWebServerApplicationContext
时,Spring容器启动完成,这个时候可以开始我们的数据初始化工作。
当数据量过大时,可以考虑分批次查询,每批次交给一个线程去异步执行
也可以写一个初始化的接口,url可以设置为一个随机字符串,防止接口被滥用,为保证初始化接口只能使用一次,可以将状态保存到 Redis 中,当 Redis 存在该状态时,表面已经初始化过,直接返回404.
RedisStatDataInitListener@Component public class RedisStatDataInitListener implements ApplicationListener<ContextRefreshedEvent> {
private final StrategyService strategyService; private final RedisService redisService;
public RedisStatDataInitListener(StrategyService strategyService, RedisService redisService) { this.strategyService = strategyService; this.redisService = redisService; }
@Override public void onApplicationEvent(ContextRefreshedEvent event) { ApplicationContext ctx = event.getApplicationContext(); System.out.println(ctx.getClass()); if (AnnotationConfigServletWebServerApplicationContext.class == ctx.getClass()) { System.out.println("-------------- 容器启动完成,执行初始化数据 --------------"); List<Strategy> strategies = strategyService.list(); System.out.println("[攻略统计数据初始化]"); System.out.println("攻略数:" + strategies.size()); int count = 0; for (Strategy strategy : strategies) { String fullKey = StrategyRedisKeyPrefix.STRATEGIES_STAT_DATA_MAP.fullKey(strategy.getId() + ""); Boolean exists = redisService.hasKey(fullKey); if (!exists) { HashMap<String, Object> map = new HashMap<>(); map.put("viewnum", strategy.getViewnum()); map.put("thumbsupnum", strategy.getThumbsupnum()); map.put("replynum", strategy.getReplynum()); map.put("favornum", strategy.getFavornum()); map.put("sharenum", strategy.getSharenum()); redisService.setCacheMap(fullKey, map); count++; } } System.out.println("初始化:" + count); System.out.println("-------------- 数据初始化完成 --------------"); } } }
|
这里容器启动时会导致循环依赖,需要修改 StrategyServiceImpl 类,在 UserInfoFeignService 前添加@Lazy
注解。
StrategyServiceImplpublic StrategyServiceImpl(@Lazy UserInfoFeignService userInfoFeignService) { this.userInfoFeignService = userInfoFeignService; }
|