1.技术点
反射,泛型,Object,递归
2.参考源码
/**
* Created by jeremy.liang on 2018/9/3.
*/
public class SetterUtils {
public static boolean isJavaClass(Class<?> clz) {
//校验类加载器的原理可以细化探究下
return clz != null && clz.getClassLoader() == null;
}
public static Object testReturnSetMethod(Class clz) throws Exception {
//根据名字反射得到object并且实例化,被赋值的object实例
Class<?> objectClz = Class.forName(clz.getName());
Object object = objectClz.newInstance();
//遍历所有方法
for(Method method: object.getClass().getMethods())
{
//只操作相应的的setter方法
if(!method.getName().startsWith("set"))
{
continue;
}
//params - 方法参数List,argArray-参数初值
Parameter[] params = method.getParameters();
Object[] argArray = new Object[params.length];
int i = 0;
//理论上我们的方法只有一个参数要赋值
for(Parameter para :method.getParameters())
{
Class type = para.getType();
//判断参数是否是yoghurt自定义的类型,若是则递归创建内层object实例,若不是则所有是基本类型,直接区分类型设初值就可
if(isJavaClass(para.getType()))
{
if (type.toString().endsWith("String")) {
Object tempObj = new String("testString");
argArray[i++] = tempObj;
}else if(type.toString().endsWith("int") || type.toString().endsWith("Integer")){
Object tempObj = new Integer(222);
argArray[i++] = tempObj;
}else if(type.toString().endsWith("byte") || type.toString().endsWith("Byte")){
Object tempObj = new Byte((byte)1);
argArray[i++] = tempObj;
}else if(type.toString().endsWith("BigDecimal") ){
BigDecimal tempBig = BigDecimal.valueOf(1.11);
Object tempObj =tempBig;
argArray[i++] = tempObj;
}else if(type.toString().endsWith("List")) {
//得到泛型参数类型的方法,可以重点了解下ParameterizedType(继承Type接口)
ParameterizedType parameterizedType = (ParameterizedType) para.getParameterizedType();
//得到泛型<>内的类型,也同样需要是否自定义类的判断并且赋初值
Type typeInOne = parameterizedType.getActualTypeArguments()[0];
if(isJavaClass((Class)typeInOne))
{
Object tempObj2Lst = new Object();
if (typeInOne.toString().endsWith("String")) {
tempObj2Lst = new String("ListString");
}else if(typeInOne.toString().endsWith("int") || typeInOne.toString().endsWith("Integer")){
tempObj2Lst = new Integer(333);
}else if(typeInOne.toString().endsWith("byte") || typeInOne.toString().endsWith("Byte")) {
tempObj2Lst = new Byte((byte) 1);
}
else if(type.toString().endsWith("BigDecimal") ) {
BigDecimal tempBig = BigDecimal.valueOf(1.11);
Object tempObj =tempBig;
argArray[i++] = tempObj;
}
else
{
//TODO 其他数据结构如Map等
}
List<Object> tempLists = new ArrayList<>();
tempLists.add(tempObj2Lst);
argArray[i++] = tempLists;
}
else
{
List<Object> tempLists = new ArrayList<>();
Object tempObj = testReturnSetMethod((Class)typeInOne);
tempLists.add(tempObj);
argArray[i++] = tempLists;
}
}else {
//TODO 其他数据结构如Map等
}
}
else
{
//若方法入参为自定义类型,只需要递归创建实例并赋值返回接口
Object tempObj = testReturnSetMethod(type);
argArray[i++] = tempObj;
}
}
//调用相应setter method实例化即可,并且传入符合的初始化参数,否则报methodNofound方法
method.invoke(object,argArray);
System.out.println(object);
}
return object;
}
}
3.总结
一开始走了一段弯路,思路是:先遍历VO的所有field,得到name/type,判断了name的类型之后,再构造setName的方法,然后再关联其setField的方法,在处理自动装箱等这些方法不好操作,
由于 反射获取方法不支持自动装箱或拆箱,那反射调用方法支持自动装箱或拆箱,并且处理List<>的泛型类参数时候有点绕,其实也可以做到->已实现
但是后来参考了一些网上的思想,再去看了看RPC的实现方法,其实没必要检测Field,对于我的需求来说,我只需要把所有setter方法遍历一遍,然后invoke一次,就能得到效果,并不需要两层转化等关系~~思路更加清晰且容易实现
4.类似swagger、代码迁移脚本,xml配置读取,大致也是按照这些思路