从JDK动态代理到CGLIB动态代理、到自定义实现动态代理、到放弃

注:部分代码来自于享学课堂,有自己的理解修改

目录

注:部分代码来自于享学课堂,有自己的理解修改

1、JDK动态代理demo

首先是奥特曼接口

接着是赛文奥特曼

雷欧奥特曼,不是来自M78星云,不用实现AoTeMan接口

怪兽来了,奥特曼出动打怪兽

执行结果

2、执行原理图解

拉取动态生成对象

 内容如下:

我们再看看super.h.invoke(this, m3, (Object[])null);

问题:

3、使用cglib实现动态代理

引入maven

产生代理的工厂

原始类:注意没有实现任何接口

新增和删除代理

执行类

执行结果

4、自定义动态代理的实现

首先原始代码对象

自定义ClassLoader

自定义Handler接口

自定义代理对象MyProxy

测试

理解


1、JDK动态代理demo

还是以我们熟悉的奥特曼举例,赛文奥特曼腿被怪兽打折了,只能有雷欧代替赛文出战。

首先是奥特曼接口

public interface AoTeMan {
    //打怪兽方法
    public void playMaster();
}

接着是赛文奥特曼

public class SaiWen implements AoTeMan {
    @Override
    public void playMaster() {
        System.out.println("我是赛文,我腿受伤了,大不了怪兽");
    }
}

雷欧奥特曼,不是来自M78星云,不用实现AoTeMan接口

public class LeiO implements InvocationHandler {
    private AoTeMan saiWen;

    public LeiO(SaiWen saiWen) {
        this.saiWen = saiWen;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("雷欧来到地球了");
        method.invoke(saiWen,args);
        System.out.println("雷欧开始打怪兽");
        return null;
    }
}

怪兽来了,奥特曼出动打怪兽

public class Test {
    public static void main(String[] args) {
        SaiWen saiWen = new SaiWen();
        AoTeMan leiO = (AoTeMan) Proxy.newProxyInstance(Test.class.getClassLoader(), new Class<?>[]{AoTeMan.class}, new LeiO(saiWen));
        leiO.playMaster();
    }
}

执行结果

雷欧来到地球了
我是赛文,我腿受伤了,大不了怪兽
雷欧开始打怪兽

2、执行原理图解

 思考,既然是动态生成的对象,我们能不能拿到这个动态生成的对象呢?答案是可以,我们通过二进制了把动态生成的对象保存到本地文件,然后查看源码就能一目了然。

拉取动态生成对象

 修改Test方法

public class Test {
    public static void main(String[] args) {
        SaiWen saiWen = new SaiWen();
        AoTeMan leiO = (AoTeMan) Proxy.newProxyInstance(Test.class.getClassLoader(), new Class<?>[]{AoTeMan.class}, new LeiO(saiWen));
        leiO.playMaster();
        createProxyClassFile();
    }

    public static void createProxyClassFile() {
        byte[] $Proxy0s = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{AoTeMan.class});

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

执行之后得到文件

 内容如下:

public final class $Proxy0 extends Proxy implements AoTeMan {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    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 void playMaster() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    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 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"));
            m3 = Class.forName("com.xiangxue.jack.test.AoTeMan").getMethod("playMaster");
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

 最主要的就是这个方法

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

我们再看看super.h.invoke(this, m3, (Object[])null);

super.h:protected InvocationHandler h;就是实现了InvocationHandler的LeiO奥特曼类h.invoke(this, m3, (Object[])null):就是LeiO中重写的invoke方法

我们把Proxy0.class类修改,其实就是一个普通的反射调用

public class DongTaiClass implements AoTeMan {
    LeiO h;
    public DongTaiClass(LeiO h) {
        this.h = h;
    }
    public final void playMaster() {

        try {
            //其实就是一个普通的反射的调用
            Method md = AoTeMan.class.getMethod("playMaster");
            this.h.invoke(this, md, null);
        } catch (Exception e) {
            e.printStackTrace();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
    }

    public static void main(String[] args) {
        new DongTaiClass(new LeiO(new SaiWen())).playMaster();
    }
}

这样看来事情就比较明朗了

问题:

1、被代理类必须要实现接口AoTeMan,才能够被代理(cglib不需要实现接口

3、使用cglib实现动态代理

引入maven

    <dependency>
      <groupId>cglib</groupId>
      <artifactId>cglib</artifactId>
      <version>3.2.5</version>
    </dependency>
    <dependency>
      <groupId>cglib</groupId>
      <artifactId>cglib-nodep</artifactId>
      <version>3.2.5</version>
    </dependency>

产生代理的工厂

public class CglibInstanceFactory {
    public static Object getInstance() {

        //拿到字节码增强器
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Original.class);
        // 这里的0和1对应callbacks中数组的下标
        enhancer.setCallbackFilter(new CallbackFilter() {
            @Override
            public int accept(Method method) {
                if ("add".equals(method.getName())) {
                    return 0;
                } else{
                    return 1;
                }
            }
        });

        //AddInterceptor对应0,DelInterceptor对应1
        Callback[] callbacks = {new AddInterceptor(), new DelInterceptor()};
        enhancer.setCallbacks(callbacks);
        return enhancer.create();
    }
}

原始类:注意没有实现任何接口

public class Original {
    public void add(){
        System.out.println("add");
    }

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

新增和删除代理

public class AddInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {

        System.out.println("新增之前");
        methodProxy.invokeSuper(o,objects);
        System.out.println("新增之后");
        return null;
    }
}
public class DelInterceptor  implements MethodInterceptor {
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {

        System.out.println("删除之前");
        methodProxy.invokeSuper(o,objects);
        System.out.println("删除之后");
        return null;
    }
}

执行类

public class Test {
    public static void main(String[] args) {
        Original instance = (Original)CglibInstanceFactory.getInstance();
        instance.add();
    }
}

执行结果

新增之前
add
新增之后

4、自定义动态代理的实现

这个用到了很多开发中基本不会使用到的类,所以是直接把享学jack老师的代码放在这里,给大家做个参考,也方便自己以后查阅

首先原始代码对象

还是奥特曼、雷欧、赛文三个实体,在1的demo中有的,就不再贴代码了

自定义ClassLoader

public class MyClassLoader extends ClassLoader {

    private File dir;

    MyClassLoader(String path) {
        dir = new File(path);
    }
    
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        if (dir != null) {
            File clazzFile = new File(dir, name + ".class");
            if (clazzFile.exists()) {
                try {
                    FileInputStream inputStream = new FileInputStream(clazzFile);

                    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
                    byte[] buffer = new byte[1024];
                    int len;
                    while ((len = inputStream.read(buffer)) != -1) {
                        byteArrayOutputStream.write(buffer, 0, len);
                    }
                    //返回反射对象
                    return defineClass("com.zhuguang.jack.myProxy." + name, byteArrayOutputStream.toByteArray(), 0,
                            byteArrayOutputStream.size());
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return super.findClass(name);
    }
}

自定义Handler接口

public interface MyInvocationHandler {
    public Object invoke(Object proxy, Method method, Object args)
        throws Throwable;
}

自定义代理对象MyProxy

public class MyProxy {

    private static String rt = "\r\n";

    //项目所在的目录
    private static String path = "E:\\idea\\public\\lesson20190305-aop\\src\\main\\java\\com\\zhuguang\\jack\\myProxy";

    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          MyInvocationHandler h) {

        //1、以字符串的形式拼凑出一个代理类
        String javaStr = getJavaStr(interfaces);
        //2、以流的方式写到java文件里面
        createFile(javaStr);
        //3、java文件动态编译成.class文件
        compiler();
        //4、自定义类加载器把磁盘里面的.class文件加载到内存
        //5、实例化这个内存里面的类然后把把实例返回
        MyClassLoader myClassLoader = new MyClassLoader(path);
        try {
            Class<?> $Proxy0 = myClassLoader.findClass("$Proxy0");

            Constructor<?> constructor = $Proxy0.getConstructor(MyInvocationHandler.class);
            Object o = constructor.newInstance(h);

            return o;
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return null;
    }

    private static void createFile(String javaStr) {
        File file = new File(path + "\\$Proxy0.java");
        try {
            FileWriter fw = new FileWriter(file);
            fw.write(javaStr);
            fw.flush();
            fw.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /*
    * 运行时编译.java文件
    * */
    private static void compiler() {
        try {
            JavaCompiler systemJavaCompiler = ToolProvider.getSystemJavaCompiler();
            StandardJavaFileManager standardFileManager = systemJavaCompiler.getStandardFileManager(null, null, null);
            Iterable<? extends JavaFileObject> javaFileObjects = standardFileManager.getJavaFileObjects(path + "\\$Proxy0.java");

            JavaCompiler.CompilationTask task = systemJavaCompiler.getTask(null, standardFileManager, null, null, null, javaFileObjects);
            task.call();
            standardFileManager.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

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

        Method[] methods = interfaces[0].getMethods();

        String proxyClassStr = "package com.zhuguang.jack.myProxy;" + rt
                + "import java.lang.reflect.Method;" + rt
                + "public class $Proxy0 implements " + interfaces[0].getName()
                + "{" + rt + "MyInvocationHandler h;" + rt
                + "public $Proxy0(MyInvocationHandler h) {" + rt
                + "this.h=h;" + rt + "}"
                + getMethodString(methods, interfaces[0]) + rt + "}";

        return proxyClassStr;
    }

    private static String getMethodString(Method[] methods, Class intf) {

        String proxyMe = "";

        for (Method method : methods) {
            proxyMe += "public void " + method.getName()
                    + "() throws Throwable {" + rt + "Method md = "
                    + intf.getName() + ".class.getMethod(\"" + method.getName()
                    + "\",new Class[]{});" + rt
                    + "this.h.invoke(this,md,null);" + rt + "}" + rt;

        }

        return proxyMe;
    }
}

测试

public class Test {
    public static void main(String[] args) {
        People o = (People)MyProxy.newProxyInstance(Test.class.getClassLoader(),
                new Class<?>[]{People.class}, new Parent(new Xiaoming()));

        try {
            o.zhaoduixiang();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
    }
}

理解

就是通过解析被代理的类,生成了一个新的java类字符串,然后输出流把java类字符串转换成本地的文件,然后类加载器加载这个java文件,变异成为class文件,租后通过反射拿到Class,然后调用代理方法。

简单来说就是生成了一个全新的类,在我们看不见的地方。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值