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 的標籤取值原理同理的。