登录消息中有很多开关属性,这些属性在赋值的时候会大段执行结构相同的代码,为了减少代码量和通用性使用反射合并这些类似的代码。
Java使用注解加反射赋值属性
- 1. 原始代码
- 2. 修改后的代码
- 开始实操
- 1. 定义注解
- 2. 使用定义好的注解
- 3. 使用反射读取注解并给属性赋值
- 命名规范问题
1. 原始代码
灰色部分都是在设置开关类型的字段
2. 修改后的代码
// 反射写入配置
try {
Class c = response.getClass();
Field[] fieldArr = c.getDeclaredFields();
APlatInfoAttribute aia;
PropertyDescriptor descriptor;
Method setMethod;
for (Field f : fieldArr) {
if (!f.isAnnotationPresent(APlatInfoAttribute.class)) {
continue;
}
aia = f.getAnnotation(APlatInfoAttribute.class);
descriptor = new PropertyDescriptor(f.getName(), c);
setMethod = descriptor.getWriteMethod();
switch (aia.useMethods()) {
case "getParamsValByKey":
setMethod.invoke(response, info.getParamsValByKey(isIos, aia.configName(), aia.valueLength(), aia.defaultValue()));
break;
case "getParamsOpByVersion":
setMethod.invoke(response, info.getParamsOpByVersion(isIos, aia.configName(), msgInfo.getVersionNum()));
break;
case "getOpByKey":
setMethod.invoke(response, info.getOpByKey(isIos, aia.configName()));
break;
case "getParamStrByKey":
setMethod.invoke(response, info.getParamStrByKey(isIos, aia.configName(), aia.valueLength()));
break;
default:
logger.error("OverseaGameLoginLogic - response." + f.getName() + " 获取失败");
break;
}
}
} catch (Exception e) {
e.printStackTrace();
}
开始实操
1. 定义注解
根据实际获取配置的方式来定义注解中的内容,以下为实际使用中的4中情况
info.getParamStrByKey(isIos, "notice", 8);
info.getParamsValByKey(isIos, "qqact", 1, 0);
info.getParamsOpByVersion(isIos, "isShowAds", msgInfo.getVersionNum());
info.getOpByKey(isIos, "energyToProp1");
- 四种获取方式
- 相同的参数为 isIos, key, valueLength
- 不相同的为 默认值
- 经过分析可以得到对应的注解结构如下,其中@Target(ElementType.FIELD)和@Retention(RetentionPolicy.RUNTIME) 是源注解,就是注解的注解,这里使用的FIELD 是作用属性字段、枚举的常量。
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface APlatInfoAttribute {
/**
* 配置的key
*/
String configName();
/**
* 使用PlatInfo的哪个方法获取值
*/
String useMethods() default "getParamsValByKey";
/**
* getParamsValByKey 值的长度
*/
int valueLength() default 1;
/**
* getParamsValByKey 默认值
*/
int defaultValue() default 0;
}
2. 使用定义好的注解
在属性的上方写上 @APlatInfoAttribute(configName = “energyToProp1”, useMethods
= “getOpByKey”, valueLength = 4, defaultValue = 50) 其中 useMethods,valueLength,defaultValue 是有默认值,是可以省略不写的
@APlatInfoAttribute(configName = "stype")
private int securitytype;
@APlatInfoAttribute(configName = "security", useMethods = "getParamsOpByVersion")
private int showsecurity;
@APlatInfoAttribute(configName = "notice", useMethods = "getParamStrByKey", valueLength = 8)
private String showNotice;
@APlatInfoAttribute(configName = "bullet", valueLength = 4, defaultValue = 50)
private int perfectBetBullet;
3. 使用反射读取注解并给属性赋值
通过反射获取指定对象中带有指定注解的字段,进行反射调用对应的 set 方法,当然可以使用反射直接对属性赋值,但是有些属性赋值后需要一些逻辑判断,这里选用的 set 进行赋值。
- 通过 Object.getClass().getDeclaredFields(); 获取类或接口声明的所有字段,包含公共、受保护、默认(包)访问和私有字段,但不包括继承的字段 。返回的数组中的元素没有排序,也没有任何特定的顺序。
- 通过遍历获得到的数组,通过 isAnnotationPresent(APlatInfoAttribute.class) 筛选出带有我们指定注解的属性
- 使用getAnnotation(APlatInfoAttribute.class);获取注解
- 使用 Method m = new PropertyDescriptor(f.getName(), c).getWriteMethod(); 获取属性的 set 方法,通过m.invoke();进行调用set方法
try {
Class c = response.getClass();
// 获取属性
Field[] fieldArr = c.getDeclaredFields();
APlatInfoAttribute aia;
Method setMethod;
for (Field f : fieldArr) {
// 判断是否有指定注解
if (!f.isAnnotationPresent(APlatInfoAttribute.class)) {
continue;
}
// 获取注解
aia = f.getAnnotation(APlatInfoAttribute.class);
// 获取set方法
setMethod = new PropertyDescriptor(f.getName(), c).getWriteMethod();
// 判断要使用的方法获取配置,并执行set
switch (aia.useMethods()) {
case "getParamsValByKey":
setMethod.invoke(response, info.getParamsValByKey(isIos, aia.configName(), aia.valueLength(), aia.defaultValue()));
break;
case "getParamsOpByVersion":
setMethod.invoke(response, info.getParamsOpByVersion(isIos, aia.configName(), msgInfo.getVersionNum()));
break;
case "getOpByKey":
setMethod.invoke(response, info.getOpByKey(isIos, aia.configName()));
break;
case "getParamStrByKey":
setMethod.invoke(response, info.getParamStrByKey(isIos, aia.configName(), aia.valueLength()));
break;
default:
logger.error("OverseaGameLoginLogic - response." + f.getName() + " 获取失败");
break;
}
}
} catch (Exception e) {
e.printStackTrace();
}
命名规范问题
属性名如果是 b + 大写字母开头的,获取到的 get,set为 is 开头比如:
如果使用自动生成 get,set 方法,非 boolean 类型的要注意命名规范
public class C1 {
private int bVa1;
// 自动生成的 get set
public int getbVa1() {
return bVa1;
}
public void setbVa1(int bVa1) {
this.bVa1 = bVa1;
}
}
C1 c1 = new C1();
Class c = c1.getClass();
Field[] fArr = c.getDeclaredFields();
PropertyDescriptor descriptor = new PropertyDescriptor(fArr[0].getName(), c);
// 这里就会报错:java.beans.IntrospectionException: Method not found: isBVa1