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 客户端
加密解密库
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk18on</artifactId>
<version>1.77</version>
</dependency>
对称加密
在软件开发中,常用的对称加密算法有:
算法 | 密钥长度 | 工作模式 | 填充模式 |
---|---|---|---|
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共定义了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.xml
、logback-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:用来指定受此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等)格式档案读和写的功能。
使用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上拉取源码 :
- 将Java项目使用maven打包
- 执行dockerfile :构建一个基础镜像:keking/kkfileview-base
- 执行dockerfile :构建文件预览服务镜像:kkfileview:v4.4.0
LibreOffice 和 OpenOffice
2011年,Oracle将OpenOffice.org 的商标权和代码捐赠给Apache软件基金会,继续OpenOffice.org的开发工作,并将它重命名为Apache OpenOffice。
Apache OpenOffice与LibreOffice 都发源于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
参考: