1. 程式人生 > >動態修改Java 列舉類的值

動態修改Java 列舉類的值

參考地址:111

package com.itmuch.empty;

import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtField;
import sun.reflect.ConstructorAccessor;
import sun.reflect.FieldAccessor;
import sun.reflect.ReflectionFactory;

import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class DynamicEnumUtil {

    public static void addField(String classname, String[] fields) throws Exception {
        ClassPool pool = ClassPool.getDefault();
        // pool.appendClassPath(new LoaderClassPath(ByteUtil.class.getClassLoader()));
        //		pool.insertClassPath(Thread.currentThread().getContextClassLoader().getResource(".").getFile());
        CtClass cc = pool.get(classname);
        for (String field : fields) {
            field = field.trim().toUpperCase();
            CtField ctf = new CtField(cc, field, cc);
            ctf.setModifiers(Modifier.PUBLIC);
            cc.addField(ctf);
        }
        Class c = cc.toClass();
        addEnum(c, fields, new Class<?>[] {}, new Object[] {});
    }

    public static <T> void addEnum(Class enumType, String[] enumNames, Class<?>[] paramClass, Object[] paramValue) {
        // 0. Sanity checks
        if (!Enum.class.isAssignableFrom(enumType)) {
            throw new RuntimeException("class " + enumType + " is not an instance of Enum");
        }
        // 1. Lookup "$VALUES" holder in enum class and get previous enum instances
        Field valuesField = null;
        Field[] fields = enumType.getDeclaredFields();
        Method[] methods = enumType.getDeclaredMethods();
        for (Field field : fields) {
            if (field.getName().contains("$VALUES")) {
                valuesField = field;
                break;
            }
        }
        AccessibleObject.setAccessible(new Field[] { valuesField }, true);
        try {
            for (String enumName : enumNames) {
                enumName = enumName.trim().toUpperCase();
                // 2. Copy it
                T[] previousValues = (T[]) valuesField.get(enumType);
                List<T> values = new ArrayList<T>(Arrays.asList(previousValues));
                // 3. build new enum
                T newValue = (T) makeEnum(enumType, // The target enum class
                        enumName, // THE NEW ENUM INSTANCE TO BE DYNAMICALLY ADDED
                        values.size(),
                        // new Class<?>[] {}, // could be used to pass values to the enum constuctor if needed
                        paramClass,
                        // new Object[] {}
                        paramValue); // could be used to pass values to the enum constuctor if needed

                // 4. add new value
                values.add(newValue);
                Object object = values.toArray((T[]) Array.newInstance(enumType, 0));
                // 5. Set new values field
                setFailsafeFieldValue(valuesField, null, object);
                // 6. Clean enum cache
                cleanEnumCache(enumType);
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e.getMessage(), e);
        }
    }

    private static Object makeEnum(Class<?> enumClass, String value, int ordinal, Class<?>[] additionalTypes, Object[] additionalValues) throws Exception {
        Object[] parms = new Object[additionalValues.length + 2];
        parms[0] = value;
        parms[1] = Integer.valueOf(ordinal);
        System.arraycopy(additionalValues, 0, parms, 2, additionalValues.length);
        return enumClass.cast(getConstructorAccessor(enumClass, additionalTypes).newInstance(parms));
    }

    private static ConstructorAccessor getConstructorAccessor(Class<?> enumClass, Class<?>[] additionalParameterTypes) throws NoSuchMethodException {
        Class<?>[] parameterTypes = new Class[additionalParameterTypes.length + 2];
        parameterTypes[0] = String.class;
        parameterTypes[1] = int.class;
        System.arraycopy(additionalParameterTypes, 0, parameterTypes, 2, additionalParameterTypes.length);
        return reflectionFactory.newConstructorAccessor(enumClass.getDeclaredConstructor(parameterTypes));
    }

    private static ReflectionFactory reflectionFactory = ReflectionFactory.getReflectionFactory();

    private static void setFailsafeFieldValue(Field field, Object target, Object value) throws NoSuchFieldException, IllegalAccessException {

        // let's make the field accessible
        field.setAccessible(true);
        // next we change the modifier in the Field instance to
        // not be final anymore, thus tricking reflection into
        // letting us modify the static final field
        Field modifiersField = Field.class.getDeclaredField("modifiers");
        modifiersField.setAccessible(true);
        int modifiers = modifiersField.getInt(field);

        // blank out the final bit in the modifiers int
        modifiers &= ~Modifier.FINAL;
        modifiersField.setInt(field, modifiers);

        FieldAccessor fa = reflectionFactory.newFieldAccessor(field, false);
        fa.set(target, value);
    }

    private static void blankField(Class<?> enumClass, String fieldName) throws NoSuchFieldException, IllegalAccessException {
        for (Field field : Class.class.getDeclaredFields()) {
            if (field.getName().contains(fieldName)) {
                AccessibleObject.setAccessible(new Field[] { field }, true);
                setFailsafeFieldValue(field, enumClass, null);
                break;
            }
        }
    }

    private static void cleanEnumCache(Class<?> enumClass) throws NoSuchFieldException, IllegalAccessException {
        blankField(enumClass, "enumConstantDirectory"); // Sun (Oracle?!?) JDK 1.5/6
        blankField(enumClass, "enumConstants"); // IBM JDK
    }

    public static void main(String[] args) throws Exception {
        addField("com.ctrip.framework.apollo.core.enums.Env", new String[] { "mj1", "mj2" });

        Gson gson = new Gson();
        System.out.println(Env.valueOf("MJ1"));
        System.out.println(gson.toJson(Env.values()));
        System.out.println(gson.toJson(Env.valueOf("MJ1")));
        System.out.println(gson.toJson(Env.valueOf("MJ2")));

        //        addEnum(Env.class, "testmj", new Class<?>[]{}, new Object[]{});
        //        for(Env codeInfo:Env.values()){
        //            System.out.println(codeInfo.toString());
        //        }
        //        String test="{\"test1\":\"123\",\"test2\":\"456\"}";
        //        Gson gson = new Gson();
        //        Map<String,String> map = gson.fromJson(test, Map.class);
        //        System.out.println(gson.toJson(Env.valueOf("testmj")));

    }
}