package top.niunaijun.blackreflection; import java.lang.annotation.Annotation; import java.lang.ref.WeakReference; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.HashMap; import java.util.Map; import java.util.WeakHashMap; import top.niunaijun.blackreflection.annotation.BClass; import top.niunaijun.blackreflection.annotation.BClassName; import top.niunaijun.blackreflection.annotation.BClassNameNotProcess; import top.niunaijun.blackreflection.annotation.BConstructor; import top.niunaijun.blackreflection.annotation.BConstructorNotProcess; import top.niunaijun.blackreflection.annotation.BField; import top.niunaijun.blackreflection.annotation.BFieldCheckNotProcess; import top.niunaijun.blackreflection.annotation.BFieldNotProcess; import top.niunaijun.blackreflection.annotation.BFieldSetNotProcess; import top.niunaijun.blackreflection.annotation.BMethodCheckNotProcess; import top.niunaijun.blackreflection.annotation.BParamClass; import top.niunaijun.blackreflection.annotation.BParamClassName; import top.niunaijun.blackreflection.utils.Reflector; @SuppressWarnings("unchecked") public class BlackReflection { public static boolean DEBUG = false; public static boolean CACHE = false; private static final Map, Object> sProxyCache = new HashMap<>(); private static final Map, Object> sProxyWithExceptionCache = new HashMap<>(); // key caller private static final WeakHashMap, Object>> sCallerProxyCache = new WeakHashMap<>(); // public static T create(Class clazz, final Object caller) { // return create(clazz, caller, false); // } public static T create(Class clazz, final Object caller, boolean withException) { try { T proxy = getProxy(clazz, caller, withException); if (proxy != null) return proxy; final WeakReference weakCaller = caller == null ? null : new WeakReference<>(caller); final Class aClass = getClassNameByBlackClass(clazz); Object o = Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz}, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String name = method.getName(); Class returnType = method.getReturnType(); try { boolean isStatic = weakCaller == null; Object callerByWeak = isStatic ? null : weakCaller.get(); // fidel BField bField = method.getAnnotation(BField.class); BFieldNotProcess bFieldNotProcess = method.getAnnotation(BFieldNotProcess.class); if (bField != null || bFieldNotProcess != null) { Object call; Reflector on = Reflector.on(aClass).field(name); if (isStatic) { call = on.get(); } else { if (callerByWeak == null) { return generateNullValue(returnType); } call = on.get(callerByWeak); } return call; } // void BFieldSetNotProcess bFieldSetNotProcess = method.getAnnotation(BFieldSetNotProcess.class); if (bFieldSetNotProcess != null) { // startsWith "_set_" name = name.substring("_set_".length()); Reflector on = Reflector.on(aClass).field(name); if (isStatic) { on.set(args[0]); } else { if (callerByWeak == null) { return 0; } on.set(callerByWeak, args[0]); } return 0; } // check field BFieldCheckNotProcess bFieldCheckNotProcess = method.getAnnotation(BFieldCheckNotProcess.class); if (bFieldCheckNotProcess != null) { // startsWith "_check_" name = name.substring("_check_".length()); try { Reflector on = Reflector.on(aClass).field(name); return on.getField(); } catch (Throwable ignored) { return null; } } Class[] paramClass = getParamClass(method); // check method BMethodCheckNotProcess bMethodCheckNotProcess = method.getAnnotation(BMethodCheckNotProcess.class); if (bMethodCheckNotProcess != null) { // startsWith "_check_" name = name.substring("_check_".length()); try { return Reflector.on(aClass).method(name, paramClass).getMethod(); } catch (Throwable ignored) { return null; } } // method BConstructor bConstructor = method.getAnnotation(BConstructor.class); BConstructorNotProcess bConstructorNotProcess = method.getAnnotation(BConstructorNotProcess.class); if (bConstructor != null || bConstructorNotProcess != null) { Reflector on = Reflector.on(aClass).constructor(paramClass); return on.newInstance(args); } Object call; Reflector on = Reflector.on(aClass).method(name, paramClass); if (isStatic) { call = on.call(args); } else { if (callerByWeak == null) { return generateNullValue(returnType); } call = on.callByCaller(callerByWeak, args); } return call; } catch (Throwable throwable) { if (DEBUG) { if (throwable.getCause() != null) { throwable.getCause().printStackTrace(); } else { throwable.printStackTrace(); } } if (throwable instanceof BlackNullPointerException) { throw new NullPointerException(throwable.getMessage()); } if (withException) { throw throwable; } } return generateNullValue(returnType); } }); if (caller == null) { if (withException) { sProxyWithExceptionCache.put(clazz, o); } else { sProxyCache.put(clazz, o); } } return (T) o; } catch (ClassNotFoundException e) { e.printStackTrace(); } return null; } private static T getProxy(Class clazz, final Object caller, boolean withException) { try { if (caller == null) { Object o; if (withException) { o = sProxyWithExceptionCache.get(clazz); } else { o = sProxyCache.get(clazz); } if (o != null) { return (T) o; } } } catch (Throwable ignore) { } return null; } private static Class[] getParamClass(Method method) throws Throwable { Annotation[][] parameterAnnotations = method.getParameterAnnotations(); Class[] parameterTypes = method.getParameterTypes(); Class[] param = new Class[parameterTypes.length]; for (int i = 0; i < parameterTypes.length; i++) { Annotation[] parameterAnnotation = parameterAnnotations[i]; boolean found = false; for (Annotation annotation : parameterAnnotation) { if (annotation instanceof BParamClassName) { found = true; param[i] = Class.forName(((BParamClassName) annotation).value()); break; } else if (annotation instanceof BParamClass) { found = true; param[i] = ((BParamClass) annotation).value(); break; } } if (!found) { param[i] = parameterTypes[i]; } } return param; } private static Class getClassNameByBlackClass(Class clazz) throws ClassNotFoundException { BClass bClass = clazz.getAnnotation(BClass.class); BClassName bClassName = clazz.getAnnotation(BClassName.class); BClassNameNotProcess bClassNameNotProcess = clazz.getAnnotation(BClassNameNotProcess.class); if (bClass == null && bClassName == null && bClassNameNotProcess == null) { throw new RuntimeException("Not found @BlackClass or @BlackStrClass"); } if (bClass != null) { return bClass.value(); } else if (bClassName != null) { return Class.forName(bClassName.value()); } else { return Class.forName(bClassNameNotProcess.value()); } } private static Object generateNullValue(Class returnType) { if (returnType == void.class) { return 0; } if (returnType.isPrimitive()) { throw new BlackNullPointerException("value is null!"); } return null; } }