代理设计模式下篇(jdk动态代理)

104 阅读3分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

动态代理

什么是动态代理?

  • 动态代理的角色和静态代理的一样
  • 动态代理的代理类是动态生成的 ,静态代理的代理类是我们写的
  • 动态代理分为两类 : 一类是基于接口动态代理 , 一类是基于类的动态代理
    • 基于接口的动态代理----JDK动态代理
    • 基于类的动态代理--cglib

动态代理就是当有大量的类需要执行一些共同代码时,我们自己写太麻烦,那能不能直接使用java代码,自动生成一个类帮助我们批量的增强某些方法。 本文主要介绍JDK动态代理。

JDK原生的动态代理

【核心】:InvocationHandler和Proxy

具体从代码部分入手帮助大家理解。 我现在写了个singer接口,如下。

public interface Singer {
    void sing();
}

现在有个男歌手,当然他要达到某个标准才能成为一个歌手,所以这个男歌手要实现singer这个接口,这个标准,最后成了一个真正的男歌手。 具体实现如下:

public class ManSinger implements Singer {
    //歌手需要一个名字
    private String name;
    //构造器给他名字

    public ManSinger(String name) {
        this.name = name;
    }

    @Override
    public void sing() {
        System.out.println(name+"开始唱歌了!");
    }
}

正如之前静态代理那块说的,歌手只会唱歌,举办演唱会其他事宜,包括协商价格、演唱会怎么举办歌手并不懂,所以他需要一个经纪人。我们就定义一个动态代理类来表示这个经纪人,如下。

public class Agent implements InvocationHandler {
    //要代理的对象
    private Singer singer;
    public Agent(Singer singer) {
        this.singer = singer;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("经纪人把把关...");
        //singer.sing();
        Object runObj = method.invoke(singer, args);
        System.out.println("唱完了...");
        return runObj;
    }
    public Object getProxy(){
        return Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[]{Singer.class},this);
    }
}

先声明一下需要代理的对象,我们需要代理的接口类型是Singer,这里可以理解成这个代理类负责去卖singer这个接口标准的插座。
invoke这个方法就是使用处理代理实例上的方法,这个方法有三个参数:

  • 第一个参数proxy是要代理的实例;
  • 第二个参数method是要处理的方法,对应于调用代理实例上的接口方法的实例。方法对象的声明类将是该方法声明的接口,它可以是代理类继承该方法的代理接口的超级接口;
  • args包含的方法调用传递代理实例的参数值的对象的阵列,或null如果接口方法没有参数。原始类型的参数包含在适当的原始包装器类的实例中,例如java.lang.Integer或java.lang.Boolean。

其实就是只要写invoke这个方法就可以了,正如我要代理singer这个接口实例,我就把singer传了进去,把代理要做的事情写在invoke方法前后,去增强这个接口方法。 而getProxy这个函数就是返回一个代理实例,给我们生成一个代理对象。下面说一下测试部分。

public class Client {
    public static void main(String[] args) {
        ManSinger luhan = new ManSinger("luhan");
        Agent agent = new Agent(luhan);
        //agent.setSinger(luhan);
        Singer singer = (Singer)agent.getProxy();
        singer.sing();
    }
}

测试代码解释:这里就好比有一个男歌手luhan,然后他去找了个agent经纪人,或者说有个经纪人来找luhan了。然后他用自己的invoke来帮我们筹划演唱会的各种事宜,最后用getProxy把这个演唱会打包。