Java后端开发
文章平均质量分 91
SpringBoot、SSM框架、消息中间件、Nginx、Docker和K8S、ElasticSearch...
Java架构何哥
从事政务信息化、大数据治理、智慧党建相关工作
展开
-
哪些场景会产生OOM?线上遇到OOM怎么解决?
线上如遇到 JVM 内存溢出,可以分以下几步排查jmap -heap查看是否内存分配过小查看是否有明显的对象分配过多且没有释放情况jmap -dump导出 JVM 当前内存快照,使用 JDK 自带或 MAT 等工具分析快照如果上面还不能定位问题,那么需要排查应用是否在不断创建资源,比如网络连接或者线程,都可能会导致系统资源耗尽如果线上遇到了OOM,该如何解决?Arthas(阿尔萨斯)的基本使用。原创 2024-08-04 23:27:36 · 1198 阅读 · 0 评论 -
Spring循环依赖解决方案
循环依赖指的是两个或者多个bean之间相互依赖,形成一个闭环。直接表现为两个service层互相调用对方。一般场景是一个Bean A依赖Bean B,而Bean B也依赖Bean A.当然我们也可以添加更多的依赖层次,比如:有很多种方法来应对Spring的循环依赖。但考虑的第一件事就是重新设计你的bean,所以没有必要循环依赖:他们通常是可以提高设计的一种症状。但是,如果你在你的项目中确实是需要有循环依赖,那么你可以遵循一些这里提出的解决方法。痛快!SpringBoot终于禁掉了循环依赖。原创 2023-11-08 15:32:04 · 238 阅读 · 0 评论 -
SpringBoot集成Thymeleaf模板引擎的html、css和js存放位置
如我的页面为hello-world.html,它所在的目录结构就应该为resource–> templates --> hello-world.html在controller中,return的值则为 /hello-world ,系统会自动给加上.html的后缀。css引入增加 th:href配置路径,js引入增加th:src路径配置。我的页面中本来都是直接使用的element-ui和vue直接提供的在线js,但是因为页面嵌套会有跨域的问题,所以页面中的js、css都要改为使用本地文件。原创 2023-04-12 21:24:31 · 3137 阅读 · 0 评论 -
工作中常用的Stream集合处理
Java8的新特性主要是Lambda表达式和流,当流和Lambda表达式结合起来一起使用时,因为流申明式处理数据集合的特点,它允许把函数作为一个方法的参数,让我们的代码更优雅简洁。Java8最大的特性就是引入Lambda表达式,即函数式编程,可以将行为进行传递。总结就是:使用不可变值与函数,函数对不可变值进行处理,映射成另一个值。原创 2023-02-13 23:30:39 · 646 阅读 · 0 评论 -
【线上故障记录】java.lang.OutOfMemoryError: GC overhead limit exceeded和Java heap space
Caused by: java.lang.OutOfMemoryError: GC overhead limit exceeded 超出GC开销限制原因:详细消息“超出 GC 开销限制”表示垃圾收集器一直在运行,Java 程序进展非常缓慢。在垃圾收集之后,如果Java进程花费大约 98% 以上的时间进行垃圾收集,并且如果它正在恢复堆的不到 2%,并且到目前为止一直在进行最后5次(编译时常量)连续垃圾收集,则Java进程将停止。将抛出 lang.OutOfMemoryError。原创 2023-01-03 14:36:51 · 6518 阅读 · 0 评论 -
SpringBoot自定义logback日志配置
前言:默认情况下,SpringBoot内部使用logback作为日志实现的框架,将日志输出到控制台,不会写到日志文件。如果在application.properties或application.yml配置,这样只能配置简单的场景,保存路径、日志格式等。复杂的场景(区分 info 和 error 的日志、每天产生一个日志文件等)满足不了,只能自定义配置文件logback-spring.xml。一、application.properties简单配置logback日志1.1、SpringBoot默认的日原创 2022-01-20 17:17:20 · 16222 阅读 · 0 评论 -
SpringBoot使用AOP记录接口操作日志
前言:我们项目中可能有这种需求,每个人请求了哪些接口?做了什么事情?参数是什么?重要的接口我们需要记日志以便查找。我们不可能在每个接口中去一一处理,可以借助Spring提供的AOP能力+自定义注解轻松应对。...原创 2022-01-07 01:03:35 · 21749 阅读 · 3 评论 -
SpringBoot集成knife4j实现Swagger接口文档
前言:如果你是后台开发,提供restful接口给前端,建议你使用Swagger3提供restful的接口文档自动生成和在线接口调试。knife4j是对Swagger进一步封装,其优化了API文档的UI界面,是本人最推荐的方式。一、Swagger简介1.1、SwaggerSwagger是一个规范和完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Web 服务。Swagger拥有接口文档自动生成和在线调试接口的两大功能。Swagger拥有众多不同语言和平台的开源实现与工具,他有..原创 2022-01-06 02:24:44 · 6733 阅读 · 0 评论 -
SpringBoot利用@Async注解实现异步调用
前言:异步编程是让程序并发运行的一种手段。它允许多个事情同时发生,当程序调用需要长时间运行的方法时,它不会阻塞当前的执行流程,程序可以继续运行,当方法执行完成时通知给主线程根据需要获取其执行结果或者失败异常的原因。使用异步编程可以大大提高我们程序的吞吐量,可以更好的面对更高的并发场景并更好的利用现有的系统资源,同时也会一定程度上减少用户的等待时间等。一、异步编程1.1、什么是异步调用?异步调用是相对于同步调用而言的,同步调用是指程序按预定顺序一步步执行,每一步必须等到上一步执行完后才能执行,异步原创 2022-01-04 02:08:39 · 8830 阅读 · 5 评论 -
SpringBoot Validation优雅的参数校验
前言:大多数项目中都需要后台对传过来的对象进行校验,所以经常需要写一些字段校验的代码,比如特殊字段非空、字段长度限制和邮箱格式验证等等。之前我们可能都是使用if…else…,写这些与业务逻辑关系不大的代码,不仅验证代码繁琐而且重复劳动,今天我们了解一下Hibernate-Validator的@validated注解。一、Hibernate-Validator简介1.1、为什么需要参数校验在日常的接口开发中,为了防止非法参数对业务造成影响,经常需要对接口的参数做校验,例如登录的时候需要校验用户名密原创 2022-01-02 01:40:01 · 7842 阅读 · 0 评论 -
@ControllerAdvice 和 @ExceptionHandler注解处理全局异常
前言:@ControllerAdvice ,很多初学者可能都没有听说过这个注解,实际上,这是一个非常有用的解,顾名思义,这是一个增强的 Controller。一般配合@ExceptionHandler使用来处理全局异常。一、@ControllerAdvice@ControllerAdvice 是Spring 3.2提供的新注解,他是一个controller增强器,可以对controller中使用到@RequestMapping注解的方法做逻辑处理。使用这个注解 ,可以实现三个方面的功能:原创 2021-11-26 17:20:18 · 12358 阅读 · 5 评论 -
SpringMVC中get和post方法获取参数的几种方式
前言:get与post两种请求方法的显著区别就是,get请求方式参数是在url后,而post请求方式的参数是在request body中。因此两者获取参数的方式也大不一样,如果在post方法用了get的传值方式,可能出现未找到请求URI匹配的HTTP资源的异常。定义一个User类备用@Getter@Setter@AllArgsConstructor@NoArgsConstructorpublic class User implements Serializable{ privat原创 2021-11-26 16:57:39 · 6150 阅读 · 0 评论 -
SpringBoot快速集成JPA
前言:一说JavaWeb,很多小伙伴都知道SSH,这个H代表的就是Hibernate框架,可是什么又是JPA呢?相信许多刚入门的小伙伴听说过但不是特别清楚,首先JPA的全称叫做Java Persistence API,JPA是一个基于O/R映射的标准规范,在这个规范中,JPA只定义标准规则,不提供实现,使用者则需要按照规范中定义的方式来使用。目前JPA的主要实现有Hibernate、EclipseLink、OpenJPA等,事实上,由于Hibernate在数据访问解决技术领域的霸主地位,JPA的标准基本是由原创 2021-09-23 01:50:08 · 695 阅读 · 0 评论 -
OAuth2第三方登录快速接入
前言:现在很多网站和App都支持第三方登录功能,这里以GitHub第三方登录举例,因为注册应用申请ID比微信和QQ简单。目前市面上主流的第三方登录协议就是 OAuth2.0, 例如 QQ,微信,微博等等。 所以只要搞明白大概流程,那么接入其他供应商的第三方登录也是小菜一碟了。一、OAuth 2.0说到第三方登录,不得不提的一个知识点就是 oauth 2.0。OAuth(开放授权)是一个开放标准,允许用户让第三方应用访问该用户在某一网站上存储的私密的资源(如照片,视频,联系人列表),而无需将用.原创 2021-09-22 17:15:19 · 7198 阅读 · 3 评论 -
Netty快速入门攻略
一、Netty简介1.1、Netty概述Netty 是一个基于nio的客户、服务器端编程框架,Netty提供异步的,事件驱动的网络应用程序框架和工具,可以快速开发高可用的客户端和服务器。Netty是基于nio的,它封装了jdk的nio,让我们使用起来更加方便灵活。Netty是由jboss提供的一款开源框架,常用于搭建RPC中的TCP服务器、WebSocket服务器,甚至是类似Tomcat的web服务器,反正就是各种网络服务器,在处理高并发的项目中,功能丰富且性能良好,基于Java中NIO的二原创 2021-09-18 02:33:40 · 1924 阅读 · 0 评论 -
WebSocket服务端消息推送
一、Web端实现即时消息推送五种方式股票曲线实时变化,在线IM聊天等等,Web系统里总是能见到消息推送的应用。消息推送用好了能增强用户体验,实现消息推送有N种解决方案。1.1、什么是消息推送消息推送(Push)指运营人员通过自己的产品或第三方工具对用户当前网页或移动设备进行的主动消息推送。用户可以在网页上或移动设备锁定屏幕和通知栏看到push消息通知。以此来实现用户的多层次需求,使得用户能够自己设定所需要的信息频道,得到即时消息,简单说就是一种定制信息的实现方式。我们平时浏览邮箱时突然弹出消息原创 2021-09-18 01:47:35 · 15647 阅读 · 3 评论 -
数据库表设计3:微信、QQ第三方多账号登陆
前言:相比于本地注册,第三方登录一般来说比较方便快捷。因为显著降低了用户的注册或登录成本,从而减少由于本地注册的繁琐性而带来的隐形用户流失,最终提高注册转化率。1. 什么是第三方登录所谓的第三方登录,是说基于用户在第三方平台上已有的账号和密码来快速完成己方应用的登录或者注册的功能。而这里的第三方平台,一般是已经拥有大量用户的平台,国外的比如Facebook,Twitter等,国内的比如微博、微信、QQ等。使用第三方登录后,要求用户补充一些信息(手机号、邮箱等等),如果用户信息已存在,则直接绑原创 2021-09-01 12:44:54 · 8445 阅读 · 2 评论 -
数据库表设计2:用户关注粉丝
前言:在社交类系统中,用户与用户的好友关系的设计必不可少,那么如何设计好友的数据库至关重要,本篇文章带大家学习一下微博相关的设计方案。一、用户好友关系分类基础分析第一步,有一张用户表,表内包含用户的基本信息,比如账号、姓名、性别等信息。这里用tb_user表示用户信息表。ID 用户名1 张三2 李四3 王五4 赵六第二步,需要将用户与用户直接建立好友关系。这里有两种情况:单向好友关系、互为好友关系。- 单向好友关系就是张三在李四的好友列表中,但李四...原创 2021-09-01 00:06:04 · 4992 阅读 · 0 评论 -
数据库表设计1:用户权限管理
前言:权限管理是所有后台系统的都会涉及的一个重要组成部分,主要目的是对不同的人访问资源进行权限的控制,避免因权限控制缺失或操作不当引发的风险问题,如操作错误,隐私数据泄露等问题。1、RBAC简介RBAC(Role-Based Access Control,基于角色的访问控制),通过角色关联用户,角色关联权限,间接的赋予用户的权限。简单地说,一个用户拥有若干角色,每一个角色拥有若干权限。这样,就构造成“用户-角色-权限”的授权模型。在这种模型中,用户与角色之间,角色与权限之间,一般者是多对多的原创 2021-08-30 23:39:06 · 4518 阅读 · 1 评论 -
SSM框架和SpringBoot高频率面试题
一、Spring面试题1、Spring 在SSM框架中起什么作用? Spring:Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架。 作用:Bean工厂,用来管理Bean的生命周期和框架集成。 两大核心:1、IOC/DI(控制反转/依赖注入) :把dao依赖注入到service层,service层反转给action层,Spring顶层容器为BeanFactory。2、AOP:面向切面编程 2、为什么使用Spring?①方便解耦,简化开发(IOC原创 2021-04-15 02:20:17 · 2523 阅读 · 0 评论 -
SpringBoot 快速集成 JWT 实现用户登录认证
前言:当今前后端分离时代,基于Token的会话保持机制比传统的Session/Cookie机制更加方便,下面我会介绍SpringBoot快速集成JWT库java-jwt以完成用户登录认证。一、JWT 简介1.1、 JWT的概念JWT 是 JSON Web Token 的缩写,是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519)。定义了一种简洁的,自包含的方法用于通信双方之间以JSON对象的形式安全的传递信息。因为数字签名的存在,这些信息是可信的,JWT...原创 2021-04-11 21:50:56 · 20616 阅读 · 6 评论 -
SpringBoot 快速集成 Mybatis-Plus 框架
前言:今天给大家带来一篇如何在 Spring Boot 中快速整合 Mybatis-Plus 的入门教程。MyBatis-Plus是一个 MyBatis 的增强工具包,自动注入单表基本的CRUD操作,大大解放了开发者的生产力。一、MyBatis-Plus框架简介1.1、回顾 MyBatis在说 MyBatis-Plus 之前,先回顾一下什么是 MyBatis:MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC原创 2021-04-08 01:22:38 · 1019 阅读 · 0 评论 -
API接口的RESTful设计
前言:在移动互联网、分布式和微服务盛行的今天,现在好多大点的项目都会采用的微服务框架,前后端分离方式。前后端的工作职责越来越明确,现在的前端都称之为大前端,技术栈以及生态圈都已经非常成熟,可以进行移动端跨平台开发,也可以通过node.js写服务端的Controller。一般系统的大致整体架构图如下:需要说明的是,这个架构也太简单了吧,什么网关、Redis缓存、MQ消息中间件都没有。因为这篇主要介绍的是API接口,所以我们聚焦点,其他的模块小伙伴们自行去补充。1、前后端接口交互前端和后端进原创 2021-03-26 18:03:21 · 2771 阅读 · 1 评论 -
SpringMVC的使用技巧总结
一、Spring MVC项目无法引入js,css的问题具体原因是css和js等被SpringMVC拦截了:解决方案:在spring-mvc.xml中配置<mvc:default-servlet-handler/><?xml version="1.0" encoding="UTF-8"?><beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.spr原创 2021-03-25 17:51:35 · 401 阅读 · 0 评论 -
Tomcat自动生成会话JSESSIONID
一、request.getSession(true)和request.getSession(false)的区别request.getSession(true):若存在会话则返回该会话,否则新建一个会话,默认为true;request.getSession(false):若存在会话则返回该会话,否则返回NULL;当向Session中存放登录信息时,一般建议:HttpSession session =request.getSession();当从Session中获取登录信息时,一般建议:Htt原创 2021-03-25 15:24:11 · 2428 阅读 · 0 评论 -
Http会话保持机制:Cookie、Session和Token
前言:因为http本身是无状态协议,这样,无法确定你的本次请求和上次请求是不是你发送的。如果要进行类似论坛登陆相关的操作,就实现不了。Http会话持久是Web程序中常用的技术,用来跟踪用户的整个会话。以前常用的会话跟踪技术是Cookie与Session,现在可以用Token机制。一、session和cookie的状态保持及弊端当用户第一次通过浏览器使用用户名和密码访问服务器时,服务器会验证用户数据,验证成功后在服务器端写入session数据,向客户端浏览器返回sessionid,浏览器将sessio原创 2021-03-25 10:57:31 · 7295 阅读 · 0 评论 -
开发API接口的安全验证:token,参数签名,时间戳
前言:服务端与前端对接的API接口,如果被第三方抓包并进行恶意篡改参数,可能会导致数据泄露和篡改数据,下面主要围绕token,签名,时间戳,三个部分来保证API接口的安全性。参考链接,致敬前辈:开放API接口签名验证,让你的接口从此不再裸奔API接口的安全设计验证:ticket,签名,时间戳1.用户成功登陆站点后,服务器会返回一个token,用户的任何操作都必须带了这个参数,可以将这个参数直接放到header里。2.客户端用需要发送的参数和token生成一个签名sign,作为参原创 2021-03-18 17:24:08 · 18013 阅读 · 13 评论 -
Java状态码枚举类
前言:HTTP状态码(HTTP Status Code)是用以表示网页服务器超文本传输协议响应状态的3位数字代码。它由 RFC 2616 规范定义的,所有状态码的第一个数字代表了响应的五种状态之一。当浏览者访问一个网页时,浏览者的浏览器会向网页所在服务器发出请求。当浏览器接收并显示网页前,此网页所在的服务器会返回一个包含HTTP状态码的信息头(server header)用以响应浏览器的请求。1、常用HttpStatus状态码 HttpStatus.OK = 200; HttpSt原创 2021-03-23 15:35:52 · 50878 阅读 · 0 评论 -
Java使用定时任务
前言:Java开发过程中经常会遇到使用定时任务的情况,比如在某个活动结束时,自动生成获奖名单,导出excel等。常见的有如下四种方式:Timer、ScheduledExecutorService、SpringTask、Quartz。Java定时任务的四种实现方式(1)JDK 自带的定时器实现(2)Quartz 定时器实现(3)Spring Task相关的任务调度-JDK自带:JDK自带的Timer以及JDK1.5+ 新增的ScheduledExecutorService;-...原创 2021-03-23 17:11:03 · 5287 阅读 · 0 评论 -
SpringBoot快速实现发送邮件
前言:Spring提供了非常好用的JavaMailSender接口实现邮件发送。由于SpringBoot的Starter模块也为此提供了自动化配置,所以在引入了spring-boot-starter-mail依赖之后,会根据配置文件中的内容去创建JavaMailSender实例,因此我们可以直接在需要使用的地方直接@Autowired来引入邮件发送对象。SpringBoot中发送邮件具体的使用步骤如下1、添加Starter模块依赖2、添加Spring Boot邮箱配置(QQ/网易163/Gma原创 2021-03-23 14:47:25 · 22581 阅读 · 2 评论 -
Java设计安全的登录接口
前言:大家开始写Web后台技术时,很多人的第一个功能就是写的登录/注册功能模块,但一般都只简单的实现了功能逻辑,在安全方面并没有考虑太多。安全风险一:暴力破解只要网站是暴露在公网的,那么很大概率上会被人盯上,尝试爆破这种网站简单且有效的方式:通过各种方式获得了网站的用户名之后,通过编写程序来遍历所有可能的密码,直至找到正确的密码为止。伪代码如下:#密码字典password_dict=[]#登录接口login_url=''defattack(userna...原创 2021-03-14 00:02:43 · 1654 阅读 · 0 评论 -
JavaScript和JSP使用技巧总结
前言:公司前端只会BootStrap写的html+css,不是前后端分离的开发模式,所以只能自己搞JSP和JavaScript,有点浪费光阴。不过心态要摆正,既来之则安之,总结几个常用的JavaScript和JSP的常用知识以便日后查看。1、如何等待ajax异步完成再执行相应操作要先引入jQuery.js //ajax操作 myajax = $.ajax( { url: "", async:false, ty原创 2021-03-10 16:36:08 · 1380 阅读 · 0 评论 -
浅谈常见的七种加密算法及Java实现
前言:数字签名、信息加密是前后端开发都经常需要使用到的技术,应用场景包括了用户登入、交易、信息通讯、oauth等等,不同的应用场景也会需要使用到不同的签名加密算法,或者需要搭配不一样的签名加密算法来达到业务目标。这里简单的给大家介绍几种常见的签名加密算法和一些典型场景下的应用。一、数字签名数字签名,简单来说就是通过提供可鉴别的数字信息验证自身身份的一种方式。一套数字签名通常定义两种互补的运算,一个用于签名,另一个用于验证。分别由发送者持有能够代表自己身份的私...转载 2020-11-21 17:01:35 · 1253 阅读 · 0 评论 -
SpringBoot 项目部署到服务器的两种方式
前言:目前,前后端分离的架构已成主流,而使用SpringBoot构建Web应用是非常快速的,项目发布到服务器上的时候,只需要打成一个jar包,然后通过命令 : java -jar jar包名称即可启动服务了。一、jar包(官方推荐)SpringBoot项目默认打包成jar包jar包方式启动,也就是使用SpringBoot内置的tomcat运行。服务器上面只要你配置了jdk1.8及以上就ok,不需要外置tomcat。1、SpringBoot将项目打包成jar包a.首先在pom.xm原创 2020-06-23 02:35:04 · 34499 阅读 · 3 评论 -
SpringBoot 2.x整合Redis使用
前言:SpringBoot 2.0已经使用Lettuce代替Jedis客户端。Spring框架的spring-data-redis的相关Jar包,不仅支持连接池自动管理,而且它还提供了使用Redis的模版RedisTemplate<K, V>接口或它的实现类StringRedisTemplate,可以支持Redis没有的缓存对象的操作。也就是说,我们可以在SpringBoot应用中...原创 2019-10-22 09:39:01 · 1742 阅读 · 0 评论 -
SpringBoot快速入门攻略
SpringBoot的概念:Spring Boot 是所有基于 Spring 开发的项目的起点。Spring Boot 基于“习惯优于配置”的设计理念,让你尽可能快的跑起来 Spring 应用程序并且尽可能减少你的配置文件。它并不是什么新的框架,而是默认配置了很多框架的使用方式,就像 Maven 整合了所有的 jar 包一样,Spring Boot 整合了所有框架。一、SpringB...原创 2019-10-20 10:40:36 · 595 阅读 · 2 评论 -
SSM框架自学(九)——Spring使用注解代替复杂的XML配置文件
前言:前面的例子我们都是使用XML的bean定义来配置组件。在一个稍大的项目中,通常会有上百个组件,如果这些组件采用XML的bean定义来配置,显然会增加配置文件的体积,查找及维护起来也不太方便。Spring建议最好使用注解+组件自动扫描机制的方式代替复杂的xml配置文件,来实现beans自动装配进IOC容器来管理。举个栗子: 小王:想要把一个bean交给Spring去管理,你想怎...原创 2019-10-19 19:10:51 · 1605 阅读 · 1 评论 -
SSM框架自学(八)——SpringMVC如何返回JSON格式的数据
前言:在如今前后端分离的趋势下,后端基本不需要再去关心前端页面的事情,只需要把数据处理好并通过相应的接口返回数据给前端即可。在SpringMVC中,我们可以通过@ResponseBody注解来返回JSON数据或者是XML数据。一般返回的是更轻量级的JSON 格式数据,移动端的Android、IOS客户端和Web网页前端都可以利用JSON解析器很好地解析出数据并展示。相同的后台接口适用于不同的前端使...原创 2019-10-18 07:52:34 · 11341 阅读 · 0 评论 -
SSM框架自学(七)——Junit单元测试工具使用总结
前言:Junit是单元测试工具,在项目开发中是经常用到的,比如SSM项目中在编写好每一个 Dao 的时候,我们都需要对它进行单元测试。我们使用Junit进行单元测试的话不需要启动Tomcat运行整个SSM项目,只需要测试能不能正确从数据库中取出数据就行,这样减少了启动Tomcat的时间从而提高了开发效率。利用JUnit4进行单元测试非常简单方便,所以熟悉Junit是很有必要的。一、单元测试和集...原创 2019-10-17 22:32:09 · 2254 阅读 · 0 评论 -
SSM框架自学(六)——Log4j日志系统工具使用总结
前言:前面使用Maven搭建SSM框架的时候我们曾经配置过log4j.properties文件,那么这个文件主要是干什么的呢?配置的格式有哪些?日志是大型Web应用中不可缺少的部分,Apache的开源项目Log4J是一个功能强大的日志组件,提供方便的日志记录。一、Log4j简介1、Log4j的概念Log4J 是 Apache 的一个开源项目,通过在项目中使用 Log4J,我们可以控制...原创 2019-10-17 21:25:35 · 2023 阅读 · 0 评论