1 import java.lang.reflect.AccessibleObject; 2 import java.lang.reflect.Array; 3 import java.lang.reflect.Field; 4 import java.lang.reflect.Modifier; 5 import java.util.ArrayList; 6 import java.util.Arrays; 7 import java.util.List; 8 9 import sun.reflect.ConstructorAccessor; 10 import sun.reflect.FieldAccessor; 11 import sun.reflect.ReflectionFactory; 12 13 /** 14 * @Description: 动态枚举操作工具 15 * @author wly 16 * @date 2019年11月29日 17 */ 18 @SuppressWarnings("restriction") 19 public class DynamicEnumUtils { 20 private static ReflectionFactory reflectionFactory = ReflectionFactory.getReflectionFactory(); 21 22 private static void setFailsafeFieldValue(Field field, Object target, Object value) 23 throws NoSuchFieldException, IllegalAccessException { 24 25 field.setAccessible(true); 26 27 Field modifiersField = Field.class.getDeclaredField("modifiers"); 28 modifiersField.setAccessible(true); 29 int modifiers = modifiersField.getInt(field); 30 31 modifiers &= ~Modifier.FINAL; 32 modifiersField.setInt(field, modifiers); 33 34 FieldAccessor fa = reflectionFactory.newFieldAccessor(field, false); 35 fa.set(target, value); 36 } 37 38 private static void blankField(Class<?> enumClass, String fieldName) 39 throws NoSuchFieldException, IllegalAccessException { 40 for (Field field : Class.class.getDeclaredFields()) { 41 if (field.getName().contains(fieldName)) { 42 AccessibleObject.setAccessible(new Field[] {field}, true); 43 setFailsafeFieldValue(field, enumClass, null); 44 break; 45 } 46 } 47 } 48 49 private static void cleanEnumCache(Class<?> enumClass) throws NoSuchFieldException, IllegalAccessException { 50 blankField(enumClass, "enumConstantDirectory"); // Sun (Oracle?!?) JDK 1.5/6 51 blankField(enumClass, "enumConstants"); // IBM JDK 52 } 53 54 private static ConstructorAccessor getConstructorAccessor(Class<?> enumClass, Class<?>[] additionalParameterTypes) 55 throws NoSuchMethodException { 56 Class<?>[] parameterTypes = new Class[additionalParameterTypes.length + 2]; 57 parameterTypes[0] = String.class; 58 parameterTypes[1] = int.class; 59 System.arraycopy(additionalParameterTypes, 0, parameterTypes, 2, additionalParameterTypes.length); 60 return reflectionFactory.newConstructorAccessor(enumClass.getDeclaredConstructor(parameterTypes)); 61 } 62 63 private static Object makeEnum(Class<?> enumClass, String value, int ordinal, Class<?>[] additionalTypes, 64 Object[] additionalValues) throws Exception { 65 Object[] parms = new Object[additionalValues.length + 2]; 66 parms[0] = value; 67 parms[1] = Integer.valueOf(ordinal); 68 System.arraycopy(additionalValues, 0, parms, 2, additionalValues.length); 69 return enumClass.cast(getConstructorAccessor(enumClass, additionalTypes).newInstance(parms)); 70 } 71 72 @SuppressWarnings("unchecked") 73 public static <T extends Enum<?>> void addEnum(Class<T> enumType, String enumName, Class<?>[] additionalTypes, 74 Object[] additionalValues) { 75 76 // 判断该枚举类是否继承自枚举Enum 77 if (!Enum.class.isAssignableFrom(enumType)) { 78 throw new RuntimeException("class " + enumType + " is not an instance of Enum"); 79 } 80 81 // 1. Lookup "$VALUES" holder in enum class and get previous enum instances 82 Field valuesField = null; 83 Field[] fields = enumType.getDeclaredFields(); 84 for (Field field : fields) { 85 if (field.getName().contains("$VALUES")) { 86 valuesField = field; 87 break; 88 } 89 } 90 AccessibleObject.setAccessible(new Field[] {valuesField}, true); 91 92 try { 93 94 // 2. Copy it 95 T[] previousValues = (T[])valuesField.get(enumType); 96 List<T> values = new ArrayList<T>(Arrays.asList(previousValues)); 97 98 // 3. build new enum 99 T newValue = (T)makeEnum(enumType, enumName, values.size(), additionalTypes, additionalValues); 100 101 // 4. add new value 102 values.add(newValue); 103 104 // 5. Set new values field 105 setFailsafeFieldValue(valuesField, null, values.toArray((T[])Array.newInstance(enumType, 0))); 106 107 // 6. Clean enum cache 108 cleanEnumCache(enumType); 109 110 } catch (Exception e) { 111 throw new RuntimeException(e.getMessage(), e); 112 } 113 } 114 }