设计模式之代理模式

代理模式

定义

代理模式为另一个对象提供一个替身或占位符以控制对这个对象访问。

代理模式(英语:Proxy Pattern)是程序设计中的一种设计模式

所谓的代理者是指一个类别可以作为其它东西的接口。代理者可以作任何东西的接口:网络连接、存储器中的大对象、文件或其它昂贵或无法复制的资源。

著名的代理模式例子为引用计数(英语:reference counting)指针对象。

当一个复杂对象的多份副本须存在时,代理模式可以结合享元模式以减少存储器用量。典型作法是创建一个复杂对象及多个代理者,每个代理者会引用到原本的复杂对象。而作用在代理者的运算会转送到原本对象。一旦所有的代理者都不存在时,复杂对象会被移除。

(引用维基百科)

比如要访问远程的机器,由于某些原因,直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问对象时加上一个对此对象的访问层。比如RMI技术。
RMI

类图

类图

实现

1. JDK实现

JDK

定义Subject接口
public interface PersonBean {
    String getName();
    String getGender();
    String getInterest();
    int getHotOrNotRating();

    void setName(String name);
    void setGender(String gender);
    void setInterest(String interest);
    void setHotOrNotRating(int rating);
}
定义Subject实现接口
public class PersonImpl implements PersonBean {
    String name;
    String gender;
    String interests;
    int rating;
    int ratingCount = 0;

    @Override
    public String getName() {
        return name;
    }

    @Override
    public String getGender() {
        return gender;
    }

    @Override
    public String getInterest() {
        return interests;
    }

    @Override
    public int getHotOrNotRating() {
        if (ratingCount == 0) return 0;
        return (rating / ratingCount);
    }

    @Override
    public void setName(String name) {
        this.name = name;
    }

    @Override
    public void setGender(String gender) {
        this.gender = gender;

    }

    @Override
    public void setInterest(String interest) {
        this.interests = interest;

    }

    @Override
    public void setHotOrNotRating(int rating) {
        this.rating += rating;
        ratingCount++;

    }
}
定义InvocationHandler实现类
/**
 * 步骤一: 创建InvocationHandler类。这个类与Proxy互相配合。遵循单一职责。Proxy负责动态生成代理对象。而InvocationHandler则是实现业务逻辑
 * 个人代理对象: 允许获取和设计自己属性
 */
public class OwnerInvocationHandler implements InvocationHandler {
    PersonBean personBean ;
    public OwnerInvocationHandler(PersonBean personBean) {
        this.personBean = personBean;
    }

    /**
     * 业务上的增加实现。
     *
     * @param proxy
     * @param method
     * @param args
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (method.getName().startsWith("get")) {
            return method.invoke(personBean, args);
        } else if (method.getName().equals("setHotOrNotRating")) {
            throw new IllegalAccessException();
        } else if (method.getName().startsWith("set")) {
            return method.invoke(personBean, args);
        }
        return null;
    }
}
/**
 * 步骤一: 创建InvocationHandler类
 */
public class NonInvocationHandler implements InvocationHandler {
    PersonBean personBean;

    public NonInvocationHandler(PersonBean personBean) {
        this.personBean = personBean;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (method.getName().startsWith("get")) {
            return method.invoke(personBean, args);
        } else if (method.getName().equals("setHotOrNotRating")) {
            method.invoke(personBean, args);
        } else if (method.getName().startsWith("set")) {
            throw new IllegalAccessException();
        }
        return null;
    }
}
Main
/**
 * 1. 动态代理之所以称为动态: 是因为在运行时才将它的类创建出来。代码开始执行时,还没有proxy类,它是根据需要从你传入的接口集创建的。
 * 2. InvocationHandle不是proxy,它只是一个帮助proxy的类,proxy会把调用转发给它处理。Proxy本身是复用静态的Proxy.newProxyInstance()方法在运行时动态地创建的。
 * 3. 对于newProxyInstance()的接口数据。如果接口不是public,就必须属于同一个package,不同的接口内,不可以有名称和参数完全一样的方法。
 * 4. 动态代理有点像工厂模式,对于不同的需要被代理的对象产生对应的代理对象。
 */
public class M {
    public static void main(String[] args) {
        PersonBean joe = getJoe();
        PersonBean ownerProxy = getOwnerProxy(joe);
        System.out.println("姓名: " + joe.getName());
        ownerProxy.setInterest("我喜欢篮球");
        try {
            ownerProxy.setHotOrNotRating(10);
        } catch (Exception e) {
            System.out.println("不能自己设定评分哦,需要别人对自己评分才有意义");
        }
        System.out.println("当前Joe评分:" + joe.getHotOrNotRating());

        PersonBean nonProxy = getNonProxy(joe);
        System.out.println("姓名: " + nonProxy.getName());
        nonProxy.setHotOrNotRating(10);
        try {
            nonProxy.setInterest("hey");
        } catch (Exception e) {
            System.out.println("当前兴趣爱好不能被其他人设置");
        }
        System.out.println("当前评分: " + nonProxy.getHotOrNotRating());

    }

    public static PersonBean getTom() {
        PersonBean joe = new PersonImpl();
        joe.setName("Tom");
        joe.setGender("Man");
        joe.setHotOrNotRating(1);
        joe.setInterest("Painting");
        return joe;
    }

    public static PersonBean getJoe() {
        PersonBean joe = new PersonImpl();
        joe.setName("Joe");
        joe.setGender("Man");
        joe.setHotOrNotRating(1);
        joe.setInterest("Painting");
        return joe;
    }

    public static PersonBean getOwnerProxy(PersonBean personBean) {
        return (PersonBean) Proxy.newProxyInstance(personBean.getClass().getClassLoader(), personBean.getClass().getInterfaces(), new OwnerInvocationHandler(personBean));
    }

    public static PersonBean getNonProxy(PersonBean personBean) {
        return (PersonBean) Proxy.newProxyInstance(
                personBean.getClass().getClassLoader(),
                personBean.getClass().getInterfaces(),
                new NonInvocationHandler(personBean));
    }

}

2. CbLib实现

1. 引入Maven
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>
2. 创建目标类
public class Target {
    public void targetMethod(String s1) {
        System.out.println(String.format("当前Classloader%s, 方法参数: %s", this.getClass().getClassLoader(),  s1));
    }
}
3. 实现方法拦截器
public class CgLibProxy implements MethodInterceptor {
    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        beforeMethod(args);
        Object result = proxy.invokeSuper(o, args);
        afterMethod(args);
        return result;
    }

    public void beforeMethod(Object[] args) {
        System.out.println("Aop 前置 参数校验");
        if (args.length != 1) {
            throw new IllegalArgumentException("只允许有一个参数");
        }
        args[0] = "参数校验成功: " + args[0];
    }

    public void afterMethod(Object[] args) {
        System.out.println("Aop 后置 增强操作!");
    }
}
4. 创建代理对象
public class M {
    public static void main(String[] args) {
        Enhancer eh = new Enhancer();
        // 设置被代理的类(目标类)
        eh.setSuperclass(Target.class);
        // 使用回调
        eh.setCallback(new CgLibProxy());
        // 创建 代理
        Object proxy = eh.create();
        Target t = (Target) proxy;
        t.targetMethod("h1");
    }
}

总结

  1. 代理模式属于结构型模式。

  2. 代理模式基于接口而非实现编程的设计,将原始类对象替换为代理类对象的时候,为了让代码改动较少,代理类和原始类都需要实现相同的接口。但如果原始类并没有定义接口,并且原始类代码并不是我们开发维护的,对于这种外部类的扩展,一般是采用继承方式实现。

  3. 动态代理指不事先为每个原始类编写代理类,而是在运行的时候动态地创建原始类对应的代理类,然后在系统中用代理类替换掉原始类。

  4. 应用场景

    1. 业务系统的非功能性需求开发。如监控、统计、鉴权、限流、事务、幂等、日志。

    2. RPC、缓存。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值