代理模式的实例分析

代理模式的实例分析

说明:代码以及部分总结来源于狂神说,在此基础上进一步归纳总结。关于动态代理的理解,可以参考末尾的博客链接。

代理模式可以分为两类:

  • 静态代理
  • 动态代理

静态代理

使用租房的场景来理解代理模式。场景示例如下:
在这里插入图片描述
角色介绍:

  • 抽象角色:一般指的是某个抽象的动作。使用接口或者抽象类来表示。
  • 真实角色:被代理的角色。比如:房东。
  • 代理角色:代理真实角色,代理真实角色后,还会有一些附属操作,比如:房屋中介除了负责租房外,附属操作是带租户看房子、签合同、维护房子等行为。
  • 客户端:访问代理角色的人。比如:租户。

角色之间的关系:

  • 客户端(租户)和代理角色(中介):租户直接访问的是房屋中介,包括租房过程中的一切操作,租户是没法越过中介来访问房东的(这里指的是将房子交给中介管理的情况,不包括房东直接出租的情况,后一种不是代理模式)。
  • 代理角色(房屋中介)和真实角色(房东):他们二者都需要去实现出租房子这个行为,为什么?房东出租房子这是自然而然的事情,中介这里特指的是房屋中介,而不是指婚庆中介、办证中介,所以房屋中介也是需要实现出租房子这个行为的,不然展开不了业务啊。

介绍完场景,代码实现步骤如下:

  1. 接口
  2. 真实角色
  3. 代理角色
  4. 客户端访问代理角色

租房的接口:

public interface Rent {
    void rent();
}

真实角色(房东):

public class Landlord implements Rent{
    public void rent() {
        System.out.println("出租房子");
    }
}

代理角色(房屋中介):

public class Proxy implements Rent {
    private Landlord landlord;

    public Proxy(Landlord landlord) {
        this.landlord = landlord;
    }

    // 代理类实现相关接口,所以和真实对象有相同的行为
    public void rent() {
        landlord.rent();
    }

    /*
    代理类可以有一些自己的业务
     */
    public void seeHouse() {
        System.out.println("看房子");
    }

    public void sign() {
        System.out.println("签合同");
    }
}

租户访问中介:

public class Client {
    public static void main(String[] args) {
        Landlord landlord = new Landlord();// 房东
        Proxy proxy = new Proxy(landlord);// 房屋中介
        proxy.rent();// 租户向中介租房子
        proxy.seeHouse();// 租户和中介看房子
        proxy.sign();// 租户和中介签合同
    }
}

静态代理的好处:

  • 可以使真实角色的操作更加纯粹,不用去关注一些公共的业务(比如看房、签合同)
  • 公共业务都交给代理角色,实现了业务的分工。
  • 公共业务发生扩展的时候,方便集中管理。

缺点:一个真实角色就会产生一个代理角色,代码量会翻倍,开发效率会降低。(从代码的角度来理解,创建中介对象的时候会传入一个房东对象,体现出了静态代理)

动态代理

动态代理中的各个角色和静态代理是一致的。但是动态代理的代理类是动态生成的,不是我们直接写好的。

动态代理分为两大类:基于接口的动态代理和基于类的动态代理。主要实现方式如下:

  • 基于接口:JDK动态代理
  • 基于类:cglib
  • Java字节码实现:JAVAsist

动态代理的好处:

  • 一个动态代理类代理的是一个接口,接口对应的是某一类业务,比如:房屋租赁业务。
  • 一个动态代理类可以代理多个类,这些类实现同一个接口即可。
  • 关于上面两点形象地解释:静态代理中,一个中介和一个房东是绑定的,相当于所有的房子都是一对一服务,这肯定是不现实的。动态代理中,一个房屋中介可以管理一大片的房东,因为这些房东都有出租房子的需求,代码层面就是都实现了租房的接口,所以中介可以管理所有这些房东,只要他的资金允许的话。这就很能理解动态代理的好处了。

动态代理的Java实现:
动态代理中两个重要的类和接口:Proxy(类)和InvocationHandler(接口)
1、InvocationHandler接口里面只有一个invoke()方法,该方法是proxy代理实例的调用处理程序。每一个proxy代理实例都有一个关联的调用处理程序;代理实例调用方法时,方法调用被编码分派到调用处理程序的invoke方法。invoke方法如下:

/**
* proxy:代理对象
* method:代理对象调用接口方法所对应的Method实例。
* 注:我们是通过代理对象来调用真实对象中的方法。
* args:指代理对象调用方法传递的参数
*/
public Object invoke(Object proxy, Method method, Object[] args)
    throws Throwable;

2、Proxy类就是用来创建一个代理对象的类,它提供了很多方法,但是我们最常用的是newProxyInstance方法。

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

三个参数的含义:

  • loader:一个Classloader对象,定义了由哪个Classloader对象对生成的代理类进行加载;
  • interfaces:一个interface对象数组,表示我们将要给我们的代理对象提供一组什么样的接口,如果我们提供了这样一个接口对象数组,那么也就是声明了代理类实现了这些接口,代理类就可以调用接口中声明的所有方法。
  • h:一个InvocationHandler对象,表示的是当动态代理对象调用方法的时候会关联到哪一个InvocationHandler对象上,并最终由其调用。

动态代理实现用户信息的增删改查。
接口:

public interface UserService {
    void add();
    void delete();
    void update();
    void query();
}

真实对象:

public class UserServiceImpl implements UserService {
    public void add() {
        System.out.println("调用add方法");
    }

    public void delete() {
        System.out.println("调用delete方法");
    }

    public void update() {
        System.out.println("调用update方法");
    }

    public void query() {
        System.out.println("调用query方法");
    }
}

代理对象的调用处理程序:

public class UserServiceHandler implements InvocationHandler {

    // 被代理的对象,即真实对象,类型为Object,具有通用性
    private Object target;

    public void setTarget(Object target) {
        this.target = target;
    }

    // 通过Proxy的静态方法来生成代理类
    public Object getProxy() {
        return Proxy.newProxyInstance(
            this.getClass().getClassLoader(),
            target.getClass().getInterfaces(),
            this);
    }

    // 调用处理程序,并返回结果
    public Object invoke(Object proxy, Method method, 
        Object[] args) throws Throwable {
        log(method.getName());// 增加日志的功能
        // 在真实的对象执行之前我们可以添加自己的操作,比如上面的日志功能
        System.out.println("before invoke...");
        Object invoke = method.invoke(target, args);
        // 在真实的对象执行之后我们可以添加自己的操作
        System.out.println("after invoke...");
        return invoke;
    }

    // 如果想增加日志功能,直接在代理类中的各个方法中调用日志方法
    // 日志方法
    private void log(String msg) {
        System.out.println("[Debug]调用了"+msg+"方法");
    }
}

客户端进行调用:

public class Client {
    public static void main(String[] args) {
        // 真实对象
        UserService userService = new UserServiceImpl();
        // 代理对象的调用处理程序
        UserServiceHandler handler = new UserServiceHandler();
        handler.setTarget(userService);// 设置要代理的对象
        // 动态生成了代理类
        UserService proxy =  (UserService) handler.getProxy();
        // 代理类去调用方法
        proxy.add();
    }
}

动态代理的体现:

  • 通过Proxy的静态方法Proxy.newProxyInstance()来动态生成代理对象,而不是直接写好的。
  • 对于方法调用,代理对象调用接口中的方法时,是将方法分派到调用处理程序的invoke()方法。

参考资料:
Java动态代理InvocationHandler和Proxy学习笔记
Java中InvocationHandler接口中第一个参数proxy详解

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值