jdk动态代理底层实现

一、代理设计模式

代理设计模式是Java常用的设计模式之一。

特点:

01.委托类和代理类有共同的接口或者父类;

02.代理类负责为委托类处理消息,并将消息转发给委托类;

03.委托类和代理类对象通常存在关联关系,一个代理类对象与一个委托类对象关联;

04.代理类本身不是真正的实现者,而是通过调用委托类方法来实现代理功能;

二、静态代理与动态代理

按照代理类创建的时机,代理类分为两种:

01.静态代理:由我们程序猿或者特定的工具自动生成了源代码,在程序运行之前,class文件已经存在了;例如在serviceImpl.java中调用dao.xx(),真正的实现者是dao,service就可以理解为一个代理类;

02.动态代理:在程序运行期间,通过反射创建出来的代理类;

三、jdk动态代理

顾名思义,这种方式是由jdk为我们提供的。下面通过一个例子来演示。

01.创建一个Person接口

public interface Person {
    void eat();
    void sleep();
}

02.创建ZhangSan.java实现Person接口

public class ZhangSan implements Person {

    public void eat() {
        System.out.println("吃饭...");
    }

    public void sleep() {
        System.out.println("睡觉...");
    }

}

03.创建代理类ZSProxy.java

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

public class ZSProxy implements InvocationHandler {

    //被代理类对象引用
    private ZhangSan zhangSan;

    public ZSProxy(ZhangSan zhangSan) {
        super();
        this.zhangSan = zhangSan;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before();
        //zhangSan.eat();
        Object result = method.invoke(zhangSan, args);//可以获取目标方法的返回值
        after();
        return null;
    }

    private void before() {
        System.out.println("前置...");
    }
    private void after() {
        System.out.println("后置...");
    }
}

jdk动态代理中必须了解一个类和一个接口:Proxy类和InvocationHandler接口。

001.上述中的代理类实现了 InvocationHandler接口,此接口只有一个invoke方法

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

proxy:代理类对象

method:被代理的方法

args:被代理方法的参数列表

002.Proxy类

public class Proxy implements java.io.Serializable {
        ... 

        public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException    

        ...
}

loader:类加载器

interfaces:代理类实现的所有接口

h:InvocationHandler接口的一个实例

public class Test {

    public static void main(String[] args) throws Throwable {
        System.out.println("----------------------JDK动态代理----------------------------");
        //获取代理类对象
        Person proxyInstance = (Person)Proxy.newProxyInstance(Person.class.getClassLoader(),
                new Class[] {Person.class}, new ZSProxy(new ZhangSan()));
        proxyInstance.eat();
}

通过Proxy的newProxyInstance方法创建出代理对象,再有代理对象执行方法。

程序运行结果:

虽然效果实现了,但我们并不能从代码看到哪里调用的invoke方法??那么底层到底是怎么执行的呢??

首先要了解一个类 ==》$Proxy0.java

001. $Proxy0 是内存中的代理类,在$Proxy0中会持有一个InvocationHandler接口的实例类的引用,所以此Test类先是调用了内存中的$Proxy0.eat();

002.执行$Proxy0类中的invoke

我们debug运行观察:

内存中代理类的特征:

01.它是对目标类的代理,那么这个内存中中的代理类的类型必须和被代理类(目标类)的类型一致。

代码中 (Person)Proxy.newProxyInstance.. 进行了向下转型操作。

02.内存中的代理类必须持有InvocationHandler接口的实现类引用。

整个过程中$Proxy0我们是看不到的!那么有没有办法让它原形毕露呢?

  /**
     * 使用IO的方式将内存中代理类写入到文件中,然后反编译出来进行观察
     */
    private static void createProxyClassFile() {
        byte[] data = ProxyGenerator.generateProxyClass("$Proxy0.class", new Class[] {Person.class});

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

对$Proxy0.java反编译后

  1 public final class class extends Proxy
  2     implements Person
  3 {
  4
  5     private static Method m1;
  6     private static Method m4;
  7     private static Method m2;
  8     private static Method m3;
  9     private static Method m0;
 10
 11     public class(InvocationHandler invocationhandler) //通过构造将handler传入
 12     {
 13         super(invocationhandler);
 14     }
 15
 16     public final boolean equals(Object obj)
 17     {
 18         try
 19         {
 20             return ((Boolean)super.h.invoke(this, m1, new Object[] {
 21                 obj
 22             })).booleanValue();
 23         }
 24         catch (Error ) { }
 25         catch (Throwable throwable)
 26         {
 27             throw new UndeclaredThrowableException(throwable);
 28         }
 29     }
 30
 31     public final void eat()
 32     {
 33         try
 34         {
 35             super.h.invoke(this, m4, null);// h:就是上文说的 此类中必须存在InvocationHandler接口实现类的引用,也是这里真正调用了invoke方法
 36             return;
 37         }
 38         catch (Error ) { }
 39         catch (Throwable throwable)
 40         {
 41             throw new UndeclaredThrowableException(throwable);
 42         }
 43     }
 44
 45     public final String toString()
 46     {
 47         try
 48         {
 49             return (String)super.h.invoke(this, m2, null);
 50         }
 51         catch (Error ) { }
 52         catch (Throwable throwable)
 53         {
 54             throw new UndeclaredThrowableException(throwable);
 55         }
 56     }
 57
 58     public final void sleep()
 59     {
 60         try
 61         {
 62             super.h.invoke(this, m3, null);
 63             return;
 64         }
 65         catch (Error ) { }
 66         catch (Throwable throwable)
 67         {
 68             throw new UndeclaredThrowableException(throwable);
 69         }
 70     }
 71
 72     public final int hashCode()
 73     {
 74         try
 75         {
 76             return ((Integer)super.h.invoke(this, m0, null)).intValue();
 77         }
 78         catch (Error ) { }
 79         catch (Throwable throwable)
 80         {
 81             throw new UndeclaredThrowableException(throwable);
 82         }
 83     }
 84
 85     static
 86     {
 87         try
 88         {
 89             m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] {
 90                 Class.forName("java.lang.Object")
 91             });
 92             m4 = Class.forName("cn.yzx.jdkProxy.Person").getMethod("eat", new Class[0]);
 93             m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
 94             m3 = Class.forName("cn.yzx.jdkProxy.Person").getMethod("sleep", new Class[0]);
 95             m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
 96         }
 97         catch (NoSuchMethodException nosuchmethodexception)
 98         {
 99             throw new NoSuchMethodError(nosuchmethodexception.getMessage());
100         }
101         catch (ClassNotFoundException classnotfoundexception)
102         {
103             throw new NoClassDefFoundError(classnotfoundexception.getMessage());
104         }
105     }
106 }

那么就真相大白了。

********************************************************************************************************************************************

以上就是jdk为我们提供的动态代理。我们也可以模仿它的实现原理,自定义我们的动态代理。

01.创建Person接口

package cn.yzx.myProxy;

public interface Person {
    void eat() throws Throwable;
    void sleep() throws Throwable;
}

02.创建ZhangSan实现类

package cn.yzx.myProxy;

public class ZhangSan implements Person {

    public void eat() {
        System.out.println("吃饭...");
    }

    public void sleep() {
        System.out.println("睡觉...");
    }
}

03.ZSProxy代理类

package cn.yzx.myProxy;

import java.lang.reflect.Method;

public class ZSProxy implements MyInvocationHandler {
    //目标对象
    private Person person;

    public ZSProxy(Person person) {
        this.person = person;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object args) {
        before();
        try {
            person.eat();
        } catch (Throwable e) {
            e.printStackTrace();
        }
        after();
        return null;
    }

    private void before() {
        System.out.println("前置...");
    }
    private void after() {
        System.out.println("后置...");
    }

}

04.自定义动态代理Proxy类

  1 package cn.yzx.myProxy;
  2
  3 import java.io.File;
  4 import java.io.FileWriter;
  5 import java.io.IOException;
  6 import java.lang.reflect.Constructor;
  7 import java.lang.reflect.InvocationTargetException;
  8 import java.lang.reflect.Method;
  9
 10 import javax.tools.JavaCompiler;
 11 import javax.tools.JavaCompiler.CompilationTask;
 12 import javax.tools.StandardJavaFileManager;
 13 import javax.tools.ToolProvider;
 14
 15
 16 /**
 17  * 自定义的动态代理Proxy类
 18  * @author Xuas
 19  */
 20 public class MyProxy {
 21     static String rt = "\r\n";
 22     /**
 23      * 自定义创建内存中代理实例的方法
 24      * @param loader
 25      * @param interfaces
 26      * @param handler
 27      * @return
 28      */
 29     public static Object newProxyInstance(ClassLoader loader, Class interfaces, MyInvocationHandler handler) {
 30
 31         try {
 32             Method[] methods = interfaces.getMethods();
 33             //01.使用拼凑字符串的方式将内存中的代理类拼出来
 34             String proxyClass = "package cn.yzx.myProxy;" + rt + "import java.lang.reflect.Method;" + rt
 35                     + "public class $Proxy0 implements " + interfaces.getName() + "{" + rt + "MyInvocationHandler h;" + rt
 36                     + "public $Proxy0(MyInvocationHandler h) {" + rt + "this.h = h;" + rt + "}"
 37                     + getMethodString(methods, interfaces) + rt + "}";
 38             //02.使用IO将拼凑的代理类写入到文件中
 39             String filePathName = "F:/Java/SpringAopDemo/src/main/java/cn/yzx/myProxy/$Proxy0.java";
 40             File file = new File(filePathName);
 41             FileWriter fw = new FileWriter(file);
 42             fw.write(proxyClass);
 43             fw.flush();
 44             fw.close();
 45
 46             //03.编译上面生成的java文件
 47             JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
 48             StandardJavaFileManager fileManager = compiler.getStandardFileManager(null,
 49                     null, null);//文件管理器
 50             Iterable javaFileObjects = fileManager.getJavaFileObjects(filePathName);//获取java文件
 51             CompilationTask task = compiler.getTask(null, fileManager, null, null, null, javaFileObjects);//获取编译任务
 52             task.call();//编译
 53             fileManager.close();//关闭文件管理器
 54             /**
 55              * 此时被编译后的class文件还在硬盘之中!它应该存在于JVM内存中!
 56              *         所以要把硬盘中class文件加载到JVM内存中去!
 57              */
 58             //04.把硬盘中的class文件加载到内存中去,要使用ClassLoader
 59             MyClassLoader loader1 = new MyClassLoader("F:/Java/SpringAopDemo/src/main/java/cn/yzx/myProxy");
 60
 61             //proxy0Clazz:就是内存中代理类的class对象
 62             Class<?> proxy0Clazz = loader1.findClass("$Proxy0");//返回被代理类的class对象
 63             /**
 64              * public $Proxy0(MyInvocationHandler h) {..}
 65              * 要通过构造器的参数将handler对象的引用传进来
 66              */
 67             Constructor<?> constructor = proxy0Clazz.getConstructor(MyInvocationHandler.class);
 68             Object instance = constructor.newInstance(handler);//返回内存中代理类实例
 69             return instance;
 70         } catch (IOException e) {
 71             e.printStackTrace();
 72         } catch (ClassNotFoundException e) {
 73             e.printStackTrace();
 74         } catch (NoSuchMethodException e) {
 75             e.printStackTrace();
 76         } catch (SecurityException e) {
 77             e.printStackTrace();
 78         } catch (InstantiationException e) {
 79             e.printStackTrace();
 80         } catch (IllegalAccessException e) {
 81             e.printStackTrace();
 82         } catch (IllegalArgumentException e) {
 83             e.printStackTrace();
 84         } catch (InvocationTargetException e) {
 85             e.printStackTrace();
 86         }
 87         return null;
 88     }
 89
 90     private static String getMethodString(Method[] methods, Class interfaces) {
 91
 92         String proxyMe = "";
 93
 94         for (Method method : methods) {
 95             proxyMe += "public void " + method.getName() + "() throws Throwable {" + rt + "Method md = "
 96                     + interfaces.getName() + ".class.getMethod(\"" + method.getName() + "\",new Class[]{});" + rt
 97                     + "this.h.invoke(this,md,null);" + rt + "}" + rt;
 98         }
 99
100         return proxyMe;
101     }
102 }

05.自定义InvocationHandler接口

 1 package cn.yzx.myProxy;
 2
 3 import java.lang.reflect.Method;
 4
 5 /**
 6  * 自定义InvocationHandler接口
 7  * @author Xuas
 8  *
 9  */
10 public interface MyInvocationHandler {
11     public Object invoke(Object proxy, Method method, Object args);
12 }

06.自定义的类加载器

package cn.yzx.myProxy;

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

public class MyClassLoader extends ClassLoader {

    private File dir;

    public MyClassLoader(String path) {//要加载的文件路径
        dir = new File(path);
    }
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {

        if(null != dir) {
            File clazzFile = new File(dir, name + ".class");
            if(clazzFile.exists()) {
                try {
                    FileInputStream input = new FileInputStream(clazzFile);
                    ByteArrayOutputStream baos = new ByteArrayOutputStream();
                    byte[] buffer = new byte[1024];
                    int len;
                    while((len = input.read(buffer)) != -1) {
                        baos.write(buffer, 0, len);
                    }
                    return defineClass("cn.yzx.myProxy."+name,
                            baos.toByteArray(),
                            0,
                            baos.size());//最终把输出流输出到JVM内存中
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return super.findClass(name);
    }

}

07.内存中自定义生成的 $Proxy0.java

 1 package cn.yzx.myProxy;
 2 import java.lang.reflect.Method;
 3 public class $Proxy0 implements cn.yzx.myProxy.Person{
 4 MyInvocationHandler h;
 5 public $Proxy0(MyInvocationHandler h) {
 6 this.h = h;
 7 }public void sleep() throws Throwable {
 8 Method md = cn.yzx.myProxy.Person.class.getMethod("sleep",new Class[]{});
 9 this.h.invoke(this,md,null);
10 }
11 public void eat() throws Throwable {
12 Method md = cn.yzx.myProxy.Person.class.getMethod("eat",new Class[]{});
13 this.h.invoke(this,md,null);
14 }
15
16 }

08.测试类

public class Test {

    public static void main(String[] args) throws Throwable {
        System.out.println("----------------------自定义动态代理----------------------------");
        cn.yzx.myProxy.Person person2 = (cn.yzx.myProxy.Person)MyProxy.newProxyInstance(cn.yzx.myProxy.Person.class.getClassLoader(),
                cn.yzx.myProxy.Person.class,
                new cn.yzx.myProxy.ZSProxy(new cn.yzx.myProxy.ZhangSan()));
        person2.eat();
    }
}

程序运行结果:

************************************************************************************************************************************************

总结:

通过观察Proxy中的newProxyInstance方法的参数可知,jdk动态代理只支持委托类和代理类实现共同的接口的方式。

如果是实现共同父类的情况,不能使用jdk动态代理。可以使用cglib动态代理。

既然写到这了,那我就再简单介绍下cglib动态代理吧 -。 -

cglib动态代理(接口和父类的情况都可行)

两个关键因素:

01.MethodInterceptor接口中的interceptor方法。

public Object intercept(Object obj,
              Method method,Object[] args,MethodProxy proxy) throws Throwable;

interceptor是所有拦截器执行的方法,类似于jdk动态代理中的invoke

02.Enhancer类

public void setSuperclass(Class superclass) {
                  if (superclass != null && superclass.isInterface()) {
                      setInterfaces(new Class[]{ superclass });
                  } else if (superclass != null && superclass.equals(Object.class)) {
                      // affects choice of ClassLoader
                      this.superclass = null;
                  } else {
                      this.superclass = superclass;
                  }
  }

设置委托类和代理类的公共接口或者公共父类。

代理类执行完毕后,通知委托类:

public void setCallback(final Callback callback) {
          setCallbacks(new Callback[]{ callback });
}

在Enhancer的父类AbstractClassGenerator中有一个方法 作用是创建需要的代理类

protected Object create(Object key)

写个例子:

01.创建父类Animal

package cn.yzx.cglib;

public abstract class Animal {
    abstract String eat();
}

02.实现类Cat

package cn.yzx.cglib;

public class Cat extends Animal{
    public String eat() {
        System.out.println("cat吃饭");
        return "haha";
    }
}

03.代理类

package cn.yzx.cglib;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.junit.Test;

import java.lang.reflect.Method;

public class CglibProxy implements MethodInterceptor {

    private Enhancer enhancer = new Enhancer();

    /**
     * 创建代理类对象
     *         参数:委托类Class对象
     *         返回值:代理类对象
     */
    public Object createProxyObject(Class<?> clazz){
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(this);
        return enhancer.create();
    }

    /**
     * 代理类执行委托类方法
     */
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println("前置");
        Object result = methodProxy.invokeSuper(o,args);
        System.out.println("后置");
        System.out.println(result);
        return result;
    }

    /**
     * 测试方法
     */

    @Test
    public void test(){
        CglibProxy proxy = new CglibProxy();
        Animal cat = (Animal) proxy.createProxyObject(Cat.class);
        cat.eat();
    }
}

测试结果:

ok...

就酱吧

原文地址:https://www.cnblogs.com/9513-/p/8432276.html

时间: 2024-08-27 03:03:43

jdk动态代理底层实现的相关文章

高仿JDK动态代理 底层源码实现

动态代理实现思路 实现功能:通过Proxy.newProxyInstance返回代理对象 1.创建一个处理业务逻辑的接口,我们也和JDK一样,都使用InvocationHandler作为接口名,然后接口里面一个invoke方法,jdk呢是有三个参数,我们简化了一下就只要两个参数即可. 2.声明一段java代码字符串(动态产生代理类) 3.使用IO创建代理类的java文件,将java代码字符串写入java文件 4.编译代理类,通过URLClassLoader把代理类加载到内存中 5.使用Const

jdk动态代理和cglib动态代理底层实现原理超详细解析(jdk动态代理篇)

代理模式是一种很常见的模式,关于底层原理网上看到很多的有关的讲解,但看了一些都觉得比较粗略,很多时候把底层代码copy下来也不大讲解,感觉不如自己详细的写上一篇.本文将以非常详细的说明来分析jdk动态代理底层的实现原理,篇幅较长,但是每个核心方法代码中每步都有说明.还请耐心阅读 1.举例 public class ProxyFactory implements InvocationHandler { private Class target; public <T>T getProxy(Clas

Spring AOP的底层实现技术---JDK动态代理

JDK动态代理    在JDK 1.3以后提供了动态代理的技术,允许开发者在运行期创建接口的代理实例.在Sun刚推出动态代理时,还很难想象它有多大的实际用途,现在我们终于发现动态代理是实现AOP的绝好底层技术.    JDK的动态代理主要涉及到java.lang.reflect包中的两个类:Proxy和InvocationHandler.其中InvocationHandler是一个接口,可以通过实现该接口定义横切逻辑,在并通过反射机制调用目标类的代码,动态将横切逻辑和业务逻辑编织在一起.   而

JDK动态代理[2]----JDK动态代理的底层实现之Proxy源码分析

在上一篇里为大家简单介绍了什么是代理模式?为什么要使用代理模式?并用例子演示了一下静态代理和动态代理的实现,分析了静态代理和动态代理各自的优缺点.在这一篇中笔者打算深入源码为大家剖析JDK动态代理实现的机制,建议读者阅读本篇前可先阅读一下笔者上一篇关于代理模式的介绍<JDK动态代理[1]----代理模式实现方式的概要介绍> 上一篇动态代理的测试类中使用了Proxy类的静态方法newProxyInstance方法去生成一个代理类,这个静态方法接收三个参数,分别是目标类的类加载器,目标类实现的接口

jdk动态代理和cglib动态代理底层实现原理详细解析(cglib动态代理篇)

代理模式是一种很常见的模式,关于底层原理网上看到很多的有关的讲解,但看了一些都觉得比较粗略,很多时候把底层代码copy下来也不大讲解,感觉不如自己详细的写上一篇.本文将以非常详细的说明来分析cglib动态代理底层的实现原理,篇幅较长,但是每个核心方法代码中每步都有说明.还请耐心阅读 1. 举例 使用cglib代理需要引入两个包,maven的话包引入如下 <!-- https://mvnrepository.com/artifact/cglib/cglib --> <dependency&

Java进阶之 JDK动态代理与Cglib动态代理

一.动态代理概述: 与静态代理对照(关于静态代理的介绍 可以阅读上一篇:JAVA设计模式之 代理模式[Proxy Pattern]), 动态代理类的字节码是在程序运行时由Java反射机制动态生成. 注意: 1.AspectJ是采用编译时生成AOP代理类,具有更好的性能,但是需要使用特定的编译器进行处理 2.Spring AOP采用运行时生成AOP代理类,无需使用特定编译器进行处理,但是性能相对于AspectJ较差 二.JDK动态代理 [对有实现接口的对象做代理] 1.JDK动态代理中 需要了解的

Java反射—运用反射生成jdk动态代理

1.  核心类&接口 在Java的java.lang.reflect包下提供一个Proxy类和一个InvocationHandler接口,通过使用这个类和接口可以生成jdk动态代理类或动态代理对象. Proxy是所有动态代理类的父类,它提供了两个静态方法来创建动态代理类和动态代理对象,如下: ?  static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces) ?  static Objec

Java学习之:JDK动态代理与CGLIB动态代理

代理的概念:简单的理解就是通过为某一个对象创建一个代理对象,我们不直接引用原本的对象,而是由创建的代理对象来控制对原对象的引用. 动态代理:是指在程序运行时由Java反射机制动态生成,无需手动编写代码.动态代理不仅简化了编程工作,而且提高了软件系统的可扩展性,因为Java反射机制可以生成任意类型的动态代理类. 代理原理:代理对象内部含有对真实对象的引用,从而可以操作真实对象,同时代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象.同时,代理对象可以在执行真实对象操作时,附加其他的操作

Spring AOP详解 、 JDK动态代理、CGLib动态代理

AOP是Aspect Oriented Programing的简称,面向切面编程.AOP适合于那些具有横切逻辑的应用:如性能监测,访问控制,事务管理以及日志记录.AOP将这些分散在各个业务逻辑中的代码通过横向切割的方式抽取到一个独立的模块中. 一.AOP术语 1.连接点(Joinpoint) 程序执行的某个特定位置:如类开始初始化之前.类初始化之后.类某个方法调用前.调用后等:一个类或一段程序代码拥有一些具有边界性质的特定点,这些代码中的特定点就成为“连接点”,Spring仅支持方法的连接点,即