Java动态代理剖析

Java动态代理剖析


动态代理作为一种字节码增强技术,也作为SpringAOP的核心实现,我们在使用它的同时最好能做好“知其然,知其所以然”

  • 动态代理demo
  • 代理类
  • 总结

动态代理uml

1.动态代理demo

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

/**
 * author   : 
 * date     : 16/4/20.
 * describe :
 */
public class MyInvokeHandler implements InvocationHandler {

    private Object target;

    public MyInvokeHandler(Object target){
        super();
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("before");
        Object o = method.invoke(target,args);
        System.out.println("after");
        return o;
    }

    /***
     * 代理对象
     * @return
     * 在newProxyInstance这个方法的第二个参数上,我们给这个代理对象提供了一组什么接口,那么这个代理对象就会实现了这组接口,这个时候我们当然可以将这个代理对象强制类型转化为这组接口中的任意一个。
     */
    public Object getProxy(){
        return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),target.getClass().getInterfaces(),this);
    }
}
public interface Service {
    public String info();
}

public class MyService implements Service {
    @Override
    public String info() {
        return "hello";
    }

    public static void main(String[] args) {
        MyInvokeHandler handler = new MyInvokeHandler(new MyService());
        Service service = (Service)handler.getProxy();
        //获取代理类的字节码
        byte[] clazz = ProxyGenerator.generateProxyClass("$Proxy1",MyService.class.getInterfaces());
        FileOutputStream out = null;
        try {
            out = new FileOutputStream("/Users/admin/Desktop/$Proxy1.class");
            out.write(clazz);
            out.flush();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        service.info();
    }
}

运行结果:

before
after

有以上的一个小demo可以看出,动态代理的最终实现效果就是可以在被代理的执行过程前后可以加上部分处理逻辑,就像demo中打印了部分字符串。
当然动态代理的功能远不如此,我们可以在调用前后打印日志、控制事务、增强方法等等,在不修改源码的情况下新增我们需要的功能。

上面的demo简单介绍了如何使用动态代理。下面来看看生成的代理类中的内容是什么。

2.代理类

对于同一个接口而言,其实现类与代理类之间有和关联。
我们在上面已经将代理类的字节码写入了一个文件,大家可以反编译之后看到其内容如下:

//这就是为什么代理类不能extends而要实现实现接口,因为产生的代理类会默认继承 Proxy,而java不允许多继承
public final class $Proxy1 extends Proxy
  implements Service
{
  //4个Method分别代表代理类中的4个方法。
  private static Method m1;
  private static Method m3;
  private static Method m2;
  private static Method m0;

  public $Proxy1(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);
    }
  }

//这是实现接口的方法,可以发现它会去调用InvocationHandler实现类中的invoke方法。而我们也是在invoke方法是实现一些功能的扩展。
  public final String info()
    throws 
  {
    try
    {
      return (String)this.h.invoke(this, m3, null);
    }
    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 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") });
      m3 = Class.forName("com.service.util.property.Service").getMethod("info", 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());
    }
  }
}

3.总结

1.动态代理是在jvm运行后在内存中生成一个class,用来增强一些原类所未拥有的功能,但是原类是也是不可或缺的,因为真正的核心逻辑还是在原类中。
2.代理类在内存中的存在方式,默认继承Proxy,同时实现了我们传入的接口,在运行是动态生成的一个对象,并且命名方式都是这样的形式,以$开头,Proxy为中,最后一个数字表示对象的标号。
3.代理类默认实现hashCode(),equals(),toString(),以及继承的接口所需实现的方法,所以当调用类似proxy.getClass().getName()时并不会得到代理类的类名。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值