Java反射偷懒小记
工作以来很少些博客,在各位大佬的影响下决定还是记录下自己工作的点点滴滴,以免自己以后找寻相关知识还是继续百度。像今天早上媳妇儿提到他用java-wrapper部署spring-boot,一时半会儿我居然想不起来java-wrapper是什么。想当初对wrapper熟悉得想左右手一样。哎…… 痛改前非,从今天开始把有意义的事情都记录下吧,不废话了还是来谈主题java的反射。
最近在做一个http接口时,用到一些发射和注解的东西。能方便的实现该功能。其中一个功能就是提交的客户断应该会提交叫N个字段,但是在实际中他可能只会提交M个(M < N)那每次都需要取统计这个M以便后面能快速的生成报表等。个人想到的办法有
1.直接将上报的字段全部判断一次,代码直接一个字段一个字段的取遍历完成。
2.采用注解加反射的方式完成统计。相比方法1来说至少代码会好看些。没有那么复杂的圈复杂度。
java反射
谈到java反射的列子,理论都很多,做过java项目的人基本都用到过反射。java的各类框架就更不用谈了,基本上没有反射这些框架都得回娘胎去。这里就简单说一下java反射里面比较常用的东西,个人觉得比较实用,如果想了解比较深就不用看我这个了。
核心类java.lang.Class
虽然java的反射相关的包都在java.lang.reflect包下面,但是鄙人认为反射还得从lang包下的Class类说起,因为Java类的结构包括的 静态字段,静态方法,字段,常量等都需要从这个Class上去获取:Class clz = this.getClass(); Field[] fields = clz.getDeclaredFields();//获取到该类所有的字段。包括非静态的。 Field[] fields1 = clz.getFields();//获取到该类所有的静态的字段。
可能很多人都会想java的开发者为什么会这样定义两个方法。 个人理解是既然是类,那类本身属性肯定是只有静态的啊。(毕竟静态是类的,非静态就是对象的了。纯粹个人理解,也为了好记。)所以直接从在类里面所有的申明时才可以获取到非静态的方法。
如何能获取到该类及父类所有的字段
在很多情况下我们可能需要取获取到父类的字段进行操作。这个没什么好的办法,直接一级一级的网上循环PushData pushData = new DayData(); Class clz = pushData.getClass(); while (clz != null) { //todo........... clz = clz.getSuperclass(); }
如何获取字段上的值
在我的这个需求中其实没有必要取获取对应字段的具体指,我只需要获取到该字段上是否有值就好了。后去方法很简单Field field = fields[i] field.setAccessible(true);//一定要设置获取权限,没权限会报异常 Object obj = field.get() ;
JAVA注解
Java的注解也是一个很久远的功能,从java诞生到现在依然是一个用得很多的功能,尤其现在为了减少各种XML,properties的配置,注解就成为了一个香饽饽。
注解的级别
所谓注解的级别,个人理解就是注解在Java编写到运行的过程中那些场景下可以被解读。所以与之对应的编写注解类的时候可指定注解类的级别,对应的注解标签类为:Retention
可选的RetentionPolicy参数包括:- SOURCE:注解将被编译器丢弃
- CLASS:注解在class文件中可用,但会被VM丢弃
- RUNTIME:VM将在运行期间保留注解,因此可以通过反射机制读取注解的信息。
好不好玩? 那猜一下这个Retention是哪个级别的注解.
注解可以被用到哪里
注解我们都用过,比较出名的就是@Override
我们知道在重写方法的时候会用到这个注解,并且用在方法上面。你拿去用到类上面你看看会怎么样,肯定会报错。 这个之后你肯定能理解到注解其实是可以定义他可以放到哪里了吧。
这类我们需要用到一个注解标签@Target
具体由那些地方可以放可以去看注解类java.lang.annotation.ElementType
例如我自定义的注解类:
package xx.yy.;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CheckProperty {
}
是不是很简单,这里我们把他定义到RUNTIME
应该很好理解,毕竟我们需要在虚拟机运行时对主机进行解析嘛。
- 接下来我整体描述下我整体实现的这功能
实体对象
首先我定义一给基础实体类命名为PushData
然后他
哈哈,画不来类图,先用这个代替吧,反正就是我有那么几个实体类DayData(天数据)
/MinuteData(分钟数据)
/HourData(小时数据)
让他们都继承直一个根类PushData
因为小时数据和分钟数据有部分有重叠,所以分钟数据继承直小时数据。
将自定义的字段加到需要统计字段上
//出现时间
@CheckProperty
@JsonProperty("MAXTM")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date MAXTM;
上面的@JsonProperty("MAXTM")
与@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
是fastjson的注解可以将json弄到对应的字段上,@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
就是对时间做一个格式化嘛。这个就不描述了。
统计有效字段代码
/**
*统计上报有效字段
*/
public int getCheckedValidCols() {
int count = 0;
Class clz = this.getClass();
while (clz != null) {
Field[] fields = clz.getDeclaredFields();
for (Field field : fields) {
if (checkedProperty(field)) {
try {
if (null != field.get(this)) {
count++;
}
} catch (Exception d) {
d.printStackTrace();
}
}
}
clz = clz.getSuperclass();
}
return count;
}
private boolean checkedProperty(Field field) {
CheckProperty checkProperty = field.getAnnotation(CheckProperty.class);
if (checkProperty == null) {
return false;
}
try {
field.setAccessible(true);
Object object = field.get(this);
if (object == null) {
return false;
}
} catch (IllegalAccessException e) {
e.printStackTrace();
return false;
}
return true;
}
快速生成接口提交Body 样例
采用fastjson产生json字符串时,默认都回忽略掉空字段。对于产生接口样例麻烦了点。这个操作就为偷点懒。没别的意思
public static void main(String[] args) {
PushData pushData = new DayData();
Class clz = pushData.getClass();
System.out.println("{");
while (clz != null) {
Field[] fields = clz.getDeclaredFields();
for (Field field : fields) {
JsonProperty jsonProperty = field.getAnnotation(JsonProperty.class);
if (jsonProperty != null) {
JsonFormat jsonFormat = field.getAnnotation(JsonFormat.class);
if (null == jsonFormat) {
String name = jsonProperty.value();
System.out.println("\"" + name + "\":\"1.0\",");
} else {
String name = jsonProperty.value();
System.out.println("\"" + name + "\":\"2018-09-18 13:50:23\",");
}
}
}
clz = clz.getSuperclass();
}
System.out.println("}");
}
产生的结果:
{
"FType":"1.0",
"VOL":"1.0",
"SWS":"1.0",
"SWD":"1.0",
"WS":"1.0",
"WD":"1.0",
"BP":"1.0",
"AT":"1.0",
"HU":"1.0",
"WT":"1.0",
"SL":"1.0",
"BG":"1.0",
"BX":"1.0",
"ZQ":"1.0",
"YBG":"1.0",
"YZQ":"1.0",
"TENTHBG":"1.0",
"TENTHZQ":"1.0",
"ZBG":"1.0",
"ZZQ":"1.0",
"RN":"1.0",
"VB":"1.0",
"FS":"1.0",
"WL":"1.0",
"MAXWS":"1.0",
"MAXWD":"1.0",
"MAXTM":"2018-09-13 13:50:23",
"GRTWS":"1.0",
"GRTWD":"1.0",
"GRTTM":"1.0",
"GDC":"1.0",
"DFSD":"1.0",
"SWL":"1.0",
"ZJS":"1.0",
"WARNING":"1.0",
"DT":"2018-09-13 13:50:23",//时间
"CODE":"0002"//站点CODE
}
收工,第一次写,确实写得不怎么样啊,下次努力。