zoukankan      html  css  js  c++  java
  • JAVA 动态代理原理和实现

    在 Java 中动态代理和代理都很常见,几乎是所有主流框架都用到过的知识。在面试中也是经常被提到的话题,于是便总结了本文。

    Java动态代理的基本原理为:被代理对象需要实现某个接口(这是前提),代理对象会拦截对被代理对象的方法调用,在其中可以全然抛弃被代理对象的方法实现而完成另外的功能,也可以在被代理对象方法调用的前后增加一些额外的功能。

    动态代理可以为其他对象提供一个代理以控制对某个对象的访问。

    代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。

    JDK 动态代理

    动态代理的核心其实就是代理对象的生成,即 Proxy.newProxyInstance(classLoader, proxyInterface, handler)。

    让我们进入newProxyInstance方法观摩下,核心代码其实就三行。

    这个方法需要三个参数:

    • ClassLoader,用于加载代理类的 Loader 类,通常这个 Loader 和被代理的类是同一个 Loader 类。
    • Interfaces,是要被代理的那些那些接口。
    • InvocationHandler,就是用于执行除了被代理接口中方法之外的用户自定义的操作,他也是用户需要代理的最终目的。用户调用目标方法都被代理到 InvocationHandler 类中定义的唯一方法 invoke 中。
    1
    2
    3
    4
    5
    6
    //获取代理类  
    Class cl = getProxyClass(loader, interfaces);  
    //获取带有InvocationHandler参数的构造方法  
    Constructor cons = cl.getConstructor(constructorParams);  
    //把handler传入构造方法生成实例  
    return (Object) cons.newInstance(new Object[] { h });

    一个典型的动态代理创建对象过程可分为以下四个步骤:

    1、通过实现InvocationHandler接口创建调用处理器 

    1
    IvocationHandler handler = new InvocationHandlerImpl(...);

    2、通过为Proxy类指定ClassLoader对象和一组interface创建动态代理类

    1
    Class clazz = Proxy.getProxyClass(classLoader,new Class[]{...});

    3、通过反射机制获取动态代理类的构造函数,其参数类型是调用处理器接口类型

    1
    Constructor constructor = clazz.getConstructor(new Class[]{InvocationHandler.class});

    4、通过构造函数创建代理类实例,此时需将调用处理器对象作为参数被传入

    1
    Interface Proxy = (Interface)constructor.newInstance(new Object[] (handler));

    为了简化对象创建过程,Proxy类中的newProxyInstance方法封装了2~4,只需两步即可完成代理对象的创建。

    1
    2
    Subject proxySubject = (Subject)Proxy.newProxyInstance(Subject.class.getClassLoader(),
            new Class[]{Subject.class}, new InvocationHandlerImpl (real));

    生成的proxySubject继承Proxy类实现Subject接口。实现的Subject的方法实际是调用处理器的invoke方法,而invoke方法利用反射调用的是被代理对象的方法(Object result=method.invoke(proxied,args));
    重点Proxy.newProxyInstance,源码分析,会在其他文档中单独总结记录。类Proxy的getProxyClass方法调用ProxyGenerator的 generateProxyClass方法产生ProxySubject.class的二进制数据。

    创建代理对象时序图

    创建代理对象时序图

    获取代理类

    getProxyClass(loader, interfaces)方法用于获取代理类,它主要做了三件事情:

    在当前类加载器的缓存里搜索是否有代理类,没有则生成代理类并缓存在本地JVM里。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    // 缓存的key使用接口名称生成的List  
    Object key = Arrays.asList(interfaceNames);  
    synchronized (cache) {  
        do {  
    Object value = cache.get(key);  
             // 缓存里保存了代理类的引用  
    if (value instanceof Reference) {  
        proxyClass = (Class) ((Reference) value).get();  
    }  
    if (proxyClass != null) {  
    // 代理类已经存在则返回  
        return proxyClass;  
    else if (value == pendingGenerationMarker) {  
        // 如果代理类正在产生,则等待  
        try {  
    cache.wait();  
        catch (InterruptedException e) {  
        }  
        continue;  
    else {  
        //没有代理类,则标记代理准备生成  
        cache.put(key, pendingGenerationMarker);  
        break;  
    }  
        while (true);  
    }

    生成并加载代理类

    代理类的生成主要是以下这两行代码:

    1
    2
    3
    4
    //生成代理类的字节码文件并保存到硬盘中(默认不保存到硬盘)   
    proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces);   
    //使用类加载器将字节码加载到内存中   
    proxyClass = defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length);  

    代理类的生成过程

    ProxyGenerator.generateProxyClass()方法属于sun.misc包下,Oracle并没有提供源代码,但是我们可以使用
    JD-GUI这样的反编译软件打开jrelib t.jar来一探究竟,以下是其核心代码的分析。 

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    //添加接口中定义的方法,此时方法体为空  
    for (int i = 0; i < this.interfaces.length; i++) {  
      localObject1 = this.interfaces[i].getMethods();  
      for (int k = 0; k < localObject1.length; k++) {  
         addProxyMethod(localObject1[k], this.interfaces[i]);  
      }  
    }  
    //添加一个带有InvocationHandler的构造方法  
    MethodInfo localMethodInfo = new MethodInfo("<init>""(Ljava/lang/reflect/InvocationHandler;)V"1);  
    //循环生成方法体代码(省略)  
    //方法体里生成调用InvocationHandler的invoke方法代码。(此处有所省略)  
    this.cp.getInterfaceMethodRef("InvocationHandler""invoke""Object; Method; Object;")  
    //将生成的字节码,写入硬盘,前面有个if判断,默认情况下不保存到硬盘。  
    localFileOutputStream = new FileOutputStream(ProxyGenerator.access$000(this.val$name) + ".class");  
    localFileOutputStream.write(this.val$classFile);

    生成的代理类源码

    那么通过以上分析,我们可以推出动态代理为我们生成了一个这样的代理类。把方法doSomeThing的方法体修改为调用LogInvocationHandler的invoke方法。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    public class ProxyBusiness extends Proxy implements IBusiness, IBusiness2 {  
    private LogInvocationHandler h;  
    @Override  
    public void doSomeThing2() {  
        try {  
            Method m = (h.target).getClass().getMethod("doSomeThing2",null);  
            h.invoke(this, m, null);  
        catch (Throwable e) {  
            // 异常处理(略)  
        }  
    @Override  
    public boolean doSomeThing() {  
        try {  
           Method m = (h.target).getClass().getMethod("doSomeThing"null);  
           return (Boolean) h.invoke(this, m, null);  
        catch (Throwable e) {  
            // 异常处理(略)  
        }  
        return false;  
    }  
    public ProxyBusiness(LogInvocationHandler h) {  
        this.h = h;  
    }

    测试代理的代码如下:

    1
    2
    3
    4
    5
    6
    7
    //测试
    public static void main(String[] args) {  
        //构建AOP的Advice  
        LogInvocationHandler handler = new LogInvocationHandler(new Business());  
        new ProxyBusiness(handler).doSomeThing();  
        new ProxyBusiness(handler).doSomeThing2();  
    }

    下面看一个自定义代理的实现。

    被代理类接口

    1
    2
    3
    public interface Subject   {  
      public void doSomething();  
    }

    被代理类

    1
    2
    3
    4
    5
    6
    //目标对象
    public class RealSubject implements Subject{
      public void doSomething() {
        System.out.println( "call doSomething()" );
      }
    }

    调用处理器(切面)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    public class ProxyHandler implements InvocationHandler   {  
      private Object proxied;  
      public ProxyHandler( Object proxied )   {  
        this.proxied = proxied;  
      }  
      public Object invoke( Object proxy, Method method, Object[] args ) throws Throwable {  
    //在转调具体目标对象之前,可以执行一些功能处理
    System.out.println( "doSomething before" );
        //转调具体目标对象的方法
        return method.invoke( proxied, args); 
    //在转调具体目标对象之后,可以执行一些功能处理
    System.out.println( "doSomething after" );
      }
    }

    测试我们的代理实现

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    public class DynamicProxy   {  
      public static void main( String args[] )   {  
        RealSubject real = new RealSubject();  
        Subject proxySubject = (Subject)Proxy.newProxyInstance(Subject.class.getClassLoader(),
         new Class[]{Subject.class}, new ProxyHandler(real));
        proxySubject.doSomething();
        //write proxySubject class binary data to file  
        createProxyClassFile();  
      }  
      public static void createProxyClassFile()   {  
        String name = "ProxySubject";  
        byte[] data = ProxyGenerator.generateProxyClass( name, new Class[] { Subject.class } );  
        try{  
          FileOutputStream out = new FileOutputStream( name + ".class" );  
          out.write( data );  
          out.close();  
        }catch( Exception e ) {  
          e.printStackTrace();  
        }  
      }  
    }

    运行结果:

    1
    2
    3
    doSomething before
    call doSomething()
    doSomething after

    Proxy 接口

    Proxy 的主要静态变量

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // 映射表:用于维护类装载器对象到其对应的代理类缓存
    private static Map loaderToCache = new WeakHashMap();
    // 标记:用于标记一个动态代理类正在被创建中
    private static Object pendingGenerationMarker = new Object();
    // 同步表:记录已经被创建的动态代理类类型,主要被方法 isProxyClass 进行相关的判断
    private static Map proxyClasses = Collections.synchronizedMap(new WeakHashMap());
    // 关联的调用处理器引用
    protected InvocationHandler h;
    Proxy的构造方法
    // 由于 Proxy 内部从不直接调用构造函数,所以 private 类型意味着禁止任何调用
    private Proxy() {}
    // 由于 Proxy 内部从不直接调用构造函数,所以 protected 意味着只有子类可以调用
    protected Proxy(InvocationHandler h) {this.h = h;}

    ProxySubject 源码

    创建的代理类 ProxySubject.class

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    import java.lang.reflect.*;  
    public final class ProxySubject extends Proxy  implements Subject{  
        private static Method m1;  
        private static Method m0;  
        private static Method m3;  
        private static Method m2;  
        public ProxySubject(InvocationHandler invocationhandler){  
            super(invocationhandler);  
        }  
        public final boolean equals(Object obj){  
            try {  
                return ((Boolean)super.h.invoke(this, m1, new Object[] {  
                    obj  
                })).booleanValue();  
            }catch(Error _ex) {
            }catch(Throwable throwable){  
                throw new UndeclaredThrowableException(throwable);  
            }  
        }  
        public final int hashCode()   { 
            try  {  
                return ((Integer)super.h.invoke(this, m0, null)).intValue();  
            }catch(Error _ex) {
            }catch(Throwable throwable){  
                throw new UndeclaredThrowableException(throwable);  
            }  
        }  
        /*关键部分*/
        public final void doSomething()  {  
            try {  
                // Proxy类中protected InvocationHandler h;关联的调用处理器引用
                super.h.invoke(this, m3, null);  
                return;  
            }catch(Error _ex) {  
            }catch(Throwable throwable) {  
                throw new UndeclaredThrowableException(throwable);  
            }  
        }  
        public final String toString() {  
            try {  
                return (String)super.h.invoke(this, m2, null);  
            catch(Error _ex) {
            catch(Throwable throwable){  
                throw new UndeclaredThrowableException(throwable);  
            }  
        }  
        static{  
            try {  
                m1 = Class.forName("java.lang.Object").getMethod("equals"new Class[] {  
                    Class.forName("java.lang.Object")  
                });  
                m0 = Class.forName("java.lang.Object").getMethod("hashCode"new Class[0]);  
                m3 = Class.forName("Subject").getMethod("doSomething"new Class[0]);  
                m2 = Class.forName("java.lang.Object").getMethod("toString"new Class[0]);  
            }catch(NoSuchMethodException nosuchmethodexception)   {  
                throw new NoSuchMethodError(nosuchmethodexception.getMessage());  
            }catch(ClassNotFoundException classnotfoundexception){  
                throw new NoClassDefFoundError(classnotfoundexception.getMessage());  
            }  
        }  
    }

    CGLib 动态代理

    动态字节码生成。使用动态字节码生成技术实现AOP原理是在运行期间目标字节码加载后,生成目标类的子类,将切面逻辑加入到子类中,所以使用Cglib实现AOP不需要基于接口。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    public static void main(String[] args) {  
        byteCodeGe();  
    }  
    public static void byteCodeGe() {  
        //创建一个织入器  
        Enhancer enhancer = new Enhancer();  
        //设置父类  
        enhancer.setSuperclass(Business.class);  
        //设置需要织入的逻辑  
        enhancer.setCallback(new LogIntercept());  
        //使用织入器创建子类  
        IBusiness2 newBusiness = (IBusiness2) enhancer.create();  
        newBusiness.doSomeThing2();  
    }  
    /** 
     * 记录日志 
     */  
    public static class LogIntercept implements MethodInterceptor {  
        @Override  
        public Object intercept(Object target, Method method, Object[] args, MethodProxy proxy) throws Throwable {  
            //执行原有逻辑,注意这里是invokeSuper  
            Object rev = proxy.invokeSuper(target, args);  
            //执行织入的日志  
            if (method.getName().equals("doSomeThing2")) {  
                System.out.println("记录日志");  
            }  
            return rev;  
        }  
    }

    转自:https://www.xttblog.com/?p=2732

  • 相关阅读:
    Vue 组件封装发布到npm 报错 Uncaught TypeError: Cannot read property 'toLowerCase' of undefined
    SpringBoot thymeleaf模板版本,thymeleaf模板更换版本
    SpringBoot application.properties (application.yml)优先级从高到低
    SpringBoot Cmd运行Jar文件指定active文件的命令如下
    Linux各文件夹的作用
    spring + Mybatis + pageHelper + druid 整合源码分享
    MyEclipse weblogic Deploy Location项目名称不正确解决方案
    Mybatis整合通用Dao,Mybatis整合通用Mapper,MyBatis3.x整合通用 Mapper3.5.x
    An internal error occurred during: "Launching xxx on WebLogic10.x".
    Spring集成Mybatis,spring4.x整合Mybatis3.x
  • 原文地址:https://www.cnblogs.com/incognitor/p/9759987.html
Copyright © 2011-2022 走看看