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 的标签取值原理同理的。

 

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