java代理模式静态代理和动态代理

代理模式简介

代理模式(proxy)指的是为一个对象提供一个代理以控制外界对该对象的访问,比如有些情况下对象A不能直接访问对象B,此时可以为对象B创建一个代理C,然后对象A通过访问代理C来间接访问对象B。比如你看上了你女性朋友的闺蜜,此时你可以通过你的朋友来跟她闺蜜进行一些沟通。

有的时候我们希望增强某个方法的功能,但是有不方便直接修改该方法,此时也可通过代理来实现,即在该方法执行的前后做一些事情。比如你要打官司,但是法律上的事情你可能不清楚,此时需要请一名代理律师,让这个代理律师帮助你去做一些事情。

对于一个成熟的系统,我们需要在添加新的功能,在触碰原有代码的情况下,我们可以使用代理模式来做这方面的操作。比如mybatis,spring中都有对代理模式的应用。

代理的模式的优点:

  • 提高程序的扩展性和可复用性
  • 保护目标对象

代理模式分为两种:静态代理和动态代理。

静态代理

代理类和相关的方法直接在代码中写死。下面以你看上了你女性朋友娜娜的闺蜜小曼举例:

首先定义一个接口:

package com.monkey1024.proxy.staticproxy;

/**
 * 女神
 */
public interface Beauty {

    void eat();


}

创建小曼的类并实现上面的接口:

package com.monkey1024.proxy.staticproxy;

/**
 * 你的目标对象小曼
 * 你女性朋友娜娜的闺蜜
 */
public class XiaoMan implements Beauty {

    @Override
    public void eat() {
        System.out.println("小曼吃饭");
    }
}

创建娜娜的类实现上面接口,娜娜是中间人,所以实现方式不太一样,这里的eat方法中要调用目标对象上的eat方法:

package com.monkey1024.proxy.staticproxy;

/**
 * 中间人,你的女性朋友娜娜
 */
public class NaNa implements Beauty {

    private Beauty beauty;

    /**
     * 通过构造方法传入目标对象
     * @param beauty
     */
    public NaNa(Beauty beauty) {
        this.beauty = beauty;
    }


    @Override
    public void eat() {
        //调用目标对象上的吃饭方法
        beauty.eat();
    }
}

创建你的类,里面添加交朋友的方法:

package com.monkey1024.proxy.staticproxy;

public class You {

    private Beauty beauty;

    public You(Beauty beauty) {
        this.beauty = beauty;
    }

    /**
     * 交朋友
     */
    public void makeFriend() {
        beauty.eat();
    }
}

创建测试类:

package com.monkey1024.proxy.staticproxy;


public class Test {

    public static void main(String[] args) {
        XiaoMan xiaoMan = new XiaoMan();
        NaNa naNa = new NaNa(xiaoMan);
        You you = new You(naNa);

        you.makeFriend();
    }
}

动态代理

动态代理它可以直接给某一个目标对象生成一个代理对象,而不需要代理类存在。动态代理与静态代理原理是一样的,只是它没有具体的代理类,直接在程序运行时动态生成了一个代理对象。

动态代理的技术实现:
– JDK提供的动态代理,底层使用反射原理,只能创建接口的代理。
– cglib,底层是通过使用一个小而快的字节码处理框架ASM,可以创建类和接口的代理。

jdk的动态代理:

  • Proxy.newProxyInstance():产生代理接口的实例。仅能代理实现至少一个接口的类

    • ClassLoader:类加载器。即被代理的接口的类加载器。
    • Class[] interface:被代理对象的父接口
    • InvocationHandler:将要在代理中实现的功能写在该对象中
  • InvocationHandler中的invoke方法:调用代理类的任何方法,此方法都会执行

    • Object proxy:代理对象自身的引用。
    • Method method:当前被调用的方法。
    • Object[] args:当前被调用方法用到的参数

只需要将之前的测试类中的代码修改为下面的内容即可:

package com.monkey1024.proxy.staticproxy;


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

public class Test {

    public static void main(String[] args) {
        Beauty xiaoMan = new XiaoMan();

        //不需要自己创建代理了
        //NaNa naNa = new NaNa(xiaoMan);

        //由jdk动态的为你创建一个代理
        Beauty proxy = (Beauty)Proxy.newProxyInstance(xiaoMan.getClass().getClassLoader(), xiaoMan.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("吃饭之前的准备");
                Object invoke = method.invoke(xiaoMan);
                System.out.println("吃饭之后的结果");
                return invoke;
            }
        });

        You you = new You(proxy);

        you.makeFriend();
    }
}

调用有参数的方法

在彼此认识之后,你打算送小曼礼物,此时将之前的代码稍作修改:

在接口中新增gift方法,count表示礼物的数量:

 void gift(int count);

在XiaoMan的类中添加gift方法的重写:

@Override
public void gift(int count) {
    System.out.println("收到" + count + "礼物");
}

在You类中添加show方法用来送礼物:

/**
 * 展示
 */
public void show(){
    beauty.gift(10);
}

修改之前的测试类如下,送礼物的时候,为了感谢代理人,所以每次会把礼物分一半给代理人:

Beauty xiaoMan = new XiaoMan();

    //不需要自己创建代理了
    //NaNa naNa = new NaNa(xiaoMan);

    //由jdk动态的为你创建一个代理
    Beauty proxy = (Beauty)Proxy.newProxyInstance(xiaoMan.getClass().getClassLoader(), xiaoMan.getClass().getInterfaces(), new InvocationHandler() {
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("准备");
            Object invoke = null;
            if ("gift".equals(method.getName())){
                invoke = method.invoke(xiaoMan,new Object[]{(Integer)args[0]/2});
            }else{
                invoke = method.invoke(xiaoMan);
            }

            System.out.println("结果");
            return invoke;
        }
    });

    You you = new You(proxy);

    you.makeFriend();
    you.show();