项目源码与所需资料
链接:https://pan.baidu.com/s/1azwRyyFwXz5elhQL0BhkCA?pwd=8z59
提取码:8z59
1.以前是客户端发送ajax请求给服务端,然后服务端返回数据给客户端。不利于网站进行SEO(老师解释了,我听不懂)
2.下面这种方式利于SEO:客户端请求服务端时,服务端的node.js会请求tomcat得到数据,得到数据后在node.js中把数据做封装,然后将数据一次性返回给客户端

和ajax的区别是:ajax方式是客户端请求服务端的tomcat;而nuxt方式是服务端的node.js请求服务端的tomcat,客户端只负责显示数据,请求过程是通过node.js实现的,请求数据是发生在服务端而不像ajax那样那样发生在客户端。NUXT是对node.js的封装,所以我们这里使用NUXT框架来搭建前台系统
1.在资料中提供了nuxt的压缩文件(已经下载好依赖了,直接用就行)

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

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

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

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

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

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


.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中的组件
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'
],

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

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

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

删掉layouts目录的default.vue页面中所有代码,然后将如下代码复制到default.vue页面中
删掉pages目录的index.vue页面中所有代码,然后将如下代码复制到index.vue页面中
热门课程
名师大咖
-
北京师范大学法学院副教授
北京师范大学法学院副教授、清华大学法学博士。自2004年至今已有9年的司法考试培训经验。长期从事司法考试辅导,深知命题规律,了解解题技巧。内容把握准确,授课重点明确,层次分明,调理清晰,将法条法理与案例有机融合,强调综合,深入浅出。
-
资深课程设计专家,专注10年AACTP美国培训协会认证导师
十年课程研发和培训咨询经验,曾任国企人力资源经理、大型外企培训经理,负责企业大学和培训体系搭建;曾任专业培训机构高级顾问、研发部总监,为包括广东移动、东莞移动、深圳移动、南方电网、工商银行、农业银行、民生银行、邮储银行、TCL集团、清华大学继续教育学院、中天路桥、广西扬翔股份等超过200家企业提供过培训与咨询服务,并担任近50个大型项目的总负责人。
-
上海师范大学法学院副教授
上海师范大学法学院副教授、清华大学法学博士。自2004年至今已有9年的司法考试培训经验。长期从事司法考试辅导,深知命题规律,了解解题技巧。内容把握准确,授课重点明确,层次分明,调理清晰,将法条法理与案例有机融合,强调综合,深入浅出。
-
考研政治辅导实战派专家,全国考研政治命题研究组核心成员。
法学博士,北京师范大学马克思主义学院副教授,专攻毛泽东思想概论、邓小平理论,长期从事考研辅导。出版著作两部,发表学术论文30余篇,主持国家社会科学基金项目和教育部重大课题子课题各一项,参与中央实施马克思主义理论研究和建设工程项目。
全部讲师
1.在index.vue页面中将下述代码复制粘贴到图中所示位置

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

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

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

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

1.固定路由就是:路由是固定的,不发生变化
2.比如:头信息中的首页、课程、名师、文章、问答


是通过to属性来设置路由跳转的地址
3.以课程为例,做法是:在pages文件夹下创建course文件夹,然后在course文件夹下创建index.vue页面
1.动态路由就是:每次生成的路由地址不一样,比如课程详情
2.如果我们需要根据id查询一条记录,就需要使用动态路由。NUXT的动态路由是以下划线开头的vue文件,参数名为下划线后边的文件名,如_id.vue
在pages文件夹下创建course文件夹,然后在course文件夹下创建index.vue页面并将下述代码复制过来
全部课程
没有相关数据,小编正在努力整理中...
-
-
-
-
-
-
-
-

在pages下的course目录下创建_id.vue页面,然后将下述代码复制过来
首页
\
课程列表
\
Java精品课程
课程介绍
Java的发展历史,可追溯到1990年。当时Sun Microsystem公司为了发展消费性电子产品而进行了一个名为Green的项目计划。该计划
负责人是James Gosling。起初他以C++来写一种内嵌式软件,可以放在烤面包机或PAD等小型电子消费设备里,使得机器更聪明,具有人工智
能。但他发现C++并不适合完成这类任务!因为C++常会有使系统失效的程序错误,尤其是内存管理,需要程序设计师记录并管理内存资源。这给设计师们造成
极大的负担,并可能产生许多bugs。
为了解决所遇到的问题,Gosling决定要发展一种新的语言,来解决C++的潜在性危险问题,这个语言名叫Oak。Oak是一种可移植性语言,也就是一种平台独立语言,能够在各种芯片上运行。
1994年,Oak技术日趋成熟,这时网络正开始蓬勃发展。Oak研发小组发现Oak很适合作为一种网络程序语言。因此发展了一个能与Oak配合的浏
览器--WebRunner,后更名为HotJava,它证明了Oak是一种能在网络上发展的程序语言。由于Oak商标已被注册,工程师们便想到以自己常
享用的咖啡(Java)来重新命名,并于Sun World 95中被发表出来。
课程大纲

在pages文件夹下创建teacher文件夹,然后在teacher文件夹下创建index.vue页面并将下述代码复制过来
全部讲师
全部
没有相关数据,小编正在努力整理中...
-
北京师范大学法学院副教授、清华大学法学博士。自2004年至今已有9年的司法考试培训经验。长期从事司法考试辅导,深知命题规律,了解解题技巧。内容把握准确,授课重点明确,层次分明,调理清晰,将法条法理与案例有机融合,强调综合,深入浅出。
北京师范大学法学院副教授
-
十年课程研发和培训咨询经验,曾任国企人力资源经理、大型外企培训经理,负责企业大学和培训体系搭建;曾任专业培训机构高级顾问、研发部总监,为包括广东移动、东莞移动、深圳移动、南方电网、工商银行、农业银行、民生银行、邮储银行、TCL集团、清华大学继续教育学院、中天路桥、广西扬翔股份等超过200家企业提供过培训与咨询服务,并担任近50个大型项目的总负责人。
资深课程设计专家,专注10年AACTP美国培训协会认证导师
-
上海师范大学法学院副教授、清华大学法学博士。自2004年至今已有9年的司法考试培训经验。长期从事司法考试辅导,深知命题规律,了解解题技巧。内容把握准确,授课重点明确,层次分明,调理清晰,将法条法理与案例有机融合,强调综合,深入浅出。
上海师范大学法学院副教授
-
法学博士,北京师范大学马克思主义学院副教授,专攻毛泽东思想概论、邓小平理论,长期从事考研辅导。出版著作两部,发表学术论文30余篇,主持国家社会科学基金项目和教育部重大课题子课题各一项,参与中央实施马克思主义理论研究和建设工程项目。
考研政治辅导实战派专家,全国考研政治命题研究组核心成员。
-
具备深厚的数学思维功底、丰富的小学教育经验,授课风格生动活泼,擅长用形象生动的比喻帮助理解、简单易懂的语言讲解难题,深受学生喜欢
毕业于师范大学数学系,热爱教育事业,执教数学思维6年有余
-
中国科学院数学与系统科学研究院应用数学专业博士,研究方向为数字图像处理,中国工业与应用数学学会会员。参与全国教育科学“十五”规划重点课题“信息化进程中的教育技术发展研究”的子课题“基与课程改革的资源开发与应用”,以及全国“十五”科研规划全国重点项目“掌上型信息技术产品在教学中的运用和开发研究”的子课题“用技术学数学”。
中国人民大学附属中学数学一级教师
-
中教一级职称。讲课极具亲和力。
毕业于北京大学数学系
-
政治学博士、管理学博士后,北京师范大学马克思主义学院副教授。多年来总结出了一套行之有效的应试技巧与答题方法,针对性和实用性极强,能帮助考生在轻松中应考,在激励的竞争中取得高分,脱颖而出。
长期从事考研政治课讲授和考研命题趋势与应试对策研究。考研辅导新锐派的代表。

在pages下的teacher目录下创建_id.vue页面,然后将下述代码复制过来
讲师介绍
姚晨 高级讲师
北京师范大学法学院副教授
北京师范大学法学院副教授、清华大学法学博士。自2004年至今已有9年的司法考试培训经验。长期从事司法考试辅导,深知命题规律,了解解题技巧。内容把握准确,授课重点明确,层次分明,调理清晰,将法条法理与案例有机融合,强调综合,深入浅出。

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

2.创建一个Maven项目

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

创建配置文件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

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表';

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');

先在java目录下创建包com.atguigu.educms,然后在educms包下创建启动类CmsApplication
@SpringBootApplication
@ComponentScan({"com.atguigu"}) //指定扫描位置
public class CmsApplication {
public static void main(String[] args) {
SpringApplication.run(CmsApplication.class, args);
}
}
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")

1.后台管理员可以进行curd操作,前台用户只能看到数据,不能进行curd操作
2.所以给控制器CrmBannerController改名为BannerAdminController,将请求路径/educms/banner改为/educms/banneradmin,是让后台管理员使用的

3.在控制层新建一个控制器BannerFrontController,是让前台用户使用的
@RestController
@RequestMapping("/educms/bannerfront")
@CrossOrigin
public class BannerFrontController {
}

在控制器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();
}

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);
}

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;
}

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模块编写上述这些代码
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

在vue-front-1010文件夹下创建文件夹api,在api文件夹下创建banner.js文件,定义调用接口的路径
import request from '@/utils/request'
export default {
//查询前两条banner数据
getListBanner() {
return request({
url: `/educms/bannerfront/getAllBanner`,
method: 'get'
})
}
}

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框架给我们做了封装

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.imageUrl、banner.title

1.在nginx中配置8004端口
location ~ /educms/ {
proxy_pass http://localhost:8004;
}

2.将service_cms服务注册到注册中心,这样做的原因在"demo12-课程管理"的"4.4问题",具体步骤在"demo12-课程管理"的"4.3服务注册(service_vod)",这里不再演示,自行配置吧
3.重启后端、前端项目、nginx(别忘了nacos也要启动),测试效果如下

在api目录下创建index.js文件,在index.js文件中调用后端接口
import request from '@/utils/request'
export default {
//查询热门课程和名师
getIndexData() {
return request({
url: `/eduservice/indexfront/index`,
method: 'get'
})
}
}

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
})
},

在created方法中调用上一步定义的方法实现初始化渲染
//调用查询热门课程和名师的方法
this.getHotCourseTeacher()

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

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

截图中第47行代码的v-if="Number(course.price) === 0":课程价格是0就显示免费,否则就不显示免费
3.将下图中红框圈起来的部分删掉

4.将下述代码复制到上一步删除的位置
-
{{teacher.career}}
{{teacher.intro}}

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

2.如果课程封面尺寸不一样,可能会出现排版问题,所以我们在下图所示位置添加一行代码
style="width:360px;height:185px"

笔试中经常问到的一个没有意义的问题:Redis和Memcache有什么区别?(面试中如果问到了不知道怎么回答那就说没有用过Memcache)
Redis和Memcache类似,但很大程度补偿了Memcache的不足。Redis和Memcache一样,Redis数据都是缓存在计算机内存中,不同的是,Memcache只能将数据缓存到内存中,无法自动定期写入硬盘,这就表示,一断电或重启,内存清空,数据丢失。所以Memcache的应用场景适用于缓存无需持久化的数据。而Redis不同的是它会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,实现数据的持久化
由于redis缓存是公共应用,所以我们把依赖与配置添加到了common模块下面,现在在common模块的pom.xml中添加以下依赖
我们在"demo03-后台讲师管理模块"的"4.2.2创建common模块"的第5步已经引入过依赖了,只不过我们当时是将common-pool2依赖注释掉了,现在打开即可(别忘了舒心maven)


在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秒后数据就没有了
这里只演示改造service_cms模块首页banner接口。首页热门课程和名师后期自行比葫芦画瓢修改
首先需要在service的pom.xml中引入service_base依赖
我们已经在"demo03-后台讲师管理模块"的"4.2.5在service模块中引入service-base"引入过了

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,别导错包了)

在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
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注解
