网站首页 > java教程 正文
物联网产品越来越多,很多公司都需要和单片机等硬件对接,有些硬件设备使用的是16进制的传递和接收协议;
如果突然有一天,协议更新或改变了,Java中就需要去调整每一个处理的地方,使用结构体可以有效避免出现这样的情况,协议改变了只需要修改结构体的顺序和数据类型;
不巧的是,java删除了结构体这个概念,使之只在C系列语言中得以广泛应用,这时候为了满足业务需求,就需要手写一个结构体出来。
在接受TCP或者UDP数据回传buffer时,可以直接解析成java对象进行处理,因为java中删除了结构体,所以只能自己实现,并且反射后没有顺序,所以必须有order字段
已完成功能:
功能点 | 完成度 |
排序获取对象 | 100% |
String获取长度可以定义 | 100% |
可以将某一个字段的值,作为下面某个字段的长度,用于动态获取 | 100% |
将字符串转位2进制字符串输出 | 100% |
传入class返回对象 | 100% |
使用ByteBuffer解析,避免并发 | 100% |
本教程第一部分为文件结构,第二部分为实操代码
文件结构(具体代码看下面实操部分,工具类代码在最后)
├──anno 【核心解析类文件夹】
│
│ └── SrStruct.java 【核心类(里面包含结构体解析方法,和调用方法)】
│
├──cache 【缓存文件夹】
│
│ └── StructCache.java 【缓存类(包含反射缓存,降低调用时间,充当二级缓存)】
│
├──emun 【枚举文件夹】
│
│ └── StructEnum.java 【枚举类(包含在注解方法中需要使用的枚举)】
│
├──vo 【测试实体类文件夹,不用关注】
│
└── Struct.java 【自定义注解】
实操
第一步:新建一个注解-- Struct.java
/**
* @author N_Xiang
* @describe 自定义结构体注解
* @time 2022/2/24 10:17 下午
*/
@Target({ElementType.FIELD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Struct {
//排序,java反射后没有顺序,所以需要排序字段辅助
int order();
//枚举,如果要将byte数组取出为2进制字符串时,需要使用
StructEnum value() default StructEnum.Null;
//长度,标识byte[]和String类型的变量需要取出多少个字节
int length() default 0;
//动态长度,如果当前字段获取需要获取的字节长度是依靠上面某个字段定义的,需要使用这个属性
String lengthMethod() default "";
}
第二步:写缓存类 -- StructCache.java
/**
* @author N_Xiang
* @describe 全局缓存
* @time 2022/4/26 09:07
*/
public class StructCache {
/**
* 二级缓存
*/
public static final Map<Class<?>, Map<String, StructEnum>> ANN_FIELD_MAP = new ConcurrentHashMap<>();
public static final Map<Class<?>,Map<String, Field>> ANN_FIELD_TYPE_MAP = new ConcurrentHashMap<>();
public static final Map<Class<?>,Map<Field,Integer>> ANN_LENGTH_MAP = new ConcurrentHashMap<>();
public static final Map<Class<?>,Map<String,String>> ANN_LENGTH_METHOD_MAP = new ConcurrentHashMap<>();
public static final Map<Class<?>, SortedMap<Integer, Field>> ANN_LABEL_MAP = new ConcurrentHashMap<>();
public static final Map<String, Method> ANN_METHOD_MAP = new ConcurrentHashMap<>();
}
第三部:补充枚举类--StructEnum.java (建议拷贝后在编辑器中查看)
/**
* @author N_Xiang
* @describe 类型枚举
* @time 2022/2/24 10:39 下午
*/
public enum StructEnum {
String,
Int,
Byte,
Char,
Short,
Long,
Double,
Float,
Null,
;
}
第四步:写解析类 -- SrStruct.java
(有些长,建议拷贝到编辑器中查看,主要功能就是将JavaBean中每个字段的注解值获取到,然后对应将Buffer中的值通过反射赋值)
/**
* @author N_Xiang
* @describe 注解解析器
* @time 2022/2/24 10:19 下午
*/
public class SrStruct {
/**
* 字段注解中强转属性map
* K:字段名
* V:类型枚举
*/
private static Map<String, StructEnum> fieldMap = new ConcurrentHashMap<>();
/**
* 字段FieldMap
* K:字段名
* V:Field
*/
private static Map<String, Field> fieldTypeMap = new ConcurrentHashMap<>();
/**
* 长度属性的值,k:filed v:长度
*/
private static Map<Field, Integer> lengthMap = new ConcurrentHashMap<>();
/**
* 统计所有有长度属性的字段 k:filed名字
*/
private static Map<String, String> lengthMethodMap = new ConcurrentHashMap<>();
/**
* 将实体类中的注解字段分解
* @param cls 实体类Object
* @return
*/
private static SortedMap<Integer, Field> parsStruct(Object cls) {
return parsStruct(cls.getClass());
}
/**
* 将实体类中的注解字段分解
* @param cls 实体类Class
* @return
*/
private static SortedMap<Integer, Field> parsStruct(Class<?> cls) {
if (!ObjectUtils.isEmpty(StructCache.ANN_FIELD_MAP.get(cls))) {
fieldMap = StructCache.ANN_FIELD_MAP.get(cls);
fieldTypeMap = StructCache.ANN_FIELD_TYPE_MAP.get(cls);
lengthMap = StructCache.ANN_LENGTH_MAP.get(cls);
lengthMethodMap = StructCache.ANN_LENGTH_METHOD_MAP.get(cls);
return StructCache.ANN_LABEL_MAP.get(cls);
}
SortedMap<Integer, Field> lableMap = new TreeMap<>();
Field[] fields = cls.getDeclaredFields();
Arrays.stream(fields).forEach(f -> {
if (f.isAnnotationPresent(Struct.class)) {
Struct annotation = f.getAnnotation(Struct.class);
int value = annotation.order();
StructEnum value1 = annotation.value();
String s = annotation.lengthMethod();
int length = annotation.length();
if (value1 != StructEnum.Null) {
fieldMap.put(f.getName(), value1);
}
lengthMap.put(f, length);
lengthMethodMap.put(f.getName(), s);
if (!ObjectUtils.isEmpty(lableMap.get(value))) {
throw new RuntimeException("在" + cls.getName() + " 中 " + f.getName() + " 字段@Struct的order属性出现重复!");
}
lableMap.put(value, f);
fieldTypeMap.put(f.getName(), f);
}
});
StructCache.ANN_FIELD_MAP.put(cls, fieldMap);
StructCache.ANN_FIELD_TYPE_MAP.put(cls, fieldTypeMap);
StructCache.ANN_LENGTH_MAP.put(cls, lengthMap);
StructCache.ANN_LENGTH_METHOD_MAP.put(cls, lengthMethodMap);
StructCache.ANN_LABEL_MAP.put(cls, lableMap);
return lableMap;
}
@Deprecated
public static void translation(Object o, byte[] bytes) {
translation(o, ByteBuffer.wrap(bytes));
}
@Deprecated
public static void translation(Class<?> o, byte[] bytes) {
try {
translation(o.newInstance(), ByteBuffer.wrap(bytes), 0);
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
}
@Deprecated
public static void translation(Object o, byte[] bytes, int offset) {
translation(o, ByteBuffer.wrap(bytes), offset);
}
@Deprecated
public static void translation(Object o, String bytes) {
ByteBuffer buffer = ByteBuffer.wrap(StringToHex.hexStr2Byte(bytes));
translation(o, buffer);
}
public static void translation(Object o, ByteBuffer buffer) {
translation(o, buffer, 0);
}
@Deprecated
public static void translation(Object o, String bytes, int offset) {
ByteBuffer byteBuffer = ByteBuffer.wrap(StringToHex.hexStr2Byte(bytes));
translation(o, byteBuffer, offset);
}
@Deprecated
public static <T> T translation(Class<T> cls, String bytes) {
ByteBuffer byteBuffer = ByteBuffer.wrap(StringToHex.hexStr2Byte(bytes));
try {
T tclass = cls.newInstance();
translation(tclass, byteBuffer, 0);
return tclass;
} catch (InstantiationException | IllegalAccessException e) {
throw new RuntimeException(e);
}
}
public static <T> T translation(Class<T> cls, ByteBuffer buffer) {
try {
T tclass = cls.newInstance();
translation(tclass, buffer, 0);
return tclass;
} catch (InstantiationException | IllegalAccessException e) {
throw new RuntimeException(e);
}
}
public static <T> T translation(Class<T> cls, ByteBuffer buffer, int offset) {
try {
T tclass = cls.newInstance();
translation(tclass, buffer, offset);
return tclass;
} catch (InstantiationException | IllegalAccessException e) {
throw new RuntimeException(e);
}
}
/**
* 解构为JavaBean
*
* @param source 目标对象
* @param buffer 字节流
* @param offset 偏移量
*/
public static void translation(Object source, ByteBuffer buffer, int offset) {
// 设置偏移量
buffer.position(offset);
// 获取结构体注解
SortedMap<Integer, Field> map = parsStruct(source);
for (Map.Entry<Integer, Field> entry : map.entrySet()) {
// 获取字段
Field v = entry.getValue();
// 获取字段Type
Class<?> type = getType(v);
// 获取字段名
String name = getName(v);
// 获取set方法名
String setMethodName = fieldSetMethodName(name);
// 获取set方法
Method setMethod = MethodBySet(source, setMethodName, type);
try {
// 判断是否是子类,子类进入递归
if (!ObjectUtils.isEmpty(getType(v).getSuperclass()) && !getType(v).equals(byte[].class) && !getType(v).equals(String.class) && getType(v).getSuperclass().equals(Object.class)) {
Object subclass = getType(v).newInstance();
translation(subclass, buffer, buffer.position());
setMethod.invoke(source, subclass);
continue;
}
} catch (Exception e) {
e.printStackTrace();
}
// 获取需要取字节的长度
int len = getLength(source,name,type,v);
// 获取byte流中对应的数据
Object deconstructedValue = typeToObject(type, buffer, len, fieldMap.get(name));
// 将解构的数据存储到目标Bean
try {
setMethod.invoke(source, deconstructedValue);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
}
public static void unTranslation(Object o, String bytes) {
unTranslation(o, StringToHex.hexStr2Byte(bytes));
}
/**
* 通过结构体构建成Byte数组
* @param source 源对象
* @param bytes byte数组
*/
public static void unTranslation(Object source, byte[] bytes) {
}
/**
* 获取字段type
* @param field 字段Field
* @return
*/
private static Class<?> getType(Field field) {
return field.getType();
}
/**
* 获取字段名
* @param field 字段Field
* @return
*/
private static String getName(Field field) {
return field.getName();
}
/**
* 将bytebuffer中的值取出
* @param type 源字段类型
* @param buffer 源buffer数据
* @param length 取得字段长度
* @param structEnum 字段目标类型
* @return
*/
private static Object typeToObject(Class<?> type, ByteBuffer buffer, int length, StructEnum structEnum) {
if (type.equals(String.class)) {
if (length == 0) {
length = 1;
}
byte[] bytes1 = new byte[length];
buffer.get(bytes1);
if (ObjectUtils.isEmpty(structEnum)) {
return StringToHex.bytesToHexString(bytes1);
} else if (structEnum == StructEnum.Byte) {
return StringToHex.byteArraytoByteString(bytes1);
} else {
return StringToHex.bytesToHexString(bytes1);
}
} else if (type.equals(int.class) || type.equals(Integer.class) || type.equals(U32.class)) {
return buffer.getInt();
} else if (type.equals(short.class) || type.equals(Short.class) || type.equals(U16.class)) {
return buffer.getShort();
} else if (type.equals(Byte.class)) {
return buffer.get();
} else if (type.equals(byte.class) || type.equals(U8.class)) {
return buffer.get();
} else if (type.equals(byte[].class)) {
byte[] bytes = new byte[length];
buffer.get(bytes);
return bytes;
} else {
throw new RuntimeException(type.getName() + "是不支持的属性");
}
}
/**
* 获取set方法
* @param source 源对象
* @param setMethodName set字段名
* @param type 源字段类型
* @return
*/
private static Method MethodBySet(Object source, String setMethodName, Class<?> type) {
Method method = StructCache.ANN_METHOD_MAP.get(source + setMethodName);
if (ObjectUtils.isEmpty(method)) {
try {
method = source.getClass().getMethod(setMethodName, type);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
StructCache.ANN_METHOD_MAP.put(source + setMethodName, method);
}
return method;
}
/**
* 获取get方法
* @param source 源对象
* @param getMethodName get字段名
* @param type 源字段类型
* @return
*/
private static Method MethodByGet(Object source, String getMethodName, Class<?> type) {
Method method = StructCache.ANN_METHOD_MAP.get(source + getMethodName);
if (ObjectUtils.isEmpty(method)){
try {
method = source.getClass().getMethod(getMethodName);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
StructCache.ANN_METHOD_MAP.put(source+getMethodName,method);
}
return method;
}
/**
* 获取get方法名
* @param name 源字段名
* @return
*/
private static String fieldGetMethodName(String name) {
String lengthMethodMapLetter = name.substring(0, 1).toUpperCase().concat(name.substring(1));
return "get" + lengthMethodMapLetter;
}
/**
* 获取set方法名
* @param name 源字段名
* @return
*/
private static String fieldSetMethodName(String name) {
String lengthMethodMapLetter = name.substring(0, 1).toUpperCase().concat(name.substring(1));
return "set" + lengthMethodMapLetter;
}
/**
* 获取字段值
* @param type1 字段类型
* @param getMethod get方法
* @param source 源对象
*/
private static int getValue(Type type1,Method getMethod,Object source){
try{
if (type1.equals(int.class) || type1.equals(Integer.class)
|| type1.equals(short.class) || type1.equals(Short.class)
|| type1.equals(double.class) || type1.equals(Double.class)
|| type1.equals(long.class) || type1.equals(Long.class)) {
Number invoke = (Number) getMethod.invoke(source);
return invoke.intValue();
} else if (type1.equals(byte.class) || type1.equals(Byte.class)) {
return (byte) getMethod.invoke(source) & 0xFF;
}
}catch (Exception e){
e.printStackTrace();
}
return 0;
}
/**
* 获取长度
* @param source 源对象
* @param name 目标字段名
* @param type 目标字段类型
* @param v 字段Field
*/
private static int getLength(Object source,String name,Class<?> type,Field v){
// 判断当前字段 @注解中是否有 lengthMethod 属性的字段名
String lengthMethodName = lengthMethodMap.get(name);
if (!StringUtils.isEmpty(lengthMethodName)) {
//获取目标字段的get方法名
String getLengthMethodMapName = fieldGetMethodName(lengthMethodName);
//获取目标字段的get方法
Method getMethod = MethodByGet(source, getLengthMethodMapName, type);
//从内存中获取到目标字段对应到Field属性
Field field = fieldTypeMap.get(lengthMethodName);
if (ObjectUtils.isEmpty(field)) {
throw new RuntimeException("没有在当前对象中找到属性" + lengthMethodName);
}
//获取目标字段Type
Class<?> targetType = field.getType();
//取出长度值,并赋值给当前字段
return getValue(targetType,getMethod,source);
} else {
// 没有 lengthMethod 属性就获取定义的length属性或者默认值
return lengthMap.get(v);
}
}
}
第五步:调用
1:传入字符串
SrStruct.translation(需要接收数据的对象,传入的16进制字符串);
2:传入字符串加上偏移量
SrStruct.translation(需要接收数据的对象,传入的16进制字符串,偏移量);
3:传入byte数组
SrStruct.translation(需要接收数据的对象,传入的byte数据);
4:传入byte数组加上偏移量
SrStruct.translation(需要接收数据的对象,传入的byte数据,偏移量);
5:传入ByteBuffer对象
SrStruct.translation(需要接收数据的对象,传入的byteBuffer数据,偏移量);
真实调用方法演示
1、带偏移量的(XXX为JAVABean)
XXX xx = SrStruct.translation(XXX.class,bytes,11);
2、不带偏移量(XXX为JAVABean)
XXX xx = SrStruct.translation(XXX.class,bytes);
3、(XXX为JAVABean)使用Netty的ByteBuf的(ByteBuffer.wrap()方法是将Bytebuf转为java中自带的ByteBuffer使用)
XXX xx = SrStruct.translation(XXX.class,ByteBuffer.wrap(byteBuf.array()));
补充:注解使用方法
支持子类和父类一起解析
/**
* @author N_Xiang
* @describe
* @time 2022/2/24 11:53 下午
*/
@Data
public class FooA extends Goo{
//order标注的顺序
@Struct(order = 0)
int aa;
@Struct(order = 1)
short bb;
@Struct(order = 2)
short cc;
//lengthMethod 获取cc的值作为当前需要获取的长度
@Struct(order = 3,lengthMethod = "cc")
String dd;
//获取长度为2的byte数组
@Struct(order = 4,length = 2)
byte[] ee;
//获取长度为2的byte数组转为字符串,字符串默认长度取1个byte
@Struct(order = 5,length = 2)
String dd;
// @Struct(order = 3)
// byte dd;
/**
*内部实体内
**/
@Data
public static class Foo2{
@Struct(order = 0)
short ff;
@Struct(order = 1)
String gg;
}
}
用到的工具方法--类中的StringToHex工具
//16进制字符串转byte数组
public static byte[] hexStr2Byte(String hex) {
ByteBuffer bf = ByteBuffer.allocate(hex.length() / 2);
for (int i = 0; i < hex.length(); i++) {
String hexStr = hex.charAt(i) + "";
i++;
hexStr += hex.charAt(i);
byte b = (byte) Integer.parseInt(hexStr, 16);
bf.put(b);
}
return bf.array();
}
//byte数组转16进制字符串
public static final String bytesToHexString(byte[] bArray) {
StringBuffer sb = new StringBuffer(bArray.length);
String sTemp;
for (int i = 0; i < bArray.length; i++) {
sTemp = Integer.toHexString(0xFF & bArray[i]);
if (sTemp.length() < 2) {
sb.append(0);
}
sb.append(sTemp.toUpperCase());
}
return sb.toString();
}
//byte数组转2进制字符串
public static final String byteArraytoByteString(byte[] bArray) {
StringBuilder sb = new StringBuilder(bArray.length);
for (int i = 0; i < bArray.length; i++) {
byte t = bArray[i];
StringBuilder tsb = new StringBuilder();
for (int j=7;j>=0;j--){
int temp = 0x01 & t;
tsb.append(temp);
t >>=1;
}
sb.append(tsb.reverse());
}
return sb.toString();
}
Lintge-持续分享开发新技术,代码新知识!
猜你喜欢
- 2024-12-22 彻底理解Java反射以及动态代理中对反射的应用
- 2024-12-22 Java反射机制详解 java反射机制的作用是什么
- 2024-12-22 详解 Java 中的变量 java中变量的使用步骤
- 2024-12-22 Java 中的 Function:让转换逻辑更灵活
- 2024-12-22 Java 整型数据有byte、short、int、long,它们之间有什么不一样?
- 2024-12-22 如何用计算机实现 (1+2)*3 ? 实现一个简单的计算器功能
- 2024-12-22 Java通过反射执行方法(获取方法) java通过反射获取字段的值
- 2024-12-22 java之反射(3)方法method java 反射method
- 2024-12-22 探讨 Java 中 valueOf 和 parseInt 的区别
- 2024-12-22 漫画:为什么Java里面的String对象是不可变的?
你 发表评论:
欢迎- 04-27微服务部署架构设计详解(图文全面总结)
- 04-27Java微服务架构选型与对比:一场技术流派的巅峰对决
- 04-27微服务架构下Java的最佳实践
- 04-27Java微服务架构选型:优雅拆分与高效整合
- 04-27微服务架构下的Java代码拆分策略:像拼图一样构建系统
- 04-27微服务架构下的Java最佳实践
- 04-27微服务架构下Java的挑战与机遇
- 04-27微服务架构下Java事务管理的艺术
- 最近发表
- 标签列表
-
- java反编译工具 (77)
- java反射 (57)
- java接口 (61)
- java随机数 (63)
- java7下载 (59)
- java数据结构 (61)
- java 三目运算符 (65)
- java对象转map (63)
- Java继承 (69)
- java字符串替换 (60)
- 快速排序java (59)
- java并发编程 (58)
- java api文档 (60)
- centos安装java (57)
- java调用webservice接口 (61)
- java深拷贝 (61)
- 工厂模式java (59)
- java代理模式 (59)
- java.lang (57)
- java连接mysql数据库 (67)
- java重载 (68)
- java 循环语句 (66)
- java反序列化 (58)
- java时间函数 (60)
- java是值传递还是引用传递 (62)
本文暂时没有评论,来添加一个吧(●'◡'●)