1.简介
本工具解决在自定义二进制协议中二进制和javaBean相互转换的问题; 在当代物联网行业中, 更多的公司选择使用自定义的二进制协议; JAVA同学在对这些协议进行解析时,会陷入序列化/反序列化的痛苦循环中;解决了自定义二进制协议的以下痛点:
- 二进制协议解析
- 效验计算
- 位运算难
- JAVA对象转换
- 无符号运算操作
maven项目可直接导入
<dependency>
<groupId>io.github.misterchangray</groupId>
<artifactId>magic-byte</artifactId>
<version>2.1.0</version>
</dependency>
- 引入Jar包;
@MagicClass
对当前类进行全局配置@MagicField
对需要转换的JAVA对象属性进行标注,支持对象组合嵌套- 使用
MagicByte.pack()
或则MagicByte.unpack()
对数据或对象进行快速的序列化或反序列化
4. 注解和属性说明
工具存在两个注解:
-
@MagicClass()
类注解; 主要用于数据全局配置
- byteOrder 配置序列化大小端
- strict 严格模式, 默认false, 严格模式将会抛出更多的异常
-
@MagicField()
属性注解, 未注解的属性不参与序列化/反序列化过程
- order 定义序列化顺序**(重要, 投入使用后请勿修改, 从1开始依次递增)**
- size 属性大小, 仅String和List需要设置, String 代表字节长度, List和Array代表成员长度
- charset 字符集设置, 仅
String
设置有效; 默认ASCII - dynamicSize 标记字段为动态长度, 整个数据结构只能标记一次且仅能标记
String&List&Array
类型字段 - dynamicSizeOf 从指定的 order 中获取
List或Array
的长度, 仅List,Array,String
有效;引用字段类型只能为byte, short, int
- calcLength 标记字段为长度字段, 反序列化时将自动将长度填充到此字段; 可能抛出: InvalidLengthException
- calcCheckCode 标记字段为校验和字段, 序列化或反序列化时将会校验或自动填充; 可能抛出: InvalidCheckCodeException
5. 支持的数据类型及字节大小;
数据类型 | 数据类型 | 字节大小 |
---|---|---|
byte | boolean | 1 |
short | char | 2 |
int | float | 4 |
long | double | 8 |
String | custom |
6.String类型size问题,可以使用代理动态改变注解的size的value值
实体
import com.github.misterchangray.core.annotation.MagicClass;
import com.github.misterchangray.core.annotation.MagicField;
import com.github.misterchangray.core.enums.ByteOrder;
import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;
@Data
@MagicClass(byteOrder = ByteOrder.BIG_ENDIAN)
@Accessors(chain = true)
public class ConnectionStruct implements Serializable {
@MagicField(order = 1)
private String ProId;
@MagicField(order = 2)
private String SegId;
@MagicField(order = 3)
private String Type;
@MagicField(order = 4)
private String Depth;
@MagicField(order = 5)
private String Height;
@MagicField(order = 6)
private String Layer;
@MagicField(order = 7)
public String VehicleType;
@MagicField(order = 8)
public String VehicleNo;
@MagicField(order = 9)
public String DeviceNo;
}
动态修改注解工具类
import io.swagger.models.auth.In;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/*
* 思路:
* 1、先获取需要修改的 Field
* 2、通过 Field 的方法 getDeclaredField 获取需要修改的注解
* 3、Proxy.getInvocationHandler(anno) 获取该注解代理实例所持有的 InvocationHandler
* 4、获取 AnnotationInvocationHandler 的 memberValues 字段
* 5、获取 memberValuesMap,通过该Map 对注解属性值获取修改
*
* @Param dest : 需要修改的对象
* @Param changeProperties : 类属性下注解需要修改的集合
*/
public class DynamicChangeAnnoUtils {
public static void changeAnnoValue(Object dest, Map<String, HashMap<String, Map<String, Map<String, Integer>>>> changeProperties) {
System.out.println(changeProperties+"1");
for (String filedKey: changeProperties.keySet()) {
System.out.println("filedKey=" + filedKey+"2");
try {
Field field = dest.getClass().getDeclaredField(filedKey);
System.out.println(field.getName()+"3");
String name = field.getName();
for (String annoKey : changeProperties.get(filedKey).keySet()) {
//获取字段上的注解实例
Annotation[] annotations = field.getDeclaredAnnotations();
for (Annotation anno : annotations) {
if (anno.annotationType().getSimpleName().equals(annoKey)) {
// System.out.println(anno.annotationType().getSimpleName());
// 1、获取代理实例所持有的 InvocationHandler
InvocationHandler handler = Proxy.getInvocationHandler(anno);
// 2、获取 AnnotationInvocationHandler 的 memberValues 字段
Field mValuesField = handler.getClass().getDeclaredField("memberValues");
// 3、打开访问权限
mValuesField.setAccessible(true);
// 4、获取 memberValuesMap
Map memberValuesMap = (Map) mValuesField.get(handler);
// 5、put修改注解属性值
System.out.println( changeProperties.get(filedKey).get(annoKey));
changeProperties.get(filedKey).get(annoKey).forEach((k, v) -> {
System.out.println("k=" + k + ",v=" + v.get(name)+"----4");
for(String key:v.keySet()){
Integer value = v.get(key).intValue();
System.out.println("key="+key+" vlaue="+value);
if (key.equals(name)){
memberValuesMap.put(k, value);
}
}
});
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static void printAnno(Object obj) {
// 遍历获取Field[] 集合
Field[] declaredFields = obj.getClass().getDeclaredFields();
for (Field f : declaredFields) {
// 遍历获取Annotation[] 集合
Annotation[] annotations = f.getDeclaredAnnotations();
for (Annotation anno : annotations) {
// System.out.println(anno); //@com.ymqx.动态修改类属性的注解值.TestAnno(name=test, type=1)
//System.out.println(anno.annotationType().getName()); //com.ymqx.动态修改类属性的注解值.TestAnno
//System.out.println(anno.annotationType().getSimpleName()); //TestAnno
if (anno.annotationType().getSimpleName().equals("MagicField")) {
// 1、获取代理实例所持有的 InvocationHandler
InvocationHandler handler = Proxy.getInvocationHandler(anno);
try {
// 2、获取 AnnotationInvocationHandler 的 memberValues 字段
Field mValuesField = handler.getClass().getDeclaredField("memberValues");
// 3、打开访问权限
mValuesField.setAccessible(true);
// 4、获取 memberValuesMap
Map memberValuesMap = (Map) mValuesField.get(handler);
// 5、get获取注解属性值
System.out.println(memberValuesMap.get("size"));
// System.out.println(memberValuesMap.get("type"));
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
public static int getWordCount(String s) {
int length = 0;
for (int i = 0; i < s.length(); i++) {
int ascii = Character.codePointAt(s, i);
if (ascii >= 0 && ascii <= 255)
length++;
else
length += 2;
}
return length;
}
}
使用
public static void test() throws Exception {
ConnectionStruct connection2 = new ConnectionStruct();
//xxxxxxx中文字符串
connection2.setProId(HexUtils.str2HexStr("xxxxxxx"));
connection2.setSegId(HexUtils.str2HexStr("二xxxxxxx"));
connection2.setType(HexUtils.str2HexStr("xxxxxxx"));
connection2.setHeight(HexUtils.str2HexStr("xxxxxxx"));
connection2.setDepth(HexUtils.str2HexStr("xxxxxxx"));
connection2.setLayer(HexUtils.str2HexStr("xxxxxxx"));
connection2.VehicleType = HexUtils.str2HexStr("xxxxxxx");
connection2.VehicleNo = HexUtils.str2HexStr("xxxxxxx");
connection2.DeviceNo = HexUtils.str2HexStr("xxxxxxx");
//一种方式转Byte
// ByteBuffer byteBuffer = ByteBuffer.allocate(connection.getProId().getBytes().length).order(ByteOrder.BIG_ENDIAN);
// byteBuffer.put(connection.getProId().getBytes());
// byte[] tmp = byteBuffer.array();
int ProIdCount = DynamicChangeAnnoUtils.getWordCount(connection2.getProId());
int SegIdCount = DynamicChangeAnnoUtils.getWordCount(connection2.getSegId());
int DepthCount = DynamicChangeAnnoUtils.getWordCount(connection2.getDepth());
int TypeCount = DynamicChangeAnnoUtils.getWordCount(connection2.getType());
int HeightCount = DynamicChangeAnnoUtils.getWordCount(connection2.getHeight());
int LayerCount = DynamicChangeAnnoUtils.getWordCount(connection2.getLayer());
int VehicleTypeCount = DynamicChangeAnnoUtils.getWordCount(connection2.getVehicleType());
int VehicleNoCount = DynamicChangeAnnoUtils.getWordCount(connection2.getVehicleNo());
int DeviceNoCount = DynamicChangeAnnoUtils.getWordCount(connection2.getDeviceNo());
HashMap<String, Map<String, Integer>> annoValueMap = new HashMap<>();
HashMap<String, Integer> stringIntegerHashMap = new HashMap<>();
stringIntegerHashMap.put("ProId", ProIdCount);
stringIntegerHashMap.put("SegId", SegIdCount);
stringIntegerHashMap.put("Type", TypeCount);
stringIntegerHashMap.put("Depth", DepthCount);
stringIntegerHashMap.put("Height", HeightCount);
stringIntegerHashMap.put("Layer", LayerCount);
stringIntegerHashMap.put("VehicleType", VehicleTypeCount);
stringIntegerHashMap.put("VehicleNo", VehicleNoCount);
stringIntegerHashMap.put("DeviceNo", DeviceNoCount);
annoValueMap.put("size", stringIntegerHashMap);
HashMap<String, Map<String, Map<String, Integer>>> annoMap = new HashMap<>();
annoMap.put("MagicField", annoValueMap);
HashMap<String, HashMap<String, Map<String, Map<String, Integer>>>> filedMap = new HashMap<>();
filedMap.put("ProId", annoMap);
filedMap.put("SegId", annoMap);
filedMap.put("Type", annoMap);
filedMap.put("Depth", annoMap);
filedMap.put("Height", annoMap);
filedMap.put("Layer", annoMap);
filedMap.put("VehicleType", annoMap);
filedMap.put("VehicleNo", annoMap);
filedMap.put("DeviceNo", annoMap);
System.out.println("+++修改注解属性值后+++");
DynamicChangeAnnoUtils.changeAnnoValue(connection2, filedMap);
DynamicChangeAnnoUtils.printAnno(connection2);
byte[] tmp = MagicByte.unpackToByte(connection2); // 对象序列化到字节
String s = HexUtils.byte2HexStr(tmp);
String s1 = HexUtils.hexStr2Str(s);
String s2 = HexUtils.hexStr2Str(s1);//中文再次转换
System.out.println(s2);
}