第01章-nacos和gateway的引入
1、引入nacos
1.1、启动nacos服务
资料:资料>数据字典微服务>nacos-server-1.4.2.zip
将资料中的nacos压缩包解压到非中文目录下,然后执行以下命令,单机启动nacos
startup.cmd -m standalone
访问:http://localhost:8848/nacos
用户名密码:nacos/nacos
1.2、引入依赖
service的pom.xml中配置Nacos客户端依赖
<dependencies>
<!--服务注册-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
</dependencies>
1.3、添加服务配置
配置application-dev.yml,在每个微服务模块中添加注册Nacos服务的配置信息
spring:
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
1.4、启动微服务
启动微服务,并查看nacos的服务列表
2、引入微服务网关
2.1、创建微服务网关
2.2、pom.xml引入依赖
在server-gateway中引入依赖
<dependencies>
<!-- 网关 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- 服务注册 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
</dependencies>
2.3、创建启动类
package com.atguigu.syt.gateway;
@SpringBootApplication
public class ServerGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(ServerGatewayApplication.class, args);
}
}
2.4、配置路由转发
在server-gateway模块中resources目录下创建文件
application.yml
:
spring:
application:
name: server-gateway
profiles:
active: dev
application-dev.yml
:
server:
port: 8200
spring:
cloud:
gateway:
discovery:
locator:
enabled: true
routes:
- id: service-system
predicates: Path=/*/system/**
uri: lb://service-system
- id: service-hosp
predicates: Path=/*/hosp/**
uri: lb://service-hosp
- id: service-cmn
predicates: Path=/*/cmn/**
uri: lb://service-cmn
nacos:
discovery:
server-addr: 127.0.0.1:8848
3、配置前端环境
3.1、修改代理配置
更改文件:vue.config.js
,将target改到网关地址
proxy: {
'/dev-api': { // 匹配所有以 '/dev-api'开头的请求路径
target: 'http://localhost:8200',
changeOrigin: true, // 支持跨域
pathRewrite: { // 重写路径: 去掉路径中开头的'/dev-api'
'^/dev-api': ''
}
}
}
3.2、重启程序
重启后端程序测试
第02章-数据字典列表
页面预览
1、后端接口
1.1、创建数据库
资料:资料>数据字典微服务>guigu_syt_cmn.sql
1.2、创建service-cmn微服务
1.3、添加依赖
在service-cmn中添加依赖:
<dependencies>
<!--实体-->
<dependency>
<groupId>com.atguigu</groupId>
<artifactId>model</artifactId>
<version>1.0</version>
</dependency>
<!--服务通用配置-->
<dependency>
<groupId>com.atguigu</groupId>
<artifactId>service-util</artifactId>
<version>1.0</version>
</dependency>
<!--自定义安全模块-->
<dependency>
<groupId>com.atguigu</groupId>
<artifactId>spring-security</artifactId>
<version>1.0</version>
</dependency>
<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- 单元测试 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
1.4、使用代码生成器
找到service-util模块中的代码生成器,修改moduleName为cmn
,并执行,然后删除entity包,相关类中引入model模块中的类
1.5、创建配置文件
在server-cmn模块中resources目录下创建文件
application.yml
:
spring:
application:
name: service-cmn
profiles:
active: dev,redis
application-dev.yml
:
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
mapper-locations: classpath:com/atguigu/syt/cmn/mapper/xml/*.xml
server:
port: 8202
spring:
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
password: 123456
url: jdbc:mysql://localhost:3306/guigu_syt_cmn?characterEncoding=utf-8&serverTimezone=GMT%2B8&useSSL=false
username: root
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
logging:
level:
root: info
file:
path: cmn
1.6、创建启动类
package com.atguigu.syt.cmn;
@SpringBootApplication
@ComponentScan(basePackages = {"com.atguigu"})
public class ServiceCmnApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceCmnApplication.class, args);
}
}
1.7、Controller
package com.atguigu.syt.cmn.controller.admin;
@Api(tags = "数据字典")
@RestController
@RequestMapping("/admin/cmn/dict")
public class AdminDictController {
@Resource
private DictService dictService;
@ApiOperation(value = "获取全部数据字典")
@GetMapping(value = "/findAllDictList")
public Result<List<DictTypeVo>> findAllDictList() {
List<DictTypeVo> list = dictService.findAllDictList();
return Result.ok(list);
}
}
1.8、Service
接口:DictService
/**
* 获取数据字典嵌套列表
* @return
*/
List<DictTypeVo> findAllDictList();
实现:DictServiceImpl
@Resource
private DictTypeService dictTypeService;
@Override
public List<DictTypeVo> findAllDictList() {
//获取数据字典类型列表
List<DictType> dictTypeList = dictTypeService.list();
//获取数据字典列表
List<Dict> dictList = this.list();
List<DictTypeVo> dictTypeVoList = new ArrayList<>();
//遍历数据字典类型列表
dictTypeList.forEach(dictType -> {
DictTypeVo dictTypeVo = new DictTypeVo();
dictTypeVo.setId("parent-" +dictType.getId());
dictTypeVo.setName(dictType.getName());
List<Dict> subDictList =
dictList.stream().filter(dict -> dict.getDictTypeId().longValue() == dictType.getId().longValue()).collect(Collectors.toList());
List<DictVo> children = new ArrayList<>();
subDictList.forEach(dict -> {
DictVo dictVo = new DictVo();
dictVo.setId("children-" + dict.getId());
dictVo.setName(dict.getName());
dictVo.setValue(dict.getValue());
children.add(dictVo);
});
dictTypeVo.setChildren(children);
dictTypeVoList.add(dictTypeVo);
});
return dictTypeVoList;
}
2、前端页面
2.1、创建vue组件
在src/views/syt文件夹下创建cmn/dict/list.vue文件
<template>
<div class="app-container">
数据字典列表
</div>
</template>
2.2、添加路由
静态路由:修改router/index.js文件
{
path: '/cmn',
component: Layout,
redirect: '/cmn/dict/list',
name: 'BaseInfo',
meta: { title: '基础数据', icon: 'el-icon-s-help' },
alwaysShow: true,
children: [
{
path: 'dict/list',
name: '数据字典',
component: () => import('@/views/syt/cmn/dict/list'),
meta: { title: '数据字典', icon: 'el-icon-s-unfold' }
},
]
},
动态路由:
添加菜单:
添加菜单项:
2.3、定义api接口
创建文件 src/api/syt/dict.js
import request from '@/utils/request'
const apiName = '/admin/cmn/dict'
export default {
findAllDictList() {
return request({
url: `${apiName}/findAllDictList`,
method: 'get'
})
}
}
2.4、list.vue页面
cmn/dict/list.vue
<template>
<div class="app-container">
<!-- 数据字典列表 -->
<el-table
v-loading="listLoading"
:data="list"
element-loading-text="数据加载中"
row-key="id"
border
>
<el-table-column label="名称" prop="name" />
<el-table-column label="ID" prop="id" />
<el-table-column label="值" prop="value" />
</el-table>
</div>
</template>
<script>
import dictApi from '@/api/syt/dict'
export default {
// 定义数据
data() {
return {
listLoading: true, // 是否显示loading信息
list: [],
}
},
// 当页面加载时获取数据
created() {
this.fetchData()
},
methods: {
// 调用api层获取数据库中的数据
fetchData() {
dictApi.findAllDictList().then((response) => {
this.list = response.data
this.listLoading = false
})
},
},
}
</script>
第03章-地区列表
页面预览
1、后端接口
1.1、Controller
package com.atguigu.syt.cmn.controller.admin;
@Api(tags = "地区")
@RestController
@RequestMapping("/admin/cmn/region")
public class AdminRegionController {
@Resource
private RegionService regionService;
@ApiOperation(value = "根据上级code获取子节点数据列表")
@ApiImplicitParam(name = "parentCode", value = "上级节点code", required = true)
@GetMapping(value = "/findRegionListByParentCode/{parentCode}")
public Result<List<Region>> findRegionListByParentCode(
@PathVariable String parentCode) {
List<Region> list = regionService.findRegionListByParentCode(parentCode);
return Result.ok(list);
}
}
1.2、Service
接口:RegionService
/**
* 根据上级地区code获取地区列表
* @param parentCode
* @return
*/
List<Region> findRegionListByParentCode(String parentCode);
实现:RegionServiceImpl
@Override
public List<Region> findRegionListByParentCode(String parentCode) {
LambdaQueryWrapper<Region> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(Region::getParentCode, parentCode);
List<Region> regionList = baseMapper.selectList(queryWrapper);
regionList.forEach(region -> {
boolean isHasChildren = region.getLevel().intValue() < 3 ? true : false;
region.setHasChildren(isHasChildren);
});
return regionList;
}
2、前端页面
2.1、创建vue组件
在src/views/syt文件夹下创建cmn/region/list.vue文件
<template>
<div class="app-container">
地区列表
</div>
</template>
2.2、添加路由
静态路由:修改router/index.js文件,在基础数据
下添加子节点
{
path: 'region/list',
name: '地区管理',
component: () => import('@/views/syt/cmn/region/list'),
meta: { title: '地区管理', icon: 'table' }
}
动态路由:
添加菜单项目:
2.3、定义api接口
创建文件 src/api/syt/region.js
import request from '@/utils/request'
const apiName = '/admin/cmn/region'
export default {
findRegionListByParentCode(parentCode) {
return request({
url: `${apiName}/findRegionListByParentCode/${parentCode}`,
method: 'get'
})
}
}
2.4、list.vue页面
cmn/region/list.vue
<template>
<div class="app-container">
<el-table
v-loading="listLoading"
:data="list"
element-loading-text="数据加载中"
:load="getChildren"
:tree-props="{children: 'children', hasChildren: 'hasChildren'}"
style="width: 100%;margin-top: 10px;"
row-key="id"
border
lazy>
<el-table-column label="名称" align="left">
<template slot-scope="scope">
<span>{{ scope.row.name }}</span>
</template>
</el-table-column>
<el-table-column label="地区编码" >
<template slot-scope="{row}">
{{ row.code }}
</template>
</el-table-column>
<el-table-column label="地区级别" align="left">
<template slot-scope="scope">
<span>{{ scope.row.level }}</span>
</template>
</el-table-column>
</el-table>
</div>
</template>
<script>
import regionApi from '@/api/syt/region'
export default {
// 定义数据
data() {
return {
listLoading: true,
list: [],
}
},
// 当页面加载时获取数据
created() {
this.fetchData()
},
methods: {
// 调用api层获取数据库中的数据
fetchData() {
console.log('加载列表')
regionApi.findRegionListByParentCode('0').then(response => {
this.list = response.data
console.log(this.list)
this.listLoading = false
})
},
getChildren(tree, treeNode, resolve) {
debugger
regionApi.findRegionListByParentCode(tree.code).then(response => {
resolve(response.data)
})
},
}
}
</script>
第04章-Redis
可以将地区列表的数据放入redis缓存,帮助提高页面的加载速度
1、使用缓存
1.1、RegionServiceImpl
@Resource
private RedisTemplate redisTemplate;
@Override
public List<Region> findRegionListByParentCode(String parentCode) {
//先查询redis中是否存在dictList
List<Region> regionList = null;
try {
regionList = (List<Region>)redisTemplate.opsForValue().get("regionList:" + parentCode);
if(regionList != null){
return regionList;
}
} catch (Exception e) {
log.error("redis服务器异常:get regionList");
}
LambdaQueryWrapper<Region> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(Region::getParentCode, parentCode);
regionList = baseMapper.selectList(queryWrapper);
regionList.forEach(region -> {
boolean isHasChildren = region.getLevel().intValue() < 3 ? true : false;
region.setHasChildren(isHasChildren);
});
try {
//将数据存入redis
redisTemplate.opsForValue().set("regionList:" + parentCode, regionList, 5, TimeUnit.MINUTES);
} catch (Exception e) {
log.error("redis服务异常:set regionList");
}
return regionList;
}
1.2、测试
测试获取数据:
第一次查询从MySQL数据库获取,第二次查询从Redis获取
2、Spring Cache
2.1、简介
1、Spring Cache是Spring提供的一个缓存框架。
2、Spring Cache利用了AOP,实现了基于注解的缓存功能,业务代码不用关心底层是使用了什么缓存框架,只需要简单地加一个注解,就能实现缓存功能,从而减少对代码的入侵。
3、由于市面上的缓存工具实在太多,SpringCache框架还提供了CacheManager接口,可以降低对各种缓存框架的耦合。
2.2、开启缓存
在service-util模块的RedisConfig类上添加注解
@EnableCaching
2.3、JSON序列化方案
RedisConfig
@Bean
public CacheManager cacheManager(LettuceConnectionFactory connectionFactory) {
//定义序列化器
GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
//过期时间600秒
.entryTtl(Duration.ofSeconds(600))
// 配置序列化
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(stringRedisSerializer))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(genericJackson2JsonRedisSerializer));
RedisCacheManager cacheManager = RedisCacheManager.builder(connectionFactory)
.cacheDefaults(config)
.build();
return cacheManager;
}
3、Spring Cache 常用注解
3.1、@Cacheable
作用:
在方法执行前查看是否有缓存对应的数据,如果有直接返回数据,如果没有,则调用方法获取数据返回,并缓存起来。
RegionServiceImpl
/**
* @Cacheable:
* 在方法执行前查看是否有缓存对应的数据,
* 如果有直接返回数据,如果没有,则调用方法获取数据返回,并缓存起来。
*
* value:缓存的名字
* key:缓存的key
* unless:条件符合则不缓存,是对出参进行判断,出参用#result表示
*
* @param parentCode
* @return
*/
@Override
@Cacheable(value = "regionList", key = "#parentCode", unless="#result.size() == 0")
public List<Region> findRegionListByParentCode(String parentCode) {
//先查询redis中是否存在dictList
LambdaQueryWrapper<Region> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(Region::getParentCode, parentCode);
List<Region> regionList = baseMapper.selectList(queryWrapper);
regionList.forEach(region -> {
boolean isHasChildren = region.getLevel().intValue() < 3 ? true : false;
region.setHasChildren(isHasChildren);
});
return regionList;
}
3.2、@CachePut
作用:
将方法的返回值放到缓存中
接口:RegionService
/**
* 测试 @CachePut
* @param region
* @return
*/
Region saveRegionWithCacheManager(Region region);
实现:RegionServiceImpl
/**
* 作用:将方法的返回值放到缓存中
* @param region
* @return
*/
@Override
@CachePut(value = "regionTest", key = "#region.id")
public Region saveRegionWithCacheManager(Region region) {
baseMapper.insert(region);
return region;
}
测试:在service-cmn的test目录中创建测试用例RedisTest
package com.atguigu.syt.cmn.redis;
@SpringBootTest
public class RedisTest {
@Resource
private RegionService regionService;
@Test
public void testSaveRegionWithCacheManager() {
Region region = new Region();
region.setName("test");
regionService.saveRegionWithCacheManager(region);
}
}
3.3、@CacheEvict
作用:
执行方法体,删除缓存
接口:RegionService
/**
* 测试 @CacheEvict
* @param id
*/
void deleteRegionWithCacheManager(Long id);
实现:RegionServiceImpl
/**
* 作用:执行方法体,删除缓存
* @param id
*/
@Override
@CacheEvict(value = "regionTest", key = "#id")
public void deleteRegionWithCacheManager(Long id) {
baseMapper.deleteById(id);
}
测试:RedisTest
@Test
public void testDeleteRegionWithCacheManager() {
regionService.deleteRegionWithCacheManager(3712L);//刚刚插入的测试数据的id
}
源码:https://gitee.com/dengyaojava/guigu-syt-parent
标签:cmn,缓存,尚医通,day03,region,list,源码,public,regionList From: https://www.cnblogs.com/deyo/p/17475898.html