亲宝软件园·资讯

展开

设计模式 - 代理模式详解

农夫三拳有点疼~ 人气:1

代理模式介绍

代理模式提供了对目标对象额外的访问方式,即通过代理对象访问目标对象,这样可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标对象的功能。

代理模式分为三类:

  • 静态代理
  • 动态代理
  • Cglib 代理

静态代理(不推荐)

介绍

要求目标对象和代理对象实现同一个接口,调用的时候调用代理对象的方法,从而达到增强的效果

优点:

可以在不修改目标对象的前提下,增强目标对象方法的功能(所有代理模式都可以实现,因此不推荐使用此方法)

缺点:

① 冗余。目标对象和代理对象实现同一个接口,会产生过多的代理类。

② 不易维护。当接口方法增加,目标对象与代理对象都要进行修改。

代码实现

场景:厂家生产了商品,但是没有足够的精力、人力去销售,这时候就需要一个代理商帮他售卖,但是代理商需要从中抽取 20% 的利润。

公共接口

public interface IProducer {
    void sale(float money);
}

被代理对象

public class Producer implements IProducer {
    @Override
    public void sale(float money) {
        System.out.println("卖出产品,厂家获得" + money + "元");
    }
}

代理对象

public class ProxyProducer implements IProducer{

    private IProducer producer;

    public ProxyProducer(IProducer producer) {
        this.producer = producer;
    }

    @Override
    public void sale(float money) {
        producer.sale(money * 0.8f);
    }
}

测试类

public class Client {
    @Test
    public void test(){
        IProducer producer = new Producer();
        ProxyProducer proxyProducer = new ProxyProducer(producer);
        proxyProducer.sale(1000f);
    }
}

运行结果

卖出产品,厂家获得800.0元

动态代理

介绍

动态代理也称:JDK 代理、接口代理,需要目标对象实现接口,否则不能用动态代理,利用 JDK 的 API(java.lang.reflect.Proxy),动态地在内存中构建代理对象。

静态代理和动态代理的区别:

  • 静态代理在编译时就已经实现,编译完后的代理类是一个实际的 class 文件
  • 动态代理实在运行时动态生成的,编译后没有实际的 class 文件,而是在运行时动态的生成类字节码,并加载到 JVM 中

代码实现

以静态代理的情景为例,我们只需要修改代理对象的代码,代理对象不需要实现公共接口了。

public class ProxyProducer {
    /**
     * 维护一个目标对象
     */
    private Object target;

    public ProxyProducer(Object target) {
        this.target = target;
    }

    public Object getProxyInstance() {
        return Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new InvocationHandler() {
                    /**
                     * 执行被代理对象的任何接口方法都会经过这里
                     * @param proxy 代理对象的引用
                     * @param method 当前执行的方法
                     * @param args 当前执行方法的参数
                     * @return 和被代理对象具有相同的返回值
                     * @throws Throwable
                     */
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        //代理过程中执行一些方法
                        float money = (float) args[0] * 0.8f;
                        //反射机制调用目标对象的方法
                        Object invoke = method.invoke(target, money);
                        return invoke;
                    }
                });
    }
}

Cglib 代理

介绍

Cglib 代理也叫子类代理,目标对象不需要实现任何接口,它是在内存中构建一个子类对象从而实现对目标对象功能的扩展。

Cglib 是一个强大的高性能的代码生成包,它可以在运行期间扩展 Java 类与实现 Java 接口,它广泛地被许多 AOP 的框架使用,例如 Spring AOP,用于实现方法拦截。

Cglib 包底层实通过使用字节码处理框架 ASM 来转换字节码并生成新的类。

在 AOP 编程中选择哪种代理模式?

  • 目标对象需要实现接口,用 JDK 代理
  • 目标对象不需要实现接口,用 Cglib 代理

代码实现

使用之前需要导入相关 jar 包,可去 maven 仓库下载

被代理对象,无需实现接口

public class Producer {
    public void sale(float money) {
        System.out.println("卖出产品,厂家获得" + money + "元");
    }
}

代理对象

public class ProxyProducer implements MethodInterceptor {
    /**
     * 维护一个目标对象
     */
    private Object target;

    public ProxyProducer(Object target) {
        this.target = target;
    }

    /**
     * 为目标对象生成代理对象
     */
    public Object getProxyInstance(){
        //创建一个工具类
        Enhancer enhancer = new Enhancer();
        //设置父类
        enhancer.setSuperclass(target.getClass());
        //设置回调函数
        enhancer.setCallback(this);
        //创建子类对象(代理对象)
        return enhancer.create();
    }

    /**
     * 会拦截被代理对象的所有方法
     * @param obj 增强对象
     * @param method 被代理对象的方法
     * @param args 被代理对象方法的参数
     * @param methodProxy 代理对象
     */
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println("obj:" + obj.getClass());
        Object returnValue = null;
        float money = (float) args[0] * 0.8f;
        if("sale".equals(method.getName())){
            returnValue = method.invoke(target, money);
        }
        return returnValue;
    }
}

测试类

public class Client {
    @Test
    public void test() {
        Producer producer = new Producer();
        Producer proxyInstance = (Producer) new ProxyProducer(producer).getProxyInstance();
        proxyInstance.sale(1000f);
    }
}

加载全部内容

相关教程
猜你喜欢
用户评论