Java的动态代理机制

代理模式

代理模式是常用的java设计模式,特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。简单的说就是,我们在访问实际对象时,是通过代理对象来访问的,代理模式就是在访问实际对象时引入一定程度的间接性,因为这种间接性,可以附加多种用途。

UML类图

Java的动态代理机制

静态代理

静态代理由程序员创建或特定工具自动生成源代码,也就是在编译时就已经将接口,被代理类,代理类等确定下来。在程序运行之前,代理类的.class文件就已经生成

动态代理

代理类在程序运行时创建的代理方式被成为动态代理。

运行时实现指定的接口

想实现某个接口,你需要写一个类,然后在类名字的后面给出“implements”XXX接口,这是常规实现某个接口的方式:

public interface MyInterface {
	void fun1();
	void fun2();
}

public class MyInterfaceImpl implements MyInterface {
	public void fun1() {
		System.out.println("fun1()");
	}

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

动态代理技术可以通过一个方法调用就可以生成一个对指定接口的实现类对象:

Class[] cs = {MyInterface.class};
MyInterface mi = (MyInterface)Proxy.newProxyInstance(loader, cs, h);

上面代码中,Proxy类的静态方法newProxyInstance()方法生成了一个对象,这个对象实现了cs数组中指定的接口,即返回值mi是MyInterface接口的实现类。Proxy类的静态方法newProxyInstance()方法返回的方法是实现了指定接口的实现类对象

动态代理就是在运行时生成一个类,这个类会实现你指定的一组接口(代理),而这个类没有.java文件,是在运行时生成的(动态),你也不用去关心它是什么类型的,你只需要知道它实现了哪些接口即可。

newProxyInstance()方法的参数

Proxy类的newInstance()方法有三个参数:

  • ClassLoader loader:类加载器类型,MyInterface.class.getClassLoader()就可以获取到ClassLoader对象,一个Class对象就可以获取到一个ClassLoader对象;
  • Class[] interfaces:指定newProxyInstance()方法返回的对象要实现哪些接口,可以指定多个接口,例如上面例子只指定了一个接口:Class[] cs = {MyInterface.class};
  • InvocationHandler h:它是最重要的一个参数,是一个接口,名为调用处理器。无论你调用代理对象的什么方法,它都是在调用InvocationHandler的invoke()方法!
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class ProxyDemo {
    public static void main(String[] args) {
        Class[] cs = {MyInterface.class};
        ClassLoader loader = MyInterface.class.getClassLoader();

        InvocationHandler h = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("无论你调用代理对象的什么方法,其实都是在调用invoke()...");
                return null;
            }
        };

        MyInterface mi = (MyInterface) Proxy.newProxyInstance(loader, cs, h);
        mi.fun1();
        mi.fun2();
    }
}

interface MyInterface {
    void fun1();
    void fun2();
}

输出结果为:

无论你调用代理对象的什么方法,其实都是在调用invoke()...
无论你调用代理对象的什么方法,其实都是在调用invoke()...

InvocationHandler接口只有一个方法,即invoke()方法。它是对代理对象所有方法的唯一实现。也就是说,无论你调用代理对象上的哪个方法,其实都是在调用InvocationHandler的invoke()方法。

InvocationHandler的invoke()方法

InvocationHandler的invoke()方法的参数有三个:

  • Object proxy:代理对象,也就是Proxy.newProxyInstance()方法返回的对象,通常我们用不上它;
  • Method method:表示当前被调用方法的反射对象,例如mi.fun1(),那么method就是fun1()方法的反射对象;
  • Object[] args:表示当前被调用方法的参数,当然mi.fun1()这个调用是没有参数的,所以args是一个零长数组。
public static void main(String[] args) {
    Class[] cs = {MyInterface.class};
    ClassLoader loader = MyInterface.class.getClassLoader();
    InvocationHandler h = new InvocationHandler() {
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("当前调用的方法是:" + method.getName());
            return null;
        }
    };
    MyInterface mi = (MyInterface)Proxy.newProxyInstance(loader, cs, h);
    mi.fun1();
    mi.fun2();
}

输出:

当前调用的方法是:fun1
当前调用的方法是:fun2

invoke()方法的返回值为Object类型,它表示当前被调用的方法的返回值,当然mi.fun1()方法是没有返回值的,所以invoke()返回的就必须是null。

动态代理的使用

动态代理的用途与装饰模式很相似,就是为了对某个对象进行增强。所有使用装饰者模式的案例都可以使用动态代理来替换。

有一个Waiter接口,它只有一个serve()方法,MyWaiter是Waiter接口的实现类:

interface Waiter {
    public void serve();
}

class MyWaiter implements Waiter {
    public void serve() {
        System.out.println("服务...");
    }
}

现在我们要对MyWaiter对象进行增强,要让它在服务之前以及服务之后添加礼貌用语,即在服务之前说“您好!”,在服务之后说:“很高兴为您服务!”。
Java的动态代理机制

public class ProxyDemo2 {
    public static void main(String[] args) {
        ClassLoader loader = ProxyDemo2.class.getClassLoader();
        Class[] cs = {Waiter.class};
        Waiter target = new MyWaiter();
        MyInvocationHandler h = new MyInvocationHandler(target);
        Waiter waiter = (Waiter) Proxy.newProxyInstance(loader, cs, h);
        waiter.serve();
    }
}

class MyInvocationHandler implements InvocationHandler {

    private Waiter target;

    public MyInvocationHandler(Waiter 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 result;
    }
}

动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类中的方法。是因为所有被代理执行的方法,都是通过在InvocationHandler中的invoke方法调用的,所以我们只要在invoke方法中统一处理,就可以对所有被代理的方法进行相同的操作了

动态代理的原理

动态代理的过程

现在想为RealSubject这个类创建一个动态代理对象,JDK主要会做以下工作:

  1. 获取RealSubject上的所有接口列表;
  2. 确定要生成的代理类的类名,默认为:com.sun.proxy.$ProxyXXXX ;
  3. 根据需要实现的接口信息,在代码中动态创建该Proxy类的字节码;
  4. 将对应的字节码转换为对应的class 对象;
  5. 创建InvocationHandler实例handler,用来处理Proxy所有方法调用;
  6. Proxy的class对象 以创建的handler对象为参数,实例化一个proxy对象。

newProxyInstance源码

上面我们利用Proxy类的newProxyInstance方法创建了一个动态代理对象,查看该方法的源码,发现它只是封装了创建动态代理类的步骤:

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

        final Class<?>[] intfs = interfaces.clone();
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }

        /*
         * Look up or generate the designated proxy class.
         */
        Class<?> cl = getProxyClass0(loader, intfs);

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }

            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
            return cons.newInstance(new Object[]{h});
        } catch (IllegalAccessException|InstantiationException e) {
            throw new InternalError(e.toString(), e);
        } catch (InvocationTargetException e) {
            Throwable t = e.getCause();
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else {
                throw new InternalError(t.toString(), t);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString(), e);
        }
    }

生成动态代理类的字节码

JDK提供了sun.misc.ProxyGenerator.generateProxyClass(String proxyName,class[] interfaces) 底层方法来产生动态代理类的字节码:

下面定义了一个工具类,用来将生成的动态代理类保存到硬盘中:

package com.foo.proxy;
 
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Proxy;
import sun.misc.ProxyGenerator;
 
public class ProxyUtils {
 
	/*
	 * 将根据类信息 动态生成的二进制字节码保存到硬盘中,
	 * 默认的是clazz目录下
         * params :clazz 需要生成动态代理类的类
         * proxyName : 为动态生成的代理类的名称
         */
	public static void generateClassFile(Class clazz,String proxyName)
	{
		//根据类信息和提供的代理类名称,生成字节码
                byte[] classFile = ProxyGenerator.generateProxyClass(proxyName, clazz.getInterfaces()); 
		String paths = clazz.getResource(".").getPath();
		System.out.println(paths);
		FileOutputStream out = null;  
        
        try {
            //保留到硬盘中
            out = new FileOutputStream(paths+proxyName+".class");  
            out.write(classFile);  
            out.flush();  
        } catch (Exception e) {  
            e.printStackTrace();  
        } finally {  
            try {  
                out.close();  
            } catch (IOException e) {  
                e.printStackTrace();  
            }  
        }  
	}	
}

调用该方法可以获得动态代理类的class文件:

ProxyUtils.generateClassFile(target.getClass(), "WaiterProxy");

Java的动态代理机制

用反编译工具如jd-gui.exe 打开,将会看到以下信息:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import tjtulong.aop.Waiter;

public final class WaiterProxy
  extends Proxy
  implements Waiter
{
  private static Method m1;
  private static Method m3;
  private static Method m2;
  private static Method m0;
  
  public WaiterProxy(InvocationHandler paramInvocationHandler)
  {
    super(paramInvocationHandler);
  }
  
  public final boolean equals(Object paramObject)
  {
    try
    {
      return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }
  
  public final void serve()
  {
    try
    {
      this.h.invoke(this, m3, null);
      return;
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }
  
  public final String toString()
  {
    try
    {
      return (String)this.h.invoke(this, m2, null);
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }
  
  public final int hashCode()
  {
    try
    {
      return ((Integer)this.h.invoke(this, m0, null)).intValue();
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }
  
  static
  {
    try
    {
      m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
      m3 = Class.forName("tjtulong.aop.Waiter").getMethod("serve", new Class[0]);
      m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
      m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
      return;
    }
    catch (NoSuchMethodException localNoSuchMethodException)
    {
      throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
    }
    catch (ClassNotFoundException localClassNotFoundException)
    {
      throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
    }
  }
}

仔细观察可以看出生成的动态代理类有以下特点:

  1. 继承自 java.lang.reflect.Proxy,实现了Waiter接口;
  2. 类中的所有方法都是final 的;
  3. 所有的方法功能的实现都统一调用了InvocationHandler的invoke()方法。

Java的动态代理机制

cglib

JDK中提供的生成动态代理类的机制有个鲜明的特点是: 某个类必须有实现的接口,而生成的代理类也只能代理某个类接口定义的方法,比如:如果上面例子的ElectricCar实现了继承自两个接口的方法外,另外实现了方法bee() ,则在产生的动态代理类中不会有这个方法了。更极端的情况是:如果某个类没有实现接口,那么这个类就不能同JDK产生动态代理了。CGLIB(Code Generation Library),是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口。

cglib 创建某个类A的动态代理类的模式是:

  1. 查找A上的所有非final 的public类型的方法定义;
  2. 将这些方法的定义转换成字节码;
  3. 将组成的字节码转换成相应的代理的class对象;
  4. 实现 MethodInterceptor接口,用来处理 对代理类上所有方法的请求(这个接口和JDK动态代理InvocationHandler的功能和角色是一样的)。

一个有趣的例子(来自https://blog.csdn.net/luanlouis/article/details/24589193 ):定义一个Programmer类,一个Hacker类:

// 程序猿类
public class Programmer {
 
	public void code()
	{
		System.out.println("I'm a Programmer,Just Coding.....");
	}
}

Hacker类:

package samples;
 
import java.lang.reflect.Method;
 
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
/*
 * 实现了方法拦截器接口
 */
public class Hacker implements MethodInterceptor {
	@Override
	public Object intercept(Object obj, Method method, Object[] args,
			MethodProxy proxy) throws Throwable {
		System.out.println("**** I am a hacker,Let's see what the poor programmer is doing Now...");
		proxy.invokeSuper(obj, args);
		System.out.println("****  Oh,what a poor programmer.....");
		return null;
	} 
}

客户端:

package samples;
 
import net.sf.cglib.proxy.Enhancer;
 
public class Test {
 
	public static void main(String[] args) {
		Programmer progammer = new Programmer();
		
		Hacker hacker = new Hacker();
		//cglib 中加强器,用来创建动态代理
		Enhancer enhancer = new Enhancer();  
                 //设置要创建动态代理的类
		enhancer.setSuperclass(progammer.getClass());  
               // 设置回调,这里相当于是对于代理类上所有方法的调用,都会调用CallBack,而Callback则需要实行intercept()方法进行拦截
                enhancer.setCallback(hacker);
                Programmer proxy =(Programmer)enhancer.create();
                proxy.code();
        
	}
}

输出结果为:
Java的动态代理机制

参考:https://www.cnblogs.com/gonjan-blog/p/6685611.html