獲取物件屬性型別、屬性名稱、屬性值的研究:反射和JEXL解析引擎

夏雪冬日發表於2017-05-16

 

同步釋出:http://www.yuanrengu.com/index.php/20170511.html

先簡單介紹下反射的概念:java反射機制是在執行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個物件,都能夠呼叫它的任意方法和屬性;這種動態獲取資訊以及動態呼叫物件方法的功能稱為java語言的反射機制。

反射是java中一種強大的工具,能夠使我們很方便的建立靈活的程式碼,這些程式碼可以在執行時裝配。在實際的業務中,可能會動態根據屬性去獲取值。

工具類如下:

 

package com.yaoguang.common.utils.field;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 實體屬性操作工具類
 * 
 * @author heyonggang
 * @date 2017年5月10日下午5:56:59
 */
public class ObjectFieldUtil {

	private static Logger log = LoggerFactory.getLogger(ObjectFieldUtil.class);

	/**
	 * 根據屬性名獲取屬性值
	 * 
	 * @param fieldName  欄位名
	 * @param o 實體
	 * @return
	 */
	public static Object getFieldValueByName(String fieldName, Object o) {
		try {
			String firstLetter = fieldName.substring(0, 1).toUpperCase();
			String getter = "get" + firstLetter + fieldName.substring(1);
			Method method = o.getClass().getMethod(getter, new Class[] {});
			Object value = method.invoke(o, new Object[] {});
			return value;
		} catch (Exception e) {
			log.error(e.getMessage(), e);
			return null;
		}
	}

	/**
	 * 獲取屬性名陣列
	 * 
	 * @param o 實體
	 * @return
	 */
	public static String[] getFiledName(Object o) {
		Field[] fields = o.getClass().getDeclaredFields();
		String[] fieldNames = new String[fields.length];
		for (int i = 0; i < fields.length; i++) {
			System.out.println(fields[i].getType());
			fieldNames[i] = fields[i].getName();
		}
		return fieldNames;
	}

	/**
	 * 獲取屬性型別(type),屬性名(name),屬性值(value)的map組成的list
	 * 
	 * @param o 實體
	 * @return
	 */
	public static List<Map<String, Object>> getFiledsInfo(Object o) {
		Field[] fields = o.getClass().getDeclaredFields();
//		String[] fieldNames = new String[fields.length];
		List<Map<String, Object>> list = new ArrayList<>();
		Map<String, Object> infoMap = null;
		for (int i = 0; i < fields.length; i++) {
			infoMap = new HashMap<String, Object>();
			infoMap.put("type", fields[i].getType().toString());
			infoMap.put("name", fields[i].getName());
			infoMap.put("value", getFieldValueByName(fields[i].getName(), o));
			list.add(infoMap);
		}
		return list;
	}

	/**
	 * 獲取物件的所有屬性值,返回一個物件陣列
	 * 
	 * @param o  實體
	 * @return
	 */
	public static Object[] getFiledValues(Object o) {
		String[] fieldNames = getFiledName(o);
		Object[] value = new Object[fieldNames.length];
		for (int i = 0; i < fieldNames.length; i++) {
			value[i] = getFieldValueByName(fieldNames[i], o);
		}
		return value;
	}

	/**
	 * 根據物件屬性名設定屬性值
	 * 
	 * @param fieldName 欄位名
	 * @param value 欄位值
	 * @param o 實體
	 * @return
	 */
	public static void setFieldValueByName(String fieldName, Object o,Object value) {
		try {
			BeanInfo obj =Introspector.getBeanInfo(o.getClass(), Object.class);
			PropertyDescriptor[] pds = obj.getPropertyDescriptors();
			for (PropertyDescriptor pd : pds) {
				if(pd.getName().equals(fieldName)){
					pd.getWriteMethod().invoke(o, value);
					break;
				}
			}
		} catch (Exception e) {
			log.error(e.getMessage(), e);
		}
	}
}

 

 測試用例如下:

/**
	 * 根據實體和屬性名獲取值
	 */
	@Test
	public void testGetField(){
		TruckBills truckBills = iTruckBillsService.geTruckBills("02cb5069b44f45dca578e5ada08bf513", "88");
		
		String orderSn = (String) ObjectFieldUtil.getFieldValueByName("orderSn", truckBills);
		String shipper = (String) ObjectFieldUtil.getFieldValueByName("shipper", truckBills);
		
		String[] fieldNames = ObjectFieldUtil.getFiledName(truckBills);
		
		List<Map<String, Object>> listMap = ObjectFieldUtil.getFiledsInfo(truckBills);
		
		System.out.println("---------------------------");
		System.out.println(orderSn);
		System.out.println(shipper);
		System.out.println(Arrays.toString(fieldNames));
		for (Map<String, Object> map : listMap) {
			System.out.println("---------------------------");
			Set<Entry<String, Object>> entrySet = map.entrySet();
			for (Entry<String, Object> entry : entrySet) {
				System.out.println(entry.getKey() + "-----" + entry.getValue());
			}
			System.out.println("---------------------------");
		}
	}

 

還有一種將字串轉換成java程式碼並執行的方法:Java Expression Language (JEXL) 是一個表示式語言引擎,可以用來在應用或者框架中使用

JEXL受Velocity和JSP 標籤庫 1.1 (JSTL) 的影響而產生的,需要注意的是,JEXL 並不時 JSTL 中的表示式語言的實現。

需要先新增jar包,maven配置如下:

 

		<dependency>
			<groupId>org.apache.commons</groupId>
			<artifactId>commons-jexl</artifactId>
			<version>2.0</version>
		</dependency>

 

 核心程式碼如下:

public class DyMethodUtil {
	
	/**
	 * 將字串轉換成java程式碼並執行
	 * 
	 * @param jexlExp 需要轉換的字串
	 * @param map 引數集合
	 * @return 方法執行結果
	 * 如:
	 * String jexlExp="testService.save(person)"; 
	 * map.put("testService",testService);  
	 * map.put("person",person);  
	 */
	public static Object invokeMethod(String jexlExp, Map<String, Object> map) {
		JexlEngine jexl = new JexlEngine();
		Expression e = jexl.createExpression(jexlExp);
		JexlContext jc = new MapContext();
		for (String key : map.keySet()) {
			jc.set(key, map.get(key));
		}
		if (null == e.evaluate(jc)) {
			return "";
		}
		return e.evaluate(jc);
	}
}

 測試示例如下:

/**
	 * 動態構建
	 */
	@Test
	@Rollback(false)
	public void testTemple(){
		//1.拿到結果集
		//2.構建語言表示式
		//3.動態構建
		
		TruckBills truckBills = iTruckBillsService.geTruckBills("02cb5069b44f45dca578e5ada08bf513", "88");
		
		List<TruckGoodsAddr> truckGoodsAddrs = truckBills.getTruckGoodsAddrs();
		TruckOther truckOther = truckBills.getTruckOther();
		
		Map<String, Object> map = new HashMap<>();
		map.put("truckBills", truckBills);
		
		System.out.println("------------------------");
		System.out.println(JsonBinder.buildNormalBinder().toJson(map));
		System.out.println("------------------------");
		
		String expression = "truckBills.getTruckGoodsAddrs().get(0).getBillsId()";
		
		Object aa = DyMethodUtil.invokeMethod(expression, map);
		System.out.println("------------------------");
		System.out.println(JsonBinder.buildNormalBinder().toJson(aa));
		System.out.println("------------------------");
	}

 

相關文章