java 反射多級調用實現原理 java EL表達式多級調用實現原理

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

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章