查看原文
其他

不要重复造轮子?提高生产效率!3个常用的开源工具库分享

Guide哥 JavaGuide 2022-03-15

我们实际项目开发中是比较忌讳造轮子的,但是,自己在学习过程中造轮子绝对是对自己百利而无一害的!造轮子是一种特别能够提高自己系统编程能力的手段。

今天就分享几个我常用的开源工具库,希望对小伙伴们有帮助!

  1. OSHI[1] :一款为 Java 语言提供的基于 JNA 的(本机)操作系统和硬件信息库。
  2. EasyExcel[2] :一款快速、简单避免 OOM 的 java 处理 Excel 工具。
  3. Hutool[3] : 一个非常实用的 Java 工具类库,对文件、流、加密解密、转码、正则、线程、XML 等 JDK 方法进行了封装。

以下是较为详细一点的介绍,建议小伙伴们看完,方便自己快速上手,用在自己的项目中来提高生产效率。

oshi

介绍

OSHI 是一款为 Java 语言提供的基于 JNA 的(本机)操作系统和硬件信息库。

JNA(Java Native Access)[4]是一个开源的 Java 框架,是 Sun 公司推出的一种调用本地方法的技术,是建立在经典的 JNI 基础之上的一个框架。之所以说它是 JNI 的替代者,是因为 JNA 大大简化了调用本地方法的过程,使用很方便,基本上不需要脱离 Java 环境就可以完成。

JNI(Java Native Interface) 是 JDK 提供的一个编程接口,它允许 Java 程序调用其他语言编写的程序或者代码库,其实 JDK 本身的实现也大量用到 JNI 技术来调用本地 C 程序库。

通过 OSHI ,我们不需要安装任何其他本机库,就能查看内存和 CPU 使用率、磁盘和分区使用情况、设备、传感器等信息。

OSHI 旨在提供一种跨平台的实现来检索系统信息,支持 Windows、Linux、MacOS、Unix 等主流操作系统。

官方是这样介绍 oshi 的:(翻译 Chrome 插件:Mate Translate):

使用 oshi 你可以轻松制作出项目常用的系统监控功能,如下图所示:

引入依赖

Maven

<!-- https://mvnrepository.com/artifact/com.github.oshi/oshi-core -->
<dependency>
    <groupId>com.github.oshi</groupId>
    <artifactId>oshi-core</artifactId>
    <version>5.2.5</version>
</dependency>

Gradle

// https://mvnrepository.com/artifact/com.github.oshi/oshi-core
compile group: 'com.github.oshi'name: 'oshi-core'version: '5.2.5'

功能演示

获取硬件信息对象HardwareAbstractionLayer

//系统信息
SystemInfo si = new SystemInfo();
//操作系统信息
OperatingSystem os = si.getOperatingSystem();
//硬件信息
HardwareAbstractionLayer hal = si.getHardware();

有了代表硬件信息的对象HardwareAbstractionLayer 之后,我们就可以获取硬件相关的信息了!

下面简单演示一下获取内存和 CPU 相关信息。

1.获取内存相关信息

//内存相关信息
GlobalMemory memory = hal.getMemory();
//获取内存总容量
String totalMemory = FormatUtil.formatBytes(memory.getTotal());
//获取可用内存的容量
String availableMemory = FormatUtil.formatBytes(memory.getAvailable());

有了内存总容量和内存可用容量,你就可以计算出当前内存的利用率了。

2.获取 CPU 相关信息

//CPU相关信息
CentralProcessor processor = hal.getProcessor();
//获取CPU名字
String processorName = processor.getProcessorIdentifier().getName();
//获取物理CPU数
int physicalPackageCount = processor.getPhysicalPackageCount();
//获取物理核心数
int physicalProcessorCount = processor.getPhysicalProcessorCount();

EasyExcel

介绍

Java 解析、生成 Excel 常用的框架有 Apache poi、jxl ,但是这两个框架使用起来都不够优雅,并且非常耗内存严重时会导致内存溢出

怎么解决这个问题呢?

推荐你使用阿里开源的 EasyExcel。正如这个项目官网介绍的那样,这是一款快速、简单避免 OOM 的 java 处理 Excel 工具。

官方是这样介绍 EasyExcel 的:

引入依赖

Maven

<!-- https://mvnrepository.com/artifact/com.alibaba/easyexcel -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>easyexcel</artifactId>
    <version>2.2.6</version>
</dependency>

Gradle

// https://mvnrepository.com/artifact/com.alibaba/easyexcel
compile group: 'com.alibaba'name: 'easyexcel'version: '2.2.6'

功能演示

这里直接分享官方提供读取 Excel 的例子(在实际项目中我对这部分做了简单的封装,涉及的地方比较多,就不分享出来了)。

实体对象 (Excel 导入导出实体对象)

@Data
public class DemoData {
    private String string;
    private Date date;
    private Double doubleData;
}

监听器 (自定义 AnalysisEventListener 一次读取 5 条数据存储到数据库)

// 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
public class DemoDataListener extends AnalysisEventListener<DemoData> {
    private static final Logger LOGGER = LoggerFactory.getLogger(DemoDataListener.class);
    /**
     * 每隔5条存储数据库,实际使用中可以3000条,然后清理list ,方便内存回收
     */

    private static final int BATCH_COUNT = 5;
    List<DemoData> list = new ArrayList<DemoData>();
    /**
     * 假设这个是一个DAO,当然有业务逻辑这个也可以是一个service。当然如果不用存储这个对象没用。
     */

    private DemoDAO demoDAO;
    public DemoDataListener() {
        // 这里是demo,所以随便new一个。实际使用如果到了spring,请使用下面的有参构造函数
        demoDAO = new DemoDAO();
    }
    /**
     * 如果使用了spring,请使用这个构造方法。每次创建Listener的时候需要把spring管理的类传进来
     *
     * @param demoDAO
     */

    public DemoDataListener(DemoDAO demoDAO) {
        this.demoDAO = demoDAO;
    }
    /**
     * 这个每一条数据解析都会来调用
     *
     * @param data
     *            one row value. Is is same as {@link AnalysisContext#readRowHolder()}
     * @param context
     */

    @Override
    public void invoke(DemoData data, AnalysisContext context) {
        LOGGER.info("解析到一条数据:{}", JSON.toJSONString(data));
        list.add(data);
        // 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
        if (list.size() >= BATCH_COUNT) {
            saveData();
            // 存储完成清理 list
            list.clear();
        }
    }
    /**
     * 所有数据解析完成了 都会来调用
     *
     * @param context
     */

    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        // 这里也要保存数据,确保最后遗留的数据也存储到数据库
        saveData();
        LOGGER.info("所有数据解析完成!");
    }
    /**
     * 加上存储数据库
     */

    private void saveData() {
        LOGGER.info("{}条数据,开始存储数据库!", list.size());
        demoDAO.save(list);
        LOGGER.info("存储数据库成功!");
    }
}

持久层 (mybatis 或者 jpa 来做都行)

/**
 * 假设这个是你的DAO存储。当然还要这个类让spring管理,当然你不用需要存储,也不需要这个类。
 **/

public class DemoDAO {
    public void save(List<DemoData> list) {
        // 如果是mybatis,尽量别直接调用多次insert,自己写一个mapper里面新增一个方法batchInsert,所有数据一次性插入
        System.out.println(list);
    }
}

读取数据

String fileName = "src/test/resources/demo/demo.xlsx";
// 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭
EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).sheet().doRead();

输出结果 (已过滤非必要数据)

2020-09-16 08:14:33.727 DEBUG [main] com.alibaba.excel.context.AnalysisContextImpl:91 - Began to read:ReadSheetHolder{sheetNo=0, sheetName='Sheet1'} com.alibaba.excel.read.metadata.holder.xlsx.XlsxReadSheetHolder@6f3b5d16
2020-09-16 08:14:33.870 INFO [main] com.alibaba.easyexcel.test.demo.read.DemoDataListener:54 - 解析到一条数据:{"date":1577811661000,"doubleData":1.0,"string":"字符串0"}
2020-09-16 08:14:33.870 INFO [main] com.alibaba.easyexcel.test.demo.read.DemoDataListener:54 - 解析到一条数据:{"date":1577898061000,"doubleData":2.0,"string":"字符串1"}
2020-09-16 08:14:33.871 INFO [main] com.alibaba.easyexcel.test.demo.read.DemoDataListener:54 - 解析到一条数据:{"date":1577984461000,"doubleData":3.0,"string":"字符串2"}
2020-09-16 08:14:33.871 INFO [main] com.alibaba.easyexcel.test.demo.read.DemoDataListener:54 - 解析到一条数据:{"date":1578070861000,"doubleData":4.0,"string":"字符串3"}
2020-09-16 08:14:33.872 INFO [main] com.alibaba.easyexcel.test.demo.read.DemoDataListener:54 - 解析到一条数据:{"date":1578157261000,"doubleData":5.0,"string":"字符串4"}
2020-09-16 08:14:33.872 INFO [main] com.alibaba.easyexcel.test.demo.read.DemoDataListener:80 - 5条数据,开始存储数据库!
[DemoData(string=字符串0, date=Wed Jan 01 01:01:01 CST 2020, doubleData=1.0), DemoData(string=字符串1, date=Thu Jan 02 01:01:01 CST 2020, doubleData=2.0), DemoData(string=字符串2, date=Fri Jan 03 01:01:01 CST 2020, doubleData=3.0), DemoData(string=字符串3, date=Sat Jan 04 01:01:01 CST 2020, doubleData=4.0), DemoData(string=字符串4, date=Sun Jan 05 01:01:01 CST 2020, doubleData=5.0)]
2020-09-16 08:14:33.874 INFO [main] com.alibaba.easyexcel.test.demo.read.DemoDataListener:82 - 存储数据库成功!
2020-09-16 08:14:33.875 INFO [main] com.alibaba.easyexcel.test.demo.read.DemoDataListener:54 - 解析到一条数据:{"date":1578243661000,"doubleData":6.0,"string":"字符串5"}
2020-09-16 08:14:33.875 INFO [main] com.alibaba.easyexcel.test.demo.read.DemoDataListener:54 - 解析到一条数据:{"date":1578330061000,"doubleData":7.0,"string":"字符串6"}
2020-09-16 08:14:33.876 INFO [main] com.alibaba.easyexcel.test.demo.read.DemoDataListener:54 - 解析到一条数据:{"date":1578416461000,"doubleData":8.0,"string":"字符串7"}
2020-09-16 08:14:33.876 INFO [main] com.alibaba.easyexcel.test.demo.read.DemoDataListener:54 - 解析到一条数据:{"date":1578502861000,"doubleData":9.0,"string":"字符串8"}
2020-09-16 08:14:33.876 INFO [main] com.alibaba.easyexcel.test.demo.read.DemoDataListener:54 - 解析到一条数据:{"date":1578589261000,"doubleData":10.0,"string":"字符串9"}
2020-09-16 08:14:33.877 INFO [main] com.alibaba.easyexcel.test.demo.read.DemoDataListener:80 - 5条数据,开始存储数据库!
[DemoData(string=字符串5, date=Mon Jan 06 01:01:01 CST 2020, doubleData=6.0), DemoData(string=字符串6, date=Tue Jan 07 01:01:01 CST 2020, doubleData=7.0), DemoData(string=字符串7, date=Wed Jan 08 01:01:01 CST 2020, doubleData=8.0), DemoData(string=字符串8, date=Thu Jan 09 01:01:01 CST 2020, doubleData=9.0), DemoData(string=字符串9, date=Fri Jan 10 01:01:01 CST 2020, doubleData=10.0)]
2020-09-16 08:14:33.877 INFO [main] com.alibaba.easyexcel.test.demo.read.DemoDataListener:82 - 存储数据库成功!
2020-09-16 08:14:33.877 INFO [main] com.alibaba.easyexcel.test.demo.read.DemoDataListener:80 - 0条数据,开始存储数据库!
[]
2020-09-16 08:14:33.877 INFO [main] com.alibaba.easyexcel.test.demo.read.DemoDataListener:82 - 存储数据库成功!
2020-09-16 08:14:33.877 INFO [main] com.alibaba.easyexcel.test.demo.read.DemoDataListener:73 - 所有数据解析完成!

Process finished with exit code 0

Hutool

介绍

Hutool 是一个非常实用的 Java 工具类库,对文件、流、加密解密、转码、正则、线程、XML 等 JDK 方法进行了封装。

非常使用的开源工具类库,推荐小伙伴们在自己项目中使用。

官方是这样介绍 Hutool 的:

引入依赖

Maven

<!-- https://mvnrepository.com/artifact/cn.hutool/hutool-all -->
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.4.2</version>
</dependency>

Gradle

// https://mvnrepository.com/artifact/cn.hutool/hutool-all
compile group: 'cn.hutool', name: 'hutool-all', version: '5.4.2'

功能演示

简单演示几个比较实用的功能。

邮件

在 Java 中发送邮件主要品依靠 javax.mail 包,但是由于使用比较繁琐,因此 Hutool 针对其做了封装。由于依赖第三方包,因此将此工具类归类到 extra 模块中。

实际项目中可以自定义邮箱配置,然后让配置保存在数据库,并在缓存中保存一份。

如果不想自定义配置的话,直接在配置文件中把邮箱配置写死就行了。

有了邮件的相关配置之后,定义邮件服务器之后即可发送邮件,非常方便:

MailAccount account = new MailAccount();
account.setHost("smtp.yeah.net");
account.setPort("25");
account.setAuth(true);
account.setFrom("hutool@yeah.net");
account.setUser("hutool");
account.setPass("q1w2e3");

MailUtil.send(account, CollUtil.newArrayList("hutool@foxmail.com"), "测试""邮件来自Hutool测试"false);

唯一 ID

在分布式环境中,唯一 ID 生成应用十分广泛,生成方法也多种多样,Hutool 针对一些常用生成策略做了简单封装。

https://juejin.im/post/6844903935296176141

Hutool 提供的唯一 ID 生成器的工具类,涵盖了:

  • UUID
  • ObjectId(MongoDB)
  • Snowflake(Twitter)

拿 UUID 举例!

Hutool 重写java.util.UUID的逻辑,对应类为cn.hutool.core.lang.UUID,使生成不带-的 UUID 字符串不再需要做字符替换,性能提升一倍左右

//生成的UUID是带-的字符串,类似于:a5c8a5e8-df2b-4706-bea4-08d0939410e3
String uuid = IdUtil.randomUUID();

//生成的是不带-的字符串,类似于:b17f24ff026d40949c85a24f4f375d42
String simpleUUID = IdUtil.simpleUUID();

Http 请求工具类

针对最为常用的 GET 和 POST 请求,HttpUtil 封装了两个方法,

  • HttpUtil.get
  • HttpUtil.post
// 最简单的HTTP请求,可以自动通过header等信息判断编码,不区分HTTP和HTTPS
String result1 = HttpUtil.get("https://www.baidu.com");
// 当无法识别页面编码的时候,可以自定义请求页面的编码
String result2 = HttpUtil.get("https://www.baidu.com", CharsetUtil.CHARSET_UTF_8);
//可以单独传入http参数,这样参数会自动做URL编码,拼接在URL中
HashMap<String, Object> paramMap = new HashMap<>();
paramMap.put("city""北京");
String result3 = HttpUtil.get("https://www.baidu.com", paramMap);

缓存

Hutool 提供了常见的几种缓存策略的实现:

  1. FIFO(first in first out) :先进先出策略。
  2. LFU(least frequently used) :最少使用率策略。
  3. LFU(least frequently used) :最少使用率策略。
  4. 定时缓存 :对被缓存的对象定义一个过期时间,当对象超过过期时间会被清理。
  5. ......

FIFO(first in first out) 策略缓存使用:

Cache<String,String> fifoCache = CacheUtil.newFIFOCache(3);

//加入元素,每个元素可以设置其过期时长,DateUnit.SECOND.getMillis()代表每秒对应的毫秒数,在此为3秒
fifoCache.put("key1""value1", DateUnit.SECOND.getMillis() * 3);
fifoCache.put("key2""value2", DateUnit.SECOND.getMillis() * 3);
fifoCache.put("key3""value3", DateUnit.SECOND.getMillis() * 3);

//由于缓存容量只有3,当加入第四个元素的时候,根据FIFO规则,最先放入的对象将被移除
fifoCache.put("key4""value4", DateUnit.SECOND.getMillis() * 3);

//value1为null
String value1 = fifoCache.get("key1");

控制台打印封装-Console

一般情况下,我们打印信息到控制台小伙伴们应该再熟悉不过了!

System.out.println("Hello World");

但是,这种方式不满足很多场景的需要:

  1. 不支持参数,对象打印需要拼接字符串
  2. 不能直接打印数组,需要手动调用Arrays.toString

为此,Hutool 封装了Console对象。

Console 对象的使用更加类似于 Javascript 的console.log()方法,这也是借鉴了 JS 的一个语法糖。

String[] a = {"java""c++""c"};
Console.log(a);//控制台输出:[java, c++, c]

Console.log("This is Console log for {}.""test");//控制台输出:This is Console log for test.

参考资料

[1]

oshi: https://github.com/oshi/oshi

[2]

EasyExcel: https://github.com/alibaba/easyexcel

[3]

Hutool: https://github.com/looly/hutool

[4]

JNA(Java Native Access): https://github.com/java-native-access/jna

闲聊

其实今天本来是准备更新一篇系统设计面试指南的,毕竟系统设计几乎也是面试必问了,非常重要。 最近询问这方面问题的小伙伴比较多。

文章大概要写啥内容都构思好了,结果还是过于天真。因为涉及到的内容和知识点太多了。预计会在下周分享(如果中途没出啥状况的话)。

另外,上周日和我们大学的传奇人物,研究生在华科,目前在华为的学长聊了一会。收获确实不少,果然优秀的人总有一些过人的地方(这周六应该会把自己的所得所获分享出来)。

最近更新的一些好文章推荐:


往期推荐



国博史上首次收藏代码!代码的 URL 设计还没遵循RESTful API 风格?

你见过最烂的Java代码长什么样子?

我利用业余时间通过技术写作挣了3个顶配Mac Pro

被喷了?聊聊我开源RPC框架的那些事

6k+点赞的SpringBoot+Netty分布式即时通讯系统!爱了爱了!

好一个 Spring Boot 开源在线考试系统!解决了我的燃眉之急


我整理的4本PDF文档,公众号“后端技术进阶”后台回复“面试突击”即可免费获取。

文章有帮助可以点个「在看」或「分享」,都是支持,我都喜欢!

我是Guide哥,Java后端开发,会一点前端知识,喜欢烹饪,自由的少年。一个三观比主角还正的技术人。我们下期再见!

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存