轉自:http://blog.csdn.net/liaodehong/article/details/50379351
背景:
在電商項目中所需要的業務非常多,所以我們的業務數據也會有很多種,這個時候就會有什麼VO,DTO,PO等等這些,把業務和我們的基礎數據進行分離轉換。但是一直都沒有什麼好一點的轉換類。後來用了一下BeanUtils.copyPropertie,和PropertyUtils.copyProperties()的方法,發現其效率非常低。這裏也簡單總結了一下他們的用法及原理以及自己實現的轉換類;
用法:
- BeanUtils.copyProperties("轉換後的類", "要轉換的類");
- PropertyUtils.copyProperties("轉換後的類", "要轉換的類");
原理:
其原理是通過JDK自帶的反射機制動態的去get,set,從而去轉換我們的類。但是要注意一點他們所支持的數據類型,還有一個就是假如一個類裏面又寫了一個類,例如這種:
- public class Name{
- }
- class Name1{
- }
- PropertyDescriptor[] origDescriptors =
- getPropertyDescriptors(orig);
- for (int i = 0; i < origDescriptors.length; i++) {
- String name = origDescriptors[i].getName();
- if (isReadable(orig, name) && isWriteable(dest, name)) {
- try {
- Object value = getSimpleProperty(orig, name);
- if (dest instanceof DynaBean) {
- ((DynaBean) dest).set(name, value);
- } else {
- setSimpleProperty(dest, name, value);
- }
- } catch (NoSuchMethodException e) {
- if (log.isDebugEnabled()) {
- log.debug("Error writing to '" + name + "' on class '" + dest.getClass() + "'", e);
- }
- }
- }
- }
注意差異,
主要的區別在於BeanUtils 提供類型轉換功能,即發現兩個JavaBean的同名屬性爲不同類型時,在支持的數據類型範圍內進行轉換,而前者不支持這個功能,但是速度會更快一些。BeanUtils支持的轉換類型如下:
- * java.lang.BigDecimal
- * java.lang.BigInteger
- * boolean and java.lang.Boolean
- * byte and java.lang.Byte
- * char and java.lang.Character
- * java.lang.Class
- * double and java.lang.Double
- * float and java.lang.Float
- * int and java.lang.Integer
- * long and java.lang.Long
- * short and java.lang.Short
- * java.lang.String
- * java.sql.Date
- * java.sql.Time
- * java.sql.Timestamp
閱讀其源碼,發現其內部是使用了裝飾者模式,我發現Java得工具類很喜歡使用這種模式,而且也十分好用;
他們都使用到了BeanutilsBean和PropertyUtilsBean只不過BeanUtils多了一個轉換的功能而已,但是性能上要比
PropertyUtils慢一些,其實兩個都很慢,最好不要使用;重寫反射轉換:
- /**
- * @param obj 轉換的對象值
- * @param clz 類對象
- * @return 轉換後的對象
- */
- public static<T> T transferObject(Object obj,Class clz){
- T result = null;
- if(obj!=null&&!obj.equals("")){
- Method[] methods = obj.getClass().getMethods();
- try {
- result = (T)clz.newInstance();
- } catch (Exception e1) {
- return null;
- }
- Method m;
- for(int i=0;i<methods.length;i++){
- m = methods[i];
- try {
- if(m.getName().startsWith("set")){
- String fieldName = m.getName().replaceFirst("set", "");
- Method method = result.getClass().getMethod(m.getName(), m.getParameterTypes());
- Method getMethod = obj.getClass().getMethod("get"+fieldName, new Class[]{});
- method.invoke(result, getMethod.invoke(obj, new Object[]{}));
- }
- } catch (Exception e) {
- continue;
- }
- }
- }
- return result;
- }
ReflectASM,高性能的反射:
什麼是ReflectASM ReflectASM是一個很小的java類庫,主要是通過asm生產類來實現java反射,執行速度非常快,看了網上很多和反射的對比,覺得ReflectASM比較神奇,很想知道其原理,下面介紹下如何使用及原理;
- public static void main(String[] args) {
- User user = new User();
- //使用reflectasm生產User訪問類
- MethodAccess access = MethodAccess.get(User.class);
- //invoke setName方法name值
- access.invoke(user, "setName", "張三");
- //invoke getName方法 獲得值
- String name = (String)access.invoke(user, "getName", null);
- System.out.println(name);
- }
上面代碼的確實現反射的功能,代碼主要的核心是 MethodAccess.get(User.class);
看了下源碼,這段代碼主要是通過asm生產一個User的處理類 UserMethodAccess(這個類主要是實現了invoke方法)的ByteCode,然後獲得該對象,通過上面的invoke操作user類。
ASM反射轉換:
- private static Map<Class, MethodAccess> methodMap = new HashMap<Class, MethodAccess>();
- private static Map<String, Integer> methodIndexMap = new HashMap<String, Integer>();
- private static Map<Class, List<String>> fieldMap = new HashMap<Class, List<String>>();
- public static void copyProperties(Object desc, Object orgi) {
- MethodAccess descMethodAccess = methodMap.get(desc.getClass());
- if (descMethodAccess == null) {
- descMethodAccess = cache(desc);
- }
- MethodAccess orgiMethodAccess = methodMap.get(orgi.getClass());
- if (orgiMethodAccess == null) {
- orgiMethodAccess = cache(orgi);
- }
- List<String> fieldList = fieldMap.get(orgi.getClass());
- for (String field : fieldList) {
- String getKey = orgi.getClass().getName() + "." + "get" + field;
- String setkey = desc.getClass().getName() + "." + "set" + field;
- Integer setIndex = methodIndexMap.get(setkey);
- if (setIndex != null) {
- int getIndex = methodIndexMap.get(getKey);
- // 參數一需要反射的對象
- // 參數二class.getDeclaredMethods 對應方法的index
- // 參數對三象集合
- descMethodAccess.invoke(desc, setIndex.intValue(),
- orgiMethodAccess.invoke(orgi, getIndex));
- }
- }
- }
- // 單例模式
- private static MethodAccess cache(Object orgi) {
- synchronized (orgi.getClass()) {
- MethodAccess methodAccess = MethodAccess.get(orgi.getClass());
- Field[] fields = orgi.getClass().getDeclaredFields();
- List<String> fieldList = new ArrayList<String>(fields.length);
- for (Field field : fields) {
- if (Modifier.isPrivate(field.getModifiers())
- && !Modifier.isStatic(field.getModifiers())) { // 是否是私有的,是否是靜態的
- // 非公共私有變量
- String fieldName = StringUtils.capitalize(field.getName()); // 獲取屬性名稱
- int getIndex = methodAccess.getIndex("get" + fieldName); // 獲取get方法的下標
- int setIndex = methodAccess.getIndex("set" + fieldName); // 獲取set方法的下標
- methodIndexMap.put(orgi.getClass().getName() + "." + "get"
- + fieldName, getIndex); // 將類名get方法名,方法下標註冊到map中
- methodIndexMap.put(orgi.getClass().getName() + "." + "set"
- + fieldName, setIndex); // 將類名set方法名,方法下標註冊到map中
- fieldList.add(fieldName); // 將屬性名稱放入集合裏
- }
- }
- fieldMap.put(orgi.getClass(), fieldList); // 將類名,屬性名稱註冊到map中
- methodMap.put(orgi.getClass(), methodAccess);
- return methodAccess;
- }
- }