JDK动态代理笔记

JDK动态代理

JDK提供了Java.lang.reflect.Proxy类来实现动态代理的,可通过它的newProxyInstance来获得代理实现类。同时对于代理的接口的实际处理,是一个java.lang.reflect.InvocationHandler,它提供了一个invoke方法供实现者提供相应的代理逻辑的实现。

例子

以下代码只是为了了解JDK动态代理的底层实现原理,其实一些类是jdk包中早就已经实现了的。比如MyProxy

Test.class

public class Test {
    public static void main(String[] args) {
        Object realProxy=MyProxy.newProxyInstance(new MyClassLoader(),new Class<?>[]{TargetClass.class},new MyDynamiProxy(new TargetImpl()));
        TargetClass targetClass=(TargetClass)realProxy;
        targetClass.sayHello();
    }
}

TargetClass.class

public interface TargetClass {
    public void sayHi();
    public void  sayHello();
}

TargetImpl.class

public class TargetImpl implements TargetClass{
    public void sayHi() {
        System.out.println("HI");

    }
    public void sayHello() {
        System.out.println("Hello");
    }
}

MyDynamiProxy.class

public class MyDynamiProxy implements MyInvocationHandle {
//这并不是真正的代理类,是协助代理类的一部分
    public Object target;

    public MyDynamiProxy(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("方法输出前");
        Object result=method.invoke(target,args);
        System.out.println("方法输出后");
        return null;
    }
}

MyProxy.class

public class MyProxy {
    public static  final String Ln="\r\n";
    public static Object newProxyInstance(MyClassLoader loader,
                                          Class<?>[] interfaces,
                                          MyInvocationHandle h){

//        1.动态生成一个.java的源文件
         String code=generateCode(interfaces);

//        2.把生成的这个.java源文件保存在磁盘上
        try {
            String filePath=MyProxy.class.getResource("").getPath();
//            filePath=java.net.URLDecoder.decode(filePath,"utf-8");
            File file=new File("D:\\文件\\学习制作网页\\dailimoshi.com.proxy\\src\\main\\java\\comMyProxy\\"+"$Proxy0.java");
            FileWriter fw=new FileWriter(file);
            fw.write(code);
            fw.flush();
            fw.close();
//        3.把这个.java源文件编译成.class文件
//            创建一个java编译器对象
            JavaCompiler compiler= ToolProvider.getSystemJavaCompiler();
//            java源代码文件管理器
            StandardJavaFileManager manager =compiler.getStandardFileManager(null,null,null);
//            java源文件的迭代器
            Iterable iterable=manager.getJavaFileObjects(file);
//            获取一个编译任务
            JavaCompiler.CompilationTask task =compiler.getTask(null,manager,null,null,null,iterable);
//            执行编译
            task.call();
//            关闭文件管理器
            manager.close();
//        4.把编译后的.class文件加载到jvm内存中
            Class clazz=loader.findClass("$Proxy0");
//        5.根据加载到jvm中的.class字节码文件生成class文件,然后创建class类的对象
            Constructor constructor =clazz.getConstructor(MyInvocationHandle.class);
            return constructor.newInstance(h);

        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;


    }
    public static  String generateCode(Class<?>[] interfaces){
        StringBuffer sb=new StringBuffer();
        sb.append("package comMyProxy;"+Ln);
        sb.append("import java.lang.reflect.Method;"+Ln);
        sb.append("public class $Proxy0 implements "+interfaces[0].getName()+"{"+Ln);
        sb.append("public MyInvocationHandle h;"+Ln);
        sb.append("public $Proxy0(MyInvocationHandle h){"+Ln);
        sb.append("this.h=h;"+Ln);
        sb.append("}"+Ln);
        for(Method m:interfaces[0].getMethods()){
            sb.append("public "+m.getReturnType()+" "+m.getName()+"(){"+Ln);
            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(Throwable e){"+Ln);
            sb.append("e.printStackTrace();"+Ln);
            sb.append("}"+Ln);
            sb.append("}"+Ln);
        }
        sb.append("}"+Ln);
        return sb.toString();

    }
}

MyClassLoader.class

package comMyProxy;

import java.io.*;

//自定义一个类加载器
//bootstrap ClassLoader jdk/jre目录下的jre包加载
//ext ClassLoader jdk/ext/目录下的jar包加载
//App ClassLoader 应用加载
public class MyClassLoader extends ClassLoader{
    public  File classPathFile;

    public MyClassLoader() {
//        String classPath=MyClassLoader.class.getResource("").getPath();
        String  classPath="D:\\文件\\学习制作网页\\dailimoshi.com.proxy\\src\\main\\java\\comMyProxy\\";
        this.classPathFile = new File(classPath);
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        String className=MyClassLoader.class.getPackage().getName()+"."+name;
        if(classPathFile!=null){
            File classFile=new File(classPathFile,name.replaceAll("\\.","/")+".class");
            if (classFile.exists()){
                FileInputStream fis=null;
                ByteArrayOutputStream bos=null;
                try {
                    fis=new FileInputStream(classFile);
                    byte[] bytes=new byte[4096];
                    bos=new ByteArrayOutputStream();
                    int len;
                    while ((len=fis.read(bytes))!=-1){
                        bos.write(bytes,0,len);
                    }
                    return defineClass(className,bos.toByteArray(),0,bos.size());
                } catch (Exception e) {
                    e.printStackTrace();
                }finally {
                    try {
                        if(fis!=null)fis.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    try {
                        if(bos!=null)bos.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }

                }
            }
        }
        return null;
    }
}

$Proxy0.java
底层生成的代理类的源文件

动态生成的代理类有如下特性:
  1. 继承了Proxy类,实现了代理的接口,由于java不能多继承,这里已经继承了Proxy类了,不能再继承其他的类,所以JDK的动态代理不支持对实现类的代理,只支持接口的代理。

  2. 提供了一个使用InvocationHandler作为参数的构造方法。

  3. 生成静态代码块来初始化接口中方法的Method对象,以及Object类的equals、hashCode、toString方法。

  4. 重写了Object类的equals、hashCode、toString,它们都只是简单的调用了InvocationHandler的invoke方法,即可以对其进行特殊的操作,也就是说JDK的动态代理还可以代理上述三个方法。

  5. 代理类实现代理接口的sayHello方法中,只是简单的调用了InvocationHandler的invoke方法,我们可以在invoke方法中进行一些特殊操作,甚至不调用实现的方法,直接返回。

package comMyProxy;
import java.lang.reflect.Method;
public class $Proxy0 implements comMyProxy.TargetClass{
public MyInvocationHandle h;
public $Proxy0(MyInvocationHandle h){
this.h=h;
}
public void sayHello(){
try{
Method m=comMyProxy.TargetClass.class.getMethod("sayHello",new Class[]{});
this.h.invoke(this,m,null);
}catch(Throwable e){
e.printStackTrace();
}
}
public void sayHi(){
try{
Method m=comMyProxy.TargetClass.class.getMethod("sayHi",new Class[]{});
this.h.invoke(this,m,null);
}catch(Throwable e){
e.printStackTrace();
}
}
}

$Proxy0.class
底层生成的代理类的class文件

public class $Proxy0 implements TargetClass {
    public MyInvocationHandle h;

    public $Proxy0(MyInvocationHandle var1) {
        this.h = var1;
    }

    public void sayHello() {
        try {
            Method var1 = TargetClass.class.getMethod("sayHello");
            this.h.invoke(this, var1, (Object[])null);
        } catch (Throwable var2) {
            var2.printStackTrace();
        }

    }

    public void sayHi() {
        try {
            Method var1 = TargetClass.class.getMethod("sayHi");
            this.h.invoke(this, var1, (Object[])null);
        } catch (Throwable var2) {
            var2.printStackTrace();
        }

    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值