Java类库使用集锦

综合类库

Guava

Guava 是 Google 的一组核心 Java 库,其中包括新的集合类型(例如 multimap 和 multiset)、不可变集合、图形库以及用于并发、I/O、哈希、原语、字符串等的实用程序! 它被广泛用于 Google 内部的大多数 Java 项目,也被许多其他公司广泛使用。

引入依赖

<dependency>
  <groupId>com.google.guava</groupId>
  <artifactId>guava</artifactId>
  <version>32.1.2-jre</version>
  <!-- or, for Android: -->
  <version>32.1.2-android</version>
</dependency>

Hutool

Hutool是一个Java工具包类库,对文件、流、加密解密、转码、正则、线程、XML等JDK方法进行封装,组成各种Util工具类

日期工具

通过DateUtil类,提供高度便捷的日期访问、处理和转换方式。

HTTP客户端

通过HttpUtil对HTTP客户端的封装,实现便捷的HTTP请求,并简化文件上传操作

转换工具

通过Convert类中的相应静态方法,提供一整套的类型转换解决方案,并通过ConverterRegistry工厂类自定义转换。

配置文件工具

通过Setting对象,提供兼容Properties文件的更加强大的配置文件工具,用于解决中文、分组等JDK配置文件存在的诸多问题。

日志工具

Hutool的日志功能,通过抽象Log接口,提供对Slf4j、LogBack、Log4j、JDK-Logging的全面兼容支持。

JDBC工具类

通过db模块,提供对MySQL、Oracle等关系型数据库的JDBC封装,借助ActiveRecord思想,大大简化数据库操作。

Apache commons

Apache Commons 是一个 Apache 项目,专注于可重用 Java 组件的各个方面。

Commons BeanUtils

​ 针对Bean的一个工具集。由于Bean往往是有一堆get和set组成,所以BeanUtils也是在此基础上进行一些包装。它利用Java的反射机制,从动态的生成对bean的getter和setter的调用代码,到模拟创建一个动态的bean,等等。这个包看似简单,却是很多开源项目的基石:如在著名的Struts和Spring Framework中,我们都能找到BeanUtils的影子。大家猜猜看,有哪位名人是BeanUtils的作者之一?没错,就是Struts的创始人Craig McClanahan。

​ 一个比较常用的功能是Bean Copy,也就是copy bean的属性。如果做分层架构开发的话就会用到,比如从PO(Persistent Object)拷贝数据到VO(Value Object)。

<dependency>
    <groupId>commons-beanutils</groupId>
    <artifactId>commons-beanutils</artifactId>
    <version>1.9.4</version>
</dependency>

Commons BeanUtils一共包括如下5个包:

org.apache.commons.beanutils – 核心包,定义一组Utils类和需要用到的接口规范

org.apache.commons.beanutils.converters – 转换String到需要类型的类,实现Converter接口

org.apache.commons.beanutils.locale –beanutils的locale敏感版本

org.apache.commons.beanutils.locale.converters– converters的locale敏感版本

org.apache.commons.collections – beanutils使用到的Collection

Commons Collections

是一个集合组件,扩展了Java标准Collections API,对常用的集合操作进行了很好的封装、抽象和补充,在保证性能的同时大大简化代码。

我们先来浏览一下它的包结构。一共是12个:

org.apache.commons.collections – CommonsCollections自定义的一组公用的接口和工具类

org.apache.commons.collections.bag – 实现Bag接口的一组类

org.apache.commons.collections.bidimap – 实现BidiMap系列接口的一组类

org.apache.commons.collections.buffer – 实现Buffer接口的一组类

org.apache.commons.collections.collection –实现java.util.Collection接口的一组类

org.apache.commons.collections.comparators– 实现java.util.Comparator接口的一组类

org.apache.commons.collections.functors –Commons Collections自定义的一组功能类

org.apache.commons.collections.iterators – 实现java.util.Iterator接口的一组类

org.apache.commons.collections.keyvalue – 实现集合和键/值映射相关的一组类

org.apache.commons.collections.list – 实现java.util.List接口的一组类

org.apache.commons.collections.map – 实现Map系列接口的一组类

org.apache.commons.collections.set – 实现Set系列接口的一组类
  • 作为容器类的补充,我们可以找到Bag、Buffer、BidiMap、OrderedMap等等;
  • 作为操作类的补充,我们可以找到CollectionUtils、IteratorUtils、ListUtils、SetUtils等等;
  • 作为辅助类的补充,我们可以找到MapIterator、Closure、Predicate、Transformer等等;

Commons Compress

是一个压缩、解压缩文件的组件,可以操作rar、cpio、Unix dump、tar、zip、gzip、XZ、Pack200和bzip2格式的压缩文件。

Commons Configuration

是一个Java应用程序的配置管理工具,可以从properties或者xml文件中加载配置信息。

Commons DBCP

数据库连接池。

Commons Email

是邮件操作组件,对Java Mail API进行了封装,提供了常用的邮件发送和接收类,简化邮件操作。该组件依赖Java Mail API。

Commons FileUpload

为Web应用程序或Servlet提供文件上传功能,Struts2和SpringMVC的文件上传组件。

Commons IO

是处理IO的工具类包,对java.io进行扩展,提供了更加方便的IO操作。

Commons Lang3

是处理Java基本对象方法的工具类包,该类包提供对字符、数组等基本对象的操作,弥补了java.lang api基本处理方法上的不足。

ArrayUtils – 用于对数组的操作,如添加、查找、删除、子数组、倒序、元素类型转换等;

BitField – 用于操作位元,提供了一些方便而安全的方法;

BooleanUtils – 用于操作和转换boolean或者Boolean及相应的数组;

CharEncoding – 包含了Java环境支持的字符编码,提供是否支持某种编码的判断;

CharRange – 用于设定字符范围并做相应检查;

CharSet – 用于设定一组字符作为范围并做相应检查;

CharSetUtils – 用于操作CharSet;

CharUtils – 用于操作char值和Character对象;

ClassUtils – 用于对Java类的操作,不使用反射;

ObjectUtils – 用于操作Java对象,提供null安全的访问和其他一些功能;

RandomStringUtils – 用于生成随机的字符串;

SerializationUtils – 用于处理对象序列化,提供比一般Java序列化更高级的处理能力;

StringEscapeUtils – 用于正确处理转义字符,产生正确的Java、JavaScript、HTML、XML和SQL代码;

StringUtils – 处理String的核心类,提供了相当多的功能;

SystemUtils – 在java.lang.System基础上提供更方便的访问,如用户路径、Java版本、时区、操作系统等判断;

Validate – 提供验证的操作,有点类似assert断言;

WordUtils – 用于处理单词大小写、换行等。

Commons Logging

提供统一的日志接口,同时兼顾轻量级和不依赖于具体的实现。类包给中间件/日志工具开发者一个简单的日志操作抽象,允许程序开发人员使用不同的具体日志实现工具。

Commons Net

封装了各种网络协议的客户端,支持FTP、NNTP、SMTP、POP3、Telnet等协议。

Commons Pool

提供了一整套用于实现对象池化的框架,以及若干各具特色的对象池实现,可以有效地减少处理对象池化时的工作量。类包用于提高像文件句柄、数据库连接、socket通信这类大对象的调用效率,简单的说就是一种对象一次创建多次使用的技术。

Apache HttpClient

曾经是Apache Commons的子项目,后来独立出来。HttpClient简化HTTP客户端与服务器的各种通讯,实现HTTP客户端程序(也就是浏览器程序)的功能。

Commons Codec

编解码器由一组实用程序和一个简单的框架组成,用于编码和解码文本和二进制数据。

是编码和解码组件,提供常用的编码和解码方法,如DES、SHA1、MD5、Base64、URL和Soundx等。

官方使用手册

Commons Crypto

使用 AES-NI (高级加密标准新指令)封装 Openssl 和 JCE 算法实现进行优化的加密库。

其他不常用

Commons Validator

提供了一个简单的、可扩展的框架来在一个XML文件中定义校验器(校验方法)和校验规则。支持校验规则的和错误消息的国际化。

Commons CSV

是一个用来读写各种Comma Separated Value(CSV)格式文件的Java类库。

Commons Daemon

实现将普通的Java应用变成系统的后台服务,例如 Tomcat 就是利用这个项目来实现作为 Linux 和 Windows 的服务启动和停止的。

Commons DBUtils

是JDBC工具组件,对传统操作数据库的类进行二次封装,可以把结果集转化成List。

Commons Digester

是XML到Java对象的映射工具集。

Commons Math

轻量级自容器的数学和统计计算方法类包,包含大多数常用的数值算法。

Commons Primitives

提供了一个更小,更快和更易使用的对Java基本类型的支持。

Commons JCI

提供通用的Java编译器接口。

Commons Exec

提供一些常用的方法用来执行外部进程,如执行exe文件或命令行。

时间类库

GMT和UTC时区概念

UTC(Universal Time Coordinated)是通用协调时; GMT,全称Greenwich Mean Time,即格林威治标准时,这两者几乎是一样的,都是指的格林尼治标准时间,只是UTC的称呼更为正式一点,他们都比北京时间慢八个小时。

ISO 8601的日期格式

2023-10-31T23:00:00.000Z    表示UTC 时间
2023-10-31T23:00:00.00+08:00    表示东八区时间

Date 与 LocalDateTime 转换

public class DateUtils {

	public static Date toDate(LocalDateTime localDateTime) {
        Instant instant = localDateTime.atZone(ZoneId.systemDefault()).toInstant();
        return Date.from(instant);
    }
    
    public static LocalDateTime toLocalDateTime(Date date) {
    	Instant instant = date.toInstant();
    	return LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
  	}
}

JDK 老时间库

JDK8 新时间库

Joda-Time

Joda-Time 其默认格式就是ISO8601

JSON类库

Jackson

​ Jackson 被誉为“Java JSON 库”或“最好的Java JSON 解析器”。不仅如此,Jackson 是一套用于 Java 的数据处理工具,包括旗舰级流式 JSON 解析器/生成器库、匹配的数据绑定库(与 JSON 之间的 POJO)以及附加的数据格式模块 处理以 Avro、BSON、CBOR、CSV、Smile、(Java) Properties、Protobuf、TOML、XML 或 YAML 编码的数据。

​ GitHub地址:https://github.com/FasterXML/jackson

示例:

public class ExamineSignUpService {

	private ObjectMapper objectMapper = new ObjectMapper();
    
    public void testStrToJsonNode(){

        //字符串转JsonNode
        JsonNode objectNode = objectMapper.readTree("{\"name\":\"Jackpot\"}");
        
        JsonNode jsonNode = objectMapper.readTree("[{\"name\":\"wyc\"}]");
        ArrayNode arrayNode = (ArrayNode) jsonNode;
        arrayNode.add(objectNode);
        
        //字符串转为pojo
        Customer customer = objectMapper.readValue("{\"name\":\"wyc\"}", Customer.class);
        System.out.println(customer.getName());
        
        //JsonNode 转为pojo
        User user = objectMapper.convertValue(jsonNode,User.class)
            
        //手动创建一个ObjectNode并写入数据
        ObjectNode objectNode2 = objectMapper.createObjectNode();   
        objectNode2.put("name","Jackpot2");
        
        //手动创建一个ArrayNode并写入数据
        ArrayNode mergedArray = objectMapper.createArrayNode();  
        mergedArray.add(arrayNode);

        //合并两个ArrayNode:注意使用 add和 addAll的结果是不一样的
        mergedArray.add((ArrayNode) arrayNode);

    }
}

这个类中有一些常用的方法:

  • writeValue() 方法可以进行 JSON 的序列化操作,可以将 Java 对象转换成 JSON 字符串。
  • readValue() 方法可以进行 JSON 的反序列化操作,比如可以将字符串、文件流、字节流、字节数组等将常见的内容转换成 Java 对象。
//Java对象转 字符串
String str = objectMapper.writeValueAsString(jsonNode);
//字符串转 Entity
Student student = objectMapper.readValue(jsonStr, Student.class);
//字符串转JsonNode
JsonNode jsonNode = objectMapper.readTree(response.body());

objectMapper的readValue和readTree 区别:

  • objectMapper的readValue 将json数据反序列化为Java对象。
  • objectMapper的readTree 将json数据解析为JsonNode对象。

配置

忽略字段

如果在进行 JSON 转 Java 对象时,JSON 中出现了 Java 类中不存在的属性,那么在转换时会遇到 com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException 异常。

使用以下设置可以忽略不存在的属性:

objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

@JsonIgnore

使用 @JsonIgnore 可以忽略某个 Java 对象中的属性,它将不参与 JSON 的序列化与反序列化。

Jackson的JsonNode和ObjectNode两个类,前者是不可变的,一般用于读取。后者可变,一般用于创建Json对象图。

Gson

引入依赖:

<dependency>
      <groupId>com.google.code.gson</groupId>
      <artifactId>gson</artifactId>
    </dependency>

XML 类库

常用XML 类库

  • jackson
  • dom4j
<dependency>
    <groupId>com.fasterxml.jackson.dataformat</groupId>
    <artifactId>jackson-dataformat-xml</artifactId>
</dependency>

XPath 语法

XPath 使用路径表达式来选取 XML 文档中的节点或节点集。节点是通过沿着路径 (path) 或者步 (steps) 来选取的。

下面列出了最有用的路径表达式:

表达式 描述
nodename 选取此节点的所有子节点。
/ 从根节点选取(取子节点)。
// 从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置(取子孙节点)。
. 选取当前节点。
.. 选取当前节点的父节点。
@ 选取属性。

在下面的表格中,我们已列出了一些路径表达式以及表达式的结果:

路径表达式 结果
bookstore 选取所有名为 bookstore 的节点。
/bookstore 选取根元素 bookstore。注释:假如路径起始于正斜杠( / ),则此路径始终代表到某元素的绝对路径!
bookstore/book 选取属于 bookstore 的子元素的所有 book 元素。
//book 选取所有 book 子元素,而不管它们在文档中的位置。
bookstore//book 选择属于 bookstore 元素的后代的所有 book 元素,而不管它们位于 bookstore 之下的什么位置。
//@lang 选取名为 lang 的所有属性。

谓语(Predicates)

谓语用来查找某个特定的节点或者包含某个指定的值的节点。

语法:谓语被嵌在方括号中

在下面的表格中,我们列出了带有谓语的一些路径表达式,以及表达式的结果:

路径表达式 结果
/bookstore/book[1] 选取属于 bookstore 子元素的第一个 book 元素。
/bookstore/book[last()] 选取属于 bookstore 子元素的最后一个 book 元素。
/bookstore/book[last()-1] 选取属于 bookstore 子元素的倒数第二个 book 元素。
/bookstore/book[position()<3] 选取最前面的两个属于 bookstore 元素的子元素的 book 元素。
//title[@lang] 选取所有拥有名为 lang 的属性的 title 元素。
//title[@lang=’eng’] 选取所有 title 元素,且这些元素拥有值为 eng 的 lang 属性。
/bookstore/book[price>35.00] 选取 bookstore 元素的所有 book 元素,且其中的 price 元素的值须大于 35.00。
/bookstore/book[price>35.00]//title 选取 bookstore 元素中的 book 元素的所有 title 元素,且其中的 price 元素的值须大于 35.00。

命名空间

inz-query-1.0.xsd

<?xml version="1.0"?>
<xs:schema xmlns="http://www.beecode.cn/schema/inz-query"
	xmlns:xs="http://www.w3.org/2001/XMLSchema"
	targetNamespace="http://www.beecode.cn/schema/inz-query"
	elementFormDefault="qualified">
	
	<xs:complexType name="refType">
		<xs:sequence>
			<xs:element name="name" type="xs:string" />
			<xs:element name="type" type="xs:string" />
		</xs:sequence>
	</xs:complexType>
	
	<xs:complexType name="parameterType">
		<xs:sequence>
			<xs:element name="key" type="xs:string" minOccurs="0"/>
			<xs:element name="value" type="xs:string" minOccurs="0"/>
		</xs:sequence>
	</xs:complexType>
	
	<xs:complexType name="parametersType">
		<xs:sequence>
			<xs:element name="parameter" type="parameterType" minOccurs="1" maxOccurs="1000"/>
		</xs:sequence>
	</xs:complexType>
	
	<xs:complexType name="fieldType">
		<xs:sequence>
			<xs:element name="name" type="xs:string"/>
			<xs:element name="title" type="xs:string" />
			<xs:element name="type" type="xs:string"/>
			<xs:element name="ref"  type="refType" />
			<xs:element name="desc" type="xs:string" />
		</xs:sequence>
	</xs:complexType>
	
	<xs:complexType name="fieldsType">
		<xs:sequence>
			<xs:element name="field" type="fieldType" minOccurs="0" maxOccurs="1000"/>
		</xs:sequence>
	</xs:complexType>
	
	
	<xs:complexType name="innerSceneType">
		<xs:sequence>
			<xs:element name="id" type="xs:string" />
			<xs:element name="title" type="xs:string" />
			<xs:element name="javaImplement" type="xs:string" />
			<xs:element name="parameters" type="parametersType"  minOccurs="0" />
			<xs:element name="defaultExecute" type="xs:string"  minOccurs="0" />
			<xs:element name="hide" type="xs:string"  minOccurs="0" />
		</xs:sequence>
	</xs:complexType>
	
	<xs:complexType name="innerScenesType">
		<xs:sequence>
			<xs:element name="innerScene" type="innerSceneType" minOccurs="0" maxOccurs="1000"/>
		</xs:sequence>
	</xs:complexType>

	<xs:element name="query">
		<xs:complexType>
			<xs:sequence>
				<xs:element name="type" type="xs:string" />
				<xs:element name="dataProcessor" type="xs:string" minOccurs="0" maxOccurs="1"/>
				<xs:element name="authorityItem" type="xs:string" minOccurs="0" maxOccurs="1"/>
				<xs:element name="innerScenes" type="innerScenesType" minOccurs="0" maxOccurs="1"/>
				<xs:element name="fields" type="fieldsType" minOccurs="0" maxOccurs="1"/>
			</xs:sequence>
		</xs:complexType>
	</xs:element>
</xs:schema>

网络请求类库

HTTP 请求客户端

RestClient

​ RestClient是从 Spring6.1 开始支持的一个 HTTP 请求工具,一句话来让Spring开发者认识RestClient的话:像WebClient一样具备流畅API的RestTemplate。所以,RestClient的使命就是淘汰已经有14年历史的RestTemplate

RestClient restClient = RestClient.create();

String result = restClient.get()
  .uri("https://example.com")
  .retrieve()
  .body(String.class);
System.out.println(result);

关于GET请求,很多时候我们返回的不仅仅是String,更多的时候是一些实体;同时我们有时候还需要获取HTTP状态码以及头信息。这个时候,我们可以使用toEntity方法来返回一个更为通用的ResponseEntity来进行后续操作,比如下面这样:

在业务层面,为了更方便的解析业务数据。RestClient还支持对结果进行对象转换。比如下面的例子,就是把HTTP请求返回的JSON数据转化为Pet对象。这样就免去了开发者手动从ResponseEntity中获取内容,再进行消息转化的麻烦。

Pet pet = restClient.get()
  .uri("https://petclinic.example.com/pets/{id}", id)
  .accept(APPLICATION_JSON)
  .retrieve()
  .body(Pet.class);

RestTemplate

​ RestTemplate 是从 Spring3.0 开始支持的一个 HTTP 请求工具,它提供了常见的REST请求方案的模版,例如 GET 请求、POST 请求、PUT 请求、DELETE 请求以及一些通用的请求执行方法 exchange 以及 execute。RestTemplate 继承自 InterceptingHttpAccessor 并且实现了 RestOperations 接口,其中 RestOperations 接口定义了基本的 RESTful 操作,这些操作在 RestTemplate 中都得到了实现。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

示例:

private static void postDemo() {
  RestTemplate restTemplate = new RestTemplate();
  try {
    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.APPLICATION_JSON);
    JSONObject jsonObject = new JSONObject();
    jsonObject.put("appId", "d26d9f929af04873ad43c5810adcbcc4");
    jsonObject.put("tenantId", "8b60c9b967c947e6b6b5a5fe37525cdb");
    HttpEntity httpEntity = new HttpEntity<>(jsonObject.toString(), headers);
    String response = restTemplate.postForObject("https://swj.beijing.gov.cn/mobile/saveClicked", httpEntity, String.class);
    System.out.println(response);
  }catch(Exception e) {
    throw new AuthenticationServiceException("接口调用失败!",e);
  }
}

RestTemplate 位于 org.springframework.web.client 包下,设置该包下的打印日志为 DEBUG 级别即可查看详细的网络请求信息。

18:03:27.890 [main] DEBUG org.springframework.web.client.RestTemplate - HTTP POST https://smart.swj.beijing.gov.cn/moa/party/mobile/app/saveClicked
18:03:27.898 [main] DEBUG org.springframework.web.client.RestTemplate - Accept=[text/plain, application/json, application/*+json, */*]
18:03:27.907 [main] DEBUG org.springframework.web.client.RestTemplate - Writing [{"appId":"d26d9f929af04873ad43c5810adcbcc4","tenantId":"8b60c9b967c947e6b6b5a5fe37525cdb","orgUnitId":"e414d55728d7432bbd2c2f7413bf6319"}] as "application/json"
18:03:29.060 [main] DEBUG org.springframework.web.client.RestTemplate - Response 200 OK
18:03:29.062 [main] DEBUG org.springframework.web.client.RestTemplate - Reading to [java.lang.String] as "application/json"

JDK11 HttpClient

​ HttpClient在Java11 正式标记为正式,不仅支持异步,也支持reactive streams,同时也支持了HTTP2以及WebSocket,非常值得大家使用。

java.net.http 包下的 HttpClient 是自JDK11开始引入的新工具类,可以很方便的发送HTTP请求,支持同步和异步两种方式。

HTTP 请求

public void demo(){
   //    Synchronous Example
    HttpClient client = HttpClient.newBuilder().version(HttpClient.Version.HTTP_1_1).followRedirects(HttpClient.Redirect.NORMAL).connectTimeout(Duration.ofSeconds(20)).proxy(ProxySelector.of(new InetSocketAddress("proxy.example.com", 80))).authenticator(Authenticator.getDefault()).build();
    HttpRequest request = HttpRequest.newBuilder().uri(URI.create(approvalUrl+"/y9home/mobile/agry/querySafetyManagerInfoList"))
      .timeout(Duration.ofMinutes(2)).header("Content-Type", "application/json")
      .GET().build();
    HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
    System.out.println(response.statusCode());
    System.out.println(response.body());
//    Asynchronous Example
    HttpRequest request = HttpRequest.newBuilder().uri(URI.create("https://foo.com/"))
      .timeout(Duration.ofMinutes(2)).header("Content-Type", "application/json")
      .POST(HttpRequest.BodyPublishers.ofFile(Paths.get("file.json"))).build();
    client.sendAsync(request, HttpResponse.BodyHandlers.ofString()).thenApply(HttpResponse::body).thenAccept(System.out::println); 
}

Apache HttpClient

public String sendHttpGet(String url,Map<String,String> headers) {
    String resultContent = null;
    HttpGet httpGet = new HttpGet(url);
    if(headers!=null){
        headers.keySet().forEach(key->{
            httpGet.addHeader(key,headers.get(key));
        });
    }
    try (CloseableHttpClient httpclient = HttpClients.createDefault()) {
        try (CloseableHttpResponse response = httpclient.execute(httpGet)) {
            HttpEntity entity = response.getEntity();
            resultContent = EntityUtils.toString(entity);
        }
    } catch (IOException | ParseException e) {
        e.printStackTrace();
    }
    return resultContent;
}

WebSocket

WebSocket 服务端

WebSocket 客户端

加密解密库

Bouncy Castle

<dependency>
			<groupId>org.bouncycastle</groupId>
			<artifactId>bcprov-jdk18on</artifactId>
			<version>1.77</version>
		</dependency>

Hutool-crypto

对称加密

在软件开发中,常用的对称加密算法有:

算法 密钥长度 工作模式 填充模式
DES 56/64 ECB/CBC/PCBC/CTR/… NoPadding/PKCS5Padding/…
AES 128/192/256 ECB/CBC/PCBC/CTR/… NoPadding/PKCS5Padding/PKCS7Padding/…
IDEA 128 ECB PKCS5Padding/PKCS7Padding/…

​ 密钥长度直接决定加密强度,而工作模式和填充模式可以看成是对称加密算法的参数和格式选择。Java标准库提供的算法实现并不包括所有的工作模式和所有填充模式,但是通常我们只需要挑选常用的使用就可以了。

​ 注意,DES算法由于密钥过短,可以在短时间内被暴力破解,所以现在已经不安全了。

​ AES算法是目前应用最广泛的加密算法。

非对称加密

RSA非对称加密

openssl工具

# 使用openssl
openssl
# 生成私钥
OpenSSL> genrsa -out id_rsa_private 2048
# 从私钥文件中提取公钥
OpenSSL> rsa -in id_rsa_private -pubout -out id_rsa_public.pub
# 使用公钥加密数据
OpenSSL> rsautl -encrypt -in test.txt -inkey id_rsa_public.pub -pubin -out test.sec
# 使用私钥解密数据
OpenSSL> rsautl -decrypt -in test.sec -inkey id_rsa_private -out test.sec.dec

检查密钥对:可以使用以下命令验证生成的密钥对是否匹配:

openssl rsa -in private_key.pem -pubout -outform DER | openssl sha256
openssl rsa -in public_key.pem -pubin -outform DER | openssl sha256

如果输出的哈希值相同,则表示密钥对匹配。

摘要算法

算法 输出长度
(位)
最大消息长度
(位)
安全性 时间
MD5 128 2^64^ − 1 不安全(已发现碰撞)
SHA-0 160 2^64^ − 1 不安全(已发现碰撞)
SHA-1 160 2^64^ − 1 不安全(已发现碰撞) 安全散列算法(美国国家安全局)
SHA-2 SHA-256 256
SHA-512 512
2^64^ − 1
2^128^ − 1
安全 第二代安全散列算法,2001年发布
SHA-3 无限制 安全 第三代安全散列算法,2015年发布
SM3 国密算法(中国)

解压缩库

进程内缓存库

​ 缓存分为本地缓存和远端缓存。常见的远端缓存有Redis,MongoDB;本地缓存一般使用map的方式保存在本地内存中。一般我们在业务中操作缓存,都会操作缓存和数据源两部分。如:put数据时,先插入DB,再删除原来的缓存;ge数据时,先查缓存,命中则返回,没有命中时,需要查询DB,再把查询结果放入缓存中 。如果访问量大,我们还得兼顾本地缓存的线程安全问题。必要的时候也要考虑缓存的回收策略。

Caffeine

Caffeine 是一个高性能、接近最佳的缓存库。

SpringBoot2 将该缓存引入。

Ehcache

EhCache是一个纯Java的进程内缓存框架,具有快速、精干的特点。注意的这里的关键字进程,基于进程的缓存直觉告诉我们效率肯定要高一些,因为它直接在进程之内进行操作,但不同应用之间缓存的共享可能就会有问题。

​ EhCache是Hibernate中默认的CacheProvider,Spring Boot也对其进行了支持,Spring中提供的缓存抽象也支持对EhCache缓存框架的绑定,而且支持基于注解的方式来使用。因此,EhCache是一款被广泛使用的基于Java的高速缓存框架,使用起来也非常方便。

EhCache提供了多种缓存策略,主要分为内存和磁盘两级,是一款面向通用缓存、Java EE和轻量级容器的缓存框架。

Guava Cache

Guava Cache 是google guava中的一个内存缓存模块,用于将数据缓存到JVM内存中。他很好的解决了上面提到的几个问题:

  • 很好的封装了get、put操作,能够集成数据源 ;
  • 线程安全的缓存,与ConcurrentMap相似,但前者增加了更多的元素失效策略,后者只能显示的移除元素;
  • Guava Cache提供了三种基本的缓存回收方式:基于容量回收、定时回收和基于引用回收。定时回收有两种:按照写入时间,最早写入的最先回收;按照访问时间,最早访问的最早回收;
  • 监控缓存加载/命中情况
public void setCache() {
       LoadingCache<Integer, String> cache = CacheBuilder.newBuilder()
               //设置并发级别为8,并发级别是指可以同时写缓存的线程数
               .concurrencyLevel(8)
               //设置缓存容器的初始容量为10
               .initialCapacity(10)
               //设置缓存最大容量为100,超过100之后就会按照LRU最近虽少使用算法来移除缓存项
               .maximumSize(100)
               //是否需要统计缓存情况,该操作消耗一定的性能,生产环境应该去除
               .recordStats()
               //设置写缓存后n秒钟过期
               .expireAfterWrite(60, TimeUnit.SECONDS)
               //设置读写缓存后n秒钟过期,实际很少用到,类似于expireAfterWrite
               //.expireAfterAccess(17, TimeUnit.SECONDS)
               //只阻塞当前数据加载线程,其他线程返回旧值
               //.refreshAfterWrite(13, TimeUnit.SECONDS)
               //设置缓存的移除通知
               .removalListener(notification -> {
                   System.out.println(notification.getKey() + " " 
                                      + notification.getValue() + " 被移除,原因:" + notification.getCause());
               })
               //build方法中可以指定CacheLoader,在缓存不存在时通过CacheLoader的实现自动加载缓存
               .build(new DemoCacheLoader());
}

任务调度

Spring Scheduled

Quartz

Quartz 是一款功能强大的开源的任务调度框架

发邮件

SpringBoot mail

添加依赖

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-mail</artifactId>
        </dependency>

添加配置:(以华为云企业邮箱为例)

spring:
  mail:
    host: smtp.sparkspace.huaweicloud.com
    username: gw@shichuangyida.com
    password: eAfSFzB3hUbGnCeg
    port: 465
    properties:
      mail:
        smtp:
          auth: true
          ssl:
            enable: true
          starttls:
            enable: true

编码:

public class MailTestController {

    @Autowired
    private JavaMailSender mailSender;
    //1.发送简单邮件
    public void sendSimpleMail(){
        SimpleMailMessage message = new SimpleMailMessage();
        message.setFrom("gw@shichuangyida.com");
        message.setTo("1558490170@qq.com");
        message.setSubject("主题:简单邮件");
        message.setText("测试邮件内容");
        mailSender.send(message);
    }
    //2. 发送带有附件的邮件
    public void sendAttachmentMail() throws MessagingException {
        MimeMessage mimeMessage = mailSender.createMimeMessage();
        MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);
        helper.setFrom("gw@shichuangyida.com");
        helper.setTo("1558490170@qq.com");
        helper.setSubject("主题:有附件");
        helper.setText("有附件的邮件");
        ClassPathResource resource = new ClassPathResource("hbm/AssetsBase.hbm.xml");
        helper.addAttachment("AssetsBase.hbm.xml", resource);
        mailSender.send(mimeMessage);
    }
}

日志类库

Logback

Logback 官方配置手册

日志级别

Logback共定义了8个级别的log(除去OFF和ALL,可以说分为6个级别),优先级从高到低依次为:OFF、FATAL、ERROR、WARN、INFO、DEBUG、TRACE、 ALL。推荐的常用的是四个级别:ERROR、WARN、INFO、DEBUG。

  • OFF-第8级,最高等级,虚拟级别,用于关闭所有日志记录。
  • FATAL-第7级,不可用的致命级别,已被slf4j弃用,官方解释是和ERROR没有绝对的界限。
  • ERROR-第6级,可用的错误级别,很常用,推荐级别,用于捕获错误事件。
  • WARN-第5级,可用的告警级别,用得相对较少但位置很关键,表示潜在的可能错误。
  • INFO-第4级,可用的信息级别,最常用,推荐级别,用于打出程序的关键信息、阶段性的历程碑信息。
  • DEBUG-第3级,可用的调试级别,详尽的、可用于程序调试的级别,看日志类似看代码的执行过程。
  • TRACE-第2级,可用的追踪级别,但一般不建议使用,用于极为详尽的step-by-step日志追踪。
  • ALL-第1级,最低等级,虚拟级别,用于打开所有日志记录。

配置文件名默认的合法名称:logback.xmllogback-spring.xml,如果希望改名,可在application.yml配置文件中指定。

logging:
  config: classpath:logback-local.xml

logback-local.xml 内容

<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false" scan="false">
  ...
</configuration>

property

appender

常用的

  • FileAppender
  • RollingFileAppender

RollingFileAppender 扩展了 FileAppender,使其能够滚动更新日志文件。例如,RollingFileAppender 可以记录到名为 log.txt file 的文件,并在满足特定条件后将其日志记录目标更改为另一个文件。

rollingPolicy 滚动策略:

  • TimeBasedRollingPolicy
  • SizeAndTimeBasedRollingPolicy

归档日志自动压缩

TimeBasedRollingPolicy 支持自动文件压缩。如果 fileNamePattern 选项的值以 .gz 或 .zip 结尾,则启用此功能。

下面定义了3个appender:

  • 往控制台输出
  • 打印全部日志
  • 只打印错误级别日志
<configuration debug="false" scan="false">

  <property name="log.path" value="E:/var/log/education-mgr" />

  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
    </encoder>
  </appender>
  <appender name="ALL_LEVEL_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <!-- 正在记录的日志文档的路径及文档名 -->
    <file>${log.path}/all.log</file>
    <!--日志文档输出格式-->
    <encoder>
      <pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{50} - %msg%n</pattern>
      <charset>UTF-8</charset>
    </encoder>
    <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
    <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
      <!-- 每天日志归档路径以及格式 -->
      <fileNamePattern>${log.path}/archives/all-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
    <!-- each file should be at most 100MB, keep 60 days worth of history, but at most 20GB -->
    <maxFileSize>100MB</maxFileSize>
    <maxHistory>60</maxHistory>
    <totalSizeCap>20GB</totalSizeCap>
    </rollingPolicy>
  </appender>
  <appender name="ERROR_LEVEL_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>${log.path}/error.log</file>
    <encoder>
      <pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{50} - %msg%n</pattern>
      <charset>UTF-8</charset>
    </encoder>
    <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
      <fileNamePattern>${log.path}/archives/error-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
    <maxFileSize>10MB</maxFileSize>
    <maxHistory>60</maxHistory>
    <totalSizeCap>1GB</totalSizeCap>
    </rollingPolicy>
    <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
      <level>ERROR</level>
    </filter>
  </appender>
</configuration>

还可以在application.yml中配置

logging:
  level:
    com.example: info
    org.springframework: warn

配置info就只有info以上日志才会输出

如果配置 com.example: debug ,那么我们项目com.example包里面debug以上的日志也会输出。

除了“%d”之外,请注意“%i”转换标记。 %i 和 %d 标记都是必需的。 每次当前日志文件在当前时间段结束之前达到 maxFileSize 时,都会以从 0 开始的递增索引进行归档。

基于大小和时间的归档支持删除旧的归档文件。 您需要使用 maxHistory 属性指定要保留的周期数。 当您的应用程序停止并重新启动时,日志记录将在正确的位置继续,即当前周期的最大索引号。

在1.1.7之前的版本中,本文档提到了一个名为SizeAndTimeBasedFNATP的组件。 但是,鉴于 SizeAndTimeBasedRollingPolicy 提供了更简单的配置结构,我们不再记录 SizeAndTimeBasedFNATP。 尽管如此,使用 SizeAndTimeBasedFNATP 的早期配置文件将继续正常工作。 事实上,SizeAndTimeBasedRollingPolicy 是通过 SizeAndTimeBasedFNATP 子组件实现的。

root

root节点是必选节点,用来指定最基础的日志输出级别,只有一个level属性。

level:用来设置打印级别,不区分大小写:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,不能设置为INHERITED或者同义词NULL。默认是DEBUG。

如果这里的level设置为info的话,那么旗下设置的所有 appender 最高也只能输出info级别的日志。

<root level="debug">
    <appender-ref ref="console" />
    <appender-ref ref="file" />
</root>

logger

<logger>用来设置某一个包或者具体的某一个类的日志打印级别、以及指定仅有一个name属性,一个可选的level和一个可选的addtivity属性。

  • name:用来指定受此logger约束的某一个包或者具体的某一个类。
  • level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,还有一个特殊值INHERITED或者NULL,代表强制执行上级的级别。如果未设置此属性,那么当前logger将会继承上级的级别。
  • addtivity:是否向上级logger传递打印信息。默认是true。

logger在实际使用的时候有两种情况,先来看一看代码中的使用:

public void testLog() {
    //日志级别从低到高分为 DEBUG < INFO < WARN < ERROR,如果设置为WARN,则低于WARN的信息都不会输出。
    LOG.debug("日志输出 debug");
    LOG.info("日志输出 info");
    LOG.warn("日志输出 warn");
    LOG.error("日志输出 error");
}

第一种:带有logger的配置,不指定级别,不指定appender

<logger name=``"com.sue.controller"/>

将控制controller包下的所有类的日志的打印,没有设置打印级别,所以继承他的上级的日志级别“info”;

没有设置addtivity,默认为true,将此logger的打印信息向上级传递;

没有设置appender,此logger本身不打印任何信息。

<root level="info">将root的打印级别设置为“info”,指定了名字为“console”的appender。

当执行com.sue.demo.controller.LoggerController类的testLog方法时,LoggerController在包com.sue.demo.controller中,所以首先执行<logger name="com.sue.demo.controller"/>,将级别为“info”及大于“info”的日志信息传递给root,本身并不打印;

root接到下级传递的信息,交给已经配置好的名为“console”的appender处理,“console”appender将信息打印到控制台;

...日志输出 info
...日志输出 warn
...日志输出 error

第二种:带有多个logger的配置,指定级别,指定appender

<logger name="com.sue.demo.controller.TestController" level="WARN" additivity="false">
	<appender-ref ref="console"/>
</logger>

​ 控制com.sue.demo.controller.LoggerController类的日志打印,打印级别为“WARN”;additivity属性为false,表示此logger的打印信息不再向上级传递;指定了名字为“console”的appender;

​ 这时候执行com.sue.demo.controller.LoggerController类的testLog方法时,先执行匹配的logger,将级别为“WARN”及大于“WARN”的日志信息交给此logger指定的名为“console”的appender处理,在控制台中打出日志,不再向上级root传递打印信息。

​ 当然如果你把additivity=”false”改成additivity=”true”的话,就会打印两次,因为打印信息向上级传递,logger本身打印一次,root接到后又打印一次。

Filter

ThresholdFilter

ThresholdFilter 拒绝级别低于 指定 的所有事件,只记录大于等于所指级别的日志。

<configuration>
  <appender name="CONSOLE"
    class="ch.qos.logback.core.ConsoleAppender">
    <!-- 拒绝级别低于 INFO 的所有事件,即 TRACE 和 DEBUG -->
    <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
      <level>INFO</level>
    </filter>
    <encoder>
      <pattern>
        %-4relative [%thread] %-5level %logger{30} -%kvp -%msg%n
      </pattern>
    </encoder>
  </appender>
  <root level="DEBUG">
    <appender-ref ref="CONSOLE" />
  </root>
</configuration>

LevelFilter

LevelFilter 根据精确级别匹配筛选事件。如果事件的级别等于配置的级别,则筛选器将接受或拒绝事件,具体取决于 onMatch 和 onMismatch 属性的配置。下面是一个示例配置文件。

<configuration>
  <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
    <filter class="ch.qos.logback.classic.filter.LevelFilter">
      <level>INFO</level>
      <onMatch>ACCEPT</onMatch>
      <onMismatch>DENY</onMismatch>
    </filter>
    ...
</configuration>

log4j2

Office 文档类库

注:Windows 字体库存储位置:C:\Windows\Fonts

Apache POI

Apache POI 使用Java编写的开源免费的Java API,提供了对Microsoft Office(Excel、WORD、PowerPoint、Visio等)格式档案读和写的功能。

官网:https://poi.apache.org

使用API

  • HSSF : 读写 Microsoft Excel XLS 格式文档
  • XSSF : 读写 Microsoft Excel OOXML XLSX 格式文档
  • SXSSF : 读写 Microsoft Excel OOXML XLSX 格式文档
  • HWPF : 读写 Microsoft Word DOC 格式文档
  • XWPF 读写Microsoft Word DOC2003格式文档
  • HSLF : 读写 Microsoft PowerPoint 格式文档
  • HDGF : 读 Microsoft Visio 格式文档
  • HPBF : 读 Microsoft Publisher 格式文档
  • HSMF : 读 Microsoft Outlook 格式文档

操作 Excel

Excel中的工作簿、工作表、行、单元格中的关系:

  • 一个Excel文件对应于一个workbook(HSSFWorkbook),
  • 一个workbook可以有多个sheet(HSSFSheet)组成,
  • 一个sheet是由多个row(HSSFRow)组成,
  • 一个row是由多个cell(HSSFCell)组成

添加 Apache POI 的依赖

<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi</artifactId>
    <version>5.1.0</version> 
</dependency>
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>5.1.0</version> 
</dependency>

事先准备一个Excel 文件模版:

public void downloadSubsidyGatherDetail(List<StudyPeriodRecord> studyPeriodRecords, HttpServletResponse response) {
InputStream templateStream = getClass().getClassLoader().getResourceAsStream("templates/studyPeriodDetail.xlsx");
try (Workbook workbook = new XSSFWorkbook(templateStream)) {
  Sheet sheet = workbook.getSheetAt(0); // 模板中只有一个 sheet
  int i = 1;
  for (StudyPeriodRecord studyPeriodRecord:studyPeriodRecords) {
    Row row = sheet.createRow(i);
    row.createCell(0).setCellValue(studyPeriodRecord.getUserName());
    row.createCell(1).setCellValue(studyPeriodRecord.getCourseName());
    row.createCell(2).setCellValue(studyPeriodRecord.getStdPeriod()+"");
    row.createCell(3).setCellValue(studyPeriodRecord.getPeriod()+"");
    i++;
  }
  response.setContentType("application/vnd.ms-excel");
  response.setHeader("content-Disposition", "attachment;filename=" + URLEncoder.encode("学时查询", "utf-8"));
  response.setHeader("Access-Control-Expose-Headers", "content-Disposition");
  workbook.write(response.getOutputStream());
} catch (IOException e) {
  throw new RuntimeException(e);
}
}

操作Word

  • HWPF 是 POI 支持 Word(97-2003) 的 Java 组件,支持读写Word文档,但是写功能目前只实现一部分;它也提供更早版本的Word6和Word95版本的简单的文本摘录功能。
  • XWPF是 POI 支持 Word 2007+ 的 Java组件,提供简单文件的读写功能。

XWPF(XML Word Processing Format)用于处理 Microsoft .docx 文档,在 POI 中称为 XWPF;

docx 格式 -》 poi-ooxml 组件

doc 格式 -》poi-scratchpad 组件

注意 :

​ poi-ooxml 这个组件对应的文档类型是 Word DOCX,即后缀名为 .docx 的 word 文档。如果要操作或生成 .doc 后缀名的文档,请使用 poi-scratchpad 这个组件。这两套组件的 API 不是通用的,使用之前一定要确定好模板版本并注意区分。

java 调用word模板实现循环套打生成word文档

1 模版里做循环,需要循环的地方 在模版里加入 <#list reportListas a > </#list> 编辑好。
2 代码里

Map<String,Object> resMap = new HashMap<>();
resMap.put("reportList", list);
t.process(resMap,out);

Poi-tl Word模板引擎

poi-tl 是Java实现开源的 Word模板引擎,基于Apache POI,提供更友好的API。

implementation 'com.deepoove:poi-tl:1.12.2'

Aspose 套件

Aspose 商用套件:https://www.aspose.com/zh/

<dependency>
  <groupId>com.aspose</groupId>
  <artifactId>aspose-words</artifactId>
  <version>15.8.0</version>
  <scope>system</scope>
  <systemPath>${project.basedir}/libs/aspose-words-15.8.0-jdk16.jar</systemPath>
</dependency>

word文档转PDF非常简单:

if (AsposeUtil.getLicense()) {
        fileInputStream = new FileInputStream(tmpFile);
        Document doc = new Document(fileInputStream);
        doc.save(outputStream, SaveFormat.PDF);
      }

Aspose 产品:(商业产品需要授权)

public static void getLicense() {
    InputStream is = AsposeUtil.class.getClassLoader().getResourceAsStream("aspose.licence/licence.xml");
    License license = new License();
    license.setLicense(is);
}

授权文件内容:licence.xml

<?xml version="1.0" encoding="UTF-8" ?>
<License>
    <Data>
        <Products>
            <Product>Aspose.Total for Java</Product>
            <Product>Aspose.Words for Java</Product>
        </Products>
        <EditionType>Enterprise</EditionType>
        <SubscriptionExpiry>20991231</SubscriptionExpiry>
        <LicenseExpiry>20991231</LicenseExpiry>
        <SerialNumber>8bfe198c-7f0c-4ef8-8ff0-acc3237bf0d7</SerialNumber>
    </Data>
    <Signature>sNLLKGMUdF0r8O1kKilWAGdgfs2BvJb/2Xp8p5iuDVfZXmhppo+d0Ran1P9TKdjV4ABwAgKXxJ3jcQTqE/2IRfqwnPf8itN8aFZlV3TJPYeD3yWE7IT55Gz6EijUpC7aKeoohTb4w2fpox58wWoF3SNp6sK6jDfiAUGEHYJ9pjU=</Signature>
</License>

在线预览

jodconverter

word 转PDF

  • 利用 JODConverter 转化文档为 PDF 格式,依赖于 OpenOffice.org 或者 LibreOffice 提供的服务来进行转换

kkFileView

kkFileView 开源项目,也是基于 jodconverter+ LibreOffice/OpenOffice 方案,使用springboot封装为一个单独的服务。

发版历史:

  • 2017年12月:首次开源

  • 2021年6月 :v3.6.0版本

  • 2021年7月6日,v4.0.0

  • 2022-12 4.1.0

  • 2023-04 4.2.0

  • 2023-07 4.3.0

  • 2025-01 4.4.0

想要使用最新版的kkFileView镜像,需要自己打包并构建镜像,或者加入收费的社区进行获取。

自己打包并构建镜像也非常方便,在gitee上拉取源码 :

  1. 将Java项目使用maven打包
  2. 执行dockerfile :构建一个基础镜像:keking/kkfileview-base
  3. 执行dockerfile :构建文件预览服务镜像:kkfileview:v4.4.0

LibreOffice 和 OpenOffice

​ 2011年,Oracle将OpenOffice.org 的商标权和代码捐赠给Apache软件基金会,继续OpenOffice.org的开发工作,并将它重命名为Apache OpenOffice

Apache OpenOfficeLibreOffice 都发源于OpenOffice.org这个开源的office办公套件。

​ 2011年,Oracle将OpenOffice.org的商标权和代码捐赠给Apache软件基金会,继续OpenOffice.org的开发工作,并将它重命名为Apache OpenOffice。

​ LibreOffice的初始版本号码被设置为与OpenOffice.org 一致,故初始发布(2010年)即为第三版,并不存在第二版、第一版。

#安装文件
yum -y install libreoffice
#安装中文包
yum -y install libreoffice-langpack-zh-Han*

#安装的目录在/usr/lib64/libreoffice

通过命令行测试LibreOffice

# centos
/usr/bin/libreoffice  --invisible --convert-to pdf  <待转换的word路径> --outdir <生成的pdf路径>
比如:/usr/bin/libreoffice  --invisible --convert-to pdf  test.txt --outdir abc

# windows
soffice.exe --headless --invisible --convert-to pdf test.txt --outdir d:\abc
# 通过 ps -ef |grep soffice 获取pid
soffice --headless --accept="socket,host=0.0.0.0,port=8100;urp;" --nofirststartwizard
# 
nohup soffice --headless --accept="socket,host=0.0.0.0,port=8100;urp;" --nofirststartwizard &

# –headless:在后台运行 LibreOffice,无需打开 GUI
–convert-to:将文档转换为指定格式
–print-to-file:将文档打印到文件
–invisible:运行 LibreOffice 而不显示 GUI

PDF 文档类库

电子签字

盖章

水印

文件加密

在线生成印章网站:https://tools.kalvinbg.cn/convenience/seal

iText pdf

​ iText是著名的开放源码的站点sourceforge一个项目(由Bruno Lowagie编写),是一个用Java和.NET语言写的库,用来创建和修改PDF文件。通过iText不仅可以生成PDF或rtf的文档,而且可以将XML、Html文件转化为PDF文件。

​ 在java技术栈中,PDF创建和操作最为常用的itext了,但是使用itext一定要了解其版本历史和License问题,在早前版本使用的是MPL和LGPL双许可协议,在5.x以上版本中使用的是AGPLv3(这个协议意味着,只有个人用途和开源的项目才能使用itext这个库,否则是需要收费的)。

发展历史:

  • iText 0.x-2.x/iTextSharp 3.x-4.x
    • 更新时间是2000-2009
    • 使用的是MPL和LGPL双许可协议
    • 最近的更新是2009年,版本号是iText 2.1.7/iTextSharp 4.1.6.0
    • 此时引入包的GAV版本如下:
<dependency>
  <groupId>com.lowagie</groupId>
  <artifactId>itext</artifactId>
  <version>2.1.7</version>
</dependency>
  • iText 5.x和iTextSharp 5.x
    • 更新时间是2009-2016, 公司化运作,并标准化和提高性能
    • 开始使用AGPLv3协议:只有个人用途和开源的项目才能使用itext这个库,否则是需要收费的
    • iTextSharp被设计成iText库的.NET版本,并且与iText版本号同步,iText 5.0.0和iTextSharp5.0.0同时发布
    • 新功能不在这里面增加,但是官方会修复重要的bug
    • 此时引入包的GAV版本如下:
<dependency>
  <groupId>com.itextpdf</groupId>
  <artifactId>itextpdf</artifactId>
  <version>5.5.13.3</version>
</dependency>
  • iText 7.x
    • 更新时间是2016到现在
    • AGPLv3协议
    • 完全重写,重点关注可扩展性和模块化
    • 不适用iTextSharp这个名称,都统称为iText,有Java和.Net版本
    • JDK 1.7+
    • 此时引入包的GAV版本如下:
<dependency>
  <groupId>com.itextpdf</groupId>
  <artifactId>itext7-core</artifactId>
  <version>7.2.2</version>
  <type>pom</type>
</dependency>

注:iText变化后,GitHub上有团队基于4.x版本(MPL和LGPL双许可协议)fork了一个分支成为OpenPDF,并继续维护该项目。

OpenPDF

OpenPDF 是具有 LGPL 和 MPL 许可证的开源软件。 它是 iText 版本 4(更具体地说是 iText svn tag 4.2.0)的一个分支,它公开托管在 sourceforge 上,源代码中包含 LGPL 和 MPL 许可证头,svn 存储库中包含 LGPL 和 MPL 许可证文档。 从 iText 5.0 版本开始,开发人员已转向 AGPL,只有个人用途和开源的项目才能使用iText这个库,否则是需要收费的

Apache PDFBox

Apache PDFBox 库是一个用于处理 PDF 文档的开源 Java 工具。 该项目允许创建新的 PDF 文档、操作现有文档以及从文档中提取内容的能力。

特性

  • 提取文本 - 使用PDFBox,您可以从PDF文件中提取Unicode文本。
  • 拆分和合并 - 使用PDFBox,您可以将单个PDF文件分成多个文件,并将它们作为单个文件合并。
  • 填写表单 - 使用PDFBox,您可以填写文档中的表单数据。
  • 打印 - 使用PDFBox,您可以使用标准Java打印API打印PDF文件。
  • 另存为图像 - 使用PDFBox,您可以将PDF保存为图像文件,如PNG或JPEG。
  • 创建PDF - 使用PDFBox,您可以通过创建Java程序创建新的PDF文件,还可以包括图像和字体。
  • 签名 - 使用PDFBox,您可以将数字签名添加到PDF文件。

中文教程:https://www.yiibai.com/pdfbox/pdfbox_document_properties.html

添加依赖:

<dependency>
       <groupId>org.apache.pdfbox</groupId>
       <artifactId>pdfbox</artifactId>
       <version>2.0.25</version>
</dependency>

常用操作:

// 文本
public void showTextByLeft(PDPageContentStream overContent, String txt, String def, float x, float y) throws Exception {
    //Begin the Content stream
    overContent.beginText();
    //overContent.setWordSpacing(0.01F);
    if (null == txt) {
        txt = def;
    }

    //Setting the position for the line
    overContent.newLineAtOffset(x, y);

    //Adding text in the form of string
    overContent.showText(txt);

    //Ending the content stream
    overContent.endText();
}

private float FONT_SIZE = 12;
//载入现有的pdf模板 
InputStream in = this.getClass().getClassLoader().getResourceAsStream("pdf/1.pdf");
PDDocument document = PDDocument.load(in);
//加载字体
InputStream inFont = this.getClass().getClassLoader().getResourceAsStream("pdf/simfang.ttf");
PDType0Font font = PDType0Font.load(document, inFont);


// Retrieving the pages of the document, and using PREPEND mode
// 获取第一页
PDPage page = document.getPage(0);
PDRectangle pdRectangle = page.getMediaBox();
//添加新的空白页面
PDPage page2 = new PDPage(pdRectangle);
document.addPage(page2);

//获取某一页的流
PDPageContentStream contentStream = new PDPageContentStream(document, page,
        PDPageContentStream.AppendMode.APPEND, true, false);

contentStream.setFont(font , FONT_SIZE);
contentStream.setNonStrokingColor(Color.black);
contentStream.setStrokingColor(Color.black);
//添加文本
showTextByLeft(contentStream, MessageUtil.getMessage("report.label.date") + DateUtil.formatDate(date), "", 30, 720);

//添加矩形并填充背景色
contentStream.addRect(30, 690, 535, 20);
contentStream.setNonStrokingColor(new Color(236, 238, 242));
contentStream.fill();
//添加图片
InputStream stream = getClass().getClassLoader().getResourceAsStream("pdf/football.png");
BufferedImage bi = ImageIO.read(stream);
pdImage = LosslessFactory.createFromImage(document, bi);
//x y ,w,h 
contentStream.drawImage(pdImage, 400, 460, 150, 95);

//保存pdf 
document.save(new File(filePath));
// Closing the document
 document.close();

模版引擎

<!-- velocity代码生成使用模板 -->
        <dependency>
            <groupId>org.apache.velocity</groupId>
            <artifactId>velocity-engine-core</artifactId>
        </dependency>

验证码

图形验证码

Google Kaptcha

依赖:

<dependency>
            <groupId>com.google.code</groupId>
            <artifactId>kaptcha</artifactId>
            <version>2.3.2</version>
        </dependency>

<dependency>
      <groupId>com.github.penggle</groupId>
      <artifactId>kaptcha</artifactId>
      <version>2.3.2</version>
    </dependency>

配置类:

@Configuration
public class KaptchaConfig {
    @Bean(name="captchaProducer")
    public DefaultKaptcha getKaptchaBean(){
        DefaultKaptcha defaultKaptcha=new DefaultKaptcha();
        Properties properties=new Properties();
        //验证码是否带边框 No
        properties.setProperty("kaptcha.border", "no");
        //验证码字体颜色
        properties.setProperty("kaptcha.textproducer.font.color", "blue");
        //验证码整体宽度
        properties.setProperty("kaptcha.image.width", "400");
        //验证码整体高度
        properties.setProperty("kaptcha.image.height", "125");
        //文字个数
        properties.setProperty("kaptcha.textproducer.char.length", "4");
        //文字大小
        properties.setProperty("kaptcha.textproducer.font.size","120");
        //文字随机字体
        properties.setProperty("kaptcha.textproducer.font.names", "宋体,楷体,微软雅黑");
        //文字距离
        properties.setProperty("kaptcha.textproducer.char.space","16");
        //干扰线颜色
        properties.setProperty("kaptcha.noise.color","blue");
        //自定义验证码样式
        properties.setProperty("kaptcha.obscurificator.impl","com.zhiyingwl.modules.imageCode.base.DisKaptchaCssImpl");
        //自定义验证码背景
        properties.setProperty("kaptcha.background.impl","com.zhiyingwl.modules.imageCode.base.NoKaptchaBackhround");
        Config config=new Config(properties);
        defaultKaptcha.setConfig(config);
        return defaultKaptcha;
    }
}

行为验证码

AJ-Captcha 是一个开源的行为验证码工具库。行为验证码采用嵌入式集成方式,接入方便,安全,高效。抛弃了传统字符型验证码展示-填写字符-比对答案的流程,采用验证码展示-采集用户行为-分析用户行为流程,用户只需要产生指定的行为轨迹,不需要键盘手动输入,极大优化了传统验证码用户体验不佳的问题;同时,快速、准确的返回人机判定结果。

目前对外提供两种类型的验证码:

  • 滑动拼图
  • 文字点选
<dependency>
    <groupId>com.anji-plus</groupId>
    <artifactId>captcha</artifactId>
    <version>1.4.0</version>
</dependency>

二维码/条形码

​ Google ZXing是一个开源Java类库,用于解析多种格式的1D/2D条码图像处理库。目标是能够对QR编码、Data Matrix、UPC的1D条形码进行解码。其提供了多种平台下的客户端包括:J2ME、J2SE和Android。

<dependency>
    <groupId>com.google.zxing</groupId>
    <artifactId>core</artifactId>
    <version>3.5.3</version>
</dependency>
<dependency>
    <groupId>com.google.zxing</groupId>
    <artifactId>javase</artifactId>
    <version>3.5.3</version>
</dependency>

代码:

import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.OutputStream;
import java.util.Hashtable;

/**
 * 二维码生成工具类
 */
public class QrCodeUtils {
    private static final String CHARSET = "utf-8";
    public static final String FORMAT = "JPG";
    // 二维码尺寸
    private static final int QRCODE_SIZE = 300;

    /**
     * 生成二维码
     * @param content      二维码内容
     * @return 图片
     * @throws Exception
     */
    public static BufferedImage createImage(String content) throws Exception {
        // 二维码参数设置
        Hashtable<EncodeHintType, Object> hints = new Hashtable<>();
        // 安全等级,最高h
        hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
        // 编码设置
        hints.put(EncodeHintType.CHARACTER_SET, CHARSET);
        // 设置margin=0-10
        hints.put(EncodeHintType.MARGIN, 1);
        // 创建矩阵容器
        BitMatrix bitMatrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, QRCODE_SIZE, QRCODE_SIZE,
                hints);
        int width = bitMatrix.getWidth();
        int height = bitMatrix.getHeight();
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        // 矩阵转换图像
        for (int x = 0; x < width; x++) {
            for (int y = 0; y < height; y++) {
                image.setRGB(x, y, bitMatrix.get(x, y) ? 0xFF000000 : 0xFFFFFFFF);
            }
        }
        return image;
    }


    /**
     * 生成二维码
     *
     * @param content      内容
     * @param output       输出流
     * @throws Exception
     */
    public static void encode(String content, OutputStream output)
            throws Exception {
        BufferedImage image = QrCodeUtils.createImage(content);
        ImageIO.write(image, FORMAT, output);
    }

}
@GetMapping("/createQrCode")
	public void createQrCode(HttpServletRequest request, HttpServletResponse response) {
		String content = request.getParameter("content");
		try {
			OutputStream os = response.getOutputStream();
			//requestUrl:需要生成二维码的连接,os:响应输出流
			QrCodeUtils.encode(content,  os);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

字节码操作类库

Hibernate 框架内置了两款字节码操作类库:javassist 和 bytebuddy,默认使用 bytebuddy。

Java字节码增强工具对比

对比 ASM Javassist JDK Proxy Cglib ByteBuddy
起源时间 2002 1999 2000 2011 2014
包大小 130KB (版本9.3) 788KB (版本3.28.0-GA) 3.7MB (版本1.10.19)
增强方式 字节码指令 字节码指令和源码 源码 源码 源码
源码编译 NA 不支持 支持 支持 支持
agent支持 支持 支持 不支持,依赖框架 不支持,依赖框架 支持
性能
维护状态 停止升级 停止维护 活跃
优点 超高性能,应用场景广泛 同时支持字节码指令和源码两种增强方式 JDK原生类库支持 零侵入,提供良好的API扩展编程
缺点 字节码指令对应用开发者不友好 场景非常局限,只适用于Java接口 已经不再维护,对于新版JDK17+支持不好,官网建议切换到ByteBuddy
应用场景 小,高性能,广泛用于语言级别 广泛用于框架场景 广泛用于Trace场景

注:相关性能数据来自这里

ByteBuddy

Byte Buddy 是一个代码生成和操作库,用于在 Java 应用程序运行时创建和修改 Java 类,而无需编译器的帮助。除了 Java 类库附带的代码生成实用程序外,Byte Buddy 还允许创建任意类,并且不限于实现用于创建运行时代理的接口。

此外,Byte Buddy 提供了一种方便的 API,可以使用 Java 代理或在构建过程中手动更改类。

  • 无需理解字节码指令,即可使用简单的 API 就能很容易操作字节码,控制类和方法。
  • 已支持Java 11,库轻量,仅取决于Java字节代码解析器库ASM的访问者API,它本身不需要任何其他依赖项。
  • 比起JDK动态代理、cglib、Javassist,Byte Buddy在性能上具有一定的优势。

2015年10月,Byte Buddy被 Oracle 授予了 Duke’s Choice大奖。

示例:

String helloWorld = new ByteBuddy()
            .subclass(Object.class)
            .method(named("toString"))
            .intercept(FixedValue.value("Hello World!"))
            .make()
            .load(getClass().getClassLoader())
            .getLoaded()
            .newInstance()
            .toString();    

System.out.println(helloWorld);  // Hello World!

Javassist

Javassist 是一个非常早的字节码操作类库,开始于1999年。

它能够支持两种编辑方式:

  • 源码级别
  • 字节码指令级别,

相比于晦涩的字节码级别,源码级别更加人性化,代码编写起来更加易懂。

​ Javassist (JAVA programming ASSISTant) 是在 Java 中编辑字节码的类库;它使 Java 程序能够在运行时定义一个新类, 并在 JVM 加载时修改类文件。

​ 我们常用到的动态特性主要是反射,在运行时查找对象属性、方法,修改作用域,通过方法名称调用方法等。在线的应用不会频繁使用反射,因为反射的性能开销较大。其实还有一种和反射一样强大的特性,但是开销却很低,它就是Javassit。

​ 与其他类似的字节码编辑器不同, Javassist 提供了两个级别的 API: 源级别和字节码级别。 如果用户使用源级 API, 他们可以编辑类文件, 而不知道 Java 字节码的规格。 整个 API 只用 Java 语言的词汇来设计。 您甚至可以以源文本的形式指定插入的字节码; Javassist 在运行中编译它。 另一方面, 字节码级 API 允许用户直接编辑类文件作为其他编辑器。

ID 生成

UUID

UUID 有四种不同的基本类型:基于时间的 UUID、DCE 安全性的 UUID、基于名称的 UUID 和随机生成的 UUID。这些类型的版本值分别为 1、2、3 和 4。

JDK 中的 UUID.randomUUID() 方法生成的是 UUIDv4 的方法。

JDK 自己并没有提供快速的基于时间的 UUID 生成方法。基于时间的 UUID 生成,我们通常会依赖使用第三方的类库。

<dependency>
    <groupId>com.fasterxml.uuid</groupId>
    <artifactId>java-uuid-generator</artifactId>
    <version>5.0.0</version>
</dependency>
System.out.println("UUID Version 1: " + Generators.timeBasedGenerator().generate());
System.out.println("UUID Version 6: " + Generators.timeBasedReorderedGenerator().generate());
System.out.println("UUID Version 7: " + Generators.timeBasedEpochGenerator().generate());

README

参考:

MJDK 如何实现压缩速率的 5 倍提升?

ByteBuddy(史上最全) - 疯狂创客圈 - 博客园

常用开发库 - Apache Common包

Logback 官方配置手册


Java类库使用集锦
http://jackpot-lang.online/2023/10/22/Java/Java类库使用集锦/
作者
Jackpot
发布于
2023年10月22日
许可协议