java中的代理模式

代理模式的原理

使用一个代理将对象包装起来, 然后用该代理对象取代原始对象。任何对原始对象的调用都要通过代理。代理对象决定是否以及何时将方法调用转到原始对象上。

简单例子:

小强意外成为了一个网红明星,此时他请了一个专业的经纪人进行各种事务的管理,小强的演出活动啊都得经过经纪人的同意。此时小强是被代理对象,经纪人是代理对象。

此时快乐大本营想请小强去参加节目,需要找到小强的经纪人,小强的经纪人然后进行排挡,找小强去完成唱歌,才艺展示等动作。

静态代理

代理对象和被代理对象(目标对象)实现同一接口。优点是不用修改目标对象的源码,扩展其功能。

特点

  • 在编译期间,代理类和被代理对象(目标对象)的类都确定下来了,不利于程序的扩展
  • 每一个代理类只能为一个接口服务,这样一来程序开发中必然产生过多的代理。

举例

创建一个接口,这个接口是代理工厂(代理类)和Nike工厂(被代理类)的公共接口,

通过代理工厂的方法调用同时也调用了Nike工厂的同名方法,完成不修改被代理类的源码而扩展其功能。

共同接口

interface ClothFactory{
    void produceCloth();
}

代理类

// 代理类
class ProxyClothFactory implements ClothFactory {
    private ClothFactory factory;//用被代理类对象进行实例化

    public ProxyClothFactory(ClothFactory factory) {
        this.factory = factory;
    }

    @Override
    public void produceCloth() {
        System.out.println("代理工厂做一些准备工作");
        factory.produceCloth();
        System.out.println("代理工厂做一些后续的工作");
    }
}

被代理类

// 被代理类
class NikeClothFactory implements ClothFactory{

    @Override
    public void produceCloth() {
        System.out.println("nike工厂生产运动服");
    }
}

测试代码

public class StaticProxyTest {
    public static void main(String[] args) {

        // 创建被代理类对象
        NikeClothFactory nikeClothFactory = new NikeClothFactory();
        // 创建代理类对象
        ProxyClothFactory proxyClothFactory = new ProxyClothFactory(nikeClothFactory);
        proxyClothFactory.produceCloth();
    }
}

输出结果

代理工厂做一些准备工作
nike工厂生产运动服
代理工厂做一些后续的工作

动态代理

动态代理是指客户通过代理类来调用其它对象的方法,并且是在程序运行时根据需要动态创建目标类的代理对象。

特点

代理对象不需要实现接口,但是要求被代理对象(目标对象)必须实现接口,否则不能使用动态代理。

相关API

Proxy:专门完成代理的操作类,是所有动态代理类的父类。通过此类为一个或多个接口动态地生成实现类。

static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h) 直接创建一个动态代理对象

参数详解:

  • ClassLoader loader:和被代理对象使用相同的类加载器
  • Class<?>[] interfaces:和被代理对象具有相同的行为,实现相同的接口
  • InvocationHandler h:得到InvocationHandler接口的实现类实例

实现动态代理

实现动态代理,要解决的问题

  1. 如何根据加载到内存中的被代理类,动态创建一个代理类及其对象;
  2. 当通过代理类的对象调用方法时,如何动态的去调用被代理类中的同名方法

具体步骤如下:

  1. 创建被代理类和接口

    接口:Human接口

    //共同接口
    interface Human{
        String getBelief();
        void eat(String food);
    }
    

    被代理类:SuperMan类

    // 被代理类
    class SuperMan implements Human{
    
        @Override
        public String getBelief(){
            return "我能飞";
        }
        @Override
        public void eat(String food){
            System.out.println("我喜欢吃"+food);
        }
    }
    
  2. 创建一个用来生产代理类的工厂,该工厂有一个静态方法getProxyInstance可以返回一个代理类的对象,该方法需传入被代理类对象。

    class ProxyFactory{
        // 调用此方法,返回一个代理类的对象。需要传入被代理类对象
        public static Object getProxyInstance(Object obj){
    
            MyInvocationHandler handler = new MyInvocationHandler();
            handler.bind(obj);
            // 哪个类加载的?被代理类
            // 被代理类实现了哪个接口?得到被代理类实现的全部接口
            // 得到InvocationHandler接口的实现类实例
            return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),handler);
        }
    }
    
  3. 创建一个实现接口InvocationHandler的类,实现inoke方法,以完成代理的具体操作。当我们通过代理类的对象调用方法a时,就会自动的调用如下方法invoke(),将被代理类要执行的方法a的功能声明在invoke()中。

    class MyInvocationHandler implements InvocationHandler{
        private Object obj;//需要使用被代理类的对象进行赋值
        public void bind(Object obj){
            this.obj = obj;
        }
        
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            // obj:被代理类对象
            // method:即为代理类对象调用的方法,此方法作为被代理类对象要调用的方法
            // args: 方法调用时所需要的参数
            Object returnValue = method.invoke(obj,args);
            //该返回值作为当前类中的invoke()的返回值
            return returnValue;
        }
    }
    
  4. 测试类

    public class ProxyTest {
        public static void main(String[] args) {
            SuperMan superMan = new SuperMan();//创建一个被代理类对象
            // 动态创建代理类
            Human proxyInstance = (Human)ProxyFactory.getProxyInstance(superMan);
            
            String belief = proxyInstance.getBelief();
            System.out.println(belief);
            proxyInstance.eat("肉夹馍");
        }
    }
    
  5. 输出结果

我们发现,只需要知道接口和被代理类,就能实现创建一个代理类。把上面静态代理部分的例子拿过来

NikeClothFactory nike = new NikeClothFactory();
ClothFactory proxyInstance1 = (ClothFactory) ProxyFactory.getProxyInstance(nike);
proxyInstance1.produceCloth(); //nike工厂生产运动服

动态代理实现两种方式

动态代理

特点:字节码随用随创建,随用随加载;
作用:不修改源码的基础上对方法增强。

基于接口的动态代理

提供者: JDK 官方的 Proxy 类。
要求:被代理类最少实现一个接口。

实现步骤

  1. 创建一个接口,该接口可以理解成对生产厂家的规范

    /**
     * 对生产厂家要求的接口
     */
    public interface IProducer {
    
        /**
         * 销售
         * @param money
         */
        public void saleProduct(float money);
    
        /**
         * 售后
         * @param money
         */
        public void afterService(float money);
    }
    

    代理类:是一个生产者,它实现了接口,具有接口中的方法,也就是说符合生产厂家的要求,产品销售和售后是ok的。

    /**
     * 一个生产者
     */
    public class Producer implements IProducer{
        /**
         * 销售
         * @param money
         */
        public void saleProduct(float money){
            System.out.println("销售产品,并拿到钱:"+money);
        }
        /**
         * 售后
         * @param money
         */
        public void afterService(float money){
            System.out.println("提供售后服务,并拿到钱:"+money);
        }
    }
    
  2. 模拟一个消费者,如果该消费者如果没有通过代理商去买电脑,而是直接去到厂家处购买。但现实中,很少有人这样购买。一般都是厂家把产品交给代理商进行销售以及提供售后服务,顾客只需跟代理商进行交易,而不直接与厂家进行联系。

    没有代理之前

    Producer producer1 = new Producer();
    producer.saleProduct(10000f);//销售产品,并拿到钱:10000.0
    

    有了代理,代理要从中提取20%的手续费。那么生产者只能得到80%的钱

    public class Client {
        public static void main(String[] args) {
            final Producer producer = new Producer();
            IProducer proxyProducer = (IProducer) Proxy.newProxyInstance(producer.getClass().getClassLoader(),
                    producer.getClass().getInterfaces(),
                    new InvocationHandler() {
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            //提供增强的代码
                            Object returnValue = null;
    
                            //1.获取方法执行的参数
                            Float money = (Float)args[0];
                            //2.判断当前方法是不是销售
                            if("saleProduct".equals(method.getName())) {
                                returnValue = method.invoke(producer, money*0.8f);
                            }
                            return returnValue;
                        }
                    });
            proxyProducer.saleProduct(10000f);
        }
    }
    

    涉及的参数:

    Proxy类

    public static Object newProxyInstance(ClassLoader loader,
                                              Class<?>[] interfaces,
                                              InvocationHandler h)
    ClassLoader:类加载器 代理谁写谁的类加载器,它是用于加载代理对象字节码的。和被代理对象使用相同的类加载器。固定写法。
    Class[]:字节码数组,它是用于让代理对象和被代理对象有相同方法。固定写法。
    InvocationHandler:用于提供增强的代码
    它是让我们写如何代理。我们一般都是些一个该接口的实现类,通常情况下都是匿名内部类,但不是必须的。此接口的实现类都是谁用谁写。
    

    InvocationHandler接口

    public Object invoke(Object proxy, Method method, Object[] args) 
    作用:执行被代理对象的任何接口方法都会经过该方法
    方法参数的含义
    proxy   代理对象的引用
    method  当前执行的方法
    args    当前执行方法所需的参数
    

基于子类的动态代理

提供者:第三方的 cglib。
要求:被代理类不能是最终类(用 final 修饰的类)。

涉及的类:Enhancer

如何创建代理对象:使用Enhancer类中的create(Class,Callback)方法

create方法的参数:

Class:字节码,它是用于指定被代理对象的字节码。

Callback:如何代理,即就是用于提供增强的代码。我们一般都是写一个该接口的实现类,通常情况下都是匿名内部类,但不是必须的。此接口的实现类都是谁用谁写。我们一般写的都是该接口的子接口实现类:MethodInterceptor

还是消费者买产品的例子,只不过不让它实现接口。

举例:

生产者

/**
 * 一个生产者
 */
public class Producer {

    /**
     * 销售
     * @param money
     */
    public void saleProduct(float money){
        System.out.println("销售产品,并拿到钱:"+money);
    }

    /**
     * 售后
     * @param money
     */
    public void afterService(float money){
        System.out.println("提供售后服务,并拿到钱:"+money);
    }
}

消费者

/**
 * 模拟一个消费者
 */
public class Client {

    public static void main(String[] args) {
        final Producer producer = new Producer();
        Producer cglibProducer = (Producer) Enhancer.create(producer.getClass(), new MethodInterceptor() {
            /**
             * 执行被代理对象的任何方法都会经过该方法
             * @param proxy
             * @param method
             * @param args
             *    以上三个参数和基于接口的动态代理中invoke方法的参数是一样的
             * @param methodProxy :当前执行方法的代理对象
             * @return
             * @throws Throwable
             */
            @Override
            public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                //提供增强的代码
                Object returnValue = null;

                //1.获取方法执行的参数
                Float money = (Float)args[0];
                //2.判断当前方法是不是销售
                if("saleProduct".equals(method.getName())) {
                    returnValue = method.invoke(producer, money*0.8f);
                }
                return returnValue;
            }
        });
        cglibProducer.saleProduct(12000f);
    }
}

参考链接:

https://blog.csdn.net/briblue/article/details/73928350

原文地址:https://www.cnblogs.com/benjieqiang/p/11309733.html