14、Spring Cache
Spring Cache 是一个框架,实现了基于注解的缓存功能,只需要简单地加一个注解,就能实现缓存功能。在项目中用来缓存套餐
使用步骤
1. 导入依赖
使用Redis作为缓存技术,我需要到导入redis的依赖和springcache的依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
2. 在启动类上加入@EnableCaching注解,开启缓存注解功能
@SpringBootApplication
@EnableTransactionManagement
@Slf4j
@EnableCaching
public class SkyApplication {
public static void main(String[] args) {
SpringApplication.run(SkyApplication.class, args);
}
}
3. 在相关的接口上添加注解
注解 | 说明 |
---|---|
@EnableCaching | 开启缓存注解功能,通常加在启动类上 |
@Cacheable | 在方法执行前先查询缓存中是否有数据,如果有数据,则直接返回缓存数据;如果没有缓存数据,调用方法并将方法返回值放到缓存中 |
@CachePut | 将方法的返回值放到缓存中 |
@CacheEvict | 将一条或多条数据从缓存中删除 |
@CachePut
/**
* CachePut:将方法返回值放入缓存
* value:缓存的名称,每个缓存名称下面可以有多个key
* key:缓存的key
*/
@PostMapping
@CachePut(value = "userCache", key = "#user.id")//key的生成:userCache::1
public User save(@RequestBody User user){
userMapper.insert(user);
return user;
}
@Cacheable
/**
* Cacheable:在方法执行前spring先查看缓存中是否有数据,如果有数据,则直接返回缓存数据;若没有数据调用方法并将方法返回值放到缓存中
* value:缓存的名称,每个缓存名称下面可以有多个key
* key:缓存的key
*/
@GetMapping
@Cacheable(cacheNames = "userCache",key="#id")
public User getById(Long id){
User user = userMapper.getById(id);
return user;
}
@CacheEvict
@DeleteMapping
@CacheEvict(cacheNames = "userCache",key = "#id")//删除某个key对应的缓存数据
public void deleteById(Long id){
userMapper.deleteById(id);
}
@DeleteMapping("/delAll")
@CacheEvict(cacheNames = "userCache",allEntries = true)//删除userCache下所有的缓存数据
public void deleteAll(){
userMapper.deleteAll();
}
15、HttpClient
HttpClient作用:
-
发送HTTP请求
-
接收响应数据
为什么要在Java程序中发送Http请求?有哪些应用场景呢?
HttpClient应用场景:当我们在使用扫描支付、查看地图、获取验证码、查看天气等功能时
其实,应用程序本身并未实现这些功能,都是在应用程序里访问提供这些功能的服务,访问这些服务需要发送HTTP请求,并且接收响应数据,可通过HttpClient来实现。
HttpClient的核心API:
-
HttpClient:Http客户端对象类型,使用该类型对象可发起Http请求。
-
HttpClients:可认为是构建器,可创建HttpClient对象。
-
CloseableHttpClient:实现类,实现了HttpClient接口。
-
HttpGet:Get方式请求类型。
-
HttpPost:Post方式请求类型。
使用步骤(以GET方式请求为例)
-
导入依赖
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.13</version>
</dependency>
-
创建HttpClient对象
-
创建Http请求对象
-
调用HttpClient的execute方法发送请求,接受结果
-
解析结果
-
关闭资源
在访问http://localhost:8080/user/shop/status请求时,需要提前启动项目。
测试结果:
自定义HttpClient工具类
/**
* Http工具类
*/
public class HttpClientUtil {
static final int TIMEOUT_MSEC = 5 * 1000;
/**
* 发送GET方式请求
* @param url
* @param paramMap
* @return
*/
public static String doGet(String url,Map<String,String> paramMap){
// 创建Httpclient对象
CloseableHttpClient httpClient = HttpClients.createDefault();
String result = "";
CloseableHttpResponse response = null;
try{
URIBuilder builder = new URIBuilder(url);
if(paramMap != null){
for (String key : paramMap.keySet()) {
builder.addParameter(key,paramMap.get(key));
}
}
URI uri = builder.build();
//创建GET请求
HttpGet httpGet = new HttpGet(uri);
//发送请求
response = httpClient.execute(httpGet);
//判断响应状态
if(response.getStatusLine().getStatusCode() == 200){
result = EntityUtils.toString(response.getEntity(),"UTF-8");
}
}catch (Exception e){
e.printStackTrace();
}finally {
try {
response.close();
httpClient.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return result;
}
/**
* 发送POST方式请求
* @param url
* @param paramMap
* @return
* @throws IOException
*/
public static String doPost(String url, Map<String, String> paramMap) throws IOException {
// 创建Httpclient对象
CloseableHttpClient httpClient = HttpClients.createDefault();
CloseableHttpResponse response = null;
String resultString = "";
try {
// 创建Http Post请求
HttpPost httpPost = new HttpPost(url);
// 创建参数列表
if (paramMap != null) {
List<NameValuePair> paramList = new ArrayList();
for (Map.Entry<String, String> param : paramMap.entrySet()) {
paramList.add(new BasicNameValuePair(param.getKey(), param.getValue()));
}
// 模拟表单
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(paramList);
httpPost.setEntity(entity);
}
httpPost.setConfig(builderRequestConfig());
// 执行http请求
response = httpClient.execute(httpPost);
resultString = EntityUtils.toString(response.getEntity(), "UTF-8");
} catch (Exception e) {
throw e;
} finally {
try {
response.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return resultString;
}
/**
* 发送POST方式请求
* @param url
* @param paramMap
* @return
* @throws IOException
*/
public static String doPost4Json(String url, Map<String, String> paramMap) throws IOException {
// 创建Httpclient对象
CloseableHttpClient httpClient = HttpClients.createDefault();
CloseableHttpResponse response = null;
String resultString = "";
try {
// 创建Http Post请求
HttpPost httpPost = new HttpPost(url);
if (paramMap != null) {
//构造json格式数据
JSONObject jsonObject = new JSONObject();
for (Map.Entry<String, String> param : paramMap.entrySet()) {
jsonObject.put(param.getKey(),param.getValue());
}
StringEntity entity = new StringEntity(jsonObject.toString(),"utf-8");
//设置请求编码
entity.setContentEncoding("utf-8");
//设置数据类型
entity.setContentType("application/json");
httpPost.setEntity(entity);
}
httpPost.setConfig(builderRequestConfig());
// 执行http请求
response = httpClient.execute(httpPost);
resultString = EntityUtils.toString(response.getEntity(), "UTF-8");
} catch (Exception e) {
throw e;
} finally {
try {
response.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return resultString;
}
private static RequestConfig builderRequestConfig() {
return RequestConfig.custom()
.setConnectTimeout(TIMEOUT_MSEC)
.setConnectionRequestTimeout(TIMEOUT_MSEC)
.setSocketTimeout(TIMEOUT_MSEC).build();
}
}
HttpClient应用
苍穹外卖项目中HttpClient的应用
/**
* 调用微信接口服务,获取微信用户的openid
* @param code
* @return
*/
private String getOpenid(String code){
//调用微信接口服务,获得当前微信用户的openid
Map<String, String> map = new HashMap<>();
map.put("appid",weChatProperties.getAppid());
map.put("secret",weChatProperties.getSecret());
map.put("js_code",code);
map.put("grant_type","authorization_code");
String json = HttpClientUtil.doGet("https://api.weixin.qq.com/sns/jscode2session", map);
// HttpClientUtil工具类已写好 封装好了相关HttpClient函数
JSONObject jsonObject = JSON.parseObject(json);
String openid = jsonObject.getString("openid");
return openid;
}
16、微信登录
微信登录的核心是通过微信小程序提供的临时凭证code换取永久凭证openid的过程
-
小程序端,调用wx.login()获取code,就是授权码。
-
小程序端,调用wx.request()发送请求并携带code,请求开发者服务器(自己编写的后端服务)。
-
开发者服务端,通过HttpClient向微信接口服务发送请求,并携带appId+appsecret+code三个参数。
-
开发者服务端,接收微信接口服务返回的opendId。判断该用户是否新用户
-
开发者服务端,将openid等数据返回给小程序端,方便后绪请求身份校验。
-
后面的验证和前面用户验证一样了
17、Spring Task
Spring Task 是Spring框架提供的任务调度工具,可以按照约定的时间自动执行某个代码逻辑。
-
定位:定时任务框架
-
作用:定时自动执行某段Java代码
-
cron表达式: 其实就是一个字符串,通过cron表达式可以定义任务触发的时间
Spring Task使用步骤
-
导入依赖spring-context(在本项目中包含在spring-boot-starter)
-
启动类添加注解@EnableScheduling(用于开启任务调度)
-
自定义启动类(在其中添加方法用于实现基础功能)
在本项目中学习SpringTask用来解决:
-
用户下单后,一直没有支付。超过我们设置的支付时限15分钟
-
解决方法:定时任务每分钟检查一次
-
-
一些订单用户已经收到,订单一直处于“派送中”。我们需要将这些订单改为“已完成”
-
解决方法:定时任务每天凌晨检查一次
-
使用步骤
1. 启动类添加注解
@SpringBootApplication
@EnableTransactionManagement
@Slf4j
@EnableCaching
@EnableScheduling//用于开启任务调度
public class SkyApplication {
public static void main(String[] args) {
SpringApplication.run(SkyApplication.class, args);
log.info("server started");
}
}
2. 自定义定时任务类
/**
* 自定义定时任务,实现订单状态定时处理
*/
@Component
@Slf4j
public class OrderTask {
/**
* 定时任务 每隔5秒触发一次
*/
@Scheduled(cron = "0/5 * * * * ?")
public void executeTask(){
log.info("定时任务开始执行:{}",new Date());
}
}
3. 测试
启动服务,查看日志
每隔5秒执行一次。
18、WebSocket
一种新的网络协议。类似于电话通信。实现的功能:客户催单、来单提醒
实现步骤
1. 导入maven坐标
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
2. 定义WebSocket服务端组件(资料中已提供)
/**
* WebSocket服务
*/
@Component
@ServerEndpoint("/ws/{sid}")
public class WebSocketServer {
//存放会话对象
private static Map<String, Session> 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<Session> sessions = sessionMap.values();
for (Session session : sessions) {
try {
//服务器向客户端发送消息
session.getBasicRemote().sendText(message);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
3. 定义配置类,注册WebSocket的服务端组件(从资料中直接导入即可)
/**
* WebSocket配置类,用于注册WebSocket的Bean
*/
@Configuration
public class WebSocketConfiguration {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
4. 定义定时任务类,定时向客户端推送数据(从资料中直接导入即可)
@Component
public class WebSocketTask {
@Autowired
private WebSocketServer webSocketServer;
/**
* 通过WebSocket每隔5秒向客户端发送消息
*/
@Scheduled(cron = "0/5 * * * * ?")
public void sendMessageToClient() {
webSocketServer.sendToAllClient("这是来自服务端的消息:" + DateTimeFormatter.ofPattern("HH:mm:ss").format(LocalDateTime.now()));
}
}
19、Apache ECharts
-
营业额统计
-
用户统计
-
订单统计
-
销量排名Top10
20、Apache POI
实现导出excel报表
依赖
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.16</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.16</version>
</dependency>
代码开发
1. 基于POI向Excel文件写入数据
public class POITest {
/**
* 基于POI向Excel文件写入数据
* @throws Exception
*/
public static void write() throws Exception{
//在内存中创建一个Excel文件对象
XSSFWorkbook excel = new XSSFWorkbook();
//创建Sheet页
XSSFSheet sheet = excel.createSheet("itcast");
//在Sheet页中创建行,0表示第1行
XSSFRow row1 = sheet.createRow(0);
//创建单元格并在单元格中设置值,单元格编号也是从0开始,1表示第2个单元格
row1.createCell(1).setCellValue("姓名");
row1.createCell(2).setCellValue("城市");
XSSFRow row2 = sheet.createRow(1);
row2.createCell(1).setCellValue("张三");
row2.createCell(2).setCellValue("北京");
XSSFRow row3 = sheet.createRow(2);
row3.createCell(1).setCellValue("李四");
row3.createCell(2).setCellValue("上海");
FileOutputStream out = new FileOutputStream(new File("D:\\itcast.xlsx"));
//通过输出流将内存中的Excel文件写入到磁盘上
excel.write(out);
//关闭资源
out.flush();
out.close();
excel.close();
}
public static void main(String[] args) throws Exception {
write();
}
}
2. 实现效果
在D盘中生成itcast.xlsx文件,创建名称为itcast的Sheet页,同时将内容成功写入。
1. 基于POI读取Excel文件
package com.sky.test;
public class POITest {
/**
* 基于POI读取Excel文件
* @throws Exception
*/
public static void read() throws Exception{
FileInputStream in = new FileInputStream(new File("D:\\itcast.xlsx"));
//通过输入流读取指定的Excel文件
XSSFWorkbook excel = new XSSFWorkbook(in);
//获取Excel文件的第1个Sheet页
XSSFSheet sheet = excel.getSheetAt(0);
//获取Sheet页中的最后一行的行号
int lastRowNum = sheet.getLastRowNum();
for (int i = 0; i <= lastRowNum; i++) {
//获取Sheet页中的行
XSSFRow titleRow = sheet.getRow(i);
//获取行的第2个单元格
XSSFCell cell1 = titleRow.getCell(1);
//获取单元格中的文本内容
String cellValue1 = cell1.getStringCellValue();
//获取行的第3个单元格
XSSFCell cell2 = titleRow.getCell(2);
//获取单元格中的文本内容
String cellValue2 = cell2.getStringCellValue();
System.out.println(cellValue1 + " " +cellValue2);
}
//关闭资源
in.close();
excel.close();
}
public static void main(String[] args) throws Exception {
read();
}
}
2. 实现效果
将itcast.xlsx文件中的数据进行读取
项目是如何进行异常处理的
在我们的项目中,异常处理都是通过spring的全局异常处理器来实现的,核心是两个注解:
一个是@RestControllerAdvice,表示该类为全局异常处理类 一个是@ExceptionHandler,表明方法处理哪些异常
/**
* 全局异常处理器,处理项目中抛出的业务异常
*/
@RestControllerAdvice//定义类为异常处理类
@Slf4j
public class GlobalExceptionHandler {
/**
* 捕获业务异常
* @param ex
* @return
* @ExceptionHandler(Exception.class)//标明方法处理哪些异常
*/
@ExceptionHandler
public Result exceptionHandler(BaseException ex){
log.error("异常信息:{}", ex.getMessage());
return Result.error(ex.getMessage());
}
/**
* 如果出现SQLIntegrityConstraintViolationException
* 就会调用该方法
*/
@ExceptionHandler
public Result exceptionHandler(SQLIntegrityConstraintViolationException ex){
//Duplicate entry 'likeyou2' for key 'employee.idx_username'
String msg = ex.getMessage();
if(msg.contains("Duplicate entry")){
String[] split = msg.split(" ");
String username = split[2];
String ans = username+ MessageConstant.ALREADY_EXISTS;
return Result.error(ans);
}else{
return Result.error(MessageConstant.UNKNOWN_ERROR);
}
}
}
项目是如何进行参数校验的
使用Validation
- @Null 可以标注在任意类型元素上,被标注的元素必须为null
- @NotEmpty 可以标注 在字符串,集合,数组,map上,被标注的元素必须不能为 null ,也不能是空串
- @Range 标注在字符串和数值的大小必须在指定的范围内,对于null无效
- @Digits(integer(数值的位数) =3 , fraction(小数的位数)=2)
- @size(min=,max=) 可以标注在字符串,数组,集合,map用于控制长度
- @Email 邮箱
- @URL 合法的地址
项目如何设置时间格式的?
法1:在实体类上添加@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
法2:使用对象映射器
这下面是我写在简历上面的,我这个写的不好看到的,能不能给提个建议?
- 使用JWT令牌技术,用自定义的拦截器完成用户认证,通过ThreadLocal优化逻辑
- 使用Nginx部署反向代理和负载均衡
- 使用Swagger实现接口文档的自动生成
- 使用AOP思想,实现公共字段自动填充
- 使用Redis将店铺营业状态和菜品套餐进行缓存,提高开发效率,其中使用SpringCache简化开发
- 通过HttpClient向微信接口服务发送请求,实现微信登陆
- 使用Spring Task定时任务框架,实现订单功能代码的完善
- 使用WebSocket实现来单提醒和客户催单业务
- 基于Apache ECharts在工作台展示指定时间段内的数据统计
- 使用Apache POI导出excel表格