手写基于jdk的动态代理

满足代理模式应用场景的三个必要条件(穷取法)
1.两个角色:执行者、被代理对象
2.注重过程,必须要做,被代理对象没时间做或者不想做
3.执行者必须拿到被代理对象的引用


动态代理深究其归根到底最的只有一件事:字节码重组


专业的事交给专业的事做
买车票–>黄牛(代理对象)
租房子–>中介
打官司–>律师

基于jdk的动态代理

//接口
package com.csj.jdk;

public interface Person {

    /**
     * 寻找真爱
     */
    void findLove();
}

//被代理类
package com.csj.jdk;


public class Coder implements Person{

    @Override
    public void findLove() {

        System.out.println("寻找程序猿❥(^_-)");
    }

}

package com.csj.jdk;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * 媒婆
 */
public class MeiPo implements InvocationHandler {

    private Person person;

    public Person getInstance(Person person) {
        this.person = person;
        return (Person) Proxy.newProxyInstance(person.getClass().getClassLoader(), person.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        System.out.println("媒婆来了,要获取相应的资料信息");
        this.person.findLove();
        System.out.println("获取资料信息1完毕");
        return null;
    }
}

//testMain
package com.csj.jdk;

public class JdkProxyTest {

    public static void main(String[] args) {

        Person coder = new Coder();
        Person proxy = new MeiPo().getInstance(coder);
        proxy.findLove();
    }
}


执行结果
在这里插入图片描述
在这里插入图片描述
可以看到这个jdk动态代理已经帮我们创建好了代理类,且类名为$Proxy0
在这里插入图片描述
我们可以把其字节码文件打印出来查看生成的代理类信息

在运行测试方法追加

   byte[] $Proxy0s = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{
                Person.class
        });


        try {
            FileOutputStream fos = new FileOutputStream("$Proxy0.class");
            fos.write($Proxy0s);
            fos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

在这里插入图片描述
因为idea是自带反编译的,我们可以双击查看动态代理生成的类信息,或者是通过一些其他的工具。
在这里插入图片描述在这里插入图片描述在这里插入图片描述
只观看我们业务相关的核心代码,可以看到动态代理生成的字节码继承了Proxy实现了Person接口且Method m3是我们要使用的findLove方法,在静态代码块的将其初始化,当执行findLove的时候执行的是我们动态代理$Proxy0的findLove方法,继而追看到我们的public final void findLove()方法(因为不允许其子类修改,final修饰)
super.h.invoke(this, m3, (Object[])null);
super就是我们的Proxy,因为我们在业务处理类Meipo中调用了
在这里插入图片描述
所以我们已经把h传入进来给Proxy类的h(InvocationHandler)参数
在这里插入图片描述
当其调用invoke方法的时候就是我们相关在Meipo中的
在这里插入图片描述
即动态代理方法执行完毕

手写jdk的动态代理

原理

原理:

  1. 拿到被代理类的引用,然后获取它的接口(getInstances)
  2. jdk代理重新生成一个类,同时实现我们给的代理对象实现的接口
  3. 把被代理对象的引用也拿到了
  4. 重新动态生成一个class字节码
  5. 然后编译

在这里插入图片描述
在之前我们用到了InvocationHandler,Proxy,还有ClassLoader,所以这次我们自己来手动实现这些业务相关使用的类,来一探究竟底层做了什么
在这里插入图片描述
新的目录结构

//person接口
 public interface Person {

    /**
     * 寻找真爱
     */
    void findLove();
}
//接口其子类coder
 class Coder implements Person {

   @Override
   public void findLove() {

       System.out.println("Manual-->寻找程序猿❥(^_-)");
   }

}


/**
 * 业务处理类实现了我们自己写的接口ManualInvocationHandler
 */
public class ManualMeipo implements ManualInvocationHandler{



    private Person person;

    public Person getInstance(Person person) throws Exception {
        this.person = person;
        return (Person) ManualProxy.newProxyInstance( new ManualClassLoader(), person.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args)  {
        System.out.println("ManualMeipo-->");
        this.person.findLove();
        System.out.println("findLove匹配完毕--》");
        return null;
    }
}

//模仿者InvocationHandler定义个接口方法
public interface ManualInvocationHandler {

    public Object invoke(Object proxy, Method method, Object[] args);
}


/**
 * custom Proxy 自己定义的实例生成newProxyInstance(自己的ClassLoader,接口(简单实现),自己的InvocationHandler)
 */
public class ManualProxy {

    //换行
    private static final String LN = "\r\n";

    public static Object newProxyInstance(ManualClassLoader loader,
                                          Class<?>[] interfaces,
                                          ManualInvocationHandler h) throws Exception {

        try {


            //1.生成源代码
            String proxyCode = generateCode(interfaces);
            //2.将生成的源代码输出到磁盘,保存为.java文件
            String path = ManualProxy.class.getResource("").getPath();
            File file = new File(path + "$Proxy0.java");
            FileWriter fw = new FileWriter(file);
            fw.write(proxyCode);
            fw.flush();
            fw.close();
            //3.编译源代码,并且生成.class文件
            //java代理提供的
            JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
            StandardJavaFileManager manager = compiler.getStandardFileManager(null, null, null);
            Iterable<? extends JavaFileObject> it = manager.getJavaFileObjects(file);

            JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, null, null, it);
            task.call();
            manager.close();
//            file.delete();
            //4.将class文件中的内容,动态加载到jvm中来
            //5.返回被代理后的代理对象
            Class<?> proxyClass = loader.findClass("$Proxy0");
            Constructor<?> c = proxyClass.getConstructor(ManualInvocationHandler.class);
            file.delete();
            return c.newInstance(h);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;

    }

    private static String generateCode(Class<?>[] interfaces) {

        StringBuffer sb = new StringBuffer();
        sb.append("package com.csj.manual;" + LN);
        sb.append("import java.lang.reflect.Proxy;" + LN);
        sb.append("import java.lang.reflect.Method;" + LN);
        sb.append("public final class $Proxy0 implements " + interfaces[0].getName() + " {" + LN);

        sb.append("ManualInvocationHandler h;" + LN);
        sb.append("public $Proxy0 (ManualInvocationHandler h) {" + LN);
        sb.append("this.h = h;" + LN);
        sb.append("}" + LN);
        for (Method m : interfaces[0].getMethods()) {
//            public final void findLove()为了简单,不做复杂的处理,只是为了生成findLove业务方法
            sb.append("public final " + m.getReturnType().getName() + " " + m.getName() + "(){");
            sb.append("try{" + LN);
            sb.append("Method m = " + interfaces[0].getName() + ".class.getMethod(\"" + m.getName() + "\",new Class[]{});" + LN);
            sb.append("this.h.invoke(this,m,null);" + LN);
            sb.append("}catch(Exception e){" + LN);
            sb.append("}" + LN);
        }
        sb.append("}}");

        return sb.toString();
    }
}

//自己的classLoader
/**
 * simple custom classLoader
 */
//代码生成,编译,重新动态load到jvm
public class ManualClassLoader extends ClassLoader{

    private File baseDir;
    public ManualClassLoader(){

        String basePath = ManualClassLoader.class.getResource("").getPath();
        this.baseDir = new File(basePath);
    }
    //找到之后加载到我们的jvm中来
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {

        //扎到我们的class(包名+类名)
        String  className = ManualClassLoader.class.getPackage().getName() + "." + name;
        if(baseDir != null) {

            File classFile = new File(baseDir, name.replaceAll("\\.", "/")+ ".class");
            if (classFile.exists()) {

                FileInputStream in = null;
                ByteArrayOutputStream baos = null;
                byte[] buff = new byte[1024];
                try {

                    baos =new ByteArrayOutputStream();
                    in = new FileInputStream(classFile);
                    int len;

                    while ((len = in.read(buff)) != -1) {

                        baos.write(buff, 0, len);
                    }
                    //load进jvm
                    return defineClass(className, baos.toByteArray(), 0, baos.size());
                } catch (Exception e) {
                    e.printStackTrace();
                }finally {
                    try {
                        in.close();
                        baos.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    classFile.delete();
                }
            }

        }


        return null;
    }
}

//测试方法
/**
 * 代理关注的是过程
 */
 class ManualJdkTest {

    public static void main(String[] args) throws Exception {

        new ManualMeipo().getInstance(new Coder()).findLove();
    }
}

在这里插入图片描述
整个的实现过程就是这样。
动态代理的时候动态生成一个类实现了我们定义的接口和继承了Proxy这个类。
同时Proxy有其InvocationHandler的引用命名为h,在我们Proxy.newInstance的时候会创建这个代理类根据动态生成模板进行生成,根据其java高级api进行编译生成class文件,通过其classLoader的findClass进行查找相应的.class文件进行装载数据defineClass方法来loader进我们的jvm,同时在实行完成之后删除动态代理生成的.java,.class文件。

PS:动态代理方便了我们的使用,让我们更专注的进行某一模块的业务开发。提高了解耦的能力,动态代理使用的频繁会生成很多的类信息,loader进我们jvm的栈中,堆中,增长我们的内存空间,不过这些并不会让我们杜绝选择动态代理,因为有其相应的垃圾回收机制,和其可自定义配置的内存空间。
如果一个项目的栈空间消耗很大的话(栈内存分配也达到了机器的上限),也可以适当的对一些不必要的模块的动态代理给取消,来提升相应的空间进行优化。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值