Entity:实体,通常与数据库表对应 DTO:数据传输对象,前端后端传输数据,封装成一个DTO,通常用于程序各层之间传输数据 VO:视图对象,为前端展示数据提供对象 POJO:衍生出他们三个
问题:前端请求路径和后端所需不一致 nginx反向代理:将前端发送的动态请求由nginx转发到后端服务器 反向代理好处: 提高访问速度:nginx提供缓存,如果请求同一个地址,就在nginx处返回 进行负载均衡:将大量的请求按照指定方式,均衡的分配给集群中的服务器 保证后端服务安全:前端只能通过nginx反向代理请求后端服务 nginx反向代理配置方式:nginx.conf文件 eg:监听80端口,匹配/api/ ,拼接url
nginx负载均衡的配置:
Swagger Knife4j是Java MVC框架集成Swagger生成Api文档的增强解决方案 引入依赖:knife4j-spring-boot-starter
com.github.xiaoymin
knife4j-spring-boot-starter
${knife4j}
配置类中配置信息
/**
* 通过knife4j生成接口文档
* @return
*/
@Bean
public Docket docket() {
ApiInfo apiInfo = new ApiInfoBuilder()
.title("苍穹外卖项目接口文档")
.version("2.0")
.description("苍穹外卖项目接口文档")
.build();
Docket docket = new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo)
.select()
.apis(RequestHandlerSelectors.basePackage("com.sky.controller"))
.paths(PathSelectors.any())
.build();
return docket;
}
/**
* 设置静态资源映射,主要是为了访问doc.html
* @param registry
*/
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/doc.html").addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
}
常用注解: @Api 类,controller,表示对类的说明 @Api(tags="员工相关接口") @ApiModel 类,pojo @ApiModelProperty 属性,描述属性信息 @ApiOperation 方法,说明方法用途 @Api(value="员工登录") 员工管理、分类管理: 当前端提交的数据和实体类中对应的是属性差别比较大时,建议使用DTO来封装数据 属性复制赋值: BeanUtils.copyProperties(employeeDTO,employee); ThreadLocal并不是一个Thread,而是Thread的局部变量 ThreadLocal为每个线程提供单独一份存储空间,具有线程隔离的效果,只有在线程内才能获取到对应的值 每次http请求都是一次线程 常用方法: set(T value) 在当前线程存值 T get() 取值 remove() 移除 日期格式化: 方式一:在pojo属性加入注解@JsonFormat(pattern="yyy-MM-dd HH:mm:ss) 方式二:在webMVCConfiguration中扩展Spring MVC的消息转换器,统一对日期类型进行格式化处理
/**
* 扩展SpringMVC MVC消息展示器
* 作用:后端转给前端的数据进行统一的处理,比如日期格式化
* 项目启动的时候自动加载
* @param converters
*/
@Override
protected void extendMessageConverters(List> converters) {
log.info("扩展消息展示器");
//创建消息转换器对象
final MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
//需要为消息转换器设置一个转换器对象,对象转换器可以将java对象序列化为json数据
converter.setObjectMapper(new JacksonObjectMapper());
//将自己的消息转换器加入到集合中
converters.add(0, converter);
}
公共字段自动填充 比如create_time create_user update_time update_user 1.自定义注解,用于标识 2.Aop
/**
* 自定义切面,实现公共字段自动填充
*/
@Slf4j
public class AutoFillAspect {
//切入点
@Pointcut("execution(* com.sky.mapper.*.*(..)) && @annotation(com.sky.annotation.AutoFill)")
public void autoFillPointCut() {
}
//前置通知,在通知中为公共字段自动填充
@Before("autoFillPointCut()")
public void autoFill(JoinPoint joinPoint) {
log.info("为公共字段自动填充");
//获取到当前被拦截的方法上的数据库操作类型,也就是update/insert
MethodSignature signature = (MethodSignature) joinPoint.getSignature();//获取方法签名对象
final AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class);//获取方法上的注解对象
final OperationType operationType = autoFill.value();//获取数据库操作类型
//获取当前被拦截的方法的参数--实体对象
final Object[] args = joinPoint.getArgs();
if (args == null || args.length == 0) {
return;
}
final Object entity = args[0];
//准备填充
final LocalDateTime now = LocalDateTime.now();
final Long currentId = BaseContext.getCurrentId();
//根据当前不同的操作类型,为当前对应的属性通过反射赋值
if (Objects.equals(operationType, OperationType.INSERT)) {
try {
final Method setCreateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_TIME, LocalDateTime.class);
final Method setCreateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_USER, Long.class);
final Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
final Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);
setCreateTime.invoke(entity, now);
setCreateUser.invoke(entity, currentId);
setUpdateTime.invoke(entity, now);
setUpdateUser.invoke(entity, currentId);
} catch (Exception e) {
e.printStackTrace();
}
} else if (Objects.equals(operationType, OperationType.UPDATE)) {
try {
final Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
final Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);
setUpdateTime.invoke(entity, now);
setUpdateUser.invoke(entity, currentId);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
}
Redis Redis入门 简介: 基于 内存 的key-value结构数据库 基于内存存储,读写性能高 适合存储热点数据(热点商品、咨询等,特定时间点有大量用户访问) 启动:redis-server redis.windows.conf 客户端连接:(-h host -p port 可省略,没有用户的概念)redis-cli.exe -h localhost -p 6379 -a password 密码配置:requirepass 123456 Redis数据类型 Redis存储的是key-value结构的数据,其中key是字符串类型,value有5种常见数据类型: 字符串:string 哈希:hash (field1 value1),也叫散列,类似于Java中HashMap 列表:list 类似LinkedList 按照插入排序排序,可以有重复元素 集合:set 类似HashSet,无序集合,没有重复元素 有序集合:sorted set / zset 集合中每个元素关联一个分数score,根据分数升序排序,没有重复元素
常用命令(不区分大小写) 字符串操作命令: set key value 设置指定key的值,相当于插入 get key 查询 setex key seconds value 设置指定key的值,并将key的过期时间设置为seconds秒 setnx key value 只有在key不存在时设置key的值 哈希:redis hash是一个string类型的field和value的映射 表 ,hash特别适合存储对象,常用命令: hset key field value 将哈希表key中的字段field的值设为value hget key field 获取存储在哈希表中指定字段的值 hdel key field 删除存储在哈希表中指定字段的值 hkeys key 获取哈希表中所有字段 hvals key 获取哈希表中所有值 列表:redis列表是简单的字符串列表,按照插入顺序排序 (l代表的是left,而不是list) lpush key value1[value2] 将一个或多个值插入到列表(队列)头部 lrange key start stop 获取指定范围内的元素 lrange key 0 -1 获取所有元素 rpop key 移除并获取列表最后一个元素(最右侧,最先插入的) llen key 获取列表长度 集合:string类型无序集合,集合成员唯一 sadd key member1[member2] 向集合添加一个或多个成员 smembers key 返回集合中所有成员 scard key 返回集合的成员数量 sinter key1[key2] 返回给定所有集合的交集 sunion key1[key2] 返回所有给定集合的并集 srem key member1[member2] 删除集合中的成员 有序集合:string类型元素集合,不允许重复。每个元素会关联一个double类型的分数 zadd key score1 member1[score2 member2] 添加 zrange key start stop [withscores] 通过索引区间返回,加上withscores会返回分数 zincrby key increment member 有序集合中对指定成员分数加上增量increment zrem key member [member1...] 移除 通用命令: keys pattern 查找给定模式(pattern)的key 比如: keys * 返回所有key exists key 检查key是否存在 type key 返回key所储存的值的类型 del key 删除
Java中操作Redis Redis的Java客户端: Jedis Lettuce Spring Data Redis:是Spring的一部分,对Redis进行封装 操作步骤: 1.导入spring-boot-starter-data-redis 2.配置Redis数据源 port host password [database] 3.编写配置类,创建RedisTemplate对象 4.通过RedisTemplate对象操作Redis
@Configuration
@Slf4j
public class RedisConfiguration {
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {
log.info("开始创建redis模板对象");
final RedisTemplate redisTemplate = new RedisTemplate();
//设置redis的连接工厂对象
redisTemplate.setConnectionFactory(redisConnectionFactory);
//设置redis key的序列化器
redisTemplate.setKeySerializer(new StringRedisSerializer());
return redisTemplate;
}
}
@Autowired
private RedisTemplate redisTemplate;
@Test
public void testRedisTemplate() {
System.out.println(redisTemplate);
ValueOperations valueOperations = redisTemplate.opsForValue();
HashOperations hashOperations = redisTemplate.opsForHash();
ListOperations listOperations = redisTemplate.opsForList();
SetOperations setOperations = redisTemplate.opsForSet();
ZSetOperations zSetOperations = redisTemplate.opsForZSet();
}
@Test
public void testString() {
final ValueOperations valueOperations = redisTemplate.opsForValue();
//set get setex setnx
valueOperations.set("name", "jack");
final String name = (String) valueOperations.get("name");
//指定时间销毁
valueOperations.set("code", "1234", 30L, TimeUnit.SECONDS);
valueOperations.setIfAbsent("lock", "1");
}
@Test
public void testHash() {
final HashOperations hashOperations = redisTemplate.opsForHash();
//hset hget hdel hkeys hvals
hashOperations.put("100", "name", "tom");
hashOperations.put("100", "age", "20");
hashOperations.put("100", "gender", "1");
hashOperations.put("100", "address", "北京");
System.out.println((String) hashOperations.get("100", "name"));
//删除key对应的value
hashOperations.delete("100", "gender", "address");
//获取所有key
System.out.println(hashOperations.keys("100"));
//获取key对应所有value
System.out.println(hashOperations.values("100"));
}
@Test
public void testList() {
final ListOperations listOperations = redisTemplate.opsForList();
//lpust lrange rpop llen
listOperations.leftPushAll("mylist","a","b","c");
listOperations.leftPush("mylist","d");
System.out.println(listOperations.range("mylist", 0, -1));
listOperations.rightPop("mylist");
System.out.println(listOperations.size("mylist"));
}
@Test
public void testSet() {
final SetOperations setOperations = redisTemplate.opsForSet();
//sadd smenbers scard sinter sunion srem
setOperations.add("set1","a","b","c","d");
setOperations.add("set2","a","b","x","y");
System.out.println(setOperations.members("set1"));
//scard
System.out.println(setOperations.size("set1"));
//sinter交集 sunion并集
System.out.println(setOperations.intersect("set1", "set2"));
System.out.println(setOperations.union("set1", "set2"));
setOperations.remove("set1","a","b");
}
@Test
public void testZSet() {
final ZSetOperations zSetOperations = redisTemplate.opsForZSet();
//zadd zrange zincrby zrem
zSetOperations.add("zset1","a",10);
zSetOperations.add("zset1","b",12);
zSetOperations.add("zset1","c",9);
System.out.println(zSetOperations.range("zset1", 0, -1));
zSetOperations.incrementScore("zset1","a",2);
zSetOperations.remove("zset1","c");
}
@Test
public void testCommon(){
//keys exists type del
Set keys=redisTemplate.keys("*");
System.out.println(redisTemplate.hasKey("name"));
for(Object key:keys){
final DataType type = redisTemplate.type(key);
System.out.println(type.name());
}
redisTemplate.delete("mylist");
}
HttpClient 介绍: 提供http协议的客户端编程工具包,构造http请求 依赖:httpclient 核心API: HttpClient 发送http请求 HttpClients 获取http对象 CloseableHttpClient 实现httpClient HttpGet get请求 HttpPost 发送请求的方式: 创建HttpClient对象 创建Http请求对象(HttpGet/Post) 调用HttpClient的execute方法发送请求 aliyun-sdk-oss底层导入了httpclient依赖
//通过httpClient发送get请求
@Test
public void testGet() throws IOException {
//创建httpClient对象
CloseableHttpClient httpClient = HttpClients.createDefault();
//创建请求对象
HttpGet httpGet = new HttpGet("http://localhost:8080/user/shop/status");
//发送请求,接收响应结果
CloseableHttpResponse response = httpClient.execute(httpGet);
int statusCode = response.getStatusLine().getStatusCode();
System.out.println("服务端返回的状态码为:" + statusCode);
//服务端返回的数据
HttpEntity entity = response.getEntity();
String body = EntityUtils.toString(entity);
System.out.println("服务端返回的数据为:" + body);
//关闭资源
response.close();
httpClient.close();
}
@Test
public void testPost() throws IOException {
CloseableHttpClient httpClient = HttpClients.createDefault();
HttpPost httpPost = new HttpPost("http://localhost:8080/admin/employee/login");
JSONObject jsonObject = new JSONObject();
jsonObject.put("username", "admin");
jsonObject.put("password", "123456");
StringEntity entity = new StringEntity(jsonObject.toString());
//指定请求编码方式
entity.setContentEncoding("utf-8");
//数据格式
entity.setContentType("application/json");
httpPost.setEntity(entity);
CloseableHttpResponse response = httpClient.execute(httpPost);
int code = response.getStatusLine().getStatusCode();
HttpEntity entityResponse = response.getEntity();
String body = EntityUtils.toString(entityResponse);
System.out.println("响应码:"+code+"\n"+"响应数据:"+body);
}
缓存商品、购物车 缓存菜品: http-后端服务-读取缓存-查询数据库-载入缓存 缓存分析: 每个分类下的菜品保存一份缓存数据 数据库中菜品数据有变更时及时清理缓存数据 Spring Cache 是一个框架,实现了基于注解的缓存功能。 Spring Cache提供了一层抽象,底层可以切换不同的缓存实现,例如: EHCache Caffeine Redis 导入依赖:spring boot starter cache 常用注解: @EnableCaching 启动类 开启缓存注解功能 @Cacheable 方法 在方法执行前先查询缓存中是否有数据,如果有数据,则直接返回缓存数据;如果没有缓存数据,调用方法并将方法的返回值放到缓存中 @CachePut 方法 将方法的返回值放到缓存中 @CacheEvict 将一条或多条数据从缓存删除 insertController: spEL @CachePut(cacheNames="userCache",key="#形参名.主键属性") 或者:key="#result.id" result表示函数返回值 或者:key="#a0.id" a0或p0表示形参第一个参数 如果使用Spring Cache缓存数据,key的生成:userCache::key树形结构 前提:主键返回 selectController: @Cacheable(cacheName="userCache",key="#id") deleteController: @CacheEvict(cacheNames="userCache",key="#id") 清理所有:@CacheEvict(cacheNames="userCache",allEntries="true")
Spring Task与WebSocket Spring Task是Spring家族提供的任务调度工具,可以按照约定时间自动执行某个代码逻辑。 作用:定时任务框架 应用场景:超时订单的处理 派送中订单处理 cron表达式: cron表达式是一个字符串,通过cron表达式可以定义任务触发的时间 构成规则:分为6或7个域,每个域代表一个含义 每个域的含义:秒、分钟、小时、日、月,周(星期)、年(可选) https://cron.qqe2.com/ 步骤: 导入spring-context 在启动类上添加@EnableScheduling 自定义定时任务类 该类需要交给Spring容器 方法注解@Scheduled(cron="") WebSocket webSocket是基于TCP协议的一种新的 网络协议。实现了浏览器与服务器全双工通信-浏览器与服务器只需要完成一次握手,两者之间就能创建 持久性 连接,并进行双向数据传输。 Http:请求响应模式 客户端请求服务器,服务端响应客户端。 短连接,一次请求一次连接 相同点:都是基于TCP连接 应用场景:视频弹幕 网页聊天 实况更新 步骤: 导入依赖spring-boot-starter-websocket 导入WebSocket服务端组件webSocketServer,用于和客户端通信
package com.sky.webSocket;
import org.springframework.stereotype.Component;
import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
/**
* WebSocket服务
*/
@Component
@ServerEndpoint("/ws/{sid}")
public class WebSocketServer {
//存放会话对象
private static Map sessionMap = new HashMap();
/**
* 连接建立成功调用的方法
*/
@OnOpen
public void onOpen(Session session, @PathParam("sid") String sid) {
System.out.println("客户端:" + sid + "建立连接");
sessionMap.put(sid, session);
}
/**
* 收到客户端消息后调用的方法
*
* @param message 客户端发送过来的消息
*/
@OnMessage
public void onMessage(String message, @PathParam("sid") String sid) {
System.out.println("收到来自客户端:" + sid + "的信息:" + message);
}
/**
* 连接关闭调用的方法
*
* @param sid
*/
@OnClose
public void onClose(@PathParam("sid") String sid) {
System.out.println("连接断开:" + sid);
sessionMap.remove(sid);
}
/**
* 群发
*
* @param message
*/
public void sendToAllClient(String message) {
Collection sessions = sessionMap.values();
for (Session session : sessions) {
try {
//服务器向客户端发送消息
session.getBasicRemote().sendText(message);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
导入配置类WebSocketConfiguration,注册WebSocket服务端组件
package com.sky.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
/**
* WebSocket配置类,用于注册WebSocket的Bean
*/
@Configuration
public class WebSocketConfiguration {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
Apache POI 在Java中操作Office各种文件的开源项目 应用场景: 银行交易明细 导出Excel报表 使用: 依赖:poi 和 poi-ooxml
package com.sky.test;
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.junit.jupiter.api.Test;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class POITest {
@Test//创建Excel文件
public void testExcelWrite() throws IOException {
//创建一个excel文件(在内存中)
XSSFWorkbook excel = new XSSFWorkbook();
//在excel文件中创建一个sheet页
XSSFSheet sheet = excel.createSheet();
//在sheet页中创建行,0开始
XSSFRow rowFirst = sheet.createRow(0);
//在行上面创建单元格
XSSFCell cell00 = rowFirst.createCell(0);
XSSFCell cell01 = rowFirst.createCell(1);
//向单元格写入内容
cell00.setCellValue("姓名");
cell01.setCellValue("城市");
XSSFRow rowSecond = sheet.createRow(1);
XSSFCell cell10 = rowSecond.createCell(0);
XSSFCell cell11 = rowSecond.createCell(1);
cell10.setCellValue("张三");
cell11.setCellValue("北京");
//将内存中excel文件写入磁盘,没有会创建
FileOutputStream fileOutputStream = new FileOutputStream("D:\\Users\\JavawebCode\\web四期\\lib\\1、黑马程序员Java项目《苍穹外卖》企业级开发实战\\资料\\day12\\Excel\\info.xlsx");
excel.write(fileOutputStream);
excel.close();
fileOutputStream.close();
}
@Test
public void testReadExcel() throws IOException {
//读取磁盘上的excel文件
XSSFWorkbook excel = new XSSFWorkbook(new FileInputStream(new File("D:\\Users\\JavawebCode\\web四期\\lib\\1、黑马程序员Java项目《苍穹外卖》企业级开发实战\\资料\\day12\\Excel\\info.xlsx")));
//读取sheet页
XSSFSheet sheetOne = excel.getSheetAt(0);
//读取sheet最后有文字的一行的行号
int lastRowNum = sheetOne.getLastRowNum();
for (int i = 0; i <= lastRowNum; i++) {
//获取每一行对象
XSSFRow row = sheetOne.getRow(i);
//获取单元格对象
short lastCellNum = row.getLastCellNum();
for (int j = 0; j < lastCellNum; j++) {
XSSFCell cell = row.getCell(j);
String value = cell.getStringCellValue();
System.out.print(value + "\t");
}
System.out.println();
}
excel.close();
}
}
实际实现步骤: 1.设计Excel模板文件 2.查询数据库 3.将查询到的数据写入模板文件 4.通过输出流将Excel文件下载到客户端浏览器
@Override
public void export(HttpServletResponse response) {
//1.查数据库获得数据
LocalDate dateBegin = LocalDate.now().minusDays(30);
LocalDate dateEnd = LocalDate.now().minusDays(1);
BusinessDataVO businessDataVO = workspaceService.getBusinessData(
LocalDateTime.of(dateBegin, LocalTime.MIN),
LocalDateTime.of(dateEnd, LocalTime.MAX)
);
//2.将数据写入
InputStream in = this.getClass().getClassLoader().getResourceAsStream("template/运营数据报表模板.xlsx");
try {
//基于模板文件创建新的excel文件
XSSFWorkbook excel = new XSSFWorkbook(in);
//获取表格文件的sheet页
XSSFSheet sheet = excel.getSheet("Sheet1");
//填充数据-时间 第二行
sheet.getRow(1).getCell(1).setCellValue("时间:"+dateBegin+"至"+dateEnd);
//营业额 第4行 第3格
//获得第4行
XSSFRow row = sheet.getRow(3);
row.getCell(2).setCellValue(businessDataVO.getTurnover());
row.getCell(4).setCellValue(businessDataVO.getOrderCompletionRate());
row.getCell(6).setCellValue(businessDataVO.getNewUsers());
//获得第5行
row = sheet.getRow(4);
row.getCell(2).setCellValue(businessDataVO.getValidOrderCount());
row.getCell(4).setCellValue(businessDataVO.getUnitPrice());
//明细数据
for (int i = 0; i < 30; i++) {
LocalDate date = dateBegin.plusDays(i);
//查询某一天的营业数据
BusinessDataVO businessData = workspaceService.getBusinessData(LocalDateTime.of(date, LocalTime.MIN), LocalDateTime.of(date, LocalTime.MAX));
//获得某一行
row = sheet.getRow(7 + i);
row.getCell(1).setCellValue(date.toString());
row.getCell(2).setCellValue(businessData.getTurnover());
row.getCell(3).setCellValue(businessData.getValidOrderCount());
row.getCell(4).setCellValue(businessData.getOrderCompletionRate());
row.getCell(5).setCellValue(businessData.getUnitPrice());
row.getCell(6).setCellValue(businessData.getNewUsers());
}
//3.通过输出流下载到客户端浏览器
ServletOutputStream outputStream = response.getOutputStream();
excel.write(outputStream);
excel.close();
outputStream.close();
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
操作: Debug:查看选中代码结果 箭头:上 调上次命令 右键->Evaluate Expression
需要开启的: @EnableScheduling Spring Task @EnableCaching Spring Cache @EnableTransactionManagement //开启注解方式的事务管理