java动态代理机制详细分析

java动态代理机制详细分析

 

一、代理模式

代理模式是常用的java设计模式,特征是代理类与委托类实现同样的接口,代理类负责为委托类执行信息预处理、信息过滤、信息转发给委托类、事后信息处理。代理对象并不直接提供服务,而是通过调用委托对象相应方法实现,为对象访问提供了间接性。Spring中的AOP即通过动态代理实现,具体场景比如Spring中的事物控制。

二、静态代理

静态代理是在程序运行前已写好代理类并编译完成,代理类并持有目标对象引用,并在接口实现方法里调用目标服务。

如果委托类方法较多,且被代理类执行的操作相同,一个一个代理去写会很繁琐。

三、动态代理

程序运行过程时,在内存中临时生成动态代理类,称为动态代理。

1、动态代理简单实现

在java的java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过这个类和这个接口可以生成JDK动态代理类和动态代理对象。

具体代码如下:

  • 首先是定义CarI接口

package com.dengqi.spring.dyproxy;

/**
 * @Auther: dengqi
 * @Date: 2018/7/30 18:01
 * @Description:
 */
public interface CarI {

    void run();

    void stop();

}
  • 然后是定义CarI接口的实现类CarE
package com.dengqi.spring.dyproxy;

/**
 * @Auther: dengqi
 * @Date: 2018/7/30 18:02
 * @Description:
 */
public class CarE implements CarI {

    private String car_code;

    public CarE() {
    }

    public CarE(String car_code) {
        this.car_code = car_code;
    }

    @Override
    public void run()  {
        System.out.println("the car " + car_code + "runs");
        try {
            Thread.sleep(2000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void stop() {
        System.out.println("the car " + car_code + "stop");
    }

}
  • 定义一个InvocationHandler实现类,持有目标对象CarE
package com.dengqi.spring.dyproxy;

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

/**
 * @Auther: dengqi
 * @Date: 2018/7/30 18:05
 * @Description:
 */
public class CarInvocationHandler implements InvocationHandler {
    private Object car ;

    public CarInvocationHandler(Object car){
        this.car = car;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("----merthod is : " + method + "--------");
        System.out.println("car test begin----------");
        MonitorUtil.start();
        method.invoke(car,args);
        MonitorUtil.end();
        System.out.println("car test finish,");
        return null;
    }
}

还可以换一种写法,ProxyFactory写法如下

package com.dengqi.spring.dyproxy;

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

/**
 * @Auther: dengqi
 * @Date: 2018/7/30 18:52
 * @Description:
 */
public class TimeProxyFactory {

    /**
     * target 直接利用目标对象构建动态代理对象
     * @param target
     * @return
     */
    public static Object createProxy(final Object target){
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("----merthod is : " + method + "--------");
                System.out.println("car test begin----------");
                MonitorUtil.start();
                method.invoke(target,args);
                MonitorUtil.end();
                System.out.println("car test finish,");
                return null;
            }
        });

    }

}
  • 计时工具类MonitorUtil
package com.dengqi.spring.dyproxy;

/**
 * @Auther: dengqi
 * @Date: 2018/7/31 10:51
 * @Description:
 */
public class MonitorUtil {
    private static ThreadLocal<Long> threadLocal = new ThreadLocal();

    public static void start(){
        long start = System.currentTimeMillis();
        threadLocal.set(start);
    }

    public static void end(){
        long end = System.currentTimeMillis();
        System.out.println("operation time is : " + (end - threadLocal.get()) + "mills");
    }

}
  • 执行Client调用
package com.dengqi.spring.dyproxy;

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

/**
 * @Auther: dengqi
 * @Date: 2018/7/30 18:09
 * @Description:
 */
public class Client {
    public static void main(String[] args) {
        //创建委托对象
        CarI car = new CarE("BMW");
        //创建委托对象关联的InvocationHandler对象
        InvocationHandler handler = new CarInvocationHandler(car);
        //创建动态代理对象
        CarI carProxy = (CarI) Proxy.newProxyInstance(CarI.class.getClassLoader(),new Class[]{CarI.class},handler);
        //打印代理对象名
        System.out.println(carProxy.getClass().getName());
        //执行代理服务
        carProxy.run();
        carProxy.stop();
    }
}
  • 执行结果
com.sun.proxy.$Proxy0
----merthod is : public abstract void com.dengqi.spring.dyproxy.CarI.run()--------
car test begin----------
the car BMWruns
operation time is : 2000mills
car test finish,
----merthod is : public abstract void com.dengqi.spring.dyproxy.CarI.stop()--------
car test begin----------
the car BMWstop
operation time is : 0mills
car test finish,

Process finished with exit code 0

四、动态代理原理分析

java动态代理类的创建

利用Proxy类的newProxyInstance方法创建了一个动态代理对象,查看该方法的源码,动态代理类的创建步骤见注释

public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        if (h == null) {
            throw new NullPointerException();
        }

        //1、获取实现接口
        final Class<?>[] intfs = interfaces.clone();
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }

        /*
         *2、动态生成的代理类
         * Look up or generate the designated proxy class.
         */
        Class<?> cl = getProxyClass0(loader, intfs);

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            //3、获取构造对象
            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 {
                //4、利用构造器对象生成代理类实例
                return newInstance(cons, ih);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString());
        }
    }

    private static Object newInstance(Constructor<?> cons, InvocationHandler h) {
        try {
            return cons.newInstance(new Object[] {h} );
        } catch (IllegalAccessException | InstantiationException e) {
            throw new InternalError(e.toString());
        } catch (InvocationTargetException e) {
            Throwable t = e.getCause();
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else {
                throw new InternalError(t.toString());
            }
        }
    }

其中Class<?> cl = getProxyClass0(loader, intfs)最重要的一步,在java内存中动态生成了一个类文件,通过以下方法我们将这个类文件打印出来一看究竟。

byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0", CarE.class.getInterfaces());
        String path = "/resource/CarProxy.class";
        try {
            FileOutputStream out = new FileOutputStream(path);
            out.write(bytes);
            out.flush();
            System.out.println("类文件打印成功");
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

打印出来的类文件反编译后内容如下所示

1、调用父类构造方法赋值invocationHandler

2、静态代码块中,会列出CarI接口所有的Method对象,作为$Proxy0类的属性持有

3、调用$Proxy0类的服务方法时,实际统一调用的是InvocationHandler.invoke()方法,将方法Method和参数传入。

这样无论CarI接口有多少个方法,都已经实现了代理。InvocationHandler的invoke方法中可以判断method对象,有需要的话可以实现不同的策略。

import com.dengqi.spring.dyproxy.CarI;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy0 extends Proxy implements CarI {

   private static Method m1;
   private static Method m4;
   private static Method m3;
   private static Method m0;
   private static Method m2;


   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})).booleanValue();
      } catch (RuntimeException | Error var3) {
         throw var3;
      } catch (Throwable var4) {
         throw new UndeclaredThrowableException(var4);
      }
   }

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

   public final void run() throws  {
      try {
         super.h.invoke(this, m3, (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)).intValue();
      } 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);
      }
   }

   static {
      try {
         m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});
         m4 = Class.forName("com.dengqi.spring.dyproxy.CarI").getMethod("stop", new Class[0]);
         m3 = Class.forName("com.dengqi.spring.dyproxy.CarI").getMethod("run", new Class[0]);
         m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
         m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
      } catch (NoSuchMethodException var2) {
         throw new NoSuchMethodError(var2.getMessage());
      } catch (ClassNotFoundException var3) {
         throw new NoClassDefFoundError(var3.getMessage());
      }
   }
}

五、总结

动态代理对象$Proxy0 extends Proxy implements CarI,由于java的单继承机制,$Proxy0 extends Proxy后只能处理接口代理,无法实现class代理。

Spring的AOP就是利用Proxy和InvocationHandler来实现的。

猜你喜欢

转载自blog.csdn.net/javahulk/article/details/81296876