Skip to content

Instantly share code, notes, and snippets.

@honwhy
Created June 12, 2022 09:23
Show Gist options
  • Save honwhy/8f86ed2c2663624750138c83ff9359f7 to your computer and use it in GitHub Desktop.
Save honwhy/8f86ed2c2663624750138c83ff9359f7 to your computer and use it in GitHub Desktop.
copy Annotation to Map
package com.example.br;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
@MyTable("t_table")
public class AnnotatedMap<K, V> extends HashMap<K,V> {
public AnnotatedMap() {
}
public AnnotatedMap(Map<K, V> m) {
for (Entry<K, V> kvEntry : m.entrySet()) {
put(kvEntry.getKey(), kvEntry.getValue());
}
//super(m);
}
@MyField("t_field")
private String field;
@Override
public V get(Object key) {
// declared fields with MyField annotation should be cached
// as define by bytecode tech
// Field[] declaredFields = this.getClass().getDeclaredFields();
// for (Field f : declaredFields) {
// if (f.isAnnotationPresent(MyField.class)) {
// if (f.getName().equals(key)) {
// f.setAccessible(true);
// try {
// return (V) f.get(this);
// } catch (IllegalAccessException e) {
// return super.get(key);
// }
// }
// }
// }
if ("field".equals(key)) {
return (V)"abc1";
}
return super.get(key);
}
@Override
public V put(K key, V value) {
System.out.println("put k-v");
if ("field".equals(key)) {
this.field = String.valueOf(value);
}
return super.put(key, value);
}
}
package com.example.br;
import cn.hutool.core.util.HexUtil;
import javassist.*;
import javassist.bytecode.AnnotationsAttribute;
import javassist.bytecode.ConstPool;
import javassist.bytecode.annotation.Annotation;
import javassist.bytecode.annotation.StringMemberValue;
import org.springframework.core.annotation.AnnotationUtils;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class BridgeUtil {
static Map<Class<?>, Class<?>> clazzMap = new HashMap<>();
public static Class<?> buildClass(Class<?> clazz) {
MyTable annotation = AnnotationUtils.getAnnotation(clazz, MyTable.class);
if (annotation == null) {
return null;
}
Class<?> newMapClazz = null;
synchronized (clazzMap) {
newMapClazz = clazzMap.get(clazz);
if (newMapClazz == null) {
try {
ClassPool cp = ClassPool.getDefault();
String packageName = clazz.getPackage().getName();
String part = HexUtil.toHex(System.currentTimeMillis() / 1000);
CtClass ctClazz = cp.makeClass(packageName + ".Td" + part);
CtClass superClazz = cp.get(HashMap.class.getName());
ctClazz.setSuperclass(superClazz);
CtConstructor c1 = new CtConstructor(new CtClass[0], ctClazz);
c1.setBody("super();");
c1.setModifiers(Modifier.PUBLIC);
ctClazz.addConstructor(c1);
CtConstructor c2 = new CtConstructor(new CtClass[]{cp.get(Map.class.getName())}, ctClazz);
c2.setBody("super($1);");
c2.setModifiers(Modifier.PUBLIC);
ctClazz.addConstructor(c2);
ConstPool constPool = ctClazz.getClassFile().getConstPool();
// copy MyTable annotation
AnnotationsAttribute clzAnnotationsAttr = new AnnotationsAttribute(constPool, AnnotationsAttribute.visibleTag);
Annotation tableAnnotation = new Annotation(MyTable.class.getName(), constPool);
tableAnnotation.addMemberValue("value", new StringMemberValue(annotation.value(), constPool));
clzAnnotationsAttr.addAnnotation(tableAnnotation);
ctClazz.getClassFile().addAttribute(clzAnnotationsAttr);
// copy MyField annotation
Field[] declaredFields = clazz.getDeclaredFields();
List<Field> cacheField = new ArrayList<>();
for (Field f : declaredFields) {
if (f.isAnnotationPresent(MyField.class)) {
String name = f.getName();
String typeName = f.getType().getTypeName();
CtField ctField = CtField.make("private " + typeName + " " + name + ";", ctClazz);
AnnotationsAttribute fieldAnnotationsAttr = new AnnotationsAttribute(constPool, AnnotationsAttribute.visibleTag);
Annotation fieldAnnotation = new Annotation(MyField.class.getName(), constPool);
MyField myField = AnnotationUtils.getAnnotation(f, MyField.class);
fieldAnnotation.addMemberValue("value", new StringMemberValue(myField.value(), constPool));
fieldAnnotationsAttr.addAnnotation(fieldAnnotation);
ctField.getFieldInfo().addAttribute(fieldAnnotationsAttr);
ctClazz.addField(ctField);
cacheField.add(f);
}
}
// bridge method
CtMethod putMethod = new CtMethod(cp.get(Object.class.getName()), "put", new CtClass[]{cp.get(Object.class.getName()),cp.get(Object.class.getName())}, ctClazz);
String bodyStr = "";
for (Field f : cacheField) {
String name = f.getName();
String type = f.getType().getTypeName();
String str = "else if (\"" + name + "\".equals($1)) {";
str += "this." + name + "=(" + type + ")$2;";
str += "}";
bodyStr += str;
}
bodyStr = bodyStr.replaceFirst("else ", "");
bodyStr += "return super.put($1, $2);";
putMethod.setBody( "{" + bodyStr + "}");
AnnotationsAttribute putMethodAttr = new AnnotationsAttribute(constPool, AnnotationsAttribute.visibleTag);
Annotation overrideAnno = new Annotation(Override.class.getName(), constPool);
putMethodAttr.addAnnotation(overrideAnno);
putMethod.getMethodInfo().addAttribute(putMethodAttr);
ctClazz.addMethod(putMethod);
CtMethod getMethod = new CtMethod(cp.get(Object.class.getName()), "get", new CtClass[]{cp.get(Object.class.getName())}, ctClazz);
String bodyStr2 = "";
for (Field f : cacheField) {
String name = f.getName();
String str = "else if (\"" + name + "\".equals($1)) {";
str += "return this." + name + ";";
str += "}";
bodyStr2 += str;
}
bodyStr2 = bodyStr2.replaceFirst("else ", "");
bodyStr2 += "return super.get($1);";
getMethod.setBody( "{" + bodyStr2 + "}");
AnnotationsAttribute getMethodAttr = new AnnotationsAttribute(constPool, AnnotationsAttribute.visibleTag);
//Annotation overrideAnno = new Annotation(Override.class.getName(), constPool);
getMethodAttr.addAnnotation(overrideAnno);
getMethod.getMethodInfo().addAttribute(getMethodAttr);
ctClazz.addMethod(getMethod);
newMapClazz = ctClazz.toClass();
clazzMap.put(clazz, newMapClazz);
} catch (NotFoundException | CannotCompileException e) {
return null;
}
}
}
return newMapClazz;
}
public static Map<String, Object> getMap(Map<String, Object> oldMap, Class<?> clazz) {
MyTable annotation = AnnotationUtils.getAnnotation(clazz, MyTable.class);
if (annotation == null) {
return oldMap;
}
Class<?> newMapClazz = null;
synchronized (clazzMap) {
newMapClazz = clazzMap.get(clazz);
if (newMapClazz == null) {
try {
ClassPool cp = ClassPool.getDefault();
String packageName = clazz.getPackage().getName();
String part = HexUtil.toHex(System.currentTimeMillis() / 1000);
CtClass ctClazz = cp.makeClass(packageName + ".Td" + part);
CtClass superClazz = cp.get(HashMap.class.getName());
ctClazz.setSuperclass(superClazz);
CtConstructor c1 = new CtConstructor(new CtClass[0], ctClazz);
c1.setBody("super();");
c1.setModifiers(Modifier.PUBLIC);
ctClazz.addConstructor(c1);
CtConstructor c2 = new CtConstructor(new CtClass[]{cp.get(Map.class.getName())}, ctClazz);
c2.setBody("super($1);");
c2.setModifiers(Modifier.PUBLIC);
ctClazz.addConstructor(c2);
ConstPool constPool = ctClazz.getClassFile().getConstPool();
// copy MyTable annotation
AnnotationsAttribute clzAnnotationsAttr = new AnnotationsAttribute(constPool, AnnotationsAttribute.visibleTag);
Annotation tableAnnotation = new Annotation(MyTable.class.getName(), constPool);
tableAnnotation.addMemberValue("value", new StringMemberValue(annotation.value(), constPool));
clzAnnotationsAttr.addAnnotation(tableAnnotation);
ctClazz.getClassFile().addAttribute(clzAnnotationsAttr);
// copy MyField annotation
Field[] declaredFields = clazz.getDeclaredFields();
List<Field> cacheField = new ArrayList<>();
for (Field f : declaredFields) {
if (f.isAnnotationPresent(MyField.class)) {
String name = f.getName();
String typeName = f.getType().getTypeName();
CtField ctField = CtField.make("private " + typeName + " " + name + ";", ctClazz);
AnnotationsAttribute fieldAnnotationsAttr = new AnnotationsAttribute(constPool, AnnotationsAttribute.visibleTag);
Annotation fieldAnnotation = new Annotation(MyField.class.getName(), constPool);
MyField myField = AnnotationUtils.getAnnotation(f, MyField.class);
fieldAnnotation.addMemberValue("value", new StringMemberValue(myField.value(), constPool));
fieldAnnotationsAttr.addAnnotation(fieldAnnotation);
ctField.getFieldInfo().addAttribute(fieldAnnotationsAttr);
ctClazz.addField(ctField);
cacheField.add(f);
}
}
// bridge method
CtMethod putMethod = new CtMethod(cp.get(Object.class.getName()), "put", new CtClass[]{cp.get(Object.class.getName()),cp.get(Object.class.getName())}, ctClazz);
String bodyStr = "";
for (Field f : cacheField) {
String name = f.getName();
String type = f.getType().getTypeName();
String str = "else if (\"" + name + "\".equals($1)) {";
str += "this." + name + "=(" + type + ")$2;";
str += "}";
bodyStr += str;
}
bodyStr = bodyStr.replaceFirst("else ", "");
bodyStr += "return super.put($1, $2);";
putMethod.setBody( "{" + bodyStr + "}");
ctClazz.addMethod(putMethod);
CtMethod getMethod = new CtMethod(cp.get(Object.class.getName()), "get", new CtClass[]{cp.get(Object.class.getName())}, ctClazz);
String bodyStr2 = "";
for (Field f : cacheField) {
String name = f.getName();
String str = "else if (\"" + name + "\".equals($1)) {";
str += "return this." + name + ";";
str += "}";
bodyStr2 += str;
}
bodyStr2 = bodyStr2.replaceFirst("else ", "");
bodyStr2 += "return super.get($1);";
getMethod.setBody( "{" + bodyStr2 + "}");
ctClazz.addMethod(getMethod);
newMapClazz = ctClazz.toClass();
clazzMap.put(clazz, newMapClazz);
} catch (NotFoundException | CannotCompileException e) {
return oldMap;
}
}
}
if (newMapClazz != null) {
try {
Constructor<?> constructor = newMapClazz.getConstructor(Map.class);
Object object = constructor.newInstance(oldMap);
return (Map<String, Object>) object;
} catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
}
return oldMap;
}
}
package com.example.br;
import cn.hutool.json.JSONUtil;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
public class CheckAnnotatedMap {
public static void main(String[] args) throws IllegalAccessException {
Map<String, Object> map = new HashMap<>();
map.put("field", "abc");
map.put("foo", "bar");
System.out.println(JSONUtil.toJsonStr(map));
AnnotatedMap<String, Object> map1 = new AnnotatedMap<>(map);
Field declaredField = map1.getClass().getDeclaredFields()[0];
declaredField.setAccessible(true);
declaredField.set(map1, "hellomix");
map = map1;
System.out.println("key=field,value=" + map.get("field"));
System.out.println("key=foo,value=" + map.get("foo"));
}
}
package com.example.br;
import cn.hutool.core.util.HexUtil;
import cn.hutool.json.JSONUtil;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;
public class CheckBridge {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
Map<String, Object> map = new HashMap<>();
map.put("field", "abc");
map.put("foo", "bar");
System.out.println(JSONUtil.toJsonStr(map));
//map = BridgeUtil.getMap(map, TdTable.class);
Class<?> clazz = BridgeUtil.buildClass(TdTable.class);
Constructor<?> constructor = clazz.getConstructor();
Object instance = constructor.newInstance();
Map<String,Object> map1 = (Map<String, Object>) instance;
for (Map.Entry<String, Object> entry : map.entrySet()) {
map1.put(entry.getKey(), entry.getValue());
}
for (Field field : clazz.getDeclaredFields()) {
field.setAccessible(true);
Object oldValue = field.get(instance);
oldValue += HexUtil.toHex(System.currentTimeMillis() / 1000);
field.set(instance, oldValue);
}
System.out.println("key=field,value=" + map1.get("field"));
System.out.println("key=foo,value=" + map1.get("foo"));
}
}
package com.example.br;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface MyField {
String value() default "";
}
package com.example.br;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTable {
String value() default "";
}
package com.example.br;
@MyTable("t_table")
public class TdTable {
@MyField("t_field")
private String field;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment