package jef.accelerator.bean; import java.io.File; import java.io.IOException; import java.lang.annotation.Annotation; import java.util.Arrays; import java.util.Comparator; import java.util.HashSet; import java.util.IdentityHashMap; import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; import jef.accelerator.GeeField; import jef.tools.IOUtils; import jef.tools.reflect.BeanUtils; import jef.tools.reflect.ClassEx; import jef.tools.reflect.FieldEx; import jef.tools.reflect.MethodEx; import jef.tools.reflect.UnsafeUtils; /** * 用于生成动态访问者类(Accessor)的类工厂。 * * @author jiyi * */ final class ASMAccessorFactory implements BeanAccessorFactory { @SuppressWarnings("rawtypes") private static final Map<Class, BeanAccessor> map = new IdentityHashMap<Class, BeanAccessor>(); public BeanAccessor getBeanAccessor(Class<?> javaBean) { if (javaBean.isPrimitive()) { throw new IllegalArgumentException(javaBean + " invalid!"); } BeanAccessor ba = map.get(javaBean); if (ba != null) return ba; ba = generateAccessor(javaBean); synchronized (map) { map.put(javaBean, ba); } return ba; } private BeanAccessor generateAccessor(Class<?> javaClz) { String clzName = javaClz.getName().replace('.', '_'); ClassLoader cl = Thread.currentThread().getContextClassLoader(); if (cl == null) cl = BeanAccessorFactory.class.getClassLoader(); Class<?> cls = null; try { cls = cl.loadClass(clzName); } catch (ClassNotFoundException e1) { } FieldInfo[] fields = getFields(javaClz); boolean isHashProperty = sortFields(fields); ClassGenerator asm; byte[] clzdata = null; if (cls == null) { if (isHashProperty) { asm = new ASMHashGenerator(javaClz, clzName, fields, cl); } else { asm = new ASMSwitcherGenerator(javaClz, clzName, fields,cl); } clzdata = asm.generate(); // DEBUG //saveClass(clzdata, clzName); cls = UnsafeUtils.defineClass(clzName, clzdata, 0, clzdata.length, cl); if (cls == null) { throw new RuntimeException("Dynamic class accessor for " + javaClz + " failure!"); } } try { BeanAccessor ba = (BeanAccessor) cls.newInstance(); initAnnotations(ba, fields); initGenericTypes(ba, fields); return ba; } catch (Error e) { if (clzdata != null) { saveClass(clzdata, clzName); } throw e; } catch (Exception e) { if (clzdata != null) { saveClass(clzdata, clzName); } throw new RuntimeException(e); } } /* * 保存文件 * * @param mclz * * @throws IOException * * @throws CannotCompileException */ protected static void saveClass(byte[] data, String name) { File file = new File("c:/test/", name + ".class"); try { IOUtils.saveAsFile(file, data); } catch (IOException e) { e.printStackTrace(); } System.out.println(file.getAbsolutePath() + " was generated to debug."); } /* * 计算全部要反射代理的字段信息 * * @param javaBean * * @return */ private static FieldInfo[] getFields(Class<?> javaBean) { ClassEx cw = new ClassEx(javaBean); FieldEx[] fs = cw.getFields(); Map<String, FieldInfo> result = new LinkedHashMap<String, FieldInfo>(fs.length); for (FieldEx f : fs) { GeeField fieldAnno = f.getAnnotation(GeeField.class); if (fieldAnno != null && fieldAnno.ignore()) { continue; } MethodEx getter = BeanUtils.getGetter(f); MethodEx setter = BeanUtils.getSetter(f); if (getter == null || setter == null) { continue; } FieldInfo fi = new FieldInfo(); fi.setGetter(getter.getJavaMethod()); fi.setSetter(setter.getJavaMethod()); String name = (fieldAnno != null && fieldAnno.name().length() > 0) ? fieldAnno.name() : f.getName(); fi.setName(name); fi.setType(f.getGenericType()); fi.setAnnoOnField(processAnnotations(f)); fi.setAnnoOnGetter(processAnnotations(getter)); fi.setAnnoOnSetter(processAnnotations(setter)); result.put(fi.getName(), fi); } MethodEx[] methods = cw.getMethods(); for (MethodEx m : methods) { GeeField field = m.getAnnotation(GeeField.class); if (field==null || field.ignore()) continue; String name = field.name(); if (result.containsKey(name)) continue; MethodEx setter; MethodEx getter; if (m.getParameterTypes().length == 1) {// set setter = m; getter = find(name, methods, true); } else if (m.getParameterTypes().length == 0) {// get getter = m; setter = find(name, methods, false); } else { throw new IllegalArgumentException("The @GeeField annotated on unknown method: " + m.getName()); } if (setter == null || getter == null) { continue; } FieldInfo fi = new FieldInfo(); fi.setGetter(getter.getJavaMethod()); fi.setSetter(setter.getJavaMethod()); fi.setName(name); fi.setType(getter.getGenericReturnType()); fi.setAnnoOnGetter(processAnnotations(getter)); fi.setAnnoOnSetter(processAnnotations(setter)); } return result.values().toArray(new FieldInfo[result.size()]); } private static IdentityHashMap<Class<?>, Annotation> processAnnotations(MethodEx getter) { Annotation[] anno = getter.getAnnotations(); if (anno == null || anno.length == 0) { return null; } else { IdentityHashMap<Class<?>, Annotation> amap = new IdentityHashMap<Class<?>, Annotation>(); for (Annotation a : anno) { amap.put(a.getClass(), a); } return amap; } } private static IdentityHashMap<Class<?>, Annotation> processAnnotations(FieldEx f) { Annotation[] anno = f.getAnnotations(); if (anno == null || anno.length == 0) { return null; } else { IdentityHashMap<Class<?>, Annotation> amap = new IdentityHashMap<Class<?>, Annotation>(); for (Annotation a : anno) { amap.put(a.annotationType(), a); } return amap; } } private static MethodEx find(String name, MethodEx[] methods, boolean getter) { for (MethodEx m : methods) { GeeField gf = m.getAnnotation(GeeField.class); if (gf != null && gf.name().equals(name)) { if (m.getParameterTypes().length == (getter ? 0 : 1)) { return m; } } } return null; } private void initGenericTypes(BeanAccessor ba, FieldInfo[] fields) { for (int i = 0; i < fields.length; i++) { FieldInfo fi = fields[i]; ba.initNthGenericType(i, fi.getRawType(), fi.getType(), fields.length, fi.getName()); } } @SuppressWarnings("unchecked") private <T> void initAnnotations(BeanAccessor ba, FieldInfo[] fields) { IdentityHashMap<Class<?>, Annotation>[] f = new IdentityHashMap[fields.length]; IdentityHashMap<Class<?>, Annotation>[] g = new IdentityHashMap[fields.length]; IdentityHashMap<Class<?>, Annotation>[] s = new IdentityHashMap[fields.length]; for (int n = 0; n < fields.length; n++) { f[n] = fields[n].getAnnoOnField(); g[n] = fields[n].getAnnoOnGetter(); s[n] = fields[n].getAnnoOnSetter(); } ba.initAnnotations(f, g, s); } private boolean sortFields(FieldInfo[] fields) { if (fields == null) return false; boolean isDup = false; { Set<Integer> intSet = new HashSet<Integer>(); for (FieldInfo fi : fields) { boolean old = intSet.add(fi.getName().hashCode()); if (!old) { isDup = true; break; } } } Arrays.sort(fields, new Comparator<FieldInfo>() { public int compare(FieldInfo o1, FieldInfo o2) { int x = o1.getName().hashCode(); int y = o2.getName().hashCode(); return (x < y) ? -1 : ((x == y) ? 0 : 1); } }); return isDup; } }