编辑
2023-12-06
实用工具
00

Bean

BeanUtils

java
package com.ruoyi.common.utils.bean; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Bean 工具类 * * @author ruoyi */ public class BeanUtils extends org.springframework.beans.BeanUtils { /** Bean方法名中属性名开始的下标 */ private static final int BEAN_METHOD_PROP_INDEX = 3; /** * 匹配getter方法的正则表达式 */ private static final Pattern GET_PATTERN = Pattern.compile("get(\\p{javaUpperCase}\\w*)"); /** * 匹配setter方法的正则表达式 */ private static final Pattern SET_PATTERN = Pattern.compile("set(\\p{javaUpperCase}\\w*)"); /** * Bean属性复制工具方法。 * * @param dest 目标对象 * @param src 源对象 */ public static void copyBeanProp(Object dest, Object src) { try { copyProperties(src, dest); } catch (Exception e) { e.printStackTrace(); } } /** * 获取对象的setter方法。 * * @param obj 对象 * @return 对象的setter方法列表 */ public static List<Method> getSetterMethods(Object obj) { // setter方法列表 List<Method> setterMethods = new ArrayList<Method>(); // 获取所有方法 Method[] methods = obj.getClass().getMethods(); // 查找setter方法 for (Method method : methods) { Matcher m = SET_PATTERN.matcher(method.getName()); if (m.matches() && (method.getParameterTypes().length == 1)) { setterMethods.add(method); } } // 返回setter方法列表 return setterMethods; } /** * 获取对象的getter方法。 * * @param obj 对象 * @return 对象的getter方法列表 */ public static List<Method> getGetterMethods(Object obj) { // getter方法列表 List<Method> getterMethods = new ArrayList<Method>(); // 获取所有方法 Method[] methods = obj.getClass().getMethods(); // 查找getter方法 for (Method method : methods) { Matcher m = GET_PATTERN.matcher(method.getName()); if (m.matches() && (method.getParameterTypes().length == 0)) { getterMethods.add(method); } } // 返回getter方法列表 return getterMethods; } /** * 检查Bean方法名中的属性名是否相等。<br> * 如getName()和setName()属性名一样,getName()和setAge()属性名不一样。 * * @param m1 方法名1 * @param m2 方法名2 * @return 属性名一样返回true,否则返回false */ public static boolean isMethodPropEquals(String m1, String m2) { return m1.substring(BEAN_METHOD_PROP_INDEX).equals(m2.substring(BEAN_METHOD_PROP_INDEX)); } }
编辑
2023-12-06
学习记录
00

前提

AOP,全称是Aspect Oriented Programming,即面向切面编程。AOP的目的是将那些与业务无关,但是业务模块都需要的功能,如日志统计、安全控制、事务处理等,封装成可重用的组件,从而将它们从业务逻辑代码中划分出来,编写成独立的切面。这样做,既可以保持业务逻辑的纯净和高内聚性,又可以使得系统的多个模块都可以共享这些公共的功能。 Spring框架提供了对AOP的支持,Spring Boot自然也不例外。使用Spring Boot的AOP功能,我们可以在运行时动态地将代码横向切入到各个关注点(方法或者类)中。这种横向切面的方式,比传统的纵向切面(继承)更加灵活。

依赖

xml

java
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> </dependency>
编辑
2023-12-05
学习记录
00

前提

redis是我们经常使用的一个中间件,他是内存数据库,使用中经常会存一些list或者其他数据结构,内容很多的话,就会产生大key

导致问题

  1. 内存占用过高:大Key会占用大量的内存空间,可能导致可用内存不足,从而触发内存淘汰策略。在极端情况下,可能导致内存耗尽,Redis实例崩溃,影响系统的稳定性。
  2. 性能下降:大Key会占用大量内存空间,导致内存碎片增加,进而影响Redis的性能。对于大Key的操作,如读取、写入、删除等,都会消耗更多的CPU时间和内存资源,进一步降低系统性能。
  3. 阻塞其他操作:某些对大Key的操作可能会导致Redis实例阻塞。例如,使用DEL命令删除一个大Key时,可能会导致Redis实例在一段时间内无法响应其他客户端请求,从而影响系统的响应时间和吞吐量。
  4. 网络拥塞:每次获取大key产生的网络流量较大,可能造成机器或局域网的带宽被打满,同时波及其他服务。例如:一个大key占用空间是1MB,每秒访问1000次,就有1000MB的流量。
  5. 主从同步延迟:当Redis实例配置了主从同步时,大Key可能导致主从同步延迟。由于大Key占用较多内存,同步过程中需要传输大量数据,这会导致主从之间的网络传输延迟增加,进而影响数据一致性。
  6. 数据倾斜:在Redis集群模式中,某个数据分片的内存使用率远超其他数据分片,无法使数据分片的内存资源达到均衡。另外也可能造成Redis内存达到maxmemory参数定义的上限导致重要的key被逐出,甚至引发内存溢出。

解决方式

  1. 压缩 适用于字符串类型的 redis key。采用压缩算法,将 key 压缩至可接受的范围内。压缩也是有讲究的,首先要选择无损的压缩算法,然后在压缩速率和压缩率之间也要权衡。比较常用的压缩算法/工具如下:

google snappy:无损压缩,追求压缩速度而不是压缩率(Compression rate) message pack:无损压缩,仅适用于 json 字符串的压缩,可以得到一个更小的 JSON,官网是:msgpack.org

  1. 分割 适用于 list,set,hash 等容器类型的 redis key。规范要求容器的元素数量 < 5000,我们可以在写 redis 的时候做个逻辑,如果超过了 5000 的容器就做切片。

  2. 抛弃 redis不存储过大的,可以直接查询数据库,或者是将不太重要的数据存储再Mongodb中,直接从mongodb查询

编辑
2023-12-05
学习记录
00

前提

在项目中有很多情况下需要使用多线程的方式执行任务,那么有什么比较好的方法来判断多线程任务执行完毕,我们再进行下一步的操作,之前的话我会设置等待时间来等待

java
executor.awaitTermination(30, TimeUnit.MINUTES);

方案

  • 使用 getCompletedTaskCount() 统计出【已完成任务数】和使用Java线程池中的getTaskCount() 方法来获取【总任务数】,二者进行对比即可。
  • 使用 FutureTask对象 ,等待所有任务都执行完,线程池的任务就都执行完了。
  • 使用 CountDownLatch对象 或 CyclicBarrier对象,等待所有线程都执行完之后,再执行后续流程,计数。
  • 使用isTerminated() 方法。利用线程池的终止状态(TERMINATED)来判断线程池的任务是否已经全部执行完,但想要线程池的状态发生改变,就需要调用线程池的 shutdown() 方法,不然线程池一直会处于 RUNNING 运行状态,那就没办法使用终止状态来判断任务是否已经全部执行完了,shutdown() 方法是启动线程池有序关闭的方法,它在完全关闭之前会执行完之前所有已经提交的任务,并且不会再接受任何新任务。当线程池中的所有任务都执行完之后,线程池就进入了终止状态,调用 isTerminated() 方法返回的结果就是 true 了,以这点作为依据来判断即可。
编辑
2023-12-05
遇到的问题
00

前提

在使用mongodb排序的时候需要构建一个sort对象,传入query.with()方法中,实现排序功能,但是直接new对象会提示

'Sort(org.springframework.data.domain.Sort.Direction, java.util.List<java.lang.String>)' has private access in 'org.springframework.data.domain.Sort'

因为springboot2.2.1(含)以上的版本Sort已经不能再实例化了,构造方法已经是私有的了!

可以改用Sort.by获得Sort对象。

解决方法

使用Sort s=Sort.by()方法创建对象by方法的底层:

java
public static Sort by(List<Sort.Order> orders) { Assert.notNull(orders, "Orders must not be null!"); return orders.isEmpty() ? unsorted() : new Sort(orders); }