Java动态代理概述和手写实现动态代理

一:前提

Spring中最重要的两种思想:控制反转IOC(Inversion of Control)和面向切面编程AOP(Aspect-Oriented Programming),而AOP最重要的原理就是动态代理,今天我们谈一下动态代理。动态代理顾名思义是代替别人做某些事,它自己不干,让代理帮他做。别的不多说,直接上代码(举的例子可能不恰当)!

二:基于JDK的动态代理

1:因为Java的动态代理是基于接口的,所以先定义一个顶级接口Person

package dynamicProxy.jdkDynamic;
/**
 * 顶级接口
 */
public interface Person {
    public void speak();
}

2:写一个Person的实现类GuangDongPerson 

package dynamicProxy.jdkDynamic;

/**
 * 实现类
 * */
public class GuangDongPerson implements Person {

    @Override
    public void speak() {
        System.out.println("我说的是~¥#~&~#");
    }
}

3:找一个翻译作为代理类,进行翻译普通话。

package dynamicProxy.jdkDynamic;

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

public class Translation implements InvocationHandler {

    private Person target;

    public Translation(Person target) {
        this.target = target;
    }

    public static Object getInstance(Person target){
        Class<? extends Person> clazz = target.getClass();
        System.out.println(clazz.getName());
        Object poxy = Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), new Translation(target));
        System.out.println(poxy.getClass().getName());
        return poxy;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("我是翻译,你说话我翻译,请说话!");
        Object invoke = method.invoke(target,args);
        System.out.println("他说的是xxxxxxxxxx");
        return invoke;
    }
}

说明:

1:Proxy.newInstance()方法

        * 第一个参数为目标类的类加载器

        * 第二个参数为目标类的接口集合,JDK动态代理目标类必须实现一个接口,这是为了保证生成的代理类和目标类之前的强一致性关系

        * 第三个参数为InvocationHandler接口的实现类,这里直接通过匿名内部类实现,重写了invoke方法

  2:InvocationHandler接口实现类

        * 实现该类必须重写invoke()方法,动态代理实现在代理类中该接口的实现类调用该方法,并在该方法中反射执行该方法完成整个动态代理流程;

        * 第一个参数为生成的动态代理对象;

        * 第二个参数为动态代理在客户端执行的方法;

        * 第三个参数为该方法的参数列表;

        * 通过反射来完成方法调用;

4:测试类

package dynamicProxy.jdkDynamic;

import dynamicProxy.jdkDynamic.custom.TLGuangDongPerson;
import dynamicProxy.jdkDynamic.custom.TLPerson;
import dynamicProxy.jdkDynamic.custom.TLTranslation;
import sun.misc.ProxyGenerator;

import java.io.FileOutputStream;

public class Test {

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

        Person translationInstance = (Person)Translation.getInstance(new GuangDongPerson());
        translationInstance.speak();

        byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{Person.class});
        FileOutputStream outputStream = new FileOutputStream("E:\\$Proxy0.class");
        outputStream.write(bytes);
        outputStream.close();
//        TLPerson translationInstance = (TLPerson)TLTranslation.getInstance(new TLGuangDongPerson());
//        translationInstance.speak();
//
//        byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{Person.class});
//        FileOutputStream outputStream = new FileOutputStream("E:\\$Proxy0.class");
//        outputStream.write(bytes);
//        outputStream.close();
    }
}

5:测试结果

dynamicProxy.jdkDynamic.Test
//未被代理之前的类
dynamicProxy.jdkDynamic.GuangDongPerson
//代理之后的类
com.sun.proxy.$Proxy0

我是翻译,你说话我翻译,请说话!
我说的是~¥#~&~#
他说的是xxxxxxxxxx

Process finished with exit code 0

注:看到上面的输出,可以很明显的看代理之后的类编程了$Proxy0,下面是此类的反编译结果

import dynamicProxy.jdkDynamic.Person;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy0 extends Proxy implements Person {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void speak() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("dynamicProxy.jdkDynamic.Person").getMethod("speak");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

总结:Java动态代理使用反射机制进行代理,可以看到该虚拟类继承Proxy类并实现了目标类顶层接口,并反射获取了目标类中的所有方法的Method对象,同时,虚拟类也同时重写了目标类的所有方法,并通过super.h.invoke(this, m3, (Object[])null)的方式进行调用。

三:手写实现JDK动态代理

步骤:

1:TLPerson类,定义newInstance()方法,进行代理对象创建

  • 在该方法中首先需要输出代理类的.java文件
  • 编译该.java文件生成.class文件
  • 通过类加载器加载该.class文件
  • 生成该.class文件的事例对象作为代理对象返回

2:TLInvocationHandler接口,定义invoke()方法,在代理类中进行目标方法调用

  • 在.java文件中,定义构造器,传递该类引用
  • 通过SelfInvocationHandler实现类对象调用invoke()方法,实现代理方式的方法调用

3:TLClassLoader,自定义类加载器,继承JDK的ClassLoader类,实现自定义的加载方式

具体操作:

1:创建顶级接口

package dynamicProxy.jdkDynamic.custom;

public interface TLPerson {
    public void speak();
}

2:创建实现类

package dynamicProxy.jdkDynamic.custom;

public class TLGuangDongPerson implements TLPerson{
    @Override
    public void speak() {
        System.out.println("我说的是~¥#~&~#");
    }
}

3:使用自己的TLClassLoader代替JDK的ClassLoader,重写findClass(String name)方法。

package dynamicProxy.jdkDynamic.custom;

import dynamicProxy.custom.SelfClassLoader;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;

public class TLClassLoader extends ClassLoader {
    private File classPathFile;

    public TLClassLoader() {
        String classPath = TLClassLoader.class.getResource("").getPath();
        this.classPathFile = new File(classPath);
    }

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

        return null;
    }
}

4:使用自己的TLInvocationHandler代替JDK的InvocationHandler,重写invoke(Object proxy, Method method, Object[] args)方法。

package dynamicProxy.jdkDynamic.custom;

import java.lang.reflect.Method;

public interface TLInvocationHandler {

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

5:使用自己的TLProxy代替JDK的Proxy

package dynamicProxy.jdkDynamic.custom;

import com.sun.org.apache.regexp.internal.RE;
import dynamicProxy.custom.SelfInvocationHandler;
import dynamicProxy.custom.SelfProxy;

import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class TLProxy {
    private static final String LN = "\r\n";
    public static Object newProxyInstance(TLClassLoader loader, Class<?>[] interfaces, TLInvocationHandler h) throws Exception {
        // 动态生成源代码
        String srcClass = generateSrc(interfaces);
        // 输出Java文件
        String filePath = TLProxy.class.getResource("").getPath()  + "$ProxyO.java";
        System.out.println(filePath);
        FileWriter fileWriter = new FileWriter(filePath);
        fileWriter.write(srcClass);
        fileWriter.flush();
        fileWriter.close();
        // 编译Java文件为class文件
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
        Iterable iterable = fileManager.getJavaFileObjects(filePath);
        JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, null, null, null, iterable);
        task.call();
        fileManager.close();
        // 加载编译生成的class文件到JVM
        Class<?> proxyClass = loader.findClass("$ProxyO");
        Constructor<?> constructor = proxyClass.getConstructor(TLInvocationHandler.class);
        // 删掉虚拟代理类
        File file = new File(filePath);
        file.delete();
        // 返回字节码重组以后的代理对象
        return constructor.newInstance(h);
    }
    private static String generateSrc(Class<?>[] interfaces) {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("package dynamicProxy.jdkDynamic.custom;" + LN );
        stringBuilder.append("import dynamicProxy.jdkDynamic.custom.TLPerson;" + LN);
        stringBuilder.append("import java.lang.reflect.Method;" + LN);
        stringBuilder.append("public class $ProxyO implements " + interfaces[0].getName() + "{" + LN);
        stringBuilder.append("TLInvocationHandler h;" + LN);
        stringBuilder.append("public $ProxyO(TLInvocationHandler h) {" + LN);
        stringBuilder.append("this.h = h;" + LN);
        stringBuilder.append("}" + LN);

        for (Method method : interfaces[0].getMethods()) {
            stringBuilder.append("public " + method.getReturnType().getName() + " " + method.getName() + "() {" + LN);
            stringBuilder.append("try {" + LN);
            stringBuilder.append("Method m = " + interfaces[0].getName() + ".class.getMethod(\"" + method.getName() + "\", new Class[]{});" + LN);
            stringBuilder.append("this.h.invoke(this, m, null);" + LN);
            stringBuilder.append("} catch(Throwable able) {" + LN);
            stringBuilder.append("able.getMessage();" + LN);
            stringBuilder.append("}" + LN);
            stringBuilder.append("}" + LN );
        }
        stringBuilder.append("}" + LN);
        return stringBuilder.toString();
    }
}

6:创建手写翻译类

package dynamicProxy.jdkDynamic.custom;

import dynamicProxy.jdkDynamic.Translation;

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

public class TLTranslation implements TLInvocationHandler {
    private TLPerson target;

    public TLTranslation(TLPerson target) {
        this.target = target;
    }

    public static Object getInstance(TLPerson target) {
        try {
            Class<? extends TLPerson> clazz = target.getClass();
            System.out.println(clazz.getName());
            Object poxy = TLProxy.newProxyInstance(new TLClassLoader(), clazz.getInterfaces(), new TLTranslation(target));
            System.out.println(poxy.getClass().getName());
            return poxy;
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{
        System.out.println("我是翻译,你说话我翻译,请说话!");
        Object invoke = method.invoke(target,args);
        System.out.println("他说的是xxxxxxxxxx");
        return invoke;
    }
}

7:测试类

package dynamicProxy.jdkDynamic;

import dynamicProxy.jdkDynamic.custom.TLGuangDongPerson;
import dynamicProxy.jdkDynamic.custom.TLPerson;
import dynamicProxy.jdkDynamic.custom.TLTranslation;
import sun.misc.ProxyGenerator;

import java.io.FileOutputStream;

public class Test {

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

//        Person translationInstance = (Person)Translation.getInstance(new GuangDongPerson());
//        translationInstance.speak();
//
//        byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{Person.class});
//        FileOutputStream outputStream = new FileOutputStream("E:\\$Proxy0.class");
//        outputStream.write(bytes);
//        outputStream.close();
        TLPerson translationInstance = (TLPerson)TLTranslation.getInstance(new TLGuangDongPerson());
        translationInstance.speak();

        byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{Person.class});
        FileOutputStream outputStream = new FileOutputStream("E:\\$Proxy0.class");
        outputStream.write(bytes);
        outputStream.close();
    }
}

8:测试结果

dynamicProxy.jdkDynamic.custom.TLGuangDongPerson
/F:/project/TimeTest/target/classes/dynamicProxy/jdkDynamic/custom/$ProxyO.java
dynamicProxy.jdkDynamic.custom.$ProxyO
我是翻译,你说话我翻译,请说话!
我说的是~¥#~&~#
他说的是xxxxxxxxxx

Process finished with exit code 0

这仅是一个理解动态代理的思路,可能会给你一点启发,共同进步!

 

 

 

 

 

 

 

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值