JDK动态代理

37次阅读

共计 5975 个字符,预计需要花费 15 分钟才能阅读完成。

1 动态代理

    动态代理设计模式的原理:使用一个代理对象将原对象(目标对象)包装起来,然后利用该代理对象取代原对象。任何对原对象的调用都要经过代理。代理对象决定是否以及何时将方法调用转到原对象上。

2 动态代理用那些?

        1 基于接口的动态代理:如 JDk 提供的代理
        2 基于继承的动态代理:如第三方包 Cglib,javassist 动态代理

这里我们进行演示 JDK 自身提供的代理:
jdk 动态代理需要实现两个成员:一个是 Proxy 代理类,一个是 InvocationHandler 接口


    1  JDK 动态代理类
        Proxy:所有动态代理对象的父类,专门用于生成代理类对象或者代理对象

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

    2 接口
        InvocationHandler   完成动态代理的整个过程(动态代理类的调用处理程序都必须实现 InvocationHandler 接口)
         proxy : 动态代理对象
         method : 正在被调用的方法对象
         args   : 正在被调用的方法参数

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

接口和实现方法

public interface Arithmetic {public int add(int a,int b);
    public int mul(int a,int b);
}



public class ArithmeticImpl implements Arithmetic {
    @Override
    public int add(int a, int b) {return a + b;}

    @Override
    public int mul(int a, int b) {return a * b;}
}

代理对象


/**
    生成代理对象
 **/
public class ProxyObject  {

    // 目标对象
    private Object target;

    public ProxyObject(Object target) {this.target = target;}

    // 获取代理对象
    public Object getProxy(){
        // 代理对象
        Object proxy = null;

          /*

                ClassLoader loader : 目标对象的类加载器
                Class<?>[] interfaces : 接口们,目标对象提供的所有接口对象
                InvocationHandler h :  代理类的调用处理对象需要实现的接口
         */
        ClassLoader loader = target.getClass().getClassLoader();
        Class<?>[] interfaces = target.getClass().getInterfaces();
        InvocationHandler h = new MyInvocationHandler(target);
        // 这里创建代理对象,使得代理对象和目标对象拥有相同的方法行为,h 是代理对象调用处理执行的方法。proxy = Proxy.newProxyInstance(loader,interfaces,h);
        return proxy;
    }


}


class MyInvocationHandler implements InvocationHandler{
    private Object target;

    public MyInvocationHandler(Object target) {this.target = target;}

    // 代理对象调用代理方法,会回来调用 invoke 方法
    // proxy 代理对象,一般不会使用
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("动态代理对象:" + proxy.getClass());
        System.out.println("正在被调用的方法方法对象:" + method);
        System.out.println("正在被调用的方法方法名:" + method.getName());
        System.out.println("正在被调用的方法参数:" + Arrays.asList(args));


        System.out.println("执行方法前");
        // 实际上是内部在这儿使用目标对象执行目标方法。Object result = method.invoke(target,args); // 目标对象,调用目标方法 相当于 ArithmeticImpl 调用自己的方法
        System.out.println("执行方法后 result =" +  result);
        return result;
    }


 

}

测试


    public static void main(String[] args) {
        // 目标对象
        Arithmetic target = new ArithmeticImpl();
        // 获取代理对象
        Object object =  new ProxyObject(target).getProxy();
        Arithmetic proxyObject = (Arithmetic) object;
        proxyObject.add(1,2);
    }

结果

 动态代理对象:class com.sun.proxy.$Proxy0
 正在被调用的方法方法对象:public abstract int com.example.demo.service.Arithmetic.add(int,int)
 正在被调用的方法方法名:add
 正在被调用的方法参数:[1, 2]
执行方法前 
执行方法后 result = 3

$Proxy0 就是我们的动态代理对象

可以在 main 方法中添加一行代码将代理的对象字节码打印出来!


   byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0", new Class<?>[]{Arithmetic.class});
        try {

            String pathDir = "E:\\";
            String path = "\\$Proxy0.class";
            File f = new File(pathDir);
            if (!f.exists()) {f.mkdir();
            }
            path = f.getAbsolutePath() + path;
            f = new File(path);
            if (f.exists()) {f.delete();
            }
            f.createNewFile();

            try (FileOutputStream fos = new FileOutputStream(path)) {fos.write(bytes);
            } catch (Exception e) {e.printStackTrace();
            }
        
        } catch (IOException e) {e.printStackTrace();
        }

反编译后 $Proxy0 代码

import com.example.demo.service.Arithmetic;
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 Arithmetic
{
  private static Method m1;
  private static Method m2;
  private static Method m4;
  private static Method m3;
  private static Method m0;
  
  public $Proxy0(InvocationHandler paramInvocationHandler)
    throws 
  {
  // 调用父类 Proxy  设置 InvocationHandler  代理对象处理器,设置调用代理方法 h 
    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 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);
    }
  }
  
  public final int mul(int paramInt1, int paramInt2)
    throws 
  {
    try
    {
        // 处理调用 invoke 方法,this 当前代理对象 
      return ((Integer)this.h.invoke(this, m4, new Object[] {Integer.valueOf(paramInt1), Integer.valueOf(paramInt2) })).intValue();}
    catch (Error|RuntimeException localError)
    {throw localError;}
    catch (Throwable localThrowable)
    {throw new UndeclaredThrowableException(localThrowable);
    }
  }
  
  public final int add(int paramInt1, int paramInt2)
    throws 
  {
    try
    {return ((Integer)this.h.invoke(this, m3, new Object[] {Integer.valueOf(paramInt1), Integer.valueOf(paramInt2) })).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);
    }
  }
  
  static
  {
    try
    {
        // 判断 是否是对象的方法
      m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] {Class.forName("java.lang.Object") });
      m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
      m4 = Class.forName("com.example.demo.service.Arithmetic").getMethod("mul", new Class[] {Integer.TYPE, Integer.TYPE});
      m3 = Class.forName("com.example.demo.service.Arithmetic").getMethod("add", new Class[] {Integer.TYPE, Integer.TYPE});
      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());
    }
  }
}

通过代理对象源码可以看到
初始化 h

  public $Proxy0(InvocationHandler paramInvocationHandler)
    throws 
  {
  // 调用父类 Proxy  设置 InvocationHandler  代理对象处理器,设置调用代理方法 h 
    super(paramInvocationHandler);
  }

底层调用

 return ((Integer)this.h.invoke(this, m3, new Object[] {Integer.valueOf(paramInt1), 

这行代码实际上是通过 invoke 调用当前 原目标方法对象 以及参数,也就是我们写的 MyInvocationHandler 类中 invoke 方法。

关于 InvocationHandler 接口中 invocation 方法中第一个参数 proxy 说明。
proxy 参数表示的是代理对象,那这个参数有何意义呢,什么时候去使用呢,为什么这里没有使用这个参数呢。
看了很多博客感觉 描述不清,众说纷纭。
jdk 中直接这么描述:

the proxy instance that the method was invoked on

一般常用后面两个参数,第一个是代理对象,一般方法中没有用到!

正文完
 0

JDK动态代理

37次阅读

共计 1766 个字符,预计需要花费 5 分钟才能阅读完成。

JDK 动态代理是 java.lang.reflect.* 包提供的方式,它必须借助一个接口才能产生代理对象,所以要预先定义接口。
1. 接口
public interface Hello {
public void sayHello() ;
}
2. 实现接口
  提供实现类 HelloImpl 来实现接口:
public class HelloImpl implements Hello {
@Override
public void sayHello() {
System.out.println(“Hello world!”);
}
}
3. 动态代理绑定
public class JdkProxyExample implements InvocationHandler {

private Object target = null;

/**
* 建立代理对象和真是对象的代理关系,并返回代理对象
* @param obj 真实对象
* @return 代理对象
*/
public Object bind(Object obj) {
this.target = obj;
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), this);
}
/**
* 代理方法逻辑
* @param proxy 代理对象
* @param method 当前的调度方法
* @param args 方法参数
* @return 代理结果返回
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(“ 进入代理逻辑对象 ”);
System.out.println(“ 在调度真实对象之前的服务 ”);
Object obj = method.invoke(target, args);
System.out.println(“ 在调度真实对象之后的服务 ”);
return obj;
}
}
  在 JDK 动态代理中,要实现代理逻辑类必须去实现 java.lang.reflect.InvocationHandler 接口,它里面定义了一个 invoke 方法,并提供接口数组用于下挂代理对象。要建立起代理对象和真实服务对象的关系,然后实现代理逻辑,所以一共分为两个步骤。第 1 步,建立代理对象和真实对象的关系。这里是使用了 bind 方法去完成的,方法里面首先用类的属性 target 保存了真实对象,然后通过如下代码建立并生成代理对象。
Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), this);
  其中 newProxyInstance 方法包含 3 个参数。

第 1 个是类加载器,我们采用了 target 本身的类加载器。
第 2 个是把生成的动态代理对象下挂在哪些接口下,这个写法就是放在 target 实现的接口下。HelloWorldlmpl 对象的接口显然就是 HelloWorld,代理对象可以这样声明:Hello proxy = xxxx。
第 3 个是定义实现方法逻辑的代理类,this 表示当前对象,它必须实现 InvocationHandler 接口的 invoke 方法,它就是代理逻辑方法的现实方法。

  第 2 步,实现代理逻辑方法。invoke 方法可 以实现代理逻辑,invoke 方法的 3 个参数的含义如下所示。

proxy,代理对象,就是 bind 方法生成的对象。
method,当前调度的方法。
args,调度方法的参数。

4. 测试动态代理
public void testJdkProxy() {
JdkProxyExample jdk =new JdkProxyExample();
// 绑定关系,因为挂在接口 Hello 下,所以声明代理对象 Hello proxy
Hello proxy= (Hello)jdk.bind(new HelloImpl());
// 注意,此时 Hello 对象己经是一个代理对象,它会进入代理的逻辑方法 invoke 里
proxy.sayHelloWorld();
}
测试结果:
进入代理逻辑对象
在调度真实对象之前的服务
Hello world!
在调度真实对象之后的服务

正文完
 0