文档章节

java 动态代理学习笔记

 漂泊的二货
发布于 2017/05/18 17:10
字数 1035
阅读 3
收藏 0

代理模式:代理模式就是对一个对象进行包装,包装生成的对象具有和原对象一样的方法列表,但是每个方法都进行了包装

 

静态代理

public interface MoveAble {
    
    public void run();
}

public class Car implements MoveAble {

    @Override
    public void run() {
        System.out.println("car is running");
    }

}

public class CarProxy implements MoveAble{
    
    private MoveAble target;

    public CarProxy(MoveAble target) {
        super();
        this.target = target;
    }

    @Override
    public void run() {
        this.target.run();
    }
    
    public static void main(String[] args) {
        CarProxy proxy = new CarProxy(new Car());
        proxy.run();
    }
    
}

    首先 我们定义了一个MoveAble接口,然后新建一个实现类,并且新建一个CarProxy代理类,CarProxy实现moveAble接口,同时在它内部定义一个变量MoveAble用来保存代理的实现类,这样代理类实现moveAble接口中的run方法时,其实调用的还是被代理类car中的run方法,同时 我们可以在代理类CarProxy中的run方法中添加 我们需要添加的逻辑,这样就完成了对car的代理。但是如果moveAble中添加方法时,代理类CarProxy也要增加相应的实现;如果不想这样的话,就得使用动态代理;

动态代理

下面我们征对上面的静态代理类 我们创建一个动态的代理类:

public class MoveAbleProxy implements InvocationHandler{
    
    private Object target;


    public MoveAbleProxy(Object target) {
        super();
        this.target = target;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        System.out.println(proxy.getClass().getSimpleName());
        method.invoke(target, args);
        return null;
    }
    
    public static void main(String[] args) {
        MoveAble car = new Car();
        //得到的moveProxy实现了MoveAble接口 继承了Proxy
    MoveAble moveProxy = (MoveAble)Proxy.newProxyInstance(car.getClass().getClassLoader(),         car.getClass().getInterfaces(), new MoveAbleProxy(car) ); 
        moveProxy.run();     
    }

}

这边与静态代理的区别在于 动态代理类实现的是InvocationHandler,与静态代理的main方法中测试代码比较会发现静态代理中我们是直接new CarProxy ,而在动态代理中我们是:

Proxy.newProxyInstance(car.getClass().getClassLoader(),         car.getClass().getInterfaces(), new MoveAbleProxy(car) ); 并将返回的对象强转成MoveAble。

所以动态代理的实现要一下几步:

(1)要定义一个接口,和一个该接口的实现类,这个实现类就是我们要代理的对象,代理其实就是在调用实现类方法时,在该方法的调用前后增加一个逻辑

(2)动态的代理类中必须要实现InvocationHandler接口,实现invoke方法。动态代理中生成的代理实例不会去直接调用实现类的方法,而是调用invoke方法。

(3)生成的代理里去调用实现类的方法时

MoveAble moveProxy = (MoveAble)Proxy.newProxyInstance(car.getClass().getClassLoader(),         car.getClass().getInterfaces(), new MoveAbleProxy(car) ); 

断点会发现moveProxy = $Proxy0 ,可以在main测试方法中添加

System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");可以看一下该生成的代理类的源码:(moveProxy 其实是 实现了MoveAble接口 继承了Proxy)

public final class $Proxy0
  extends Proxy
  implements MoveAble
{
  private static Method m1;
  private static Method m3;
  private static Method m0;
  private static Method m2;
  
  public $Proxy0(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 void run()
    throws 
  {
    try
    {
      this.h.invoke(this, m3, null);
      return;
    }
    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 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") });
      m3 = Class.forName("com.myec.com.MoveAble").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]);
      return;
    }
    catch (NoSuchMethodException localNoSuchMethodException)
    {
      throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
    }
    catch (ClassNotFoundException localClassNotFoundException)
    {
      throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
    }
  }
}

在Proxy中的newProxyInstance的方法中:

   

 Class cl = getProxyClass(loader, interfaces);
    try {
        Constructor cons = cl.getConstructor(constructorParams);
        return (Object) cons.newInstance(new Object[] { h });
    } 

1这个生成了代理类的字节码,然后用参数里的classLoader加载这个代理类,生成$Proxy0实例

2.使用代理类父类的构造函数 Proxy(InvocationHandler h)来创造一个代理类的实例,将我们自定义的InvocationHandler(MoveAbleProxy )的子类传入。

3.返回这个代理类实例,因为我们构造的代理类实现了interfaces(也就是我们程序中传入的car.getClass().getInterfaces())里的所有接口,因此返回的代理类可以强转成MoveAble类型来调用接口中定义的方法。

 

 

 

 

 

 

© 著作权归作者所有

共有 人打赏支持
粉丝 0
博文 2
码字总数 1138
作品 0
浦东
私信 提问
java RMI 源码总结(个人理解)

明天早上详细写下两个过程: 1、初始化过程(服务器) 2、客户端调用过程(包括客户端流程、调用服务端流程、返回客户端流程) 当客户端通过RMI注册表找到一个远程接口的时候,所得到的其实是...

Java搬砖工程师
11/16
0
0
动态生成字节码--Javassist

前言 Javaassist是一个高层的Java字节码处理类库,能运行时动态生成类,修改类。Javaassit能动态生成类的基础源于JavaClass的字节码技术:只要遵从规范,JavaClass可以来自任何地方。类似的技术还...

淡淡的倔强
08/15
0
0
咕泡-代理 proxy 设计模式笔记

##查看代码:https://gitee.com/jly521/proxy.git 代理模式(Proxy) 应用场景:为其他对象提供一种代理以控制对这个对象的访问 从结构上来看和Decorator 模式类似, 但Proxy 是控制,更像是...

职业搬砖20年
08/22
0
0
给大家分享一个JAVA技术大牛的课程笔记

目前,Java语言已经广泛应用于生活中的各个领域,无论是网络编程还是数据库编程,甚至是web开发都有Java语言的身影 很多小伙伴想学习java却布置从何学起,现在给大家分享一个JAVA大神课程笔记...

dadaxiaoxiao
2015/12/31
304
1
美团猫眼团队面试题:Maven+OSGi+Spring+Zookeeper+Dubb

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/t4i2b10X4c22nF6A/article/details/84327220 交叉一面 concurrent包下面用过哪些? countdownlatch功能实现 ...

JAVA高级架构v
11/21
0
0

没有更多内容

加载失败,请刷新页面

加载更多

没什么启示的启示——《中国生存启示录》读后感4300字范文

没什么启示的启示——《中国生存启示录》读后感4300字范文: 文:夜晨1981。先后两次阅读这本书,第一次是2016年3月,第二次是2018年12月。读了两遍,都有一个理由,就是梁晓声这个名字。不知...

原创小博客
昨天
3
0
ubuntu常用操作

显卡GPU 查看显卡信息sudo lshw -numeric -class video# 查看显卡型号lspci | grep -i nvidia# 查看驱动版本sudo dpkg --list | grep nvidia-*或者 ubuntu-drivers devices#查看显卡...

hc321
昨天
2
0
mysql密码重置

方法一: 在my.ini的[mysqld]字段加入: skip-grant-tables 重启mysql服务,这时的mysql不需要密码即可登录数据库 然后进入mysql mysql>use mysql; mysql>更新 update user set password=pas...

architect刘源源
昨天
6
1
SpringBoot + Mybatis 配置多数据源(Srping boot 二)

前置条件,你已经配置好spring boot+mybatis,可以参考之前的博客 实现逻辑通过注解+aop切面编程来动态更新datasource 第一步,配置多个DataSource server: port: 8080freezing: ...

小海bug
昨天
13
0
连续潜在变量---概率PCA

最大似然PCA 用于PCA的EM算法 贝叶斯PCA 因子分解

中国龙-扬科
昨天
6
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部