主要是黑马的苍穹外卖项目学习记录
一、Springboot3依赖及其用法
1、lombok
1 2 3 4
| <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency>
|
-
方便打日志
类上加@Slf4j
1
| log.info("新增员工:{}"+employeeDTO);
|
-
@Data
给属性自动加上getter以及setter方法
-
@Builder
1 2 3
| Employee employee = Employee.builder() .id(id) .status(status).build();
|
二、其他用法
1、加密密码
通过MD5加密密码
1
| password = DigestUtils.md5DigestAsHex(password.getBytes());
|
2、后端统一返回结果格式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
|
@Data public class Result<T> implements Serializable {
private Integer code; private String msg; private T data;
public static <T> Result<T> success() { Result<T> result = new Result<T>(); result.code = 1; return result; }
public static <T> Result<T> success(T object) { Result<T> result = new Result<T>(); result.data = object; result.code = 1; return result; }
public static <T> Result<T> error(String msg) { Result result = new Result(); result.msg = msg; result.code = 0; return result; }
}
|
3、JWT工具类(另)
1 2 3 4 5
| <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.11.5</version> </dependency>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| public class JwtUtil {
public static String createJWT(String secretKey, long ttlMillis, Map<String, Object> claims) { SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
long expMillis = System.currentTimeMillis() + ttlMillis; Date exp = new Date(expMillis);
JwtBuilder builder = Jwts.builder() .setClaims(claims) .signWith(signatureAlgorithm, secretKey.getBytes(StandardCharsets.UTF_8)) .setExpiration(exp);
return builder.compact(); }
public static Claims parseJWT(String secretKey, String token) { Claims claims = Jwts.parser() .setSigningKey(secretKey.getBytes(StandardCharsets.UTF_8)) .parseClaimsJws(token).getBody(); return claims; }
}
|
4、数据库操作时间字段显示有问题
-
方式一
@JsonFormat(pattern = “yyyy-MM-dd HH:mm:ss”)
在属性上加上注解 ,对日期进行格式化
-
序列化:当 Event 对象被转化为 JSON 时,eventDate 字段会以指定的格式 yyyy-MM-dd HH:mm:ss 进行输出。
-
反序列化:当一个 JSON 字符串被转化为 Event 对象时,eventDate 字段会根据给定的格式 "yyyy-MM-dd HH:mm:ss" 进行解析。
-
方式二
消息转换器用于将 HTTP 请求或响应的内容从一个格式(如 JSON、XML)转换为 Java 对象,或者反向操作,从 Java 对象转换为相应的格式(如 JSON、XML)。
在WebMvcConfiguration中扩展SpringMVC的消息转换器,统一对日期类型进行格式处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
|
@Configuration @Slf4j public class WebMvcConfiguration extends WebMvcConfigurationSupport {
@Override protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) { log.info("扩展消息转换器..."); MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(); converter.setObjectMapper(new JacksonObjectMapper());
converters.add(0,converter); } }
|
1 2 3 4 5 6 7 8 9 10
| package com.sky.json;
public class JacksonObjectMapper extends ObjectMapper {
public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm";
} }
|
5、自定义注解@AutoFill
其中一个例子:在创建、更新对象的时候,自动填充对象的创建时间,更新时间等内容
实现步骤:
1). 自定义注解 AutoFill,用于标识需要进行公共字段自动填充的方法
2). 自定义切面类 AutoFillAspect,统一拦截加入了 AutoFill 注解的方法,通过反射为公共字段赋值
3). 在 Mapper 的方法上加入 AutoFill 注解
-
自定义注解
1 2 3 4 5 6 7 8 9 10 11
|
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface AutoFill { OperationType value();
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
|
public enum OperationType {
UPDATE,
INSERT }
2. 自定义切面
主要结构:
``` java
@Aspect @Component @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("开始进行公共字段自动填充..."); } }
|
具体方法如下(仅供参考):
| 复杂用法 |
解释 |
| JoinPoint |
连接点的信息 |
| joinPoint.getSignature() |
得到被拦截方法的签名(即方法的名字、返回类型、参数类型等) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| @Before("autoFillPointCut()") public void autoFill(JoinPoint joinPoint) throws InvocationTargetException, IllegalAccessException { log.info("开始进行公共字段的自动填充...");
MethodSignature signature = (MethodSignature) joinPoint.getSignature(); AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class); OperationType operationType = autoFill.value();
Object[]args = joinPoint.getArgs(); if(args == null || args.length== 0){ return; }
Object entity = args[0]; LocalDateTime now = LocalDateTime.now(); Long currentId = BaseContext.getCurrentId();
if(operationType == OperationType.INSERT){ try { Method setCreateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_TIME, LocalDateTime.class); Method setCreateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_USER, Long.class); Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class); 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 (NoSuchMethodException e) { throw new RuntimeException(e); } }else if(operationType == OperationType.UPDATE){
try { Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class); Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);
setUpdateTime.invoke(entity,now); setUpdateUser.invoke(entity,currentId); } catch (NoSuchMethodException e) { throw new RuntimeException(e); } } }
|
反射机制:
-
Method setCreateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_TIME, LocalDateTime.class)
得到参数实体名为AutoFillConstant.SET_CREATE_TIME,方法参数为 LocalDateTime.class的方法
-
setCreateTime.invoke(entity,now);
动态执行entity对应的setCreateTime方法传入now(当前时间)作为参数
-
对应地方使用
1 2
| @AutoFill(OperationType.UPDATE) void update(Employee employee);
|
三、Swagger
Swagger 是一个规范和完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Web 服务。快速生成接口文档,并且可以测试接口
Knife4j 主要特点包括:
-
Swagger UI 增强:Knife4j 提供了一个更美观、更易用的 Swagger UI 界面,增加了更多的定制功能。
-
多 API 文档支持:Knife4j 支持多个 Swagger 文档的整合显示,可以展示多个 API 的文档。
-
增强的代码生成:通过 Knife4j,你可以对接口文档进行更多的配置和自定义,提升 API 文档的交互体验。
1、依赖
1 2 3 4
| <dependency> <groupId>com.github.xiaoymin</groupId> <artifactId>knife4j-spring-boot-starter</artifactId> </dependency>
|
2、配置文件
在配置类中加上Knife4j相关配置,一般是webMVC配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| @Bean public Docket docket() { ApiInfo apiInfo = new ApiInfoBuilder() .title("苍穹外卖项目接口文档") .version("2.0") .description("苍穹外卖项目接口文档") .build(); Docket docket = new Docket(DocumentationType.SWAGGER_2) .groupName("管理端接口") .apiInfo(apiInfo) .select() .apis(RequestHandlerSelectors.basePackage("com.sky.controller.admin")) .paths(PathSelectors.any()) .build(); return docket; }
|
3、设置静态资源映射,否则接口文档页面无法访问
将doc.html自动映射到/META-INF/resources/下的内容
1 2 3 4 5 6 7 8 9
|
protected void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/doc.html").addResourceLocations("classpath:/META-INF/resources/"); registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/"); }
|
使用方式例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| @Configuration @Slf4j public class WebMvcConfiguration extends WebMvcConfigurationSupport {
@Bean public Docket docket1() { ApiInfo apiInfo = new ApiInfoBuilder() .title("苍穹外卖项目接口文档") .version("2.0") .description("苍穹外卖项目接口文档") .build(); Docket docket = new Docket(DocumentationType.SWAGGER_2) .groupName("管理端接口") .apiInfo(apiInfo) .select() .apis(RequestHandlerSelectors.basePackage("com.sky.controller.admin")) .paths(PathSelectors.any()) .build(); return docket; }
protected void addResourceHandlers(ResourceHandlerRegistry registry) { log.info("开始设置静态资源映射"); registry.addResourceHandler("/doc.html").addResourceLocations("classpath:/META-INF/resources/"); registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/"); } }
|
4、常用注解
通过注解可以控制生成的接口文档,使接口文档拥有更好的可读性,常用注解如下:
| 注解 |
说明 |
| @Api |
用在类上,例如Controller,表示对类的说明 |
| @ApiModel |
用在类上,例如entity、DTO、VO |
| @ApiModelProperty |
用在属性上,描述属性信息 |
| @ApiOperation |
用在方法上,例如Controller的方法,说明方法的用途、作用 |
例如:
1 2 3 4 5 6 7 8 9 10 11
| @Data @ApiModel(description = "员工登录时传递的数据模型") public class EmployeeLoginDTO implements Serializable {
@ApiModelProperty("用户名") private String username;
@ApiModelProperty("密码") private String password;
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| @Data @Builder @NoArgsConstructor @AllArgsConstructor @ApiModel(description = "员工登录返回的数据格式") public class EmployeeLoginVO implements Serializable {
@ApiModelProperty("主键值") private Long id;
@ApiModelProperty("用户名") private String userName;
@ApiModelProperty("姓名") private String name;
@ApiModelProperty("jwt令牌") private String token;
}
|
1 2 3 4 5 6 7 8 9 10 11 12
| @RestController @RequestMapping("/admin/employee") @Slf4j @Api(tags = "员工相关接口") public class EmployeeController { @PostMapping("/logout") @ApiOperation("员工退出") public Result<String> logout() { return Result.success(); }
}
|
5、原生Swagger
1 2 3 4 5
| <!-- Spring Boot 集成 swagger --> <dependency> <groupId>com.spring4all</groupId> <artifactId>swagger-spring-boot-starter</artifactId> </dependency>
|
application.yaml或者bootstrap.yaml
1 2 3 4 5 6
| swagger: title: "学成在线内容管理系统" description: "内容系统管理系统对课程相关信息进行管理" base-package: com.xuecheng.content enabled: true version: 1.0.0
|
常用Swagger注解如下:
1 2 3 4 5 6 7 8 9 10 11
| @Api:修饰整个类,描述Controller的作用 @ApiOperation:描述一个类的一个方法,或者说一个接口 @ApiParam:单个参数描述 @ApiModel:用对象来接收参数 @ApiModelProperty:用对象接收参数时,描述对象的一个字段 @ApiResponse:HTTP响应其中1个描述 @ApiResponses:HTTP响应整体描述 @ApiIgnore:使用该注解忽略这个API @ApiError :发生错误返回的信息 @ApiImplicitParam:一个请求参数 @ApiImplicitParams:多个请求参数
|
四、HttpClient
HttpClient可以:发送HTTP请求、接收响应数据
主要用到后端。主动发起HTTP请求,接收响应数据
1 2 3 4 5
| <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.13</version> </dependency>
|
1、介绍
HttpClient的核心API:
-
HttpClient:Http客户端对象类型,使用该类型对象可发起Http请求。
-
HttpClients:可认为是构建器,可创建HttpClient对象。
-
CloseableHttpClient:实现类,实现了HttpClient接口。
-
HttpGet:Get方式请求类型。
-
HttpPost:Post方式请求类型。
HttpClient发送请求步骤:
2、使用步骤
实现步骤:
-
创建HttpClient对象
-
创建请求对象
-
发送请求,接受响应结果
-
解析结果
-
关闭资源
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
| @Test public void testGet() throws IOException { CloseableHttpClient httpClient = HttpClients.createDefault();
HttpGet httpGet = new HttpGet("http://localhost:8080/user/shop/status");
CloseableHttpResponse response = httpClient.execute(httpGet);
int code = response.getStatusLine().getStatusCode(); System.out.println("服务端放回的状态码是:"+code);
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(); System.out.println("响应码为:"+code);
HttpEntity httpEntity = response.getEntity(); System.out.println("相应结果为:"+EntityUtils.toString(httpEntity));
response.close(); httpClient.close(); }
|
3、使用场景
比如服务端主动远程调用第三方验证码服务、调用第三方登录服务
五、Spring Cache
调用spring自带缓存机制,配合Redis使用
1、介绍
Spring Cache 是一个框架,实现了基于注解的缓存功能,只需要简单地加一个注解,就能实现缓存功能。
Spring Cache 提供了一层抽象,底层可以切换不同的缓存实现,例如:
-
EHCache
-
Caffeine
-
Redis(常用)
2、依赖
1 2 3 4
| <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> <version>2.7.3</version> </dependency>
|
3、常用注解
| 注解 |
说明 |
| @EnableCaching |
开启缓存注解功能,通常加在启动类上 |
| @Cacheable |
在方法执行前先查询缓存中是否有数据,如果有数据,则直接返回缓存数据;如果没有缓存数据,调用方法并将方法返回值放到缓存中 |
| @CachePut |
将方法的返回值放到缓存中 |
| @CacheEvict |
将一条或多条数据从缓存中删除 |
在spring boot项目中,使用缓存技术只需在项目中导入相关缓存技术的依赖包,并在启动类上使用**@EnableCaching**开启缓存支持即可。
例如,使用Redis作为缓存技术,只需要导入Spring data Redis的maven坐标即可。
4、使用顺序
-
首先在启动类上加**@EnableCaching**注解,表示开启缓存注解功能
![1]()
-
在对应添加方法中使用**@CachePut**注解
作用:在该用户信息保存到数据库的同时,也往缓存中缓存一份数据
![1]()
**说明:**key的写法如下
| key填写格式 |
解释 |
| #user.id |
#user指的是方法形参的名称, id指的是user的id属性 , 也就是使用user的id属性作为key |
| #result.id |
#result代表方法返回值,该表达式 代表以返回对象的id属性作为key |
| #p0.id |
#p0指的是方法中的第一个参数,id指的是第一个参数的id属性,也就是使用第一个参数的id属性作为key |
| #a0.id |
#a0指的是方法中的第一个参数,id指的是第一个参数的id属性,也就是使用第一个参数的id属性作为key |
| #root.args[0].id |
#root.args[0]指的是方法中的第一个参数,id指的是第一个参数的id属性,也就是使用第一个参数的id属性作为key |
-
在对应查询方法中使用**@Cacheable**注解
当执行方法时会先查询缓存中是否有key,有的话直接查到缓存中的值返回
![1]()
-
当对应保存、删除方法中加入**@CacheEvict**注解
当执行此方法的时候自动删除缓存中对应的key以及value![1]()
六、Spring Task
1、介绍
Spring Task 是Spring框架提供的任务调度工具,可以按照约定的时间自动执行某个代码逻辑。
**定位:**定时任务框架 **作用:**定时自动执行某段Java代码
应用场景:
-
信用卡每月还款提醒
-
生日当天发送生日祝福
-
外卖订单下单后长时间未支付,对订单状态定时处理
-
外卖收货后,商家没有及时点击送达。我们就可以用一个定时任务自动点击送达
2、cron表达式
cron表达式是一个字符串。其可以表示自定义任务被触发的时间
构成规则:
分为6或7个域(空格隔开):秒、分钟、小时、日、月、周、年(可选)
例子:
2022年10月12日上午9点整 对应的cron表达式为:0 0 9 12 10 ? 2022
说明:一般日和周的值不同时设置,其中一个设置,另一个用?表示。
生成复杂的cron表达式我们可以利用cron表达式在线生成器:https://cron.qqe2.com/
具体细则这里不讨论
3、引入依赖
spring-context 但一般不需要自已引入,spring-boot-starter自带
![2]()
4、编写定时任务类
@Scheduled(cron = “cron表达式”)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
@Component @Slf4j public class MyTask {
@Scheduled(cron = "0/5 * * * * ?") public void executeTask(){ log.info("定时任务开始执行:{}",new Date()); } }
|
5、启动类开启任务调度
@EnableScheduling
1 2 3 4 5 6 7 8 9 10 11
| @EnableCaching @SpringBootApplication @EnableTransactionManagement @Slf4j @EnableScheduling public class SkyApplication { public static void main(String[] args) { SpringApplication.run(SkyApplication.class, args); log.info("server started"); } }
|
七、WebSocket
1、介绍
WebSocket 是基于 TCP 的一种新的网络协议。它实现了浏览器与服务器全双工通信——浏览器和服务器只需要完成一次握手,两者之间就可以创建持久性的连接, 并进行双向数据传输。
WebSocket缺点:
服务器长期维护长连接需要一定的成本各个浏览器支持程度不一
WebSocket 是长连接,受网络限制比较大,需要处理好重连
**结论:**WebSocket并不能完全取代HTTP,它只适合在特定的场景下使用
使用场景:
-
视频弹幕。弹幕一般是实时更新
-
网页聊天。我们需要在网页端实时接收和发送聊天信息
-
股票、体育实况的数据更新,不需要刷新网页
-
外卖用户催单
2、HTML前端代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
| <!DOCTYPE HTML> <html> <head> <meta charset="UTF-8"> <title>WebSocket Demo</title> </head> <body> <input id="text" type="text" /> <button onclick="send()">发送消息</button> <button onclick="closeWebSocket()">关闭连接</button> <div id="message"> </div> </body> <script type="text/javascript"> var websocket = null; var clientId = Math.random().toString(36).substr(2);
if('WebSocket' in window){ websocket = new WebSocket("ws://localhost:8080/ws/"+clientId); } else{ alert('Not support websocket') }
websocket.onerror = function(){ setMessageInnerHTML("error"); };
websocket.onopen = function(){ setMessageInnerHTML("连接成功"); }
websocket.onmessage = function(event){ setMessageInnerHTML(event.data); }
websocket.onclose = function(){ setMessageInnerHTML("close"); }
window.onbeforeunload = function(){ websocket.close(); }
function setMessageInnerHTML(innerHTML){ document.getElementById('message').innerHTML += innerHTML + '<br/>'; }
function send(){ var message = document.getElementById('text').value; websocket.send(message); } function closeWebSocket() { websocket.close(); } </script> </html>
|
3、引入依赖
1 2 3 4
| <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency>
|
4、自定义配置类
1 2 3 4 5 6 7 8 9 10 11
|
@Configuration public class WebSocketConfiguration {
@Bean public ServerEndpointExporter serverEndpointExporter() { return new ServerEndpointExporter(); } }
|
5、编写WebSocket服务代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
|
@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); }
@OnMessage public void onMessage(String message, @PathParam("sid") String sid) { System.out.println("收到来自客户端:" + sid + "的信息:" + message); }
@OnClose public void onClose(@PathParam("sid") String sid) { System.out.println("连接断开:" + sid); sessionMap.remove(sid); }
public void sendToAllClient(String message) { Collection<Session> sessions = sessionMap.values(); for (Session session : sessions) { try { session.getBasicRemote().sendText(message); } catch (Exception e) { e.printStackTrace(); } } }
}
|
6、配合定时任务使用
1 2 3 4 5 6 7 8 9 10 11 12 13
| @Component public class WebSocketTask { @Autowired private WebSocketServer webSocketServer;
@Scheduled(cron = "0/5 * * * * ?") public void sendMessageToClient() { webSocketServer.sendToAllClient("这是来自服务端的消息:" + DateTimeFormatter.ofPattern("HH:mm:ss").format(LocalDateTime.now())); } }
|
八、 Apache POI
1、介绍
Apache POI 是一个处理Miscrosoft Office各种文件格式的开源项目。简单来说就是,我们可以使用 POI 在 Java 程序中对Miscrosoft Office各种文件进行读写操作。一般情况下,POI 都是用于操作 Excel 文件。
2、依赖
1 2 3 4 5 6 7 8 9 10
| <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>
|
3、写入Excel文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| public static void write() throws IOException { XSSFWorkbook excel = new XSSFWorkbook(); XSSFSheet sheet = excel.createSheet("info"); XSSFRow row = sheet.createRow(1);
row.createCell(1).setCellValue("姓名"); row.createCell(2).setCellValue("城市");
row = sheet.createRow(2); row.createCell(1).setCellValue("张三"); row.createCell(2).setCellValue("北京");
row = sheet.createRow(3); row.createCell(1).setCellValue("李四"); row.createCell(2).setCellValue("南京");
FileOutputStream out = new FileOutputStream("D:\\info.xlsx"); excel.write(out);
out.close(); excel.close(); }
|
![3]()
4、读取Excel文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| public static void read() throws IOException { FileInputStream inputStream = new FileInputStream(new File("D:\\info.xlsx"));
XSSFWorkbook excel = new XSSFWorkbook(inputStream); XSSFSheet sheet = excel.getSheetAt(0); int lastRowNum = sheet.getLastRowNum();
for(int i=1;i<=lastRowNum;i++){ XSSFRow row = sheet.getRow(i); String cellValue1 = row.getCell(1).getStringCellValue(); String cellValue2 = row.getCell(2).getStringCellValue(); System.out.println(cellValue1+" "+cellValue2);
excel.close(); inputStream.close(); } }
|
5、如何向浏览器发送文件(下载)
例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
|
public void exportBusinessData(HttpServletResponse response) { LocalDate begin = LocalDate.now().minusDays(30); LocalDate end = LocalDate.now().minusDays(1); BusinessDataVO businessData = workspaceService.getBusinessData(LocalDateTime.of(begin,LocalTime.MIN), LocalDateTime.of(end, LocalTime.MAX)); InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("template/运营数据报表模板.xlsx"); try { XSSFWorkbook excel = new XSSFWorkbook(inputStream); XSSFSheet sheet = excel.getSheet("Sheet1");
sheet.getRow(1).getCell(1).setCellValue(begin + "至" + end); XSSFRow row = sheet.getRow(3); row.getCell(2).setCellValue(businessData.getTurnover()); row.getCell(4).setCellValue(businessData.getOrderCompletionRate()); row.getCell(6).setCellValue(businessData.getNewUsers()); row = sheet.getRow(4); row.getCell(2).setCellValue(businessData.getValidOrderCount()); row.getCell(4).setCellValue(businessData.getUnitPrice()); for (int i = 0; i < 30; i++) { LocalDate date = begin.plusDays(i); 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()); } ServletOutputStream out = response.getOutputStream(); excel.write(out); out.flush(); out.close(); excel.close();
}catch (IOException e){ e.printStackTrace(); } }
|
实际上就是:
1 2 3 4 5 6
| HttpServletResponse response
ServletOutputStream out = response.getOutputStream();
excel.write(out);
|
九、Stream
Java的流式操作也是一个难点
1、样例1
1 2 3 4 5
| List<GoodsSalesDTO> goodsSalesDTOList = orderMapper.getSalesTop10(beginTime, endTime);
goodsSalesDTOList.stream() .map(GoodsSalesDTO::getName) .collect(Collectors.toList());
|
-
goodsSalesDTOList.stream():
goodsSalesDTOList 是一个包含多个 GoodsSalesDTO 对象的列表。stream() 方法将该列表转化为一个 Stream 对象,允许我们进行一系列的流式操作。
-
.map(GoodsSalesDTO::getName):
map 是一个中间操作,用来将流中的每个元素(在此为 GoodsSalesDTO 对象)转换成另一种类型的元素。
-
map() 是 Stream API 中的一个中间操作,它会对 Stream 中的每个元素应用指定的函数,并将结果映射为一个新的 Stream。
-
在这里,GoodsSalesDTO::getName 是一个方法引用,等价于对每个 GoodsSalesDTO 对象调用 getName() 方法。getName() 方法应该是 GoodsSalesDTO 类的一个成员方法,用于获取该对象的 name 属性。
-
这一步的目的是从每个 GoodsSalesDTO 对象中提取出 name 字段。
-
.collect(Collectors.toList()):
collect() 是 Stream API 中的一个终结操作,它会将流中的元素收集到一个集合中。Collectors.toList() 是一个收集器,指示流中的元素应该被收集到一个 List 中。
- 这一步会把通过
map() 提取出来的 name 字段收集到一个新的 List<String> 中。
这段代码的执行结果是:从 goodsSalesDTOList 中提取出每个 GoodsSalesDTO 对象的 name 属性,生成一个新的 List<String>,其中包含了所有商品的名称。
2、样例2
1 2 3
| Map<String, CourseCategoryTreeDto> mapTemp = courseCategoryTreeDtos.stream() .filter(item -> !id.equals(item.getId())) .collect(Collectors.toMap(key -> key.getId(), value -> value, (key1, key2) -> key2));
|