Spring 常用工具类

Spring作为常用的开发框架,在Spring框架应用中,排在ApacheCommon、Guava、Huool等通用库后,第二优先级可以考虑使用Spring-core-xxx.jar中的util包。

内置的resouce类型

  • UrlResource

  • ClassPathResource

  • FileSystemResource

  • ServletContextResource

  • InputStreamResource

  • ByteArrayResource

  • EncodedResource 也就是Resource加上encoding, 可以认为是有编码的资源。

  • VfsResource(在jboss里经常用到, 相应还有 工具类 VfsUtils)。

  • org.springframework.util.xml.ResourceUtils 用于处理表达资源字符串前缀描述资源的工具. 如: "classpath:". 有 getURL, getFile, isFileURL, isJarURL, extractJarFileURL。

工具类

  • org.springframework.core.annotation.AnnotationUtils 处理注解。

  • org.springframework.core.io.support.PathMatchingResourcePatternResolver 用 于处理 ant 匹配风格(com/.jsp, com//.jsp),找出所有的资源, 结合上面的resource的概念一起使用,对于遍历文件很有用. 具体请详细查看javadoc。

  • org.springframework.core.io.support.PropertiesLoaderUtils 加载Properties资源工具类,和Resource结合。

  • org.springframework.core.BridgeMethodResolver 桥接方法分析器. 关于桥接方法请参考: http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#15.12.4.5

  • org.springframework.core.GenericTypeResolver 范型分析器, 在用于对范型方法, 参数分析.

  • org.springframework.core.NestedExceptionUtils

xml工具

  • org.springframework.util.xml.AbstractStaxContentHandler

  • org.springframework.util.xml.AbstractStaxXMLReader

  • org.springframework.util.xml.AbstractXMLReader

  • org.springframework.util.xml.AbstractXMLStreamReader

  • org.springframework.util.xml.DomUtils

  • org.springframework.util.xml.SimpleNamespaceContext

  • org.springframework.util.xml.SimpleSaxErrorHandler

  • org.springframework.util.xml.SimpleTransformErrorListener

  • org.springframework.util.xml.StaxUtils

  • org.springframework.util.xml.TransformerUtils

其它工具集

  • org.springframework.util.AntPathMatcherant风格的处理。

  • org.springframework.util.AntPathStringMatcher。

  • rg.springframework.util.Assert断言,在我们的参数判断时应该经常用。

  • org.springframework.util.CachingMapDecorator。

  • org.springframework.util.ClassUtils用于Class的处理。

  • org.springframework.util.CollectionUtils用于处理集合的工具。

  • org.springframework.util.CommonsLogWriter。

  • org.springframework.util.CompositeIterator。

  • org.springframework.util.ConcurrencyThrottleSupport。

  • org.springframework.util.CustomizableThreadCreator。

  • org.springframework.util.DefaultPropertiesPersister。

  • org.springframework.util.DigestUtils摘要处理, 这里有用于md5处理信息的。

  • org.springframework.util.FileCopyUtils文件的拷贝处理, 结合Resource的概念一起来处理, 真的是很方便。

  • org.springframework.util.FileSystemUtils。

  • org.springframework.util.LinkedCaseInsensitiveMap key值不区分大小写的LinkedMap。

  • org.springframework.util.LinkedMultiValueMap一个key可以存放多个值的LinkedMap。

  • org.springframework.util.Log4jConfigurer一个log4j的启动加载指定配制文件的工具类。

  • org.springframework.util.NumberUtils处理数字的工具类, 有parseNumber 可以把字符串处理成我们指定的数字格式, 还支持format格式, convertNumberToTargetClass 可以实现Number类型的转化。

  • org.springframework.util.ObjectUtils有很多处理null object的方法. 如nullSafeHashCode, nullSafeEquals, isArray, containsElement, addObjectToArray, 等有用的方法。

  • org.springframework.util.PatternMatchUtilsspring里用于处理简单的匹配. 如 Spring's typical "xxx", "xxx" and "xxx" pattern styles。

  • org.springframework.util.PropertyPlaceholderHelper用于处理占位符的替换。

  • org.springframework.util.ReflectionUtils反映常用工具方法. 有 findField, setField, getField, findMethod, invokeMethod等有用的方法。

  • org.springframework.util.SerializationUtils用于java的序列化与反序列化. serialize与deserialize方法。

  • org.springframework.util.StopWatch一个很好的用于记录执行时间的工具类, 且可以用于任务分阶段的测试时间. 最后支持一个很好看的打印格式. 这个类应该经常用。

  • org.springframework.util.StringUtils。

  • org.springframework.util.SystemPropertyUtils。

  • org.springframework.util.TypeUtils用于类型相容的判断. isAssignable。

  • org.springframework.util.WeakReferenceMonitor弱引用的监控。

和web相关的工具

  • org.springframework.web.util.CookieGenerator。

  • org.springframework.web.util.HtmlCharacterEntityDecoder。

  • org.springframework.web.util.HtmlCharacterEntityReferences。

  • org.springframework.web.util.HtmlUtils。

  • org.springframework.web.util.HttpUrlTemplate。

    • 这个类用于用字符串模板构建url, 它会自动处理url里的汉字及其它相关的编码. 在读取别人提供的url资源时, 应该经常用。
    • String url = "http://localhost/myapp/{name}/{id}";
  • org.springframework.web.util.JavaScriptUtils。

  • org.springframework.web.util.Log4jConfigListene。

    • 用listener的方式来配制log4j在web环境下的初始化。
  • org.springframework.web.util.UriTemplate。

  • org.springframework.web.util.UriUtils处理uri里特殊字符的编码。

  • org.springframework.web.util.WebUtils。

参考文章

hutool 工具类

官网戳这里

中文文档

参考API

hutool是作者的一个自造词,hu tool,hu指的是他的前公司,谐音:糊涂,意为"万事都作糊涂观,无所谓失,无所谓得",tool就是“工具”的意思。

hutool简略大纲:

image

安装

Maven

<dependency>
	<groupId>cn.hutool</groupId>
	<artifactId>hutool-all</artifactId>
	<!-- 5.1.0 -->
	<version>${hutool.version}</version>
</dependency>

Gradle

compile 'cn.hutool:hutool-all:5.1.0'

非Maven

点击 Maven中央仓库,下载 hutool-all-X.X.X.jar 即可。

注意 Hutool 5.x支持JDK8+,对Android平台没有测试,不能保证所有工具类的工具方法可用。 如果你的项目使用JDK7,请使用Hutool 4.x版本。

convert 类型转换

// int转字符串
int a = 1;
String aStr = Convert.toStr(a);

// 字符串转int
String param = "10";
int paramInt = Convert.toInt(param);
// 重载	第二个参数 defaultValue 可用于在转换失败时返回一个默认值
int paramIntDefault = Convert.toInt(param, 0);

// 转换为指定类型数组
String[] b = {"1", "2", "3", "4"};
Integer[] bArr = Convert.toIntArray(b);

// 转换为日期对象
String dateStr = "2017-05-06";
Date date = Convert.toDate(dateStr);

// 转为Unicode
String unicodeStr = "紫邪情";
String unicode = Convert.strToUnicode(unicodeStr);

// 转换为List
String[] strArr = {"a", "b", "c", "d"};
List<String> strList = Convert.toList(String.class, strArr);

DataUtil 日期时间

此工具定义了一些操作日期的方法: Date、long、Calendar之间的相互转换

// 当前时间
// 返回的其实是 DateTime,它继承自 Date 对象,重写了 toString() 方法,返回 yyyy-MM-dd HH:mm:ss 格式的字符串
Date date = DateUtil.date();


// 字符串转日期
/*
	DateUtil.parse()会自动识别一些格式,比如:
		yyyy-MM-dd HH:mm:ss
		yyyy-MM-dd
		HH:mm:ss
		yyyy-MM-dd HH:mm
		yyyy-MM-dd HH:mm:ss.SSS

	而且还可以自动识别带中文的
		年月日时分秒
*/
String dateStr = "2021-08-28";
Date date = DateUtil.parse(dateStr);

// 自定义格式化转换
date = DateUtil.parse(dateStr, "yyyy-MM-dd");

// 星座、属相
// 射手座
String zodiac = DateUtil.getZodiac(Month.DECEMBER.getValue(), 10);
// 蛇
String chineseZodiac = DateUtil.getChineseZodiac(1989);


// Calendar转Date
date = DateUtil.date(Calendar.getInstance());

// 时间戳转Date
date = DateUtil.date(System.currentTimeMillis());

// 格式化输出日期
String format = DateUtil.format(date, "yyyy-MM-dd");

// 获得年的部分
int year = DateUtil.year(date);

// 获得月份,从0开始计数
int month = DateUtil.month(date);

// 获取某天的开始、结束时间
Date beginOfDay = DateUtil.beginOfDay(date);
Date endOfDay = DateUtil.endOfDay(date);

// 计算偏移后的日期时间
Date newDate = DateUtil.offset(date, DateField.DAY_OF_MONTH, 2);

// 计算日期时间之间的偏移量
long betweenDay = DateUtil.between(date, newDate, DateUnit.DAY);

IO流相关

流操作工具类 IoUtil

文件读写操作工具类 FileUtil

文件类型判断工具类 FileTypeUtil

BufferedInputStream in = FileUtil.getInputStream("hutool/origin.txt");
BufferedOutputStream out = FileUtil.getOutputStream("hutool/to.txt");
long copySize = IoUtil.copy(in, out, IoUtil.DEFAULT_BUFFER_SIZE);

此工具还提供得有IO相关的,如下:

  • 文件操作:包括文件目录的新建、删除、复制、移动、改名等
  • 文件判断:判断文件或目录是否非空,是否为目录,是否为文件等等
  • 绝对路径:针对 ClassPath 中的文件转换为绝对路径文件
  • 文件名:主文件名,扩展名的获取
  • 读操作:包括 getReader、readXXX 操作
  • 写操作:包括 getWriter、writeXXX 操作

StrUtil 字符串工具

此工具和Apache Commons Lang 包中的 StringUtils 差不多

// 判断是否为空字符串
String str = "test";
StrUtil.isEmpty(str);
StrUtil.isNotEmpty(str);

// 去除字符串的前后缀
StrUtil.removeSuffix("a.jpg", ".jpg");
StrUtil.removePrefix("a.jpg", "a.");

// 格式化字符串	这个是比较有意思的一个点
String template = "这只是个占位符:{}";
String str2 = StrUtil.format(template, "我是占位符");
LOGGER.info("strUtil format:{}", str2);

ReflectUtil 反射工具

Hutool 封装的反射工具 ReflectUtil 包括:

  • 获取构造方法
  • 获取字段
  • 获取字段值
  • 获取方法
  • 执行方法(对象方法和静态方法)
import cn.hutool.core.util.ReflectUtil;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class ReflectDemo {
    private int id;

    public ReflectDemo() {
        System.out.println("无参构造");
    }

    public void print() {
        System.out.println("我是紫邪情");
    }

    public static void main(String[] args) throws IllegalAccessException {
        // 构建对象
        ReflectDemo reflectDemo = ReflectUtil.newInstance(ReflectDemo.class);

        // 获取构造方法
        Constructor[] constructors = ReflectUtil.getConstructors(ReflectDemo.class);
        for (Constructor constructor : constructors) {
            System.out.println(constructor.getName());
        }

        // 获取字段
        Field field = ReflectUtil.getField(ReflectDemo.class, "id");
        field.setInt(reflectDemo, 10);
        // 获取字段值
        System.out.println(ReflectUtil.getFieldValue(reflectDemo, field));

        // 获取所有方法
        Method[] methods = ReflectUtil.getMethods(ReflectDemo.class);
        for (Method m : methods) {
            System.out.println(m.getName());
        }

        // 获取指定方法
        Method method = ReflectUtil.getMethod(ReflectDemo.class, "print");
        System.out.println(method.getName());


        // 执行方法
        ReflectUtil.invoke(reflectDemo, "print");
    }
}

ZipUtil 压缩工具

Hutool 封装的 ZipUtil 针对 java.util.zip 包做了优化,可以使用一个方法搞定压缩和解压,并且自动处理文件和目录的问题,不再需要用户判断

ZipUtil.zip("hutool", "hutool.zip");
File unzip = ZipUtil.unzip("hutool.zip", "hutoolzip");+

IdcardUtil 身份证验证工具

支持大陆 15 位、18 位身份证,港澳台 10 位身份证

String ID_18 = "321083197812162119";
String ID_15 = "150102880730303";

boolean valid = IdcardUtil.isValidCard(ID_18);
boolean valid15 = IdcardUtil.isValidCard(ID_15);

Console 控制台工具

一些复杂的对象不支持直接打印,比如说数组,需要调用 Arrays.toString。Hutool 封装的 Console 类借鉴了 JavaScript 中的 console.log(),使得打印变成了一个非常便捷的方式

public class ConsoleDemo {
    public static void main(String[] args) {
        // 打印字符串
        Console.log("紫邪情,一个脸皮厚的人");

        // 打印字符串模板
        Console.log("洛阳是{}朝古都",13);

        int [] ints = {1,2,3,4};
        // 打印数组
        Console.log(ints);
    }
}

Validator 字段验证器

做 Web 开发的时候,后端通常需要对表单提交过来的数据进行验证。Hutool 封装的 Validator 可以进行很多有效的条件验证,以下的方法名见名知意

Validator.isEmail("紫邪情");
Validator.isMobile("itwanger.com");

image

ImgUtil 图片工具

Hutool 封装的 ImgUtil 可以对图片进行缩放、裁剪、转为黑白、加水印等操作

缩放图片

ImgUtil.scale(
        FileUtil.file("hutool/wangsan.jpg"),
        FileUtil.file("hutool/wangsan_small.jpg"),
        0.5f
);

裁剪图片

ImgUtil.cut(
        FileUtil.file("hutool/wangsan.jpg"),
        FileUtil.file("hutool/wangsan_cut.jpg"),
        new Rectangle(200, 200, 100, 100)
);

添加水印

ImgUtil.pressText(//
        FileUtil.file("hutool/wangsan.jpg"),
        FileUtil.file("hutool/wangsan_logo.jpg"),
        "沉默王二", Color.WHITE,
        new Font("黑体", Font.BOLD, 100),
        0,
        0,
        0.8f
);

setting 配置文件

Java 中广泛应用的配置文件 Properties 存在一个特别大的诟病:不支持中文
于是,Hutool 的 Setting 运用而生。Setting 除了兼容 Properties 文件格式外,还提供了一些特有功能,这些功能包括:

  • 各种编码方式支持
  • 变量支持
  • 分组支持

整个example.setting配置文件

name=紫邪情
age=18

读取和更新配置文件

public class SettingDemo {
    private final static String SETTING = "hutool/example.setting";
    public static void main(String[] args) {
        // 初始化 Setting
        Setting setting = new Setting(SETTING);

        // 读取
        setting.getStr("name", "紫邪情");

        // 在配置文件变更时自动加载
        setting.autoLoad(true);

        // 通过代码方式增加键值对
        setting.set("birthday", "2021年08月28日");
        setting.store(SETTING);
    }
}

SecureUtil 加密解密

加密分为三种:

  • 对称加密(symmetric),例如:AES、DES 等
  • 非对称加密(asymmetric),例如:RSA、DSA 等
  • 摘要加密(digest),例如:MD5、SHA-1、SHA-256、HMAC 等

Hutool 针对这三种情况都做了封装:

  • 对称加密 SymmetricCrypto
  • 非对称加密 AsymmetricCrypto
  • 摘要加密 Digester

快速加密工具类 SecureUtil 有以下这些方法:

1)对称加密

  • SecureUtil.aes
  • SecureUtil.des

2)非对称加密

  • SecureUtil.rsa
  • SecureUtil.dsa

3)摘要加密

  • SecureUtil.md5
  • SecureUtil.sha1
  • SecureUtil.hmac
  • SecureUtil.hmacMd5
  • cureUtil.hmacSha1
// MD5加密
String str = "123456";
String md5Str = SecureUtil.md5(str);
LOGGER.info("secureUtil md5:{}", md5Str);

String encry = aes.encryptHex("紫邪情");
System.out.println(encry);
String oo = aes.decryptStr(encry);
System.out.println(oo);

CaptchaUtil 验证码工具

此工具用于生成图形验证码

// 生成验证码图片
LineCaptcha lineCaptcha = CaptchaUtil.createLineCaptcha(200, 100);
try {
	request.getSession().setAttribute("CAPTCHA_KEY", lineCaptcha.getCode());
	response.setContentType("image/png");//告诉浏览器输出内容为图片
	response.setHeader("Pragma", "No-cache");//禁止浏览器缓存
	response.setHeader("Cache-Control", "no-cache");
	response.setDateHeader("Expire", 0);
	lineCaptcha.write(response.getOutputStream());
} catch (IOException e) {
	e.printStackTrace();
}

ClassPathResource ClassPath资源工具

此工具是获取ClassPath下的文件,在Tomcat等容器中,ClassPath一般为:WEB-INFO/classes

// 获取定义在src/main/resources文件夹中的配置文件
ClassPathResource resource = new ClassPathResource("generator.properties");
Properties properties = new Properties();
properties.load(resource.getStream());
LOGGER.info("/classPath:{}", properties);

NumberUtil 四则运算

此工具是用于各种类型数字的加减乘除操作及判断类型

double n1 = 1.234;
double n2 = 1.234;
double result;

// 对float、double、BigDecimal做加减乘除操作
result = NumberUtil.add(n1, n2);
result = NumberUtil.sub(n1, n2);
result = NumberUtil.mul(n1, n2);
result = NumberUtil.div(n1, n2);

// 保留两位小数
BigDecimal roundNum = NumberUtil.round(n1, 2);
String n3 = "1.234";

// 判断是否为数字、整数、浮点数
NumberUtil.isNumber(n3);
NumberUtil.isInteger(n3);
NumberUtil.isDouble(n3);

BeanUtil Bean与Map转换工具

此工具是用于Map与JavaBean对象的互相转换以及对象属性的拷贝

PmsBrand brand = new PmsBrand();
brand.setId(1L);
brand.setName("小米");
brand.setShowStatus(0);

// Bean转Map
Map<String, Object> map = BeanUtil.beanToMap(brand);
LOGGER.info("beanUtil bean to map:{}", map);

// Map转Bean
PmsBrand mapBrand = BeanUtil.mapToBean(map, PmsBrand.class, false);
LOGGER.info("beanUtil map to bean:{}", mapBrand);

// Bean属性拷贝
PmsBrand copyBrand = new PmsBrand();
BeanUtil.copyProperties(brand, copyBrand);
LOGGER.info("beanUtil copy properties:{}", copyBrand);

CollUtil 集合工具

// 数组转换为列表
String[] array = new String[]{"a", "b", "c", "d", "e"};
List<String> list = CollUtil.newArrayList(array);

// 数组转字符串时添加连接符号
String joinStr = CollUtil.join(list, ",");
LOGGER.info("collUtil join:{}", joinStr);

// 将以连接符号分隔的字符串再转换为列表
List<String> splitList = StrUtil.split(joinStr, ',');
LOGGER.info("collUtil split:{}", splitList);

// 创建新的Map、Set、List
HashMap<Object, Object> newMap = CollUtil.newHashMap();
HashSet<Object> newHashSet = CollUtil.newHashSet();
ArrayList<Object> newList = CollUtil.newArrayList();

// 判断列表是否为空
CollUtil.isEmpty(list);

MapUtil Map集合工具

此工具可用于创建Map和判断Map是否为null

// 将多个键值对加入到Map中
Map<Object, Object> map = MapUtil.of(new String[][]{
	{"key1", "value1"},
	{"key2", "value2"},
	{"key3", "value3"}
});

// 判断Map是否为空
MapUtil.isEmpty(map);
MapUtil.isNotEmpty(map);

AnnotationUtil 注解工具

此工具可用于获取注解和注解中指定的值

// 获取指定类、方法、字段、构造器上的注解列表
Annotation[] annotationList = AnnotationUtil.getAnnotations(HutoolController.class, false);
LOGGER.info("annotationUtil annotations:{}", annotationList);

// 获取指定类型注解
Api api = AnnotationUtil.getAnnotation(HutoolController.class, Api.class);
LOGGER.info("annotationUtil api value:{}", api.description());

// 获取指定类型注解的值
Object annotationValue = AnnotationUtil.getAnnotationValue(HutoolController.class, RequestMapping.class);

Apache Common 包

Apache common不仅被成千上万开源工具使用,更是学习Java编程比较好的代码参考工具,效率工具。

Apache Commons是对JDK的拓展,包含了很多开源的工具,用于解决平时编程经常会遇到的问题,减少重复劳动。官网网址:http://commons.apache.org

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)。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 Codec

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

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 CSV

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

Commons Daemon

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

Commons DBCP

数据库连接池。

Commons DBUtils

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

Commons Digester

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

Commons Email

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

Commons Exec

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

Commons FileUpload

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

Commons IO

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

Commons JCI

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

Commons Lang3

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

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

Commons Net

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

Commons Pool

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

Commons Primitives

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

Commons Validator

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

Apache HttpClient

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

Common

包检索和使用在这里检索吧:Apache Common 包

参考文章

Google Guava 包

Google出品,必属精品。

Guava工程包含了若干被Google的 Java项目广泛依赖 的核心库,例如:集合 [collections] 、缓存 [caching] 、原生类型支持 [primitives support] 、并发库 [concurrency libraries] 、通用注解 [common annotations] 、字符串处理 [string processing] 、I/O 等等。 所有这些工具每天都在被Google的工程师应用在产品服务中。

guava的优点:

  • 高效设计良好的API,被Google的开发者设计,实现和使用。
  • 遵循高效的java语法实践。
  • 使代码更刻度,简洁,简单。
  • 节约时间,资源,提高生产力。

相关网址:

依赖

注意:JDK 1.8 or higher。

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

基本工具 [Basic utilities]

让使用Java语言变得更舒适:

  • 使用和避免null:null是模棱两可的,会引起令人困惑的错误,有些时候它让人很不舒服。很多Guava工具类用快速失败拒绝null值,而不是盲目地接受。
  • 前置条件: 让方法中的条件检查更简单。
  • 常见Object方法: 简化Object方法实现,如hashCode()toString()
  • 排序: Guava强大的”流畅风格比较器”。
  • Throwables:简化了异常和错误的传播与检查。

集合 [Collections]

Guava对JDK集合的扩展,这是Guava最成熟和为人所知的部分。

  • 不可变集合: 用不变的集合进行防御性编程和性能提升。
  • 新集合类型: multisets, multimaps, tables, bidirectional maps等。
  • 强大的集合工具类: 提供java.util.Collections中没有的集合工具。
  • 扩展工具类:让实现和扩展集合类变得更容易,比如创建Collection的装饰器,或实现迭代器。

缓存 [Caches]

Guava Cache:本地缓存实现,支持多种缓存过期策略。

函数式风格 [Functional idioms]

Guava的函数式支持:可以显著简化代码,但请谨慎使用它。

并发 [Concurrency]

强大而简单的抽象,让编写正确的并发代码更简单。

  • ListenableFuture:完成后触发回调的Future。
  • Service框架:抽象可开启和关闭的服务,帮助你维护服务的状态逻辑。

字符串处理 [Strings]

字符串处理: 非常有用的字符串工具,包括分割、连接、填充等操作。

原生类型 [Primitives]

原生类型: 扩展 JDK 未提供的原生类型(如int、char)操作, 包括某些类型的无符号形式。

区间 [Ranges]

区间: 可比较类型的区间API,包括连续和离散类型。

散列 [Hash]

散列: 提供比Object.hashCode()更复杂的散列实现,并提供布鲁姆过滤器的实现。

事件总线 [EventBus]

事件总线:发布-订阅模式的组件通信,但组件不需要显式地注册到其他组件中。

数学运算 [Math]

数学运算:优化的、充分测试的数学工具类。

反射 [Reflection]

反射: Guava 的 Java 反射机制工具类。

参考文章

Lombok 工具库

安装方式直接在IDEA的plugin中搜索安装即可。

什么样的情况使用Lombok:

  • 团队整体的共识,IDE规范,相关代码规范等。
  • 对Lombok足够了解,比如知道其中的坑等。

不推荐使用Lombok的理由:

  • 其实我们不缺时间写Getter和Setter的,这些代码通常是由IDE生成的。简化也是有代价的。
  • 对Lombok认知不够,导致带来的坑。
  • Java14中Record了解下。

Lombok注解说明

官网说明

  1. val:用在局部变量前面,相当于将变量声明为final。

  2. @NonNull:给方法参数增加这个注解会自动在方法内对该参数进行是否为空的校验,如果为空,则抛出NPE(NullPointerException)。

  3. @Cleanup:自动管理资源,用在局部变量之前,在当前变量范围内即将执行完毕退出之前会自动清理资源,自动生成try-finally这样的代码来关闭流。

  4. @Getter/@Setter:用在属性上,再也不用自己手写setter和getter方法了,还可以指定访问范围。

  5. @ToString:用在类上,可以自动覆写toString方法,当然还可以加其他参数,例如@ToString(exclude=”id”)排除id属性,或者@ToString(callSuper=true, includeFieldNames=true)调用父类的toString方法,包含所有属性。

  6. @EqualsAndHashCode:用在类上,自动生成equals方法和hashCode方法。

  7. @NoArgsConstructor@RequiredArgsConstructor@AllArgsConstructor:用在类上

  • @NoArgsConstructor:自动生成无参构造

  • @AllArgsConstructor:使用所有参数的构造函数

  • @RequiredArgsConstructor:把所有 + @NonNull属性作为参数的构造函数,如果指定 staticName = “of” 参数,同时还会生成一个返回类对象的静态工厂方法,比使用构造函数方便很多。这个注解在controller使用起来很舒服

@RequestMapping("/users")
@RestController
@RequiredArgsConstructor  // 给一开始就需要初始化的变量做构造
public classUserController{
  /*
   * 使用 final 来将其变为常量,从而让其一开始就需要初始化  进而自动注入 故而就不需要 @Autowared 注解了
   * */
  private final IUserService userService;
}
  1. @Data:注解在类上,相当于同时使用了@ToString@EqualsAndHashCode@Getter@Setter@RequiredArgsConstrutor这些注解,对于POJO类十分有用.

  2. @Value:用在类上,是@Data的不可变形式,相当于为属性添加final声明,只提供getter方法,而不提供setter方法。

  3. @Builder:用在类、构造器、方法上,为你提供复杂的builder APIs,让你可以像如下方式一样调用:

Person.builder()
	.name("Adam Savage")
	.city("San Francisco")
	.job("Mythbusters")
	.job("Unchained Reaction")
	.build();

更多说明参考Builder。

  1. @SneakyThrows:自动抛受检异常,而无需显式在方法上使用throws语句。

  2. @Synchronized:用在方法上,将方法声明为同步的,并自动加锁,而锁对象是一个私有的属性$lock$LOCK,而Java中的synchronized关键字锁对象是this,锁在this或者自己的类对象上存在副作用,就是你不能阻止非受控代码去锁this或者类对象,这可能会导致竞争条件或者其它线程错误。

  3. @Getter(lazy=true):可以替代经典的Double Check Lock样板代码。

  4. @Log:根据不同的注解生成不同类型的log对象,但是实例名称都是log,有六种可选实现类。

  • @CommonsLog Creates log = org.apache.commons.logging.LogFactory.getLog(LogExample.class);
  • @Log Creates log = java.util.logging.Logger.getLogger(LogExample.class.getName());
  • @Log4j Creates log = org.apache.log4j.Logger.getLogger(LogExample.class);
  • @Log4j2 Creates log = org.apache.logging.log4j.LogManager.getLogger(LogExample.class);
  • @Slf4j Creates log = org.slf4j.LoggerFactory.getLogger(LogExample.class);
  • @XSlf4j Creates log = org.slf4j.ext.XLoggerFactory.getXLogger(LogExample.class);

Lombok的原理

核心之处就是对于注解的解析上。JDK5引入了注解的同时,也提供了两种解析方式。

运行时解析

运行时能够解析的注解,必须将@Retention设置为RUNTIME, 比如@Retention(RetentionPolicy.RUNTIME),这样就可以通过反射拿到该注解。java.lang,reflect反射包中提供了一个接口AnnotatedElement,该接口定义了获取注解信息的几个方法,Class、Constructor、Field、Method、Package等都实现了该接口,对反射熟悉的朋友应该都会很熟悉这种解析方式。

编译时解析

编译时解析有两种机制,分别简单描述下:

1)Annotation Processing Tool

apt自JDK5产生,JDK7已标记为过期,不推荐使用,JDK8中已彻底删除,自JDK6开始,可以使用Pluggable Annotation Processing API来替换它,apt被替换主要有2点原因:

  • api都在com.sun.mirror非标准包下。
  • 没有集成到javac中,需要额外运行。

2)Pluggable Annotation Processing API

JSR 269: Pluggable Annotation Processing API 自JDK6加入,作为apt的替代方案,它解决了apt的两个问题,javac在执行的时候会调用实现了该API的程序,这样我们就可以对编译器做一些增强,这时javac执行的过程如下:

image

Lombok本质上就是一个实现了“JSR 269 API”的程序。在使用javac的过程中,它产生作用的具体流程如下:

  • javac对源代码进行分析,生成了一棵抽象语法树(AST)。
  • 运行过程中调用实现了“JSR 269 API”的Lombok程序。
  • 此时Lombok就对第一步骤得到的AST进行处理,找到@Data注解所在类对应的语法树(AST),然后修改该语法树(AST),增加getter和setter方法定义的相应树节点。
  • javac使用修改后的抽象语法树(AST)生成字节码文件,即给class增加新的节点(代码块)。

image

从上面的Lombok执行的流程图中可以看出,在Javac 解析成AST抽象语法树之后, Lombok 根据自己编写的注解处理器,动态地修改 AST,增加新的节点(即Lombok自定义注解所需要生成的代码),最终通过分析生成JVM可执行的字节码Class文件。使用Annotation Processing自定义注解是在编译阶段进行修改,而JDK的反射技术是在运行时动态修改,两者相比,反射虽然更加灵活一些但是带来的性能损耗更加大

Lombok有什么坑?

@Data 的坑

在使用Lombok过程中,如果对于各种注解的底层原理不理解的话,很容易产生意想不到的结果。

举一个简单的例子,我们知道,当我们使用@Data定义一个类的时候,会自动帮我们生成equals()方法 。

但是如果只使用了@Data,而不使用@EqualsAndHashCode(callSuper=true)的话,会默认是@EqualsAndHashCode(callSuper=false),这时候生成的equals()方法只会比较子类的属性,不会考虑从父类继承的属性,无论父类属性访问权限是否开放。

这就可能得到意想不到的结果。

代码可读性,可调试性低

在代码中使用了Lombok,确实可以帮忙减少很多代码,因为Lombok会帮忙自动生成很多代码。但是这些代码是要在编译阶段才会生成的,所以在开发的过程中,其实很多代码其实是缺失的。

在代码中大量使用Lombok,就导致代码的可读性会低很多,而且也会给代码调试带来一定的问题。 比如,我们想要知道某个类中的某个属性的getter方法都被哪些类引用的话,就没那么简单了。

Lombok有很强的侵入性

  1. 强J队友

因为Lombok的使用要求开发者一定要在IDE中安装对应的插件。如果未安装插件的话,使用IDE打开一个基于Lombok的项目的话会提示找不到方法等错误。导致项目编译失败。也就是说,如果项目组中有一个人使用了Lombok,那么其他人就必须也要安装IDE插件。否则就没办法协同开发。

更重要的是,如果我们定义的一个jar包中使用了Lombok,那么就要求所有依赖这个jar包的所有应用都必须安装插件,这种侵入性是很高的。

  1. 影响升级

因为Lombok对于代码有很强的侵入性,就可能带来一个比较大的问题,那就是会影响我们对JDK的升级。按照如今JDK的升级频率,每半年都会推出一个新的版本,但是Lombok作为一个第三方工具,并且是由开源团队维护的,那么他的迭代速度是无法保证的。

所以,如果我们需要升级到某个新版本的JDK的时候,若其中的特性在Lombok中不支持的话就会受到影响。

还有一个可能带来的问题,就是Lombok自身的升级也会受到限制。因为一个应用可能依赖了多个jar包,而每个jar包可能又要依赖不同版本的Lombok,这就导致在应用中需要做版本仲裁,而我们知道,jar包版本仲裁是没那么容易的,而且发生问题的概率也很高。

Lombok破坏了封装性

以上几个问题,我认为都是有办法可以避免的。但是有些人排斥使用Lombok还有一个重要的原因,那就是他会破坏封装性。

众所周知,Java的三大特性包括封装性、继承性和多态性。

如果我们在代码中直接使用Lombok,那么他会自动帮我们生成getter、setter 等方法,这就意味着,一个类中的所有参数都自动提供了设置和读取方法。

举个简单的例子,我们定义一个购物车类:

@Data
public class ShoppingCart {

    // 商品数目
    private int itemsCount;

    // 总价格
    private double totalPrice;

    // 商品明细
    private List items = new ArrayList<>();

	// 例子来源于《极客时间-设计模式之美》
}

我们知道,购物车中商品数目、商品明细以及总价格三者之前其实是有关联关系的,如果需要修改的话是要一起修改的。

但是,我们使用了Lombok的@Data注解,对于itemsCount 和 totalPrice这两个属性。虽然我们将它们定义成 private 类型,但是提供了 public 的 getter、setter 方法。

外部可以通过 setter 方法随意地修改这两个属性的值。我们可以随意调用 setter 方法,来重新设置 itemsCount、totalPrice 属性的值,这也会导致其跟 items 属性的值不一致。

而面向对象封装的定义是:通过访问权限控制,隐藏内部数据,外部仅能通过类提供的有限的接口访问、修改内部数据。所以,暴露不应该暴露的 setter 方法,明显违反了面向对象的封装特性。

好的做法应该是不提供getter/setter,而是只提供一个public的addItem方法,同时去修改itemsCount、totalPrice以及items三个属性。

以上问题其实也是可以解决的,但这提醒了我们需要理解Lombok,而不是一股脑的用@Data注解。

参考文章

日志类库

直接去这里:Java日志框架体系整理

JSON库

JSON优秀资源:awesome-json

JSON在线解析工具:http://c.runoob.com/front-end/53

IDEA中的插件:JSON Viewer、JSON Parser.........

Java中并没有内置JSON的解析,因此使用JSON需要借助第三方类库。

常用的 JSON 解析类库:

  • FastJson: 阿里巴巴开发的 JSON 库,性能优秀。
  • Jackson: 社区十分活跃且更新速度很快。
  • Gson: 谷歌开发的 JSON 库,功能十分全面。

序列化次数比较小的时候,Gson性能最好,当不断增加的时候到了100000,Gson弱于Jackson和FastJson, 这时候FastJson性能是真的牛,另外不管数量少还是多,Jackson一直表现优异。

下图对比Json-lib可以直接忽略。

JSON序列化性能对比:
image

SON反序列化性能:
image

FastJson

Fastjson 是一个 Java 库,可以将 Java 对象转换为 JSON 格式,当然它也可以将 JSON 字符串转换为 Java 对象。

Fastjson 可以操作任何 Java 对象,即使是一些预先存在的没有源码的对象。

FastJson Github

FastJson 中文 Wiki

不推荐使用FastJson:

  • FastJson 源码质量较低。
  • FastJson Bug、漏洞较多。
  • FastJson 牺牲多数场景下的稳定性而提高的效率。

FastJson漏洞问题

尽量使用最新版本。

fastjson远程代码执行漏洞技术分析与防护方案

警告

远离FastJson这个库,老程序员都知道这里有多少坑:

alibaba/fastjson Issues

fastjson这么快老外为啥还是热衷 jackson?

安装

可以在 maven 中央仓库中直接下载:http://repo1.maven.org/maven2/com/alibaba/fastjson/

配置 maven 依赖:

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>x.x.x</version>
</dependency>

序列化一个对象为字符串

User user = new User();
user.setName("校长");
user.setAge(3);
user.setSalary(new BigDecimal("123456789.0123"));
String jsonString = JSON.toJSONString(user);
System.out.println(jsonString);


// 输出 {"age":3,"name":"校长","old":false,"salary":123456789.0123}

反序列化一个JSON字符串成Java对象

String jsonString = "{\"age\":3,\"birthdate\":1496738822842,\"name\":\"校长\",\"old\":true,\"salary\":123456789.0123}";
User u = JSON.parseObject(jsonString ,User.class);
System.out.println(u.getName());
 // 输出 校长




String jsonStringArray = "[{\"age\":3,\"birthdate\":1496738822842,\"name\":\"校长\",\"old\":true,\"salary\":123456789.0123}]";
List<User> userList = JSON.parseArray(jsonStringArray, User.class);
System.out.println(userList.size());
// 输出 1

日期处理

默认序列化Date输出使用”yyyy-MM-dd HH:mm:ss”格式,可以用UseISO8601DateFormat特性换成”yyyy-MM-dd’T’HH:mm:ss”格式。

JSON.defaultTimeZone = TimeZone.getTimeZone("Asia/Shanghai");
JSON.defaultLocale = Locale.US;

public static class Model {
    @JSONField(format = "MMM dd, yyyy h:mm:ss aa")
    private java.util.Date date;

    public java.util.Date getDate() {
        return date;
    }

    public void setDate(java.util.Date date) {
        this.date = date;
    }

    @JSONField(format = "MMM-dd-yyyy h:mm:ss aa")
    public java.sql.Date date2;
}

Bean和数组转换

官方例子 - BeanToArray_cn

设置字段名

public class A {
    @JSONField(name="ID")
    private int id;

    public int getId() {return id;}
    public void setId(int value) {this.id = id;}
}

设置是否不序列化某字段

public class A {
    @JSONField(serialize=false)
    public Date date;
}

public class A {
    @JSONField(deserialize=false)
    public Date date;
}

设置字段顺序

public static class VO {
    @JSONField(ordinal = 3)
    private int f0;

    @JSONField(ordinal = 2)
    private int f1;

    @JSONField(ordinal = 1)
    private int f2;
}

自定义序列化和反序列化

fastjson SerializerFeature 详解

ObjectDeserializer_cn

JackSon

Jackson Github

Jackson Wiki

Jackson 文档

Jackson组件

  1. 3个核心模块
  • Streaming: jackson-core jar,定义了底层的streaming API和实现了Json特性。
  • Annotations: jackson-annotations jar,包含了标准的Jackson注解。本文暂不介绍。
  • Databind: jackson-databind jar,实现了数据绑定和对象序列化,它依赖于streaming和annotations的包。
  1. 第三方数据类型模块

这些扩展是插件式的Jackson模块,用ObjectMapper.registerModule()注册,并且通过添加serializers和deserializers以便Databind包(ObjectMapper / ObjectReader / ObjectWriter)可以读写这些类型,来增加对各种常用的Java库的数据类型的支持。

  1. 数据格式模块

Jackson也有处理程序对JAX-RS标准实现者例如Jersey, RESTeasy, CXF等提供了数据格式支持。处理程序实现了MessageBodyReader和MessageBodyWriter,目前支持的数据格式包括JSON, Smile, XML, YAML和CBOR。

数据格式提供了除了Json之外的数据格式支持,它们绝大部分仅仅实现了streaming API abstractions,以便数据绑定组件可以按照原来的方式使用。另一些(几乎不需要)提供了databind标准功能来处理例如schemas。

Maven 依赖

<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core -->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
    <version>2.10.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.10.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-annotations -->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-annotations</artifactId>
    <version>2.10.1</version>
</dependency>

序列化一个对象成JSON字符串

public void toJson() throws JsonProcessingException {

    ObjectMapper mapper = new ObjectMapper();

    City case1 = new City();
    case1.setCity("SZ");
    case1.setAge(123);

    String jsonStr = mapper.writeValueAsString(case1);
    System.out.println("JSON:" + jsonStr);
}



// 输出:JSON:{"city":"SZ","age":123}

反序列化一个JSON字符串成Java对象

public void toObj() throws JsonParseException, JsonMappingException, IOException {
    ObjectMapper mapper = new ObjectMapper();
    String inputjsonstr = "{\"city\":\"SZ\",\"age\":123}";

    City readcase = mapper.readValue(inputjsonstr, City.class);

    System.out.println("city info:" + readcase);
}

如果里面有未知属性,比如json中有desc字段,但是City中没有相应字段,会报错, 需要设置如下:

mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);

常用注解

@JsonProperty("xxx"): 将当前的属性名在json字符串中重新命名为当前设置的这个值,比如在示例中,将age-->mAge。

@JsonIgnore: 将被标注的属性在生成json字符串的时候,直接忽略。

@JsonInclude: 是一个类级别的设置,JsonInclude.Include.NON_EMPTY标识只有非NULL的值才会被纳入json string之中,其余的都被忽略,比如这里的location属性,并没有出现在最终的结果字符串中。

@JsonSerialize: 使用自定义的类来实现自定义的字段转换。写入操作。

@JsonDeserialize: 解析的时候,自定义的转换器;读取操作。

@JsonAutoDetect: 设置类的访问策略,是否所有的属性都可以,还是按照一定的方式来提取。

@JsonRawValue: 无转换的将属性值写入到json 字符串中。 写入操作

@JsonValue: 标注方法,用以替代缺省的方法,由该方法来完成json的字符输出。

参考文章

GSON

Gson用户指南(中文翻译)

Gson是这样一个Java类库,它可以将Java对象转换为相应的JSON形式,也可以将JSON字符串转换为对应的Java对象。 Gson可以使用任意Java对象,包括哪些预先存在的、不在你的源代码中的对象(因此,你并不知道对象的属性)。

Gson的目标:

  • 提供一种机制,使得将Java对象转换为JSON或相反如使用toString()以及构造器(工厂方法)一样简单。
  • 允许预先存在的不可变的对象转换为JSON或与之相反。
  • 允许自定义对象的表现形式。
  • 支持任意复杂的对象。
  • 输出轻量易读的JSON。

Gson的使用

使用Gson的首要类是Gson类,你可以仅仅通过new Gson()的方式创建它。你也可以通过GsonBuilder类去创建Gson实例,这个类允许你进行一系列配置,例如版本控制等等。

Gson实例不会保存任何进行Json操作时的状态。因此,你可以自由的服用相同的Gson对象进行诸多的Json序列化和反序列化操作。

Maven 依赖:

<!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>28.2-jre</version>
</dependency>

序列化

// 基础类型
Gson gson = new Gson();
gson.toJson(1);            ==> prints 1
gson.toJson("abcd");       ==> prints "abcd"
gson.toJson(new Long(10)); ==> prints 10
int[] values = { 1 };
gson.toJson(values);       ==> prints [1]

// 对象
BagOfPrimitives obj = new BagOfPrimitives();
Gson gson = new Gson();
String json = gson.toJson(obj);  
==> json is {"value1":1,"value2":"abc"}

// 数组
Gson gson = new Gson();
int[] ints = {1, 2, 3, 4, 5};
String[] strings = {"abc", "def", "ghi"};
gson.toJson(ints);     ==> prints [1,2,3,4,5]
gson.toJson(strings);  ==> prints ["abc", "def", "ghi"]

// 集合
Gson gson = new Gson();
Collection<Integer> ints = Lists.immutableList(1,2,3,4,5);
String json = gson.toJson(ints); ==> json is [1,2,3,4,5]

其中的对象代码:

class BagOfPrimitives {
  private int value1 = 1;
  private String value2 = "abc";
  private transient int value3 = 3;
  BagOfPrimitives() {
    // no-args constructor
  }
}

反序列化

// 基础类型
Gson gson = new Gson();
int one = gson.fromJson("1", int.class);
Integer one = gson.fromJson("1", Integer.class);
Long one = gson.fromJson("1", Long.class);
Boolean false = gson.fromJson("false", Boolean.class);
String str = gson.fromJson("\"abc\"", String.class);
String anotherStr = gson.fromJson("[\"abc\"]", String.class);

// 对象
BagOfPrimitives obj2 = gson.fromJson(json, BagOfPrimitives.class);   
==> obj2 is just like obj

// 数组
Gson gson = new Gson();
int[] ints2 = gson.fromJson("[1,2,3,4,5]", int[].class); 
==> ints2 will be same as ints

// 集合
Gson gson = new Gson();
Type collectionType = new TypeToken<Collection<Integer>>(){}.getType();
Collection<Integer> ints2 = gson.fromJson(json, collectionType);
ints2 is same as ints

自定义序列化和反序列化机制

有时候,默认的实现并不是你想要的。这在处理类库时常常发生(例如DateTime)。Gson允许你注册自己自定义的序列化器和反序列化器。该过程分为两部分:

  • Json序列化器:需要为一个对象自定义序列化机制。
  • Json反序列化器:需要为一个类型自定义反序列化机制。

实例构造者:并不需要,如果无参构造器是可用的或者注册了一个反序列化器。

GsonBuilder gson = new GsonBuilder();
gson.registerTypeAdapter(MyType2.class, new MyTypeAdapter());
gson.registerTypeAdapter(MyType.class, new MySerializer());
gson.registerTypeAdapter(MyType.class, new MyDeserializer());
gson.registerTypeAdapter(MyType.class, new MyInstanceCreator());

registerTypeAdapter会检查类型适配器是否实现了上面三个接口中的一个以上并且它们都注册了类型适配器。

更多参考请看:Gson用户指南(中文翻译)

参考文章

EasyExcel

这玩意儿是阿里巴巴开源的,官方文档:https://easyexcel.opensource.alibaba.com/docs/current/quickstart/read

目的:Java操作(读写)excel。
场景:数据通过excel导入导出功能。

excel的几个基础知识

  1. workbook:整个excel就是workbook

image

  1. 列头:列标题。首行

image

  1. sheet:工作簿

image

依赖

<dependency>
	<groupId>com.alibaba</groupId>
	<artifactId>easyexcel</artifactId>
	<version>3.1.0</version>
</dependency>

Java实体类

注意:这个实体类就对应excel的列头。

import com.alibaba.excel.annotation.ExcelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class CategoryExcelVo {

	/**
	 * value	excel中列的名称
	 * index	excel中第几列
	 */
	@ExcelProperty(value = "id" ,index = 0)
	private Long id;

	@ExcelProperty(value = "名称" ,index = 1)
	private String name;

	@ExcelProperty(value = "图片url" ,index = 2)
	private String imageUrl ;

	@ExcelProperty(value = "上级id" ,index = 3)
	private Long parentId;

	@ExcelProperty(value = "状态" ,index = 4)
	private Integer status;

	@ExcelProperty(value = "排序" ,index = 5)
	private Integer orderNum;

}

写excel数据

	/**
     * 导出分类数据
     */
    @Override
    public void exportData(HttpServletResponse response) {
        try {
            // 设置响应结果类型  ms-excel(microsoft excel)
            response.setContentType("application/vnd.ms-excel");
            response.setCharacterEncoding("utf-8");
            // 设置头信息:让浏览器弹出下载框(以下载的方式打开传输的文件)
            response.setHeader("Content-Disposition",
                    "attachment;filename=" + URLEncoder.encode("分类数据.xlsx", StandardCharsets.UTF_8));

            // 查库:将所有分类数据查询
            List<Category> categories = categoryMapper.findCategories();
            // 将查到的数据转成easyExcel需要对象CategoryExcelVo列表
            List<CategoryExcelVo> categoryList = new ArrayList<>();
            categories.forEach(category -> {
                CategoryExcelVo categoryExcelVo = new CategoryExcelVo();
                BeanUtils.copyProperties(category, categoryExcelVo, CategoryExcelVo.class);
                categoryList.add(categoryExcelVo);
            });

            // easyExcel写数据
            EasyExcel.write(response.getOutputStream(), CategoryExcelVo.class)
                    .sheet("分类数据")
                    .doWrite(categoryList);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

读excel数据

  1. 需要定义监听器
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.util.ListUtils;

import java.util.List;

/**
 * <p>
 * easyExcel的读数据监听器     注意:这个监听器不能被Spring托管,否则会报错
 * 因为:Spring托管之后就会变成单利,那么在并发情况下就不知道监听的到底是哪个listener
 * 但是:这里面不可避免需要service 或 mapper的方法,而这些是被Spring托管的
 * 因此:这里面需要service 或 mapper时,可以通过这个listener的构造传入
 * </p>
 *
 * <p>@author : Zixq</p>
 */


public class ExcelListener<T> implements ReadListener<T> {

    /**
     * 当前listener不能被Spring托管,但需要mapper,所以构造注入
     */
    private CategoryMapper categoryMapper;

    public ExcelListener(CategoryMapper categoryMapper) {
        this.categoryMapper = categoryMapper;
    }

    /**
     * 每隔100条存储数据库,然后清理list ,方便内存回收
     */
    private static final int BATCH_COUNT = 100;

    /**
     * 缓存的数据
     */
    private List<CategoryExcelVo> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);

    /**
     * 从excel的第二行(第一行为列头)读取数据,然后放入 t 对象中
     */
    @Override
    public void invoke(T t, AnalysisContext analysisContext) {
        cachedDataList.add((CategoryExcelVo) t);
        if (cachedDataList.size() >= BATCH_COUNT) {
            // 批量插入数据库
            this.saveData();
            // 清理缓存
            cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
        }
    }

    /**
     * 批量插入数据库
     */
    private void saveData() {
        categoryMapper.insertBatch(cachedDataList);
    }

    /**
     * 都解析完(做完后)要做的事情
     */
    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
        // 插入数据,这里再调用的原因:因为数据可能未满上面定义的100条,所以一样需要插入数据库
        this.saveData();
    }
}

EasyExcel的读监听器不能被Spring托管的具体原因:https://easyexcel.opensource.alibaba.com/qa/read

  1. 读取excel中的数据,并写入数据库
    /**
     * 导入分类数据
     *
     * @param file 文件
     */
    @Override
    public void importData(MultipartFile file) {
        // 创建easyExcel中的读监听器    不能被Spring托管,故每次创建全新的listener,从而适应并发
        ExcelListener<CategoryExcelVo> listener = new ExcelListener<>(categoryMapper);
        try {
            // 读取excel的数据
            EasyExcel.read(file.getInputStream(), CategoryExcelVo.class, listener)
                    .sheet()
                    .doRead();
        } catch (IOException e) {
            e.printStackTrace();
            throw new CustomException(ResultCodeEnum.DATA_ERROR);
        }
    }
  1. mapper接口
    /**
     * 批量插入数据库
     * @param cachedDataList 要插入的数据列表
     */
    void insertBatch(List<CategoryExcelVo> cachedDataList);
  1. mapper映射文件
   		<!-- 批量插入数据库 -->
        insert into category (id,
                              name,
                              image_url,
                              parent_id,
                              status,
                              order_num,
                              create_time,
                              update_time,
                              is_deleted) values
        <foreach collection="categoryList" item="item" separator=",">
            (#{item.id},
             #{item.name},
             #{item.imageUrl},
             #{item.parentId},
             #{item.status},
             #{item.orderNum},
             now(),
             now(),
             0)
        </foreach>
posted on 2022-04-12 20:45  紫邪情  阅读(2181)  评论(0编辑  收藏  举报