天天看點

java運作時建立對象

使用JDK自帶的反射(java.lang.reflect)或者自省(java.beans.Introspector)運作時建立對象。

有很多場景需要運作時建立對象,比如Copy對象到指定類型的對象中,比如根據指定的字段和值建立指定類型的對像。使用JDK自帶的反射(java.lang.reflect)或者自省(java.beans.Introspector)都可以可實作。自省的方式會對類型資訊做緩存因而效率更加高,推薦優先使用。

  1. copy對象

兩個步驟完成,一是動态建立指定類型的對象,二是擷取據源對象屬性的值并賦到新對象對應的屬性上。

public static void doCopyNotNull(Class<?> clazz, Object source, Object target) {
        List<Field> fieldArray = getAllFields(clazz);
        if (fieldArray != null && fieldArray.size() > 0) {
            for (Field field : fieldArray) {
                try {
//					log.info("set field name=[{}]",field.getName());
                    if (Modifier.isFinal(field.getModifiers())) {
//						log.info("Modifier is final");
                        continue;
                    }
                    field.setAccessible(true);
                    Object value = field.get(source);
//					log.info("the value is [{}]",value);
        
                    if (value != null) {
//						log.info("field name=[{}] do set value [{}]",field.getName(),value);
                        field.set(target, value);
                    }

                } catch (IllegalArgumentException e) {
                    log.error("局部更新失敗", e);
                } catch (IllegalAccessException e) {
                    log.error("局部更新失敗", e);
                }
            }
        }
    }

	public static List<Field> getAllFields(Class clazz) {
        List<Field> resultFieldList = Lists.newArrayList();
        while (clazz != null && clazz != Object.class) {
            Field[] fields = clazz.getDeclaredFields();
            resultFieldList.addAll(Arrays.asList(fields));
            clazz = clazz.getSuperclass();
        }
        return resultFieldList;
    }
    

           
  1. 使用自省建立動态建立對象。

使用場景:建立指定類型的對象并根據key為字段名稱value為對應值的java.util.Map初始化對象,對象完成傳回給調用方。

特别要強調的是指定的java類型需要符合java bean命名規則。

2.1. 擷取指定Class的key為filed的名稱,value為PropertyDescriptor的map

/**
     * 傳回指定key為field名稱,value為PropertyDescriptor的map
     *
     * @param clazz
     * @return
     */
    public static Map<String, PropertyDescriptor> getPropertyDescriptorMap(Class clazz) {
        Map<String, PropertyDescriptor> map = Maps.newHashMap();
        try {
            BeanInfo beanInfo = Introspector.getBeanInfo(clazz);
            PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();

            if (propertyDescriptors != null && propertyDescriptors.length > 0) {
                for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
                    map.put(propertyDescriptor.getName(), propertyDescriptor);
                }
            }
        } catch (IntrospectionException e) {
            e.printStackTrace();
        }
        return map;
    }
           

2.2 . 建立對象并初始化對象

/**
     * 建立指定類型的對象并根據傳入的字段和對應的值給對象指派。
     *
     * @param fieldValueMap
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> T createBean(Map<String, Object> fieldValueMap, Class<T> clazz) {
        if (fieldValueMap == null | fieldValueMap.isEmpty()) {
            return null;
        }

        try {
            //建立對象
            T obj = clazz.newInstance();
            //給對象指派
            Map<String, PropertyDescriptor> propertyDescriptorMap = getPropertyDescriptorMap(clazz);
            fieldValueMap.forEach((k, v) -> {
                if (v == null) {
                    return;
                }

                PropertyDescriptor propertyDescriptor = propertyDescriptorMap.get(k);
                if (propertyDescriptor==null){
                    log.warn("類[{}]中沒有字段[{}],對應的值[{}]将無法設定.請檢查是否有問題.",clazz,k,v);
                    return;
                }
                try {
                    if (propertyDescriptor.getPropertyType() == BigDecimal.class && v.getClass() != BigDecimal.class) {
                        v = new BigDecimal(v.toString());
                    }
                    propertyDescriptor.getWriteMethod().invoke(obj, v);
                } catch (IllegalAccessException | InvocationTargetException e) {
                    new ServiceException("建立對象失敗,異常如下:", e);
                }catch(Throwable e){
                    new ServiceException("建立對象失敗,異常如下:", e);
                }

            });
            return obj;
        } catch (InstantiationException | IllegalAccessException e) {
            new ServiceException("建立對象失敗", e);
        }
        return null;
    }
           
  • fieldValueMap:以property名稱為key值為value的map。
  • clazz:建立對象的類型。

推薦使用自省的方式建立對象,因為自省會緩存類型資訊,進而不用每次調用重新擷取類型資訊,因而可以提高在多次使用時的性能。

檢視源碼如下:

public static BeanInfo getBeanInfo(Class<?> beanClass)
        throws IntrospectionException
    {
        if (!ReflectUtil.isPackageAccessible(beanClass)) {
            return (new Introspector(beanClass, null, USE_ALL_BEANINFO)).getBeanInfo();
        }
        ThreadGroupContext context = ThreadGroupContext.getContext();
        BeanInfo beanInfo;
        synchronized (declaredMethodCache) {
            beanInfo = context.getBeanInfo(beanClass);
        }
        if (beanInfo == null) {
            beanInfo = new Introspector(beanClass, null, USE_ALL_BEANINFO).getBeanInfo();
            synchronized (declaredMethodCache) {
                context.putBeanInfo(beanClass, beanInfo);
            }
        }
        return beanInfo;
    }

           

注意: synchronized (declaredMethodCache) {

beanInfo = context.getBeanInfo(beanClass);

}

首先從緩存中擷取bean資訊,如果不存在才會重新擷取,是以建立多個類型相同的對象時可以大大提高性能 。