Skip to content

Latest commit

 

History

History
673 lines (577 loc) · 23.9 KB

README.adoc

File metadata and controls

673 lines (577 loc) · 23.9 KB

Dew Common — Java常用操作工具集

dew common

Codacy code quality Apache License 2 Maven Central

Dew Common的理念是简化Java开发中最常用的操作,同时尽可能少地引入第三方包,做到通用、易用、精简。

1. 功能

  1. Json与Java对象互转,支持泛型

  2. Java Bean操作,Bean复制、反射获取/设置注解、字段、方法等

  3. Java Class扫描操作,根据注解或名称过滤

  4. Shell脚本操作,Shell内容获取、成功捕获及进度报告等

  5. 安全(加解密、信息摘要等)操作,Base64、MD5/BCrypt/AES/SHA等对称算法和RSA等非对称算法

  6. Http操作,包含Get/Post/Put/Delete/Head/Options/Patch操作

  7. 金额操作,金额转大写操作

  8. 通用拦截器栈,前/后置、错误处理等

  9. 定时器操作,定时和周期性任务

  10. 常用文件操作,根据不同情况获取文件内容

  11. 常用字段操作,各类字段验证、身份证提取、UUID创建等

  12. 常用时间处理,常规时间格式化模板

  13. 主流文件MIME整理,MIME分类

  14. 服务降级处理

  15. 响应处理及分页模型

2. 使用

<parent>
        <groupId>com.ecfront.dew</groupId>
        <artifactId>common</artifactId>
        <version>1.4.6</version>
</parent>

2.1. Json与Java对象互转($.json)

// 将Json字符串转成JsonNode
JsonNode json = $.json.toJson("{'name':'gudaoxuri'}");
// 获取json中的值
String name = json.get("name").asText();

// 将Json转成Java对象
JavaModel java = $.json.toObject(json,JavaModel.class);
// 将Json字符串转成Java对象
java = $.json.toObject("{'name':'gudaoxuri','date':'2016-07-12 12:00:00'}", JavaModel.class);
// 将Json字符串转成List对象
List<JavaModel> javas = $.json.toList("[{'name':'gudaoxuri','date':'2016-07-12 12:00:00'}]", JavaModel.class);

// 将Java对象转成JsonNode
json = $.json.toJson(java);
// 将List对象转成JsonNode
json = $.json.toJson(javas);

// 将JsonNode转成Json字符串
String str = $.json.toJsonString(json);
// 将Java对象转成Json字符串
str = $.json.toJsonString(java);
// 将List对象转成Json字符串
str = $.json.toJsonString(javas);

static class JavaModel{
    private String name;
    @JsonFormat(shape=JsonFormat.Shape.STRING, pattern="yyyy-MM-dd HH:mm:ss")
    private Date date;
    ... get/set
}
Tip
更多示例见`JsonHelperTest.java`

2.2. Java Bean操作($.bean)

// Bean Copy
User ori = new User();
ori.setName("gudaoxuri");
User dist = new User();
$.bean.copyProperties(ori, dist);
// 获取Class的注解信息(此处为IdxController类的RPC注解)
TestAnnotation.RPC rpc = $.bean.getClassAnnotation(IdxController.class, TestAnnotation.RPC.class);
// 获取Class的字段信息(此处为IdxController类)
Map<String, $.bean.FieldInfo> fieldsInfo = $.bean.findFieldsInfo(IdxController.class, null, null, null, null);
// 获取Class的方法信息(此处为IdxController类)
List<$.bean.MethodInfo> methodsInfo = $.bean.findMethodsInfo(IdxController.class, null, null, null, null);
// 获取字段对应的Get/Set方法(此处为User类)
Map<String, Method[]> rel = $.bean.parseRelFieldAndMethod(User.class, null, null, null, null);
User user = new User();
user.setName("gudaoxuri");
// 根据字段的Get方法获取对应的值
Map<String, Object> values = $.bean.findValuesByRel(user, $.bean.parseRelFieldAndMethod(User.class, null, null, null, null));
// 获取对象所有字段的值
values = $.bean.findValues(user, null, null, null, null);
// 根据字段名称获取对应的值(需要有标准的Get方法)
Object value = $.bean.getValue(user, "name");
// 根据字段名称设置值(需要有标准的Set方法)
$.bean.setValue(user, "name", "gudaoxuri");
Tip
更多示例见`BeanHelperTest.java`
Note
还有一个`$.bean(boolean useCache)`方法,用于指定是否启用缓存,启用后会缓存获取过的字段和方法列表,默认启用。

2.3. Java Class扫描操作($.clazz)

// 扫描获取指定包下符合条件的class类(此处要求包含Deprecated注解)
Set<Class<?>> resultInFile = $.clazz.scan("com.ecfront.$.common.test", new HashSet<Class<? extends Annotation>>(){{add(Deprecated.class);}},null);
// 扫描获取指定包下符合条件的class类(此处要求包含Before\\w*类名)
Set<Class<?>> resultInJar = $.clazz.scan("org.junit",null, new HashSet<String>(){{add("Before\\w*");}});
Tip
更多示例见`ClassScanHelperTest.java`

2.4. Shell脚本操作($.shell())

Important
此操作每次返回新实例!
$.shell().execute(
        "/opt/test.sh", //  sh文件路径,包含参数
        "test1", // 任务ID
        "done!", // 成功标识,只要捕捉到此标识就视为成功,为null时不会调用ReportHandler的success方法,执行结束后会调用ReportHandler的fail方法
        "step", // 进度标识,只要捕捉到此标识就更新进度,格式为 <progressFlag>空格<progress>,如: progress 40,为null时不会调用ReportHandler的progress方法
        false, // 是否返回结果(输出内容),为true时会返回结果到ReportHandler的complete方法中,结果暂存于内存中,对输出内容过多的脚本需要考虑占用内存的大小
        new ReportHandler() { // 任务报告实例
            @Override
            public void success(String taskId) { // 成功,在执行到successFlag时调用
            }

            @Override
            public void fail(String taskId, String message) { // 失败,在执行完成且successFlag不存在或发生错误时调用
            }

            @Override
            public void progress(String taskId, int progress) { // 进度回调,在执行到progressFlag且格式正确时调用
            }

            @Override
            public void complete(String taskId, String result) { // 完成,无法是否成功,在执行完成时调用
            }
        }
);
Tip
更多示例见`ShellHelperTest.java`

2.5. 加解密操作($.security)

// 字符串 转 Base64
String base64 = $.security.encodeStringToBase64("gudaoxuri", "UTF-8");
// 数组 转 Base64
base64 = $.security.encodeBytesToBase64("gudaoxuri".getBytes());
// Base64 转 字符串
String str = $.security.decodeBase64ToString(base64, "UTF-8");
// Base64 转 字符串
byte[] bytes = $.security.decodeBase64ToBytes(base64);

// 摘要(此处使用SHA-256算法,支持如bcrypt SHA-X等算法)
String digestContent = $.security.digest.digest("gudaoxuri", "SHA-256");
// 验证(对于bcrypt之类默认使用随机slat的加密算法必须使用此方法验证)
$.security.digest.validate("gudaoxuri", digestContent, "SHA-256");

// 对称加密(此处使用AES算法,需要提供密码)
String encryptContent = $.security.symmetric.encrypt("gudaoxuri", "pwd", "AES");
// 对称解密(此处使用AES算法,需要提供密码), BadPaddingException 异常表示密码错误
$.security.symmetric.decrypt(encryptContent, "pwd", "AES");

// 非对称加密 - 生成公钥和私钥
Map<String, String> keys = $.security.asymmetric.generateKeys("RSA", 1024);
String publicStr = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC47dAhLLB3xhDWccgEheqTbRimAhluN/ixNyYU/MvExN/bwytthiWViRV2H1sGrGr5JyH3qQSFa57jInbXD6lGN24aw2TfiejLWGkj0hMEXEtj4Bf9OuQhHUmy8unLygOuBlIhtfPLp9cSLBgVVHbt33buNTkL7fvdE2U9B3JVyQIDAQAB";
// 非对称加密 - 获取公钥文件
PublicKey publicKey = $.security.asymmetric.getPublicKey(publicStr, "RSA");
String privateStr = "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBALjt0CEssHfGENZxyASF6pNtGKYCGW43+LE3JhT8y8TE39vDK22GJZWJFXYfWwasavknIfepBIVrnuMidtcPqUY3bhrDZN+J6MtYaSPSEwRcS2PgF/065CEdSbLy6cvKA64GUiG188un1xIsGBVUdu3fdu41OQvt+90TZT0HclXJAgMBAAECgYEAjXFndVhHCPU3P637PGppBqW06pREeybYUkNKH1dTS4cBaYcXmke2S290OMq2xp3tm++wbUqbKKkt97AOkWNrJfq8Ecpdw9s3c7yQGWaPuwiX38Cgtq6r0utjT20YgR6etGpqafoBt93RZpEm0eEzFPUnS7qYc86HprL0RJ0/i7kCQQDaOmvO82cYIK1ESkA0GdDVQoz2A1V8HvEWOsccRGqlWuasLUccyBnx1G/LDZUxcPOraDyxI8sdl7VbweLR0H9LAkEA2O/rWXwnSYKqdpt+OhpUBHNnMs3IMvRzefJ1zObnIMyYR3KXtpQ/fL4gEquNwJgFIaPJVg5/3zHISEw3e8XOuwJAIDrGl07tZ+vTiyVoLAmwBP8KMH83jdhIBN9zbqJQGdG+Bam+Oer3ofac+CEuapni8uq3I/ZEVj+EomOVKyWe1wJAATztROd2ee7q9h5RDBfWXughsKKH//JxLkL59R9kNkW0oMPApeQWsKmNGU4tUuoLLXP31CvlAusPz4nPzz8DvQJBAJXpICPNJw84fONzS0raRqlFoZMMI0cqeGtPIiCHKaRHyzQv/FFu2KxUcCrod8PngaBFRselzrwZILmXHqrHc1M=";
// 非对称加密 - 获取私钥文件
PrivateKey privateKey = $.security.asymmetric.getPrivateKey(privateStr, "RSA");

String d = "Scala是一门多范式的编程语言,一种类似java的编程语言[1]  ,设计初衷是实现可伸缩的语言[2]  、并集成面向对象编程和函数式编程的各种特性。Scala是一门多范式的编程语言,一种类似java的编程语言[1]  ,设计初衷是实现可伸缩的语言[2]  、并集成面向对象编程和函数式编程的各种特性。Scala是一门多范式的编程语言,一种类似java的编程语言[1]  ,设计初衷是实现可伸缩的语言[2]  、并集成面向对象编程和函数式编程的各种特性。";
// 非对称加密 - 公钥加密/私钥解密
byte[] encryptByPub = $.security.asymmetric.encrypt(d.getBytes("UTF-8"), publicKey, 1024, "RSA");
String result = new String($.security.asymmetric.decrypt(encryptByPub, privateKey, 1024, "RSA"), "UTF-8");

// 非对称加密 - 私钥加密/公钥解密
byte[] encryptByPriv = $.security.asymmetric.encrypt(d.getBytes("UTF-8"), privateKey, 1024, "RSA");
byte[] decryptByPub = $.security.asymmetric.decrypt(encryptByPriv, publicKey, 1024, "RSA");
$.security.asymmetric.verify(publicKey, decryptByPub,
        $.security.asymmetric.sign(privateKey, d.getBytes("UTF-8"), "SHA1withRSA"),
        "SHA1withRSA");
Tip
更多示例见`SecurityHelperTest.java`

2.6. Http操作($.http)

// get
String result = $.http.get("https://httpbin.org/get");
// get 带 head
result = $.http.get("https://httpbin.org/get", new HashMap<String, String>() {{
    put("Customer-A", "AAA");
    put("Accept", "json");
}});
// get 包含 head、Content-Type、编码、超时等信息
result = $.http.get("https://httpbin.org/get", new HashMap<String, String>() {{
    put("Customer-A", "AAA");
    put("Accept", "json");
}}, "application/json; charset=utf-8", "utf-8", 5000, 5000);
// delete
result = $.http.delete("https://httpbin.org/delete");
Assert.assertEquals("httpbin.org", $.json.toJson(result).get("headers").get("Host").asText());
// post - data
result = $.http.post("https://httpbin.org/post", "some data");
// post - form
result = $.http.post("https://httpbin.org/post", new HashMap<String, Object>() {{
    put("a", "1");
}}, "application/x-www-form-urlencoded");
// post - file
result = $.http.post("https://httpbin.org/post", new File(this.getClass().getResource("/").getPath() + "conf1.json"));
// put - data
result = $.http.put("https://httpbin.org/put", "some data");
// put - form
result = $.http.put("https://httpbin.org/put", new HashMap<String, Object>() {{
    put("a", "1");
}}, "application/x-www-form-urlencoded");
// put - file
result = $.http.put("https://httpbin.org/put", new File(this.getClass().getResource("/").getPath() + "conf1.json"));
// put 返回带 head的内容,同样还有getWithHead、postWithHead、deleteWtihHead
HttpHelper.WrapHead responseWrap = $.http.putWithHead("https://httpbin.org/put", new File(this.getClass().getResource("/").getPath() + "conf1.json"));
// head
Map<String, String> head = $.http.head("https://httpbin.org/get");
// options
head = $.http.options("https://httpbin.org/get");
Tip
更多示例见`HttpHelperTest.java`
Note
GET POST PUT DELETE 都带 Wrap`方法,返回包含扩展信息的数据,比如 `getWrap
Note
还有一个`$.http(int maxTotal, int maxPerRoute, int defaultConnectTimeoutMS, int defaultSocketTimeoutMS, boolean retryAble)方法,用于指定`整个连接池最大连接数`及`每个路由(域)的默认最大连接$.http`是$.http(200,20)`的语法糖。

2.7. 通用拦截器栈($.interceptor)

// 没有注册拦截器的情况
Resp<DewInterceptContext<Obj, Obj>> resp =
        $.interceptor.process("none", new Obj("1"), new HashMap<>(), context -> {
            try {
                // 业务逻辑,只做简单将input对象copy到output对象
                context.setOutput($.bean.copyProperties(context.getInput(), Obj.class));
                return Resp.success(context);
            } catch (InvocationTargetException | IllegalAccessException | InstantiationException e) {
                e.printStackTrace();
                return Resp.serverError("");
            }
        });
Assert.assertTrue(resp.ok());
Assert.assertEquals("1", resp.getBody().getOutput().getF());
// 注册了一个拦截器A
$.interceptor.register("test", new InterceptorA());
resp = $.interceptor.process("test", new Obj("1"), new HashMap<>(), context -> {
    ... 同上只做简单将input对象copy到output对象
});
Assert.assertTrue(resp.ok());
Assert.assertEquals("2", resp.getBody().getInput().getF());
Assert.assertEquals("3", resp.getBody().getOutput().getF());
// 注册了另一个拦截器B,假设B执行会报错
$.interceptor.register("test", new InterceptorB());
resp = $.interceptor.process("test", new Obj("11"), new HashMap<>(), context -> {
    ... 同上只做简单将input对象copy到output对象
});
Assert.assertTrue(!resp.ok());
Tip
更多示例见`InterceptorTest.java`

2.8. 定时器操作($.timer)

// 延迟任务
$.timer.timer(1, () -> // Do Something);
// 周期性任务,参数 fixedRate 为true时表示每periodSec执行,为false表示fixedDelay,表示在当前任务执行完成后过periodSec再执行
String taskId = $.timer.periodic(1, true, () -> // Do Something);
// 取消周期性任务
$.timer.cancel(taskId);
Tip
更多示例见`TimerHelperTest.java`

2.9. 常用文件操作($.file)

// 根据classpath读取文件所有内容(jar包外路径优先),另外readAllByPath、readAllByFile、readAllByPathName等衍生方法
String conf=$.file.readAllByClassPath("conf1.json","UTF-8");
Tip
更多示例见`FileHelperTest.java`

2.10. 常用字段操作($.amount)

// 转换成中文大写:壹仟贰佰叁拾肆亿伍仟陆佰柒拾捌万玖仟零壹拾贰元叁角肆分
$.amount.convert("1234,5678,9012.34");
Tip
更多示例见`AmountHelperTest.java`

2.11. 常用字段操作($.field)

// 验证邮箱格式是否合法
$.field.validateEmail("i@sunisle.org");
// 验证手机号格式是否合法
$.field.validateMobile("18657120000");
// 是否是汉字
$.field.isChinese("孤岛旭日");
// 验证身份证是否合法
$.field.validateIdNumber("330102199901015759");
// 根据身份编号获取年龄
$.field.getAgeByIdCard("330102199901015759");
// 根据身份编号获取生日
$.field.getBirthByIdCard("330102199901015759");
// 根据身份编号获取生日年
$.field.getYearByIdCard("330102199901015759");
// 根据身份编号获取生日月
$.field.getMonthByIdCard("330102199901015759");
// 根据身份编号获取生日天
$.field.getDateByIdCard("330102199901015759");
// 根据身份编号获取性别
$.field.getGenderByIdCard("330102199901015759");
// 根据身份编号获取户籍省份
$.field.getProvinceByIdCard("330102199901015759");
Tip
更多示例见`FieldHelperTest.java`

2.12. 服务降级处理($.fallback)

// ===========================================
// =============== 降级核心方法 ===============
// ===========================================

/**
* 根据组名获取降级信息
*
* @param groupName 组名,全局唯一
* @return 降级信息
*/
$.fallback.getFallbackInfo(String groupName)

/**
 * 带降级的处理方法
 *
 * @param groupName       组名,全局唯一
 * @param normalProcessor 正常方法,抛异常时进入失败方法
 * @param errorProcessor  失败方法
 */
$.fallback.execute(String groupName, NormalProcessor normalProcessor, ErrorProcessor errorProcessor)

/**
 * 带降级的处理方法
 *
 * @param groupName        组名,全局唯一
 * @param normalProcessor  正常方法,抛异常时进入失败方法
 * @param errorProcessor   失败方法
 * @param fallbackStrategy 降级策略
 */
$.fallback.execute(String groupName, NormalProcessor normalProcessor, ErrorProcessor errorProcessor,
                     FallbackStrategy fallbackStrategy)

/**
 * 带降级的处理方法
 *
 * @param groupName             组名,全局唯一
 * @param normalProcessor       正常方法,抛异常时进入失败方法
 * @param errorProcessor        失败方法
 * @param excludeFallbackErrors 不做降级处理的异常集合(视为业务上正常的错误)
 */
$.fallback.execute(String groupName, NormalProcessor normalProcessor, ErrorProcessor errorProcessor,
                     Class<? extends Throwable>... excludeFallbackErrors)

/**
 * 带降级的处理方法
 *
 * @param groupName             组名,全局唯一
 * @param normalProcessor       正常方法,抛异常时进入失败方法
 * @param errorProcessor        失败方法
 * @param fallbackStrategy      降级策略
 * @param excludeFallbackErrors 不做降级处理的异常集合(视为业务上正常的错误)
 */
$.fallback.execute(String groupName, NormalProcessor normalProcessor, ErrorProcessor errorProcessor,
                         FallbackStrategy fallbackStrategy, Class<? extends Throwable>... excludeFallbackErrors)

// ===========================================
// ================= 使用示例 =================
// ===========================================
$.fallback.execute("testGetUser",
                this::getUserNormal,
                this::getUserError);

String getUserNormal() throws Exception {
    // 正常处理方法,或抛出异常
    throw new NotFoundException("Not Found xxx");
}

String getUserError(Throwable e, FallbackHelper.FallbackInfo fallbackInfo) {
    // 错误处理方法,降级处理
    return "-1";
}
Tip
更多示例见`FallbackHelperTest.java`

2.13. 响应处理模型(Resp)

Note

响应模型,用于统一请求响应处理,模型由三个属性组成: . code:响应编码,同HTTP状态码,200表示成功 . message:错误描述,当code不为200时用于描述错误信息 . body:返回的实际对象

/**
 * 返回是否成功
 */
public boolean ok();

/**
 * 返回成功
 *
 * @param body 实际对象
 */
public static <E> Resp<E> success(E body);

/**
 * 返回未找到资源的错误,如数据记录不存在
 *
 * @param message 错误描述
 */
public static <E> Resp<E> notFound(String message);

/**
 * 返回资源冲突的错误,如密码错误
 *
 * @param message 错误描述
 */
public static <E> Resp<E> conflict(String message);

/**
 * 返回资源被锁定的错误,如当前用户被禁用
 *
 * @param message 错误描述
 */
public static <E> Resp<E> locked(String message);

/**
 * 返回请求格式的错误,如json不合法
 *
 * @param message 错误描述
 */
public static <E> Resp<E> unsupportedMediaType(String message);

/**
 * 返回请求参数的错误,如缺少参数
 *
 * @param message 错误描述
 */
public static <E> Resp<E> badRequest(String message);

/**
 * 返回请求拒绝的错误
 *
 * @param message 错误描述
 */
public static <E> Resp<E> forbidden(String message);

/**
 * 返回未认证的错误,如资源没有权限访问
 *
 * @param message 错误描述
 */
public static <E> Resp<E> unAuthorized(String message);

/**
 * 返回服务器的错误,如捕捉到执行异常
 *
 * @param message 错误描述
 */
public static <E> Resp<E> serverError(String message);

/**
 * 返回方法未实现的错误
 *
 * @param message 错误描述
 */
public static <E> Resp<E> notImplemented(String message);

/**
 * 返回服务不可用的错误
 *
 * @param message 错误描述
 */
public static <E> Resp<E> serverUnavailable(String message);

/**
 * 返回未知错误
 *
 * @param message 错误描述
 */
public static <E> Resp<E> unknown(String message);

/**
 * 返回自定义错误
 *
 * @param message 错误描述
 */
public static <E> Resp<E> customFail(String code, String message);

/**
 * 错误转换,解决java泛型继承问题
 *
 * @param resp 原响应对象
 * @return 转换后的响应对象
 */
public static Resp<Void> error(Resp<?> resp);

/**
 * 响应body转换,将body类型A转换成类型B
 *
 * @param resp      源响应
 * @param bodyClazz 目标body类型
 * @return 目标响应
 */
public static <E> Resp<E> generic(Resp resp, Class<E> bodyClazz);

/**
 * 响应body转换(列表),将body类型A转换成类型B
 *
 * @param resp      源响应
 * @param bodyClazz 目标body类型
 * @return 目标响应
 */
public static <E> Resp<List<E>> genericList(Resp resp, Class<E> bodyClazz);

    /**
     * 响应body转换(Set),将body类型A转换成类型B
     *
     * @param resp      源响应
     * @param bodyClazz 目标body类型
     * @return 目标响应
     */
    public static <E> Resp<Set<E>> genericSet(String resp, Class<E> bodyClazz);

/**
 * 响应body转换(分页),将body类型A转换成类型B
 *
 * @param resp      源响应
 * @param bodyClazz 目标body类型
 * @return 目标响应
 */
public static <E> Resp<PageDTO<E>> genericPage(Resp resp, Class<E> bodyClazz);

2.14. 分页模型(Page)

Note

响应模型,用于统一请求响应处理,模型由三个属性组成: . code:响应编码,同HTTP状态码,200表示成功 . message:错误描述,当code不为200时用于描述错误信息 . body:返回的实际对象

/**
 * 当前页,从1开始
 */
private long pageNumber;
/**
 * 每页记录数
 */
private long pageSize;
/**
 * 总页数
 */
private long pageTotal;
/**
 * 总记录数
 */
private long recordTotal;
/**
 * 实际对象
 */
private List<E> objects;

3. 功能禁用

如果不需要某些功能可以在maven中排除以减少包体积

<!--$.json dependency begin-->
<exclusion>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
</exclusion>
<!--$.json dependency end-->
<!--$.bean dependency begin-->
<exclusion>
    <groupId>commons-beanutils</groupId>
    <artifactId>commons-beanutils</artifactId>
</exclusion>
<!--$.bean dependency end-->
<!--$.security dependency begin-->
<exclusion>
    <groupId>org.mindrot</groupId>
    <artifactId>jbcrypt</artifactId>
</exclusion>
<!--$.security dependency end-->
<!--$.http dependency begin-->
<exclusion>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
</exclusion>
<exclusion>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpmime</artifactId>
</exclusion>
<exclusion>
    <groupId>org.jooq</groupId>
    <artifactId>joox</artifactId>
</exclusion>
<!--$.http dependency end-->