Java 代理原理学习

发布于:2022-12-11 ⋅ 阅读:(826) ⋅ 点赞:(0)

一、代理介绍

代理(Proxy),是一种设计模式,使我们可以不直接访问目标对象,而通过代理对象进行访问,这样做的好处,我们可以在目标对象的基础上,添加一些额外的操作,相当于扩展了功能。

 常用的创建代理对象的方式有静态代理与动态代理:

1、静态代理:编译时就已经实现,代理类和被代理类应该共同实现一个接口,或者是共同继承某个类。

2、动态代理:利用了 JDK API,运行时动态生成

静态代理相比于动态代理:

静态代理:代理类要实现与被代理类相同的接口,如果代理较多,需要编写多个代理类。

另外,如果需要增加方法,代理类与被代理类都需要进行修改,不易维护

动态代理:不需要提前编写代理类,使用上面比较灵活。

二、静态代理

直接上代码:

先定义代理类与被代理类共同需要实现的接口:

public interface People {
    public void printName(Integer years);
}

实现被代理类:

public class Man implements People{
    public  String name = "";
    @Override
    public void printName(Integer years ) {
        System.out.println(" name:"+ name + "  years:" + years);
    }
}

实现代理类:

public class ManProxy implements People {
    private  Man man;
    public ManProxy(Man man){
        this.man = man;
    }
    @Override
    public void printName(Integer years) {
        System.out.println("begin-------------");
        man.printName(years);
        System.out.println("end-------------");
    }
}

代理类一般都包含被代理类的引用,也可直接继承代理类。打印begin、end处即可添加需要添加的流程

相关方法调用:

Man man = new Man();
man.name = "zhang san";
//静态代理测试
ManProxy manProxy = new ManProxy(man);
manProxy.printName(1000);

静态代理比较简单,这里就不做过多的陈述了。

三、动态代理

3.1、动态代理的实现

动态代理的实现主要分为以下三个步骤:

1、和静态代理一样,先创建被代理类所需实现的接口以及实现被代理类

2、实现InvocationHandler接口

3、调用Proxy.newProxyInstance方法,动态生成代理对象。

下面我们就围绕这三个步骤来做相关的介绍

3.2、创建被代理类所需实现的接口以及实现被代理类

这一步和静态代理一致,直接贴代码:

public interface People {
    public void printName(Integer years);
}
public class Man implements People{
    public  String name = "";
    @Override
    public void printName(Integer years ) {
        System.out.println(" name:"+ name + "  years:" + years);
    }
}

3.3、实现InvocationHandler接口

代码如下:

1、需要一个object对象保存被代理的对象

2、注意invoke方法,方法传入三个参数,代理对象,调用的方法,已经调用该方法传入的参数。invoke的实现,主要是利用了反射,调用了被代理对象的方法。反射相关介绍可参考另一篇文章(java 反射原理学习_weixin_43863193的博客-CSDN博客

3.4、调用Proxy.newProxyInstance方法,动态生成代理对象。

调用方法代码如下:

People people = (People) Proxy.newProxyInstance(Man.class.getClassLoader(),new Class[]{People.class},invocationHandler);

newProxyInstance方法的申明如下:

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

 接收三个参数:

1、被代理类的ClassLoader

2、生成的动态代理类中需要实现的接口

3、3.3中实现的InvocationHandler

调用Proxy.newProxyInstance方法就能动态的生成动态代理对象,调用动态代理对象的方法,就会执行到3.3中实现的TestInvocationHandler,中的invoke方法。进而通过反射调用被代理对象的相关方法。

why?为什么通过调用动态代理对象的方法会执行到TestInvocationHandler的invoke方法?

我们来看newProxyInstance的实现:

进入getProxyConstructor()方法:

 标记位置,可以简单的理解为构建了一个代理类。生成的代理类可以在项目的更目录下找到,包名一般为com.sun.proxy,示例中生成的代理类名称为$Proxy0。我们来看生成的代理类源码:

public final class $Proxy0 extends Proxy implements People {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m0;

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

    public final void printName(Integer var1) throws  {
        try {
            super.h.invoke(this, m3, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    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);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (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", Class.forName("java.lang.Object"));
            m3 = Class.forName("com.example.lib.People").getMethod("printName", Class.forName("java.lang.Integer"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

 其中有 equals、hashCode、toString等方法,我们主要关注printName这个方法,因为这个类也继承了People接口,所以实现了printName方法,来看他的实现:

调用了super.h.invoke(this, m3, new Object[]{var1});

接下来看代理类的构造方法:

 再进入父类的构造方法:

传入了InvocationHandler对象,复制给了this.h。传入的InvocationHandler对象也就是调用newProxyInstance方法时传入的第三个参数,将我们实现的TestInvocationHandler对象传入了进来。

也就是说调用 Proxy.newProxyInstance方法,返回的其实就是动态生成的代理类($Proxy0)对应的对象。所以调用该对象的printName方法,会直接调用到3.3中实现的TestInvocationHandler,中的invoke方法。进而通过反射调用被代理对象的相关方法。

本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到