• 谷粒学院16万字笔记+1600张配图(十三)——搭建前台环境、首页数据显示


    项目源码与所需资料
    链接:https://pan.baidu.com/s/1azwRyyFwXz5elhQL0BhkCA?pwd=8z59
    提取码:8z59

    demo13-搭建前台环境、首页数据显示

    1.服务端渲染技术NUXT

    1.以前是客户端发送ajax请求给服务端,然后服务端返回数据给客户端。不利于网站进行SEO(老师解释了,我听不懂)

    2.下面这种方式利于SEO:客户端请求服务端时,服务端的node.js会请求tomcat得到数据,得到数据后在node.js中把数据做封装,然后将数据一次性返回给客户端

    在这里插入图片描述

    和ajax的区别是:ajax方式是客户端请求服务端的tomcat;而nuxt方式是服务端的node.js请求服务端的tomcat,客户端只负责显示数据,请求过程是通过node.js实现的,请求数据是发生在服务端而不像ajax那样那样发生在客户端。NUXT是对node.js的封装,所以我们这里使用NUXT框架来搭建前台系统

    2.NUXT框架介绍

    2.1NUXT环境初始化

    1.在资料中提供了nuxt的压缩文件(已经下载好依赖了,直接用就行)

    在这里插入图片描述

    2.解压后得到vue-front-1010文件夹,将该文件夹复制粘贴到工作区vs1010中

    在这里插入图片描述

    3.在vue-front-1010文件夹上右键选择"终端中打开"

    在这里插入图片描述

    4.使用命令npm run dev启动项目

    在这里插入图片描述

    5.看到这个就是启动成功了

    在这里插入图片描述

    6.在地址栏输入http://localhost:3000/进行访问

    在这里插入图片描述

    7.会出现这样的警告,不用管,对项目没影响

    在这里插入图片描述

    2.2NUXT目录结构

    在这里插入图片描述

    • .nuxt:就相当于java的.class文件,里面是编译后的内容
    • assets:一般存放项目中使用到的静态资源,比如css、js、img…
    • components:存放项目中用到的组件
    • layouts:该目录下有一个default.vue文件,该文件用于定义网页布局(页面的头信息和尾信息)
    • middleware:也可以放一些相关组件
    • node_modules:存放下载好的依赖
    • pages:存放项目中的页面,该目录下的index.vue是默认首页面
    • nuxt.config.js:NUXT框架的核心配置文件

    显示页面的流程:先加载layouts下的default.vue页面,然后在该页面中使用nuxt标签将其它页面引入过来

    我们这个项目的后台的前端项目使用的vue-admin框架是基于vue和element-ui实现的。但是我们项目的前台的前端项目使用的NUXT框架只基于vue实现,本身并没有集成element-ui,如果我们想使用element-ui就需要将element-ui引入过来,否则不能使用element-ui中的组件

    3.整合项目首页面

    3.1幻灯片插件

    1.使用如下命令安装幻灯片插件

    npm install vue-awesome-swiper
    

    在这里插入图片描述

    2.配置幻灯片插件

    ①在plugins文件夹下创建文件nuxt-swiper-plugin.js并编写代码

    import Vue from 'vue'
    import VueAwesomeSwiper from 'vue-awesome-swiper/dist/ssr'
    
    Vue.use(VueAwesomeSwiper)
    

    在这里插入图片描述

    ②在nuxt.config.js文件中配置插件

    将plugins和css节点复制到module.exports节点下

    plugins: [
      { src: '~/plugins/nuxt-swiper-plugin.js', ssr: false }
    ],
    
    css: [
      'swiper/dist/css/swiper.css'
    ],
    

    在这里插入图片描述

    3.2复制静态资源

    1.删掉vs1010–>vue-front-1010–>assets下的README.md

    在这里插入图片描述

    2.静态资源在资料中已经提供

    在这里插入图片描述

    3.将上图中红框圈起来的静态资源复制粘贴到vs1010–>vue-front-1010–>assets中

    在这里插入图片描述

    3.3定义布局

    删掉layouts目录的default.vue页面中所有代码,然后将如下代码复制到default.vue页面中

    
    
    

    3.4定义首页面

    删掉pages目录的index.vue页面中所有代码,然后将如下代码复制到index.vue页面中

    
    
    
    

    3.5整合幻灯片

    1.在index.vue页面中将下述代码复制粘贴到图中所示位置

    
    

    在这里插入图片描述

    2.将index.vue页面中如下方框圈起来的部分删掉

    在这里插入图片描述

    3.将下述代码复制粘贴到刚刚删除的位置

    
    

    在这里插入图片描述

    4.将index.vue页面第14行的1525939573202.jpg 改为153525d0ef15459596.jpg

    在这里插入图片描述

    3.6看效果

    保存修改后在地址栏输入http://localhost:3000/看效果

    在这里插入图片描述

    4.路由

    4.1固定路由

    1.固定路由就是:路由是固定的,不发生变化

    2.比如:头信息中的首页、课程、名师、文章、问答
    在这里插入图片描述

    在这里插入图片描述

    通过to属性来设置路由跳转的地址

    3.以课程为例,做法是:在pages文件夹下创建course文件夹,然后在course文件夹下创建index.vue页面

    4.2动态路由

    1.动态路由就是:每次生成的路由地址不一样,比如课程详情

    2.如果我们需要根据id查询一条记录,就需要使用动态路由。NUXT的动态路由是以下划线开头的vue文件,参数名为下划线后边的文件名,如_id.vue

    5.整合页面

    5.1整合课程页面

    在pages文件夹下创建course文件夹,然后在course文件夹下创建index.vue页面并将下述代码复制过来

    
    
    

    在这里插入图片描述

    5.2整合课程详情页面

    在pages下的course目录下创建_id.vue页面,然后将下述代码复制过来

    
    
    
    

    在这里插入图片描述

    5.3整合名师页面

    在pages文件夹下创建teacher文件夹,然后在teacher文件夹下创建index.vue页面并将下述代码复制过来

    
    
    

    在这里插入图片描述

    5.4整合名师详情页面

    在pages下的teacher目录下创建_id.vue页面,然后将下述代码复制过来

    
    
    

    在这里插入图片描述

    6.首页banner显示(后端)

    6.1新建banner微服务

    6.1.1创建子子模块service_cms

    1.在service模块上右键选择New–>Module…

    在这里插入图片描述

    2.创建一个Maven项目

    在这里插入图片描述

    3.填写信息后点击"Finish"

    在这里插入图片描述

    6.1.2配置application.properties

    创建配置文件application.properties并编写配置

    # 服务端口
    server.port=8004
    # 服务名
    spring.application.name=service-cms
    
    # mysql数据库连接
    spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
    spring.datasource.url=jdbc:mysql://localhost:3306/guli?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8
    spring.datasource.username=root
    spring.datasource.password=root
    
    #返回json的全局时间格式
    spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
    spring.jackson.time-zone=GMT+8
    
    #配置mapper xml文件的路径
    mybatis-plus.mapper-locations=classpath:com/atguigu/educms/mapper/xml/*.xml
    
    #mybatis日志
    mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
    

    在这里插入图片描述

    • 注意:数据库的账号密码写自己的
    • 截图中第17行代码的作用:
      • "demo10-课程管理"的"1.9.2解决问题"的第3步说过:如果编写xml文件手动写sql语句,需要分别在properties和xml中进行配置
    6.1.3创建数据表crm_banner

    1.创建这张表的脚本在资料的guli_cms.sql文件中

    在这里插入图片描述

    在这里插入图片描述

    2.将创建crm_banner表的脚本复制到数据库中执行

    CREATE TABLE `crm_banner` (
      `id` char(19) NOT NULL DEFAULT '' COMMENT 'ID',
      `title` varchar(20) DEFAULT '' COMMENT '标题',
      `image_url` varchar(500) NOT NULL DEFAULT '' COMMENT '图片地址',
      `link_url` varchar(500) DEFAULT '' COMMENT '链接地址',
      `sort` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '排序',
      `is_deleted` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '逻辑删除 1(true)已删除, 0(false)未删除',
      `gmt_create` datetime NOT NULL COMMENT '创建时间',
      `gmt_modified` datetime NOT NULL COMMENT '更新时间',
      PRIMARY KEY (`id`),
      UNIQUE KEY `uk_name` (`title`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='首页banner表';
    

    在这里插入图片描述

    6.1.4向crm_banner表中插入数据

    1.插入数据的sql语句在guli_cms.sql文件也有

    在这里插入图片描述

    2.向crm_banner表中插入数据的脚本复制到数据库中执行

    INSERT INTO `crm_banner` VALUES ('1194556896025845762','test1','https://online-teach-file.oss-cn-beijing.aliyuncs.com/cms/2019/11/14/297acd3b-b592-4cfb-a446-a28310369675.jpg','/course',1,0,'2019-11-13 18:05:32','2019-11-18 10:28:22'),('1194607458461216769','test2','https://online-teach-file.oss-cn-beijing.aliyuncs.com/cms/2019/11/13/8f80790d-d736-4842-a6a4-4dcb0d684d4e.jpg','/teacher',2,0,'2019-11-13 21:26:27','2019-11-14 09:12:15');
    

    在这里插入图片描述

    6.1.5创建启动类

    先在java目录下创建包com.atguigu.educms,然后在educms包下创建启动类CmsApplication

    @SpringBootApplication
    @ComponentScan({"com.atguigu"}) //指定扫描位置
    public class CmsApplication {
        public static void main(String[] args) {
            SpringApplication.run(CmsApplication.class, args);
        }
    }
    
    6.1.6生成代码

    1.在service_cms模块的test包的java包下创建包codedemo

    在这里插入图片描述

    2.将service_edu模块的代码生成器CodeGenerator复制到上一步创建的codedemo包下

    在这里插入图片描述

    3.修改service_cms模块的代码生成器中的部分代码

    在这里插入图片描述

    4.在run方法上右键选择"Run ‘run()’"就可以生成代码了

    在这里插入图片描述

    5.给控制器CrmBannerController添加注解@CrossOrigin实现跨域,并且将请求路径中的crm-banner改为banner

    在这里插入图片描述

    6.因为service_cms模块要操作数据库,所以需要在启动类上添加注解@MapperScan("com.atguigu.educms.mapper")

    在这里插入图片描述

    6.2创建banner服务接口

    6.2.1分析

    1.后台管理员可以进行curd操作,前台用户只能看到数据,不能进行curd操作

    2.所以给控制器CrmBannerController改名为BannerAdminController,将请求路径/educms/banner改为/educms/banneradmin,是让后台管理员使用的

    在这里插入图片描述

    3.在控制层新建一个控制器BannerFrontController,是让前台用户使用的

    @RestController
    @RequestMapping("/educms/bannerfront")
    @CrossOrigin
    public class BannerFrontController {
    }
    

    在这里插入图片描述

    6.2.2后台管理接口

    在控制器BannerAdminController中编写代码

    @Autowired
    private CrmBannerService bannerService;
    
    //1.分页查询banner
    @GetMapping("pageBanner/{page}/{limit}")
    public R pageBanner(@PathVariable Long page, @PathVariable Long limit) {
        Page<CrmBanner> pageBanner = new Page<>(page, limit);
        bannerService.page(pageBanner,null);
        return R.ok().data("items", pageBanner.getRecords()).data("total", pageBanner.getTotal());
    }
    
    //2.添加banner
    @PostMapping("addBanner")
    public R addBanner(@RequestBody CrmBanner crmBanner) {
        bannerService.save(crmBanner);
        return R.ok();
    }
    
    //3.修改banner
    //①根据id查询
    @ApiOperation(value = "获取Banner")
    @GetMapping("get/{id}")
    public R get(@PathVariable String id) {
        CrmBanner banner = bannerService.getById(id);
        return R.ok().data("item", banner);
    }
    //②修改banner
    @ApiOperation(value = "修改Banner")
    @PutMapping("update")
    public R updateById(@RequestBody CrmBanner banner) {
        bannerService.updateById(banner);
        return R.ok();
    }
    
    @ApiOperation(value = "删除Banner")
    @DeleteMapping("remove/{id}")
    public R remove(@PathVariable String id) {
        bannerService.removeById(id);
        return R.ok();
    }
    

    在这里插入图片描述

    6.2.3前台系统接口

    1.在控制器BannerFrontController中编写代码来查询banner

    @Autowired
    private CrmBannerService bannerService;
    
    //根据banner的id进行降序排列,显示排列之后的前两条记录
    @GetMapping("getAllBanner")
    public R getAllBanner() {
        List<CrmBanner> list = bannerService.selectAllBanner();
        return R.ok().data("list", list);
    }
    

    在这里插入图片描述

    • 因为给用于展示时我们是将banner以轮播图的形式展现,所以查询banner时不需要分页查询,直接查询全部即可
    • 因为后面还要加redis,所以这里没有直接在控制层调用业务层的查询方法,而是我们自己在业务层写一个查询方法

    2.在业务层接口CrmBannerService中定义抽象方法

    //根据banner的id进行降序排列,显示排列之后的前两条记录
    List<CrmBanner> selectAllBanner();
    

    在这里插入图片描述

    3.在业务层实现类CrmBannerServiceImpl中实现上一步定义的抽象方法

    //根据id进行降序排列,显示排列之后的前两条记录
    @Override
    public List<CrmBanner> selectAllBanner() {
        QueryWrapper<CrmBanner> wrapper = new QueryWrapper<>();
        wrapper.orderByDesc("id");
        //使用last方法拼接sql语句
        wrapper.last("limit 2");
        
        List<CrmBanner> list = baseMapper.selectList(null);
        return list;
    }
    

    在这里插入图片描述

    7.首页热门课程和名师(后端)

    1.查询热门课程的sql语句:

    课程列表可以根据view_count(浏览数量)或id或gmt_create(创建时间)进行排序。我们这里根据id进行降序排列,显示排序后的前8条记录

    SELECT * FROM edu_course ORDER BY id DESC LIMIT 8
    

    2.查询名师的sql语句:

    我们这里也根据id进行降序排列,显示排序后的前4条记录

    SELECT * FROM edu_teacher ORDER BY id DESC LIMIT 4
    

    3.在service_edu模块的controller包下创建包front专一用来写前台部分,然后在front包下创建控制器IndexFrontController,并编写代码

    @RestController
    @RequestMapping("/eduservice/indexfront")
    @CrossOrigin
    public class IndexFrontController {
    
        @Autowired
        private EduCourseService courseService;
    
        @Autowired
        private EduTeacherService teacherService;
    
        //查询前8条热门课程,查询前4条名师
        @GetMapping("index")
        public R index() {
            //查询前8条热门课程
            QueryWrapper<EduCourse> wrapper = new QueryWrapper<>();
            wrapper.orderByDesc("id");
            wrapper.last("limit 8");
            List<EduCourse> eduList = courseService.list(wrapper);
    
            //查询前4条名师
            QueryWrapper<EduTeacher> wrapperTeacher = new QueryWrapper<>();
            wrapperTeacher.orderByDesc("id");
            wrapperTeacher.last("limit 4");
            List<EduTeacher> teacherList = teacherService.list(wrapperTeacher);
    
            return R.ok().data("eduList", eduList).data("teacherList", teacherList);
        }
    }
    

    在这里插入图片描述

    为什么不将上述代码写到service_cms模块?因为这些代码是需要查讲师表和课程表的,如果想在service_cms模块操作讲师表和课程表,就需要复制讲师和课程的entity、service、mapper、xml到service_cms模块,这样太麻烦了,所以我们在service_edu模块编写上述这些代码

    8.首页banner显示(前端)

    8.1准备工作(封装axios)

    1.我们后台的前端框架vue-admin是通过axios发送ajax请求,vue-admin框架本身就有axios组件,但是NUXT框架并没有axios组件,所以我们需要使用如下命令下载axios依赖(@0.19.2是为了和老师的版本一致)

    npm install axios@0.19.2
    

    在这里插入图片描述

    2.后台的前端框架vue-admin中已经给我们封装好了axios

    在这里插入图片描述

    3.但是NUXT框架没有给我们封装axios,所以我们需要自己封装:

    在vue-front-1010文件夹下创建文件夹utils,在utils文件夹下创建文件request.js并封装axios

    import axios from 'axios' //引入axios组件
    
    // 创建axios实例
    const service = axios.create({
        baseURL: 'http://localhost:9001', // api 的 base_url
        timeout: 20000 // 请求超时时间
    })
    export default service
    

    在这里插入图片描述

    8.2在api中定义方法

    在vue-front-1010文件夹下创建文件夹api,在api文件夹下创建banner.js文件,定义调用接口的路径

    import request from '@/utils/request'
    export default {
      //查询前两条banner数据
      getListBanner() {
        return request({
          url: `/educms/bannerfront/getAllBanner`,
          method: 'get'
        })
      }
    }
    

    在这里插入图片描述

    8.3调用api中的方法

    1.在pages文件夹下的index.vue文件中引入banner.js文件

    import banner from '@/api/banner'
    

    在这里插入图片描述

    2.定义数据模型bannerList用于存放查询到的两条banner数据(看清了嗷,这个数据模型可别写错位置了)

    在这里插入图片描述

    3.在pages文件夹下的index.vue文件中的export default {...}中添加如下代码以调用api中的方法

    created() {
      //调用查询得到两条banner数据的方法
      this.getBannerList()
    },
    methods: {
      //查询得到两条banner数据
      getBannerList() {
        banner.getListBanner()
          .then(response => {
            this.bannerList = response.data.data.list
          })
      }
    }
    

    在这里插入图片描述

    可能有朋友会问了,为什么前台使用NUXT框架获取后端返回数据需要通过response.data.data.list,而后台使用vue-admin框架获取后端返回数据是通过response.data.list?我们在前面也说过,vue-admin框架给我们做了封装

    在这里插入图片描述

    8.4将banner显示在首页

    1.将下图中方框圈起来的删掉

    在这里插入图片描述

    2.将下述代码复制到上一步删除的位置

    
    

    在这里插入图片描述

    • 截图中第7行使用v-for="banner in bannerList"遍历bannerList数组得到每一条banner数据

    • 截图中第7行的:key="banner.id":因为每次遍历得到的banner都不一样,所以需要用:key="banner.id"来做唯一标识

    • 因为我们实体类CrmBanner中定义的属性是linkUrl、imageUrl、title,所以截图中第8行用的是banner.linkUrl第9行用的是banner.imageUrlbanner.title

      在这里插入图片描述

    8.5测试

    1.在nginx中配置8004端口

    location ~ /educms/ {
    	proxy_pass http://localhost:8004;
    }
    

    在这里插入图片描述

    2.将service_cms服务注册到注册中心,这样做的原因在"demo12-课程管理"的"4.4问题",具体步骤在"demo12-课程管理"的"4.3服务注册(service_vod)",这里不再演示,自行配置吧

    3.重启后端、前端项目、nginx(别忘了nacos也要启动),测试效果如下

    在这里插入图片描述

    9.首页热门课程和名师(前端)

    9.1在api中定义方法

    在api目录下创建index.js文件,在index.js文件中调用后端接口

    import request from '@/utils/request'
    export default {
      //查询热门课程和名师
      getIndexData() {
        return request({
          url: `/eduservice/indexfront/index`,
          method: 'get'
        })
      }
    }
    

    在这里插入图片描述

    9.2调用api中的方法

    1.在index.vue页面引入上一步创建的index.js文件

    import index from '@/api/index'
    

    在这里插入图片描述

    2.定义数据模型eduList、teacherList分别用来封装热门课程、热门名师

    在这里插入图片描述

    3.在methods: {...}中定义方法来调用api中的方法

    //查询热门课程和名师
    getHotCourseTeacher() {
      index.getIndexData()
        .then(response => {
          this.eduList = response.data.data.eduList
          this.teacherList = response.data.data.teacherList
        })
    },
    

    在这里插入图片描述

    9.3初始化渲染

    在created方法中调用上一步定义的方法实现初始化渲染

    //调用查询热门课程和名师的方法
    this.getHotCourseTeacher()
    

    在这里插入图片描述

    9.4将热门课程和名师显示在首页

    1.将下图中红框圈起来的部分删掉

    在这里插入图片描述

    2.将下述代码复制到上一步删除的位置

  • {{course.title}}

    免费 9634人学习 | 9634评论
  • 在这里插入图片描述

    截图中第47行代码的v-if="Number(course.price) === 0":课程价格是0就显示免费,否则就不显示免费

    3.将下图中红框圈起来的部分删掉

    在这里插入图片描述

    4.将下述代码复制到上一步删除的位置

  • {{teacher.career}}

    {{teacher.intro}}

  • 在这里插入图片描述

    9.5测试

    1.保存修改,测试效果如下

    在这里插入图片描述

    2.如果课程封面尺寸不一样,可能会出现排版问题,所以我们在下图所示位置添加一行代码

    style="width:360px;height:185px"
    

    在这里插入图片描述

    10.Redis

    10.1引入redis

    • redis一般存什么样的数据?经常进行查询,不经常进行修改的数据
    • 一个网站的首页数据是被访问次数最多的,所以我们将首页数据放到redis缓存中

    10.2特点

    • 基于key-value进行存储(区别于MySQL的二维表格的形式存储)
    • 支持多种数据结构:string(字符串)、list(列表)、hash(哈希)、set(集合)、zset(有序集合)
      • zset就是有序的set集合
    • 持久化:通过内存进行存储,不过也可以存到硬盘里面去
      • 为什么要通过内存进行存储呢?存到内存中读取速度快,如果存到硬盘中读取时还要进行io操作,读取速度很慢
    • 支持过期时间、支持事务

    笔试中经常问到的一个没有意义的问题:Redis和Memcache有什么区别?(面试中如果问到了不知道怎么回答那就说没有用过Memcache)

    Redis和Memcache类似,但很大程度补偿了Memcache的不足。Redis和Memcache一样,Redis数据都是缓存在计算机内存中,不同的是,Memcache只能将数据缓存到内存中,无法自动定期写入硬盘,这就表示,一断电或重启,内存清空,数据丢失。所以Memcache的应用场景适用于缓存无需持久化的数据。而Redis不同的是它会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,实现数据的持久化

    10.3在common模块添加依赖

    由于redis缓存是公共应用,所以我们把依赖与配置添加到了common模块下面,现在在common模块的pom.xml中添加以下依赖

    我们在"demo03-后台讲师管理模块"的"4.2.2创建common模块"的第5步已经引入过依赖了,只不过我们当时是将common-pool2依赖注释掉了,现在打开即可(别忘了舒心maven)

    在这里插入图片描述

    在这里插入图片描述

    10.4在service_base模块添加redis配置类

    在service_base模块的servicebase包下创建配置类RedisConfig并编写配置

    package com.atguigu.servicebase;
    
    import com.fasterxml.jackson.annotation.JsonAutoDetect;
    import com.fasterxml.jackson.annotation.PropertyAccessor;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import org.springframework.cache.CacheManager;
    import org.springframework.cache.annotation.CachingConfigurerSupport;
    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.cache.RedisCacheManager;
    import org.springframework.data.redis.connection.RedisConnectionFactory;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
    import org.springframework.data.redis.serializer.RedisSerializationContext;
    import org.springframework.data.redis.serializer.RedisSerializer;
    import org.springframework.data.redis.serializer.StringRedisSerializer;
    import java.time.Duration;
    
    @EnableCaching //开启缓存
    @Configuration //配置类
    public class RedisConfig extends CachingConfigurerSupport {
        @Bean
        public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
            RedisTemplate<String, Object> template = new RedisTemplate<>();
            RedisSerializer<String> redisSerializer = new StringRedisSerializer();
            Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
            ObjectMapper om = new ObjectMapper();
            om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
            om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
            jackson2JsonRedisSerializer.setObjectMapper(om);
            template.setConnectionFactory(factory);
            //key序列化方式
            template.setKeySerializer(redisSerializer);
            //value序列化
            template.setValueSerializer(jackson2JsonRedisSerializer);
            //value hashmap序列化
            template.setHashValueSerializer(jackson2JsonRedisSerializer);
            return template;
        }
    
        @Bean
        public CacheManager cacheManager(RedisConnectionFactory factory) {
            RedisSerializer<String> redisSerializer = new StringRedisSerializer();
            Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
            //解决查询缓存转换异常的问题
            ObjectMapper om = new ObjectMapper();
            om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
            om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
            jackson2JsonRedisSerializer.setObjectMapper(om);
            // 配置序列化(解决乱码的问题),过期时间600秒
            RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                    .entryTtl(Duration.ofSeconds(600))
                    .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
                    .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
                    .disableCachingNullValues();
            RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
                    .cacheDefaults(config)
                    .build();
            return cacheManager;
        }
    }
    

    在这里插入图片描述

    截图中第54行的.entryTtl(Duration.ofSeconds(600):设置缓存中数据的过期时间,600秒后数据就没有了

    10.5在接口中添加redis缓存

    这里只演示改造service_cms模块首页banner接口。首页热门课程和名师后期自行比葫芦画瓢修改

    10.5.1引入service_base

    首先需要在service的pom.xml中引入service_base依赖

    我们已经在"demo03-后台讲师管理模块"的"4.2.5在service模块中引入service-base"引入过了

    在这里插入图片描述

    10.5.2添加注解

    1.@Cacheable注解:

    根据方法对其返回结果进行缓存,下次请求时,如果缓存存在,则直接读取缓存数据返回;如果缓存不存在,则执行方法,并把返回的结果存入缓存中。一般用在查询方法上

    属性值如下:

    属性/方法名解释
    value缓存名,必填,它指定了你的缓存存放在哪块命名空间
    cacheName与value差不多,二选一即可
    key可选属性,可以使用SpEL标签自定义缓存的key

    2.@CachePut注解:

    使用该注解标志的方法,每次都会执行,并将结果存入指定的缓存中。其他方法可以直接从响应的缓存中读取缓存数据,而不需要再去查询数据库。一般用在新增方法上

    属性值如下:

    属性/方法名解释
    value缓存名,必填,它指定了你的缓存存放在哪块命名空间
    cacheName与value差不多,二选一即可
    key可选属性,可以使用SpEL标签自定义缓存的key

    3.@CacheEvict注解:

    使用该注解标志的方法,会清空指定的缓存。一般用在更新或者删除方法上

    属性值如下:

    属性/方法名解释
    value缓存名,必填,它指定了你的缓存存放在哪块命名空间
    cacheName与value差不多,二选一即可
    key可选属性,可以使用SpEL标签自定义缓存的key
    allEntries是否清空所有缓存,默认为 false。如果指定为true,则方法调用后将立即清空所有的缓存
    beforeInvocation是否在方法执行前就清空,默认为 false。如果指定为true,则在方法执行前就会清空缓存

    4.我们这里使用上述注解中的@Cacheable注解

    在service_cms模块的业务层实现类CrmBannerServiceImpl的selectAllBanner方法上添加注解@Cacheable(value = "banner", key = "'selectIndexList'")(导包时导spring包中的Cacheable,别导错包了)

    在这里插入图片描述

    10.6在service_cms中配置redis地址

    在service_cms的配置文件application.properties中配置redis地址

    spring.redis.host=192.168.111.100
    spring.redis.port=6379
    spring.redis.database= 0
    spring.redis.timeout=1800000
    
    spring.redis.lettuce.pool.max-active=20
    spring.redis.lettuce.pool.max-wait=-1
    #最大阻塞等待时间(负数表示没限制)
    spring.redis.lettuce.pool.max-idle=5
    spring.redis.lettuce.pool.min-idle=0
    

    在这里插入图片描述

    截图中第25行写自己linux虚拟机的ip地址。如果我们是将redis安装在了真实的Windows系统中,那这里的ip就写127.0.0.1

    10.7测试

    1.先使用命令cd /usr/local/bin进入该目录,然后在该目录使用如下命令,目的是:在本地客户端(也就是linux虚拟机)连接linux虚拟机中的redis

    redis-cli
    

    在这里插入图片描述

    2.使用如下命令查看此时redis存的有什么

    keys *
    

    在这里插入图片描述

    3.重启后端项目,然后在地址栏访问http://localhost:3000,首页面已经展示出来了

    在这里插入图片描述

    4.再次使用命令keys *可以看到此时redis中存了数据

    在这里插入图片描述

    banner::selectIndexList这个键名的由来:

    看下图,@Cacheable注解的value属性值是banner,key属性值是selectIndexList。将banner和selectIndexList之间加两个冒号(::)就组成了存到redis中的键banner::selectIndexList

    在这里插入图片描述

    5.使用如下命令可以看到存的json数据

    get banner::selectIndexList
    

    在这里插入图片描述

    6.去控制台中可以看到执行了sql语句,说明查询了数据库

    在这里插入图片描述

    7.使用Ctrl+F5强制刷新首页面,首页仍正常显示,去控制台看此次并没有执行执行sql语句查询banner,但banner数据仍正常显示了,说明此时是从redis缓存中取得的数据

    8.首页热门课程和名师后期自行比葫芦画瓢修改

    但特别注意:

    ①获取banner的业务逻辑我们是写在了业务层,业务层的方法返回值是List集合,所以我们直接在业务层的selectAllBanner方法上加@Cacheable注解

    在这里插入图片描述

    ②但是我们获取首页热门课程和名师的业务逻辑是写在了控制层,控制层返回的是json数据,所以不能在控制器的index方法上加@Cacheable注解

    我们需要将控制层的index方法中的业务逻辑转移到业务层:在业务层写两个方法分别用来查询首页热门课程和首页名师,然后在这两个方法上都加上@Cacheable注解

    在这里插入图片描述

  • 相关阅读:
    C++友元函数声明顺序
    你真的了解IP地址吗?
    Web攻防02-MySQL注入概述&MySQL架构&注入获取数据
    C++ STL详解
    13、封装SqlSessionUtils工具类并测试功能
    JumpServer 打开RDP客户端出现由于在客户端检测到一个协议错误 错误代码 0x2104
    (附源码)springboot码头作业管理系统 毕业设计 341654
    MySQL——索引
    JavaScript:原生js实现轮播图
    Oracle数据库体系结构(四)_存储参数
  • 原文地址:https://blog.csdn.net/maxiangyu_/article/details/127029467