java 反射多级调用实现原理 java EL表达式多级调用实现原理
一、发现问题
1、在EL表达式中显示数据,使用的是 “对象名.属性名”的格式来实现,若存在对象的多级嵌套,依旧是:"对象名.对象名....xx.属性名",其实后台是反射的原理来实现取值的,那么具体是怎么实现的呢?
二、代码理解
1、创建一个 Student 学生类,里面有学校School 类,而School类中有班级 Grade 类
2、创建代码分别如下:
public class Student {
private String name ;
private int age ;
private School school;
// ignore default getter/setter/toString
}
public class School {
private String name ; // 学校名称
private String address ; // 学校地址
private Grade grade ; // 学校的班级
// ignore default getter/setter/toString
}
public class Grade {
private String name ; // 班级名称
private int count ; // 班级学生数量
// ignore default getter/setter/toString
}
3、创建 Reflects 工具类,实现反射多级调用
import java.lang.reflect.Method;
import org.apache.commons.lang3.StringUtils;
/**
* description: Reflects 工具类,实现多级调用
* @version v1.0
* @author w
* @date 2020年3月30日上午10:49:35
**/
public class Reflects {
/**
* 构造方法私有化
*/
@SuppressWarnings("unused")
private Reflects INSTANCE = new Reflects();
/**
* getter 方法前缀
*/
private static final String SETTER_PREFIX = "set";
/**
* setter 方法前缀
*/
private static final String GETTER_PREFIX = "get";
/**
* 分隔符
*/
private static final String SEPARATOR = "." ;
/**
* description: 反射方式调用getter 方法
* @param obj 对象
* @param propertyName 方法名/属性名
* @return Object 返回值
* @version v1.0
* @author w
* @date 2020年3月30日 上午11:18:10
*/
public static Object invokeGetter (Object obj , String propertyName) {
Object result = obj ;
String[] split = StringUtils.split(propertyName, SEPARATOR);
for (String m : split) {
String getMethodName = GETTER_PREFIX.concat(StringUtils.capitalize(m));
Method method = getMethod(result, getMethodName, new Class[] {});
try {
result = method.invoke(result , new Object[] {});
} catch (Exception e) {
e.printStackTrace();
}
}
return result;
}
/**
* description: 反射方法调用setter方法
* @param obj
* @param propertyName
* @param value
* @return void
* @version v1.0
* @author w
* @date 2020年3月30日 上午11:18:03
*/
public static void invokeSetter(Object obj , String propertyName , Object value) {
Object object = obj ;
String[] split = StringUtils.split(propertyName, SEPARATOR);
for(int i = 0; i < split.length ; i++) {
if(i < split.length -1) {
// 若存在多级调用,需要先获取层级对象的值
String getMethodName = GETTER_PREFIX.concat(StringUtils.capitalize(split[i]));
Method method = getMethod(object, getMethodName, new Class[] {});
try {
object= method.invoke(object, new Object[] {});
} catch (Exception e) {
e.printStackTrace();
}
}else {
String setMethodName = SETTER_PREFIX.concat(StringUtils.capitalize(split[i]));
Method method = getMethod(object, setMethodName, new Class[] {value.getClass()});
try {
method.invoke(object, value);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
/**
* description: 通过反射方式获取方法 method 对象
* @param obj 对象实例
* @param methodName 方法名
* @param parameterTypes 方法参数
* @return Method
* @version v1.0
* @author w
* @date 2020年3月30日 上午11:05:53
*/
public static Method getMethod(Object obj ,String methodName , Class<?>... parameterTypes) {
try {
Method declaredMethod = obj.getClass().getDeclaredMethod(methodName, parameterTypes);
// 获取方法访问权限
declaredMethod.setAccessible(true);
return declaredMethod;
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
}
return null;
}
}
4、测试 ReflectsTest 测试功能
import org.junit.Test;
import com.runcode.reflect.Grade;
import com.runcode.reflect.School;
import com.runcode.reflect.Student;
/**
* description: ReflectsTest 测试
* @version v1.0
* @author w
* @date 2020年3月30日上午11:08:28
**/
public class ReflectsTest {
/**
* description: 测试 --- 多级对象调用获取数据
* @return void
* @version v1.0
* @author w
* @date 2020年3月30日 下午5:31:18
*/
@Test
public void getterTest() {
Student student = init();
System.out.println("==========小明信息如下:==============");
System.out.println(student);
// 比如在 EL表达式中获取,学校信息: ${student.school.name}
Object invokeGetter = Reflects.invokeGetter(student, "school.name");
System.out.println(invokeGetter);
// 比如在EL表达式中获取,班级中的人数: ${student.school.grade.count}
Object invokeGetter2 = Reflects.invokeGetter(student, "school.grade.count");
System.out.println(invokeGetter2);
}
/**
* description: 测试 --- 多级对象中赋值
* @return void
* @version v1.0
* @author w
* @date 2020年3月30日 下午5:31:47
*/
@Test
public void setterTest() {
Student student = init();
System.out.println(student);
// 修改班级信息 --- student.school.grade.name = "六年级【三班】"
Reflects.invokeSetter(student, "school.grade.name", "六年级【三班】");
System.out.println(student);
}
public Student init() {
// 创建 Grade 班级对象信息
Grade grade = new Grade();
grade.setName("三年级【二班】");
grade.setCount(45);
// 创建 School 学校对象信息
School school = new School();
school.setName("西虹市中心小学");
school.setGrade(grade);
// 创建 Student 学生对象信息
Student student = new Student();
student.setName("小明");
student.setAge(10);
student.setSchool(school);
return student ;
}
}
5、测试结果:【略】 (满足预期要求的)
三、总结
1、以上代码基本能满足EL表达式中的"对象名.属性名"的取值方法,但是没有考虑子父类的继承情况,能否正常取值,只能作为简单的原理理解,若作为工具类,还需进一步完善。
2、SpringMVC 的标签取值原理同理的。