Java动态代理初探

说明:

  本文尝试探讨JDK1.7的Java动态代理内部执行原理,由于本人水平有限,不对之处还望指正!

一、场景说明

  需要对某些方法的性能进行监控,主要监控方法的执行时间,为此采用Java动态代理实现。

二、代码实例

2.1 业务接口

/**
 * 计算数字接口
 * 
 * @since 2016-08-19
 *
 */
public interface CountNumberService {
    
    /**
     * 计算1到max相加的值
     * 
     * @param max 最大数字
     * @return int(1到max相加的值)
     */
    public int countOneToHundredAdd(int max);
    
    /**
     * 计算1到max相乘的值
     * 
     * @param max 最大数字
     * @return int(1到max相乘的值)
     * 
     */
    public int countOneToHundredMultiply(int max);

}

2.2 业务接口实现类

/**
 * 计算数字接口默认实现
 * 
 * @since 2016-08-19
 *
 */
public class DefaultCountNumberServiceImpl implements CountNumberService {

    public int countOneToHundredAdd(int max) {
        
        int count = 0;
        
        for(int i = 1;i <= max;i++){
            count += i;
        }
        
        return count;
    }

    public int countOneToHundredMultiply(int max) {
        
        int count = 1;
        
        for(int i = 1;i <= max;i++){
            count = count * i;
        }
        
        try {
            //人为的增加该方法执行时间
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        return count;
    }

}

2.3 动态代理类

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

public class CountNumberProxy implements InvocationHandler {
    
    private Object target;
    
    public CountNumberProxy(Object target){
        this.target = target;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        
        //开始时间
        long startTime = System.currentTimeMillis();
        
        Object result = method.invoke(target, args);
        
        //结束时间
        long endTime = System.currentTimeMillis();
        
        System.out.println("该方法【" + method.getName() + "】执行时间为:" + (endTime - startTime));
        
        return result;
    }
    
    /**
     * 获取目标对象target的代理类
     * 
     * @return object 目标对象target代理类
     */
    public Object getProxy(){
        return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), target.getClass().getInterfaces(), this);
    }

}

2.4 测试

public class Test {
    
    public static void main(String[] args) {
        
        //目标对象
        CountNumberService countNumberService = new DefaultCountNumberServiceImpl();
        
        //构造动态代理类
        CountNumberProxy proxy = new CountNumberProxy(countNumberService);
        
        //获取代理对象
        countNumberService = (CountNumberService) proxy.getProxy();
        
        int max = 10;
        
        System.out.println("1到" + max + "相加的值为:" + countNumberService.countOneToHundredAdd(max));
        System.out.println("1到" + max +"相乘的值为:" + countNumberService.countOneToHundredMultiply(max));
        
    }

}

执行结果:

该方法【countOneToHundredAdd】执行时间为:0
1到10相加的值为:55
该方法【countOneToHundredMultiply】执行时间为:2012
1到10相乘的值为:3628800

2.5 总结

  通过Java的动态代理,实现了对方法的性能监控,统计出了方法的执行时间。从代码实例可以看出,要想实现Java动态代理,需要实现InvocationHandler接口,重写invoke(...)方法,通过Proxy.newProxyInstance(...)方法获得代理类。然而,Java内部是如何执行动态代理,下面进行初步探讨。

三、Java动态代理内部实现

3.1 InvocationHandler接口

  InvocationHandler接口,只有一个方法:public Object invoke(Object proxy, Method method, Object[] args) throws Throwable。该方法是真正执行监控性能的地方。

  参数说明:

    proxy - 实现InvocationHandler接口的动态代理类中目标对象的代理类,该类由Java动态代理内部自动生成并传入

    method -目标对象中的方法,Java动态代理内部通过方法获得并传入。

          例如:m3 = Class.forName("com.proxy.two.service.CountNumberService").getMethod("countOneToHundredAdd", new Class[] { Integer.TYPE });

             m3即为反射获取的method。

    args - method方法参数的入参,该参数由proxy代理类对于method方法入参传入。

3.2 Proxy类

  该类是Java动态代理获取目标对象代理类的超类,该类提供了获取目标对象代理类实例的静态方法。

  Proxy类主要关注两个方法,一个属性。

   /**
      * the invocation handler for this proxy instance.
      * @serial
      */
     protected InvocationHandler h;

   说明:
     该属性是实现InvocationHandler接口的动态代理类,3.1中invoke(...)方法的调用就是通过:this.h.invoke(...)来调用的,而this就是该属性引用的动态代理类。

 

  /**
     * Constructs a new {@code Proxy} instance from a subclass
     * (typically, a dynamic proxy class) with the specified value
     * for its invocation handler.
     *
     * @param   h the invocation handler for this proxy instance
     */
    protected Proxy(InvocationHandler h) {
        doNewInstanceCheck();
        this.h = h;
    }

  说明:
    该带参构造函数目的是:实例化目标对象代理类。
说明:
  下面该方法是实例化目标对象代理类,通过该方法就能获取目标对象代理类实例

public
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException { if (h == null) { throw new NullPointerException(); } 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 { final Constructor<?> cons = cl.getConstructor(constructorParams); final InvocationHandler ih = h; if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) { // create proxy instance with doPrivilege as the proxy class may // implement non-public interfaces that requires a special permission return AccessController.doPrivileged(new PrivilegedAction<Object>() { public Object run() { return newInstance(cons, ih); } }); } else { return newInstance(cons, ih); } } catch (NoSuchMethodException e) { throw new InternalError(e.toString()); } }

3.3 使用ProxyGenerator.generateProxyClass(...)

     使用ProxyGenerator.generateProxyClass(...)方法能获取代理类class字节数组,通过FileOutputStream写入class字节数组获取代理类class文件。

     代码实例目标对象代理类class文件内容如下:

import com.proxy.two.service.CountNumberService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class proxy89 extends Proxy
  implements CountNumberService
{
  private static Method m1;
  private static Method m4;
  private static Method m0;
  private static Method m3;
  private static Method m2;

  public proxy89(InvocationHandler paramInvocationHandler)
    throws 
  {
    super(paramInvocationHandler);
  }

  public final boolean equals(Object paramObject)
    throws 
  {
    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 int countOneToHundredMultiply(int paramInt)
    throws 
  {
    try
    {
      return ((Integer)this.h.invoke(this, m4, new Object[] { Integer.valueOf(paramInt) })).intValue();
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final int hashCode()
    throws 
  {
    try
    {
      return ((Integer)this.h.invoke(this, m0, null)).intValue();
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final int countOneToHundredAdd(int paramInt)
    throws 
  {
    try
    {
      return ((Integer)this.h.invoke(this, m3, new Object[] { Integer.valueOf(paramInt) })).intValue();
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final String toString()
    throws 
  {
    try
    {
      return (String)this.h.invoke(this, m2, null);
    }
    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") });
      m4 = Class.forName("com.proxy.two.service.CountNumberService").getMethod("countOneToHundredMultiply", new Class[] { Integer.TYPE });
      m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
      m3 = Class.forName("com.proxy.two.service.CountNumberService").getMethod("countOneToHundredAdd", new Class[] { Integer.TYPE });
      m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
      return;
    }
    catch (NoSuchMethodException localNoSuchMethodException)
    {
      throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
    }
    catch (ClassNotFoundException localClassNotFoundException)
    {
      throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
    }
  }
}

  从上可以看出:public final class proxy89 extends Proxy implements CountNumberService,目标对象代理类继承了Proxy超类,实现了目标对象实现的接口CountNumberService。这也说明了为什么Java动态代理需要接口的原因。

3.4 使用ProxyGenerator.generateProxyClass(...)实例

import java.io.FileOutputStream;
import java.io.IOException;

import sun.misc.ProxyGenerator;

public class ProxyClassUtil {

    
    @SuppressWarnings("restriction")
    public static void generateProxyClassToPath(String path,String proxyName,Class<?>[] interfaces){
        
        byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces);
         
        FileOutputStream out = null;
        try{
            out = new FileOutputStream(path);
            out.write(proxyClassFile);
            out.flush();
        }catch(Exception e){
            e.printStackTrace();
        }finally{
            if(null != out){
                try {
                    out.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

}
public class Test {
    
    public static void main(String[] args) {
        
        //目标对象
        CountNumberService countNumberService = new DefaultCountNumberServiceImpl();
        
        //构造代理类
        CountNumberProxy proxy = new CountNumberProxy(countNumberService);
        
        //获取代理
        countNumberService = (CountNumberService) proxy.getProxy();
        
        int max = 10;
        
        System.out.println("1到" + max + "相加的值为:" + countNumberService.countOneToHundredAdd(max));
        System.out.println("1到" + max +"相乘的值为:" + countNumberService.countOneToHundredMultiply(max));
//生成目标对象代理类class文件 ProxyClassUtil.generateProxyClassToPath(
"d://proxy89.class", "proxy89", DefaultCountNumberServiceImpl.class.getInterfaces()); } }

 

 

       

 

posted @ 2016-08-19 16:29  wuq126  阅读(192)  评论(0编辑  收藏  举报