SpringAop之JDK动态代理

引言
实现代理的方式?

JDK和CGLIB区别主要是jdk是代理接口,而cglib是代理类。(别的没见过。。。)

JDK动态代理

jdk的动态代理调用了Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) 方法。
通过该方法生成字节码,动态的创建了一个代理类,interfaces参数是该动态类所继承的所有接口,而继承InvocationHandler 接口的类则是实现在调用代理接口方法前后的具体逻辑,下边是具体的实现:

public class Test {
  static interface Subject{
    void sayHi();
    void sayHello();
  }
   
  static class SubjectImpl implements Subject{
 
    @Override
    public void sayHi() {
      System.out.println("hi");
    }
 
    @Override
    public void sayHello() {
      System.out.println("hello");
    }
  }
   
  static class ProxyInvocationHandler implements InvocationHandler{
    private Subject target;
    public ProxyInvocationHandler(Subject target) {
      this.target=target;
    }
 
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      System.out.print("say:");
      return method.invoke(target, args);
    }
     
  }
   
  public static void main(String[] args) {
    Subject subject=new SubjectImpl();
    Subject subjectProxy=(Subject) Proxy.newProxyInstance(subject.getClass().getClassLoader(), subject.getClass().getInterfaces(), new ProxyInvocationHandler(subject));
    subjectProxy.sayHi();
    subjectProxy.sayHello();
     
  }
}
/**   
 *    
 * 使用JDK动态代理
 *    
 *   
 */    
public class JDKProxy implements InvocationHandler {    
    
    private Object targetObject;//需要代理的目标对象    
    
    public Object newProxy(Object targetObject) {//将目标对象传入进行代理    
        this.targetObject = targetObject;     
        return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),    
                targetObject.getClass().getInterfaces(), this);//返回代理对象    
    }    
    
    public Object invoke(Object proxy, Method method, Object[] args)//invoke方法    
            throws Throwable {    
        before();
        Object ret = null;      // 设置方法的返回值    
        ret  = method.invoke(targetObject, args);       //invoke调用需要代理的方法
        after();
        return ret;    
    }    
    
    private void before() {//方法执行前   
        System.out.println("方法执行前 !");    
    }    
    private void after() {//方法执行后    
        System.out.println("方法执行后");    
    }    
}

newProxyInstance方法执行了以下几种操作。

  1. 生成一个实现了参数interfaces里所有接口且继承了Proxy的代理类的字节码,然后用参数里的classLoader加载这个代理类。
  2. 使用代理类父类的构造函数 Proxy(InvocationHandler h)来创造一个代理类的实例,将我们自定义的InvocationHandler的子类传入。
  3. 返回这个代理类实例。

源代码

public static Object newProxyInstance(ClassLoader loader, Class<?>[]interfaces,InvocationHandler h) throws IllegalArgumentException {
    // 检查 h 不为空,否则抛异常
    if (h == null) {
        throw new NullPointerException();
    }

    // 获得与指定类装载器和一组接口相关的代理类类型对象
    Class cl = getProxyClass(loader, interfaces);

    // 通过反射获取构造函数对象并生成代理类实例
    try {
        Constructor cons = cl.getConstructor(constructorParams);
        return (Object) cons.newInstance(new Object[] { h });
    } catch (NoSuchMethodException e) { throw new InternalError(e.toString());
    } catch (IllegalAccessException e) { throw new InternalError(e.toString());
    } catch (InstantiationException e) { throw new InternalError(e.toString());
    } catch (InvocationTargetException e) { throw new InternalError(e.toString());
    }
}

getProxyClass方法调用ProxyGenerator的 generateProxyClass方法产生ProxySubject.class的二进制数据:

下面我们来看源码
注:在主方法中加入System.getProperties().put(“sun.misc.ProxyGenerator.saveGeneratedFiles”,“true”),这样就会把生成的代理类Class文件保存在本地磁盘上,然后再反编译可以得到代理类的源码。

public final class $Proxy0 extends Proxy
 implements Test.Subject
{
 private static Method m4;
 private static Method m1;
 private static Method m3;
 private static Method m0;
 private static Method m2;
  
 static
 {
   try {
     m4 = Class.forName("Test$Subject").getMethod("sayHello", new Class[0]);
     m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
     m3 = Class.forName("Test$Subject").getMethod("sayHi", 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 (Exception e) {
    throw new RuntimeException(e);
  }
 }
 
 public $Proxy0(InvocationHandler paramInvocationHandler)
 {
  super(paramInvocationHandler);
 }
 
 public final void sayHello()
 {
  try
  {
   this.h.invoke(this, m4, null);
   return;
  }
  catch (RuntimeException localRuntimeException)
  {
   throw localRuntimeException;
  }
  catch (Throwable localThrowable)
  {
    throw new UndeclaredThrowableException(localThrowable);
  }
 }
 
 public final boolean equals(Object paramObject)
 {
  try
  {
   return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
  }
  catch (RuntimeException localRuntimeException)
  {
   throw localRuntimeException;
  }
  catch (Throwable localThrowable)
  {
    throw new UndeclaredThrowableException(localThrowable);
  }
 }
 
 public final void sayHi()
 {
  try
  {
   this.h.invoke(this, m3, null);
   return;
  }
  catch (RuntimeException localRuntimeException)
  {
   throw localRuntimeException;
  }
  catch (Throwable localThrowable)
  {
    throw new UndeclaredThrowableException(localThrowable);
  }
 }
 
 public final int hashCode()
 {
  try
  {
   return ((Integer)this.h.invoke(this, m0, null)).intValue();
  }
  catch (RuntimeException localRuntimeException)
  {
   throw localRuntimeException;
  }
  catch (Throwable localThrowable)
  {
    throw new UndeclaredThrowableException(localThrowable);
  }
 }
 
 public final String toString()
 {
  try
  {
   return (String)this.h.invoke(this, m2, null);
  }
  catch (RuntimeException localRuntimeException)
  {
   throw localRuntimeException;
  }
  catch (Throwable localThrowable)
  {
    throw new UndeclaredThrowableException(localThrowable);
  }
 }
}

我们可以看到代理类内部实现比较简单,在调用每个代理类每个方法的时候,都用反射去调newProxyInstanceh方法中传来的h的invoke方法(也就是我们自定义的InvocationHandler的子类中重写的invoke方法),用参数传递了代理类实例、接口方法、调用参数列表,这样我们在重写的invoke方法中就可以实现对所有方法的统一包装了。

发布了45 篇原创文章 · 获赞 3 · 访问量 2313

猜你喜欢

转载自blog.csdn.net/weixin_44046437/article/details/99741179