超详细!利用SpringBoot+SpringCloud做一个问答项目(九)

目录

一、Redis的基本使用

二、SpringBoot中的计划任务

三、定期向Redis中更新标签列表

四、从Redis中获取标签列表

五、通过网关转发访问以上控制器

六、标签列表---前端页面


一、Redis的基本使用


在Windows系统中,当安装了Redis后,会自动启动Redis的服务,每次开机时Redis就会启动,是可以直接使用的,检验的标准可以通过“登录Redis客户端的控制台”来判断:

 

如果已经登录了,但并不确定当前Redis是否正在运行,也可以通过ping命令来判断:

除了ping以外,也可以通过其它命令来判断,例如是否可以存入数据,或是否可以取出数据等。

当登录Redis客户端的控制台之后,可以通过exit退出:

在登录Redis客户端的控制台之后,可以通过set命令向Redis中存入数据,也可以通过get命令取出数据:

关于Redis存储数据的结构,可以想像成Java中的Map,每个数据需要有KeyValue,其中,Key是唯一的,如果反复使用同一个Key存入数据,最终在Redis中只有最后一次存入的数据!所以,Redis中的set命令相当于Java中操作Mapput()方法。

由于取出数据时需要使用到数据的Key,如果不明确当前Redis中有哪些数据,可以通过keys *来查看所有的Key

其实,Redis可以理解为一个数据库(Database),在Redis中默认存在16个数据表,当登录Redis时,默认使用的是第1个数据表,这些数据表使用0~15作为索引,通过select index可以切换使用数据表:

当需要清除Redis中的数据时,可以通过flushdbflushall来清除:

二、SpringBoot中的计划任务


计划任务适用于每间隔一段时间就执行一次某项目任务,例如“定时更新热门话题”等。

在SpringBoot中,如果需要使用计划任务,需要:

  • 在配置类的声明之前添加@EnableScheduling注解;
  • 计划任务方法必须放在某个组件类中;
  • 计划任务方法的声明之前必须添加@Scheduled注解,并在注解中配置执行时间。

例如,先在启动类(启动类就是一个配置类)的声明之前添加注解:

然后,在cn.tedu.straw.api.question.schedule包中创建TestSchedule类,用于测试使用计划任务:

package cn.tedu.straw.api.question.schedule;

import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component
@Slf4j
public class TestSchedule {

    private int i = 0;

    @Scheduled(fixedRate = 3 * 1000)
    public void task() {
        log.debug("测试执行计划任务:" + ++i);
    }

}

完成后,启动StrawApiQuestionApplication项目,在控制台可以看到计划任务输出的日志。

三、定期向Redis中更新标签列表


先准备计划任务,在cn.tedu.straw.api.question.schedule包中创建RedisTagSchedule类,并添加计划任务方法:

package cn.tedu.straw.api.question.schedule;

import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component
@Slf4j
public class RedisTagSchedule {

    @Scheduled(fixedRate = 10 * 1000)
    public void updateRedisTag() {
        log.debug("准备更新Redis服务器中缓存的标签列表");
    }

}

 要使用Redis的API,需要先添加spring-boot-starter-data-redis的依赖,其参考代码是:

<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-redis -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
    <version>2.3.3.RELEASE</version>
</dependency>

基于以上参考代码调整父级项目和当前项目的pom.xml

然后,还需要配置一个RedisTemplate对象,该对象的API就是操作Redis中存取数据的!

可以在任意配置类中配置这个类的对象,则在启动类中:  

由于RedisTemplate的API不够简洁,所以,一般会将RedisTemplate的API进行再次封装,以便于后续的调用,所以,在cn.tedu.straw.api.question.util包中创建RedisUtils工具类:

package cn.tedu.straw.api.question.util;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.io.Serializable;

/**
 * 操作Redis服务器中的数据的工具类
 */
@Component
public class RedisUtils {

    @Autowired
    RedisTemplate<String, Serializable> redisTemplate;

    /**
     * 删除Redis中的某个数据
     *
     * @param key 被删除的数据的Key
     * @return 删除操作是否成功
     */
    public Boolean delete(String key) {
        return redisTemplate.delete(key);
    }

    /**
     * 向Redis中的某个List集合中添加集合元素
     *
     * @param key   在Redis中的List集合类型的数据的Key
     * @param value 需要存入到Redis的List集合中的元素值
     */
    public Long rightPush(String key, Serializable value) {
        ListOperations<String, Serializable> ops = redisTemplate.opsForList();
        return ops.rightPush(key, value);
    }

}

回到计划类中,在执行计划任务时,先删除Redis中可能已经存在的标签列表,然后重新添加标签列表:

package cn.tedu.straw.api.question.schedule;

import cn.tedu.straw.api.question.service.ITagService;
import cn.tedu.straw.api.question.util.RedisUtils;
import cn.tedu.straw.commons.vo.TagVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;

@Component
@Slf4j
public class RedisTagSchedule {

    @Autowired
    RedisUtils redisUtils;
    @Autowired
    ITagService tagService;

    @Scheduled(fixedRate = 10 * 1000)
    public void updateRedisTag() {
        // 日志
        log.debug("[{}] 准备更新Redis服务器中缓存的标签列表"
                , DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(LocalDateTime.now()));
        // 在Redis中“标签列表”数据的Key
        String tagListKey = "tags";
        // 删除“标签列表”数据,避免反复增加列表元素,或某些列表元素在MySQL中更新后在Redis中无法处理
        redisUtils.delete(tagListKey);
        // 从数据库中读取新的“标签列表”
        List<TagVO> tags = tagService.getTagList();
        // 遍历“标签列表”并向Redis中逐一添加数据
        for (TagVO tag : tags) {
            // 向Redis中添加数据
            redisUtils.rightPush(tagListKey, tag);
        }
        // 日志
        log.debug("[{}] 更新Redis服务器中缓存的标签列表,完成!"
                , DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(LocalDateTime.now()));
    }

}

完成后,启动当前项目,通过日志可以观察计划任务在定期执行,同时,登录Redis客户端的控制台,也可以看到标签列表数据已经成功的添加到了Redis服务器中:

四、从Redis中获取标签列表


如果需要从Redis中获取列表数据,首先,需要在RedisUtils中补充定义获取列表数据的方法:

/**
 * 获取Redis中某List集合
 *
 * @param key 需要获取的数据在Redis中的Key
 * @return List集合
 */
public List<Serializable> listRange(String key) {
    ListOperations<String, Serializable> ops = redisTemplate.opsForList();
    long start = 0;
    long end = ops.size(key);
    return ops.range(key, start, end);
}

/**
 * 获取Redis中某List集合中的数据片段
 *
 * @param key   需要获取的数据在Redis中的Key
 * @param start 获取的数据片段在List集合中的起始位置
 * @param end   获取的数据片段在List集合中的结束位置
 * @return List集合中的数据片段
 */
public List<Serializable> listRange(String key, long start, long end) {
    ListOperations<String, Serializable> ops = redisTemplate.opsForList();
    return ops.range(key, start, end);
}

此前,在开发straw-api-user时创建了R类,用于表示向客户端响应的JSON结果的类型,目前,在straw-api-question中也需要使用到这个类型,所以,应该将R类移动到straw-commons中的cn.tedu.straw.commons.util包中!

注意:当把R类从straw-api-user移动到straw-commons中之后,需要检查straw-api-usercn.tedu.straw.api.user.controller包中代码,使用到R类的import语句都需要修正。

目前,当控制器向客户端响应R类型的结果时,无法封装“数据”,所以,应该先在R类中声明新的属性,表示“操作成功时响应到客户端的数据”,由于R类型是希望广泛使用的,无论客户端提交的是哪种请求都可以响应R类型,例如“获取当前登录的用户资料”、“获取标签列表”、“获取问题列表”、“获取问题详情”等,所以,补充添加的“数据”属性的类型是不确定的,则应该使用Object类型,或使用泛型,本例中暂且使用泛型:

同时,为了能够更加简洁的创建R对象,还应该添加方法:

最终,这些数据应该被控制器对外提供访问,在cn.tedu.straw.api.question.controller包中创建TagController用于对外提供访问:

package cn.tedu.straw.api.question.controller;

import cn.tedu.straw.api.question.util.RedisUtils;
import cn.tedu.straw.commons.util.R;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/tags")
public class TagController {

    @Autowired
    RedisUtils redisUtils;

    // http://localhost:8081/tags
    @GetMapping("")
    public R getTagList() {
        String key = "tags";
        return R.ok(redisUtils.listRange(key));
    }

}

完成后,重启straw-api-question项目,打开浏览器,通过以上测试URL进行访问,应该可以看到标签列表数据,例如:  

可以看到,以上JSON结果中还包含"message": null这一条属性,这个属性不是需要响应到客户端的,在使用Jackson框架处理服务器端响应到客户端的JSON数据时,在SpringBoot项目中,可以添加配置:

# 服务器端向客户端响应的JSON数据中将不包含为null的属性与值
spring.jackson.default-property-inclusion=non_null

五、通过网关转发访问以上控制器


要使得当前straw-api-question项目整合到集群中,首先,需要在当前项目中添加eureka-client使之成为Eureka客户端,后续,在启动项目时就会在Eureka注册中心进行注册,然后,还应该在网关路由中配置新的转发规则,使得用户通过网关可以转发来访问当前项目!

则先在straw-api-questionpom.xml中添加eureka-client的依赖:

然后,在straw-api-questionapplication.properties中补充Eureka Client的相关配置(从其它项目中复制过来即可,无需修改各配置值):

至此,先启动straw-eureka-server项目,再启动straw-api-question项目,在 http://loalhost:8761 Eureka状态页面即可看到当前项目已经成功注册:

然后,需要实现路由转发,在straw-gatewayapplication.properties配置转发规则:

为了避免转换路径的冲突,并统一转发规则的配置,以上代码中,将原有的api-user转发规则中的原路径/api/**改成了/api-user/**,由于在此前完成的“注册”功能是访问了straw-api-user的控制器的,所以,应该在register.js中修改请求路径:

在网关的WebSecurityConfigurer中的“白名单”的路径也跟随调整:

另外,为了使得访问的URL风格是统一的,在TagController中,将类的声明之前使用@RequestMapping配置的路径也添加/v1前缀:  

完成后,重启straw-api-question,并分别启动straw-api-userstraw-gateway项目,最终,在浏览器中,通过 http://localhost/api-question/v1/tags 可以访问到标签列表。

由于修改了“注册”相关的URL、配置等信息,应该再次尝试注册新用户,以检验“注册”功能是否仍正常可用!

六、标签列表---前端页面


index.html的偏顶部位置需要显示标签列表:

首先,打开index.html,找到显示标签列表的区域,在这个区域的标签上配置id属性,以对应Vue对象:

接下来,在resources/static/js中创建commons文件夹,在该文件夹下创建tags.js文件,用于编写此处将使用的程序:

并在index.html中引用该文件:

然后,在tags.js中创建Vue对象,并添加模拟数据:

然后,在index.html中,使用v-for语句显示以上数据:

关于v-for语法,它是用于遍历某个标签的,例如将v-for配置在<p>标签上,则表示需要遍历这个<p>标签及其所有子级标签(从<p>开始,直到对应的</p>),最终,在页面中会出现若干个<p>标签!

关于v-for的取值,简单语法是xx in arr,其中,arr应该是一个JS中的数组,是被遍历的对象,而xx就是遍历过程中数组元素的值,该语法与Java中的for (User user : users)这类语法是相似的!

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值