手写Jdk动态代理

通过Arthas反编译Jdk动态代理
在这里插入图片描述
容易得知Jdk动态代理实际就是动态类生成,实现对应的类,对所有方法进行拦截

也可以通过设置System.setProperty(“sun.misc.ProxyGenerator”,“true”);保留动态代理生成的文件

知道这点后就可以手动编写动态代理了

先根据接口动态生成类字符串,然后进行编译,最后加载进内存,并创建对象

效果
在这里插入图片描述
代码
拥有拦截方法执行

public interface MyHandler {

    Object invoke(Object proxy, Method method, Object[] args)throws Throwable;

}

对基本数据类型与其包装类做转换

public class TypeConversion {

    private static Map<String,String> map;

    /*
     * boolean	Boolean	java.lang.Boolean	java.lang.Object
     * byte	Byte	java.lang.Byte	java.lang.Number
     * short	Short	java.lang.Short	java.lang.Number
     * int	Integer	java.lang.Integer	java.lang.Number
     * long	Long	java.lang.Float	java.lang.Number
     * float	Float	java.lang.Double	java.lang.Number
     * double	Double	java.lang.Double	java.lang.Number
     * char	Character
     */
    static {
        map=new HashMap<>();
        map.put("boolean","Boolean");
        map.put("byte","Byte");
        map.put("short","Short");
        map.put("int","Integer");
        map.put("long","Long");
        map.put("float","Float");
        map.put("double","Double");
        map.put("char","Character");
    }

    public static String conversion(String returnType) {
        return map.getOrDefault(returnType,returnType);
    }

    public static boolean contain(String name) {
        return map.containsKey(name);
    }

}

封装方法信息

public class MethodInfo {

    private String clazzFullName;
    private String methodName;
    private String[] paramClassFullNames;
    private String[] paramClassNames;
    private String returnType;
    private List<String> classes;
    private static final String NO_RETURN="void";

    public MethodInfo(Class clazz, Method method) {
        classes=new ArrayList<>();
        clazzFullName=clazz.getName();
        methodName=method.getName();
        Class<?>[] types = method.getParameterTypes();
        paramClassFullNames=new String[types.length];
        paramClassNames=new String[types.length];
        for (int i = 0; i < types.length; i++) {
            paramClassFullNames[i]=types[i].getName();
            paramClassNames[i]=types[i].getSimpleName();
            addClass(types[i]);
        }
        returnType=method.getReturnType().getSimpleName();
        if(!NO_RETURN.equals(returnType)){
            addClass(method.getReturnType());
        }
    }

    private void addClass(Class<?> clazz) {
        while (clazz.isArray()){
            clazz=clazz.getComponentType();
        }
        String name = clazz.getName();
        if(!TypeConversion.contain(name)){
            classes.add(name);
        }
    }

    public String[] getParamClassNames() {
        return paramClassNames;
    }

    public String getReturnType() {
        return returnType;
    }

    public String getClazzFullName() {
        return clazzFullName;
    }

    public String getMethodName() {
        return methodName;
    }

    public String[] getParamClassFullNames() {
        return paramClassFullNames;
    }

    public List<String> getClasses() {
        return classes;
    }

}

自定义类加载器加载类文件

public class MyClassLoader extends ClassLoader{

    private static final String BASE_PATH="src/files";

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        try {
            byte[] bytes = Files.readAllBytes(Paths.get(BASE_PATH, name + ".class"));
            return defineClass(name,bytes,0,bytes.length);
        } catch (IOException e) {
            return super.findClass(name);
        }
    }

}

用于拼接类字符串

public class ClassMaker {

    private static final String CLASS_NAME="$Proxy";
    private static final String BASE_PATH="src/files";
    private static final String NO_RETURN="void";

    public static Class make(Class[] interfaces, ClassLoader loader) throws IOException, ClassNotFoundException {
        String classStr=buildClass(interfaces);
        writeAndCompiler(classStr);
        return loader.loadClass(CLASS_NAME);
    }

    private static void writeAndCompiler(String classStr) throws IOException {
        Files.write(Paths.get(BASE_PATH,CLASS_NAME+".java"),classStr.getBytes());
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        int res = compiler.run(System.in, System.out, System.err, BASE_PATH+"/" + CLASS_NAME + ".java");
        if(res!=0)throw new RuntimeException("编译失败");
    }

    private static String buildClass(Class[] interfaces) {
        Map<String,MethodInfo> methodInfoMap=new HashMap<>();
        String baseMethodName="method";
        int index=0;
        Set<String> imports=new HashSet<>(Arrays.asList("proxy.MyHandler","java.lang.reflect.Method",
                "proxy.MyProxy"));
        for (Class clazz:interfaces){
            imports.add(clazz.getName());
            for (Method method:clazz.getMethods()){
                MethodInfo methodInfo = new MethodInfo(clazz, method);
                methodInfoMap.put(baseMethodName+index++,methodInfo);
                imports.addAll(methodInfo.getClasses());
            }
        }
        StringBuilder builder=new StringBuilder();
        buildImport(builder,imports);
        buildStart(builder,interfaces);
        buildFields(builder,methodInfoMap);
        buildConstructor(builder);
        buildStatic(builder,methodInfoMap);
        buildMethods(builder,methodInfoMap);
        builder.append("\n}");
        return builder.toString();
    }

    private static void buildMethods(StringBuilder builder, Map<String, MethodInfo> methodInfoMap) {
        for (String key:methodInfoMap.keySet()){
            MethodInfo methodInfo = methodInfoMap.get(key);
            buildMethod(builder,methodInfo,key);
        }
    }

    private static void buildMethod(StringBuilder builder, MethodInfo methodInfo,String name) {
        builder.append("\n\tpublic final ").append(methodInfo.getReturnType()).append(" ")
                .append(methodInfo.getMethodName()).append("(");
        String[] names = methodInfo.getParamClassNames();
        String[] args=new String[names.length];
        if(names.length>0){
            String baseName="arg";
            for (int i = 0; i < args.length; i++) {
                args[i]=baseName+i;
                builder.append(names[i]).append(" ").append(args[i]).append(',');
            }
            builder.setLength(builder.length()-1);
        }
        builder.append(") {\n\t\ttry {\n\t\t\tObject res=handler.invoke(this, ").append(name)
                .append(", new Object[]{");
        if(args.length>0){
            for (String arg:args){
                builder.append(arg).append(',');
            }
            builder.setLength(builder.length()-1);
        }
        builder.append("});\n");
        if(!NO_RETURN.equals(methodInfo.getReturnType())){
            builder.append("\t\t\treturn (").append(TypeConversion.conversion(methodInfo.getReturnType()))
                    .append(")res;\n");
        }
        builder.append("\t\t}catch (Throwable throwable) {\n\t\t\tthrow new RuntimeException(throwable);\n\t\t}\n\t}\n");
    }

    private static void buildStatic(StringBuilder builder, Map<String, MethodInfo> methodInfoMap) {
        builder.append("\tstatic {\n\t\ttry {\n");
        for (String key:methodInfoMap.keySet()){
            MethodInfo methodInfo = methodInfoMap.get(key);
            builder.append("\t\t\t").append(key).append(" = Class.forName(\"").append(methodInfo.getClazzFullName())
                    .append("\").getMethod(\"").append(methodInfo.getMethodName())
                    .append("\"");
            for (String clazzName:methodInfo.getParamClassNames()){
                builder.append(",").append(clazzName).append(".class");
            }
            builder.append(");\n");
        }
        builder.append("\t\t}catch (Exception exception) {\n\t\t\tthrow new RuntimeException(exception);\n\t\t}\n\t}\n\n");
    }

    private static void buildConstructor(StringBuilder builder) {
        builder.append("\n\tpublic ").append(CLASS_NAME).append("(MyHandler handler) {\n\t\tsuper(handler);\n\t}\n\n");
    }

    private static void buildFields(StringBuilder builder, Map<String, MethodInfo> methodInfoMap) {
        for (String methodName:methodInfoMap.keySet()){
            builder.append("\tprivate static Method ").append(methodName).append(";\n");
        }
    }

    private static void buildStart(StringBuilder builder,Class[] interfaces) {
        builder.append("\npublic final class ").append(CLASS_NAME).append(" extends MyProxy implements ");
        for (Class clazz:interfaces){
            builder.append(clazz.getSimpleName()).append(',');
        }
        builder.setCharAt(builder.length()-1,'{');
        builder.append("\n\n");
    }

    private static void buildImport(StringBuilder builder, Set<String> imports) {
        for (String item:imports){
            builder.append("import ").append(item).append(";\n");
        }
    }

}

主要使用代理类

public class MyProxy implements MyHandler{

    protected MyHandler handler;

    public MyProxy(MyHandler handler) {
        this.handler = handler;
    }

    public static Object newProxyInstance(ClassLoader loader, Class[] interfaces, MyHandler handler) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, IOException, ClassNotFoundException {
        if(loader==null)loader=new MyClassLoader();
        Class clazz=ClassMaker.make(interfaces,loader);
        Constructor constructor = clazz.getConstructor(MyHandler.class);
        return constructor.newInstance(handler);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        return handler.invoke(proxy, method, args);
    }

}

总体来说没有什么难度,不过注意,返回值基本数据类型与封装数据类型转换问题,以及导包问题

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值