手动实现jdk动态代理

一.代码实现

  1. 前提知识:
  • 静态代理
    • 继承:代理对象继承目标对象,重写需要增强的方法;缺点:会代理类过多,非常复杂
    • 聚合:目标对象和代理对象实现同一个接口,代理对象当中要包含目标对象。缺点:也会产生类爆炸,只不过比继承少一点点

总结:如果在不确定的情况下,尽量不要去使用静态代理。因为一旦你写代码,就会产生类,一旦产生类就爆炸。

  1. 思路:要生成一对象一般来说都是通过class文件,被类加载器加载到jvm的方法区中才能实例化对象。但是我们要生成的代理对象没有对应的class文件,怎么办?
    通过接口反射生成一个类文件,然后调用第三方的编译技术,动态编译这个产生的类文件成class文件,继而利用UrlclassLoader(因为这个动态产生的class不在工程当中所以需要使用UrlclassLoader)把这个动态编译的类加载到jvm当中,最后通过反射把这个类实例化。
    代码如下:
ProxyUtil.newInstanse() 相当于jdk中的
WxgDao jdkproxy = (WxgDao) Proxy.newProxyInstance(Test.class.getClassLoader(),
                new Class[]{WxgDao.class},new WxgInvocationHandler(new WxgDaoImpl()));
                
public class ProxyUtil {
    /**
     *  content --->string
     *  .java  io
     * .class
     * .new   反射----》class
     * @return
     */
    public static Object newInstance(Class targetInf, CoustomInvocationHandler h){
        Object proxy=null;
        //String handlerName = CoustomInvocationHandler.class.
        Method methods[] =targetInf.getDeclaredMethods();
        String line="\n";
        String tab ="\t";
        String infName = targetInf.getSimpleName();
        String content ="";
        String packageContent = "package com.google;"+line;
        String importContent = "import "+targetInf.getName()+";"+line
                             +"import com.Wxg.dao.CoustomInvocationHandler;"+line
                             +"import java.lang.Exception;"
                             +"import java.lang.reflect.Method;"+line;
        String clazzFirstLineContent = "public class $Proxy implements "+infName+"{"+line;
        String filedContent  =tab+"private CoustomInvocationHandler h;"+line;
        String constructorContent =tab+"public $Proxy (CoustomInvocationHandler h){" +line
                                  +tab+tab+"this.h =h;"
                                  +line+tab+"}"+line;
        String methodContent = "";
        for (Method method : methods) {
            String returnTypeName = method.getReturnType().getSimpleName();
            String methodName =method.getName();
            // Sting.class String.class
            Class args[] = method.getParameterTypes();
            String argsContent = "";
            String paramsContent="";
            int flag =0;
            for (Class arg : args) {
                String temp = arg.getSimpleName();
                //String
                //String p0,Sting p1,
                argsContent+=temp+" p"+flag+",";
                paramsContent+="p"+flag+",";
                flag++;
            }
            if (argsContent.length()>0){
                argsContent=argsContent.substring(0,argsContent.lastIndexOf(",")-1);
                paramsContent=paramsContent.substring(0,paramsContent.lastIndexOf(",")-1);
            }

            methodContent+=tab+"public "+returnTypeName+" "+methodName+"("+argsContent+")throws Exception {"+line
                           +tab+tab+"Method method = Class.forName(\""+targetInf.getName()+"\").getDeclaredMethod(\""+methodName+"\");"+line
                            +tab+tab+"return ("+returnTypeName+")h.invoke(method);"+line;
            methodContent+=tab+"}"+line;
        }

        content=packageContent+importContent+clazzFirstLineContent+filedContent+constructorContent+methodContent+"}";

        File file =new File("d:\\com\\google\\$Proxy.java");
        try {
            if (!file.exists()) {
                file.createNewFile();
            }

            FileWriter fw = new FileWriter(file);
            fw.write(content);
            fw.flush();
            fw.close();
            JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
            StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
            Iterable units = fileMgr.getJavaFileObjects(file);

            JavaCompiler.CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units);
            t.call();
            fileMgr.close();

            URL[] urls = new URL[]{new URL("file:D:\\\\")};
            URLClassLoader urlClassLoader = new URLClassLoader(urls);
            Class clazz = urlClassLoader.loadClass("com.google.$Proxy");

            Constructor constructor = clazz.getConstructor(CoustomInvocationHandler.class);
            proxy = constructor.newInstance(h);
            //clazz.newInstance();
            //Class.forName()
        }catch (Exception e){
            e.printStackTrace();
        }
        return proxy;
    }
}

public interface CoustomInvocationHandler {
    public Object invoke(Method method);
}

public class TestCustomHandler implements CoustomInvocationHandler {
    Object target;
    public TestCustomHandler(Object target){
        this.target=target;
    }

    @Override
    public Object invoke(Method method) {
        try {
            System.out.println("----------------");
            return  method.invoke(target);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return null;
    }
}

测试方法:

public class Test {
    public static void main(String[] args) {
          //自定义,这里没有指定类加载器,是因为在代码中已经写死了用UrlClassLoader加载器,从磁盘文件中获取class文件。
        LubanDao proxy = (LubanDao) ProxyUtil.newInstance(LubanDao.class,new TestCustomHandler(new LubanDaoImpl()));
        try {
            proxy.proxy();
        } catch (Exception e) {
            e.printStackTrace();
        }

//        byte[] bytes=ProxyGenerator.generateProxyClass("$Proxy18",new Class[]{LubanDao.class});
//
//        try {
//            FileOutputStream fileOutputStream = new FileOutputStream("d:\\$Proxy18.class");
//            fileOutputStream.write(bytes);
//            fileOutputStream.flush();
//        } catch (FileNotFoundException e) {
//            e.printStackTrace();
//        } catch (IOException e) {
//            e.printStackTrace();
//        }
//        //        System.out.println(proxy.proxy());
//        LubanDao jdkproxy = (LubanDao) Proxy.newProxyInstance(Test.class.getClassLoader(),
//                new Class[]{LubanDao.class},new LubanInvocationHandler(new LubanDaoImpl()));
//
//        //jdkproxy.query();
//        try {
//            jdkproxy.proxy();
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
    }
}

总结:和jdk动态代理的区别
缺点:首先要生成文件
缺点:动态编译文件 class
缺点:需要一个URLclassloader
软件性能的最终体现在IO操
而jdk动态代理是直接通过接口反射得到字节码,然后把字节码转成class ,这过程是调用了native的本地方法用 c++ 实现。且jdk动态代理默认会继承Proxy这个类。

二.为什么jdk动态代理必须是基于接口的?

因为jdk动态代理底层产生的代理对象默认继承了Proxy对象,不能再继续继承其他对象,只能通过实现接口的方式操作。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值