Java高阶之代理

10. 代理

代理模式就是在不修改源码的情况下,对其进行功能扩展。比如在使用某方法前进行日志记录,对用户进行权限控制,预处理消息、过滤消息等等。

需要知道的是,代理类本身不实现服务,而是通过调用被代理类中的方法来提供服务。

在Java中主要分为静态代理动态代理模式,动态代理是一种比较常用的代理模式,因为它比静态代理更加的灵活。但是我们也是有必要了解静态代理。

10.1 静态代理

静态静态,顾名思义就是死的,专业术语来说就是已编译的,在程序运行前就已经存在代理类的字节码文件。

实现静态代理的步骤:

  1. 代理类跟被代理类需要实现同一个接口的方法,
  2. 在代理类中定义被代理类的对象,编写代理方法即 扩展被代理对象的方法。
  3. 创建代理类对象,使用代理方法。

比如我想要个代理帮我刷副本,并且在代理上线的时候输出一下日志。首先刷副本能力在于我本身,代理只是上我的号。

接口

package 静态代理;
// 定义玩家接口
public interface Player {
    // 定义刷副本函数
    void DaGuai();
}

被代理类

package 静态代理;

public class PlayerImpl implements Player {

    @Override
    public void DaGuai() {
        System.out.println("上下上下左右左右BABA");
    }
}

代理类

package 静态代理;

public class StaticProxy implements Player {
    // 定义被代理类的对象
    private Player player;

    public StaticProxy(Player player){
        this.player = player;
    }

    @Override
    public void DaGuai() {
        // 输出进度
        System.out.println(player + "准备刷副本了");
        // 被代理类的方法
        player.DaGuai();
        System.out.println(player + "刷完副本了");
    }
}

main函数

package 静态代理;

public class Main {
    public static void main(String[] args){
        // 创建玩家对象
        Player codekiang = new PlayerImpl();
        // 将玩家对象封装到代理对象中
        StaticProxy proxy = new StaticProxy(codekiang);
        proxy.DaGuai();
    }
}

输出结果
在这里插入图片描述

到此就完成了静态代理的实现。我们可以在DaGuai方法里扩展自己想要的功能。但是静态代理这么写只能代理一个方法或一个类。

如果一个类中有多个方法需要代理呢,比如我不仅要刷副本,我还想刷完副本代理帮我刷元宝,那代理跟被代理对象就需要再重写一个刷元宝的方法。

再比如,我需要再来一个类,该类的功能是播放音乐,我需要有代理类帮我播放,并输出日志。此时就需要再重新写一个代理类来播放音乐了。

可以见得,静态代理面对这种情况很棘手。我们可不可以只编写接口并实现它该有的功能,然后只创建一个代理对象帮我们代理这些方法呢?

动态代理拍了拍你的屁股并说:嘿,Boy。

10.2 动态代理

动态代理类 使用了反射,使其能在程序运行时创建代理类的代理模式。动态代理可以实现AOP编程、解耦。

实现动态代理的步骤:

  1. 在动态代理类中创建代理器(实现IncocationHandler接口的invoke方法)
  2. 使用代理处理器对象来调用被代理对象的方法

流程图:

代理器会自动帮我们创建代理对象,动态代理对象所有的方法在调用时都会被拦截,送到代理处理器的invoke()方法来处理。
Java高阶之代理_第1张图片

实现代码:

接口(Player跟Listen)

package 动态代理;

public interface Player {
    void play();
}
package 动态代理;

public interface Listen {
    void listen();
}

被代理类(实现了两个接口)

package 动态代理;

public class PlayerImpl implements Player, Listen {

    @Override
    public void play() {
        System.out.println("刷副本中。。。");
    }

    @Override
    public void listen() {
        System.out.println("正在播放《搁浅》。。。");
    }
}

动态代理类:

package 动态代理;

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

public class DynamicProxy implements InvocationHandler {

    private Player player;
    private Listen listen;

    public DynamicProxy(Player player){
        this.player = player;
    }

    public DynamicProxy(Listen listen){
        this.listen = listen;
    }

    @Override
    /*
    * Object proxy:被代理的对象
    * Method method:要调用的方法
    * Object[] args:方法调用时所需要参数
     * */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("动态代理的Class对象:"+proxy.getClass());
        System.out.println("动态代理的Class对象的类名:"+proxy.getClass().getName());
        System.out.println("动态代理的Class对象调用的方法:"+method.getName());
        Object result = null;

        switch (method.getName()){
            case "listen":
                System.out.println(listen + "准备听音乐了");
                result = method.invoke(listen, args);
                System.out.println(listen + "听完了。。。");
                break;
            case "play":
                System.out.println(player + "准备刷副本了");
                result = method.invoke(player, args);
                System.out.println(player + "刷完副本了");
                break;
            default:break;
        }

        return result;
    }
}

客户端(main函数)

package 动态代理;

import java.lang.reflect.Proxy;

public class Main {
    public static void main(String[] args){
        // 创建对象
        Player codekiangPlayer = new PlayerImpl();
        // 创建调用处理器对象
        DynamicProxy handlerPlayer = new DynamicProxy(codekiangPlayer);
        // 动态生成代理对象
        Player proxyPlayer = (Player) Proxy.newProxyInstance(
                PlayerImpl.class.getClassLoader(), // 获取对象的类加载器
                PlayerImpl.class.getInterfaces(),  // 获取对象所有的接口
                handlerPlayer  // 调用处理器对象
        );
        // 调用被代理类的方法
        proxyPlayer.play();
        
        System.out.println("============分割线============");
        
        // 创建对象
        Listen codekiangListen = new PlayerImpl();
        // 创建调用处理器对象
        DynamicProxy ListenHandler = new DynamicProxy(codekiangListen);
        // 动态生成代理对象
        Listen ListenProxy = (Listen) Proxy.newProxyInstance(
                PlayerImpl.class.getClassLoader(),
                PlayerImpl.class.getInterfaces(),
                ListenHandler
        );
        ListenProxy.listen();
    }
}

运行结果:
Java高阶之代理_第2张图片

需要注意的是,当实现多个接口时,实现的顺序很重要,当多个接口有同名方法时,代理对象会执行位于前面的接口的方法。

你可能感兴趣的:(Java学习)