设计模式-代理模式

时间:2022-07-25
本文章向大家介绍设计模式-代理模式,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

背景

日常生活中,有非常多的代办、代处理,比如代找货源、代租房、代记、代办证件等,代办人就是这个代理的对象,通过代理人来达到办理的目的或结果,并且得到一样的结果。就比如:你想喝多了,回家找一个滴滴代驾,把车开到目的地,一样实现到达的结果是一样。

代理模式是什么?

代理模式(Proxy Pattern)也叫委托模式,属于结构型模式,为其他对象提供一种代理以控制对这个对象的访问。代理分为静态代理,和动态代理。

静态代理:预先指定了代理与被代理都的关系,比如:一开始就知道代驾的是哪位师傅,男或女?

动态代理:待运行时才确定代理对象,并且代理对象是动态的;比如:等到代驾师傅到后才知道,原来是个女的,开车技术还蛮娴熟...

角色:

Subject(抽象主题角色):定义目标对象和代理对象的共同接口,这样可以在任何使用具体目标对象的地方使用代理对象。

Proxy(代理主题角色):也叫委托类,代理类。实现与具体的目标对象一样的接口,这样就可以使用代理来代替具体的目标对象。

RealSubject(真实主题角色):也称为委托角色或者被代理角色。定义了代理对象所代表的目标对象。

代理模式可以干嘛?

代理模式为对象提供一个代理的替身(代办)来控制这个对象的访问;

优点:

高拓展:修改代理角色不影响使用者,对于用户来说,代理对象是透明的。

隔离、低耦合:直接面对用户的是代理角色,所以起到一个很好的隔离作用,并且降低了与用户的耦合度,很好的遵循了:迪米特法则和隔离原则;

缺点:

增加代码复杂度:添加了代理,代码的复杂度相对来说更加复杂了;

处理速度变慢:由代理角色处理,所以导致流程增加了代理角色,增加了系统实现的复杂度。

个人理解:如代驾,喝高了,由于不想被交警拉去面壁思过(非直接面对)...,找一个司机帮你代驾,静态代理就是一开始就是确定的师傅;而动态代理,等到师傅来的时候你才知道是谁,男或女。

实现代码

静态代理

手动生成源代码,再对其进行编译。程序运行前.class文件就已经存在了。

/**
 * @Auther: csh
 * @Date: 2020/6/2 11:35
 * @Description:抽象车(subject)
 */
public interface ICar {
    //开车
    void drive();
}
/**
 * @Auther: csh
 * @Date: 2020/6/2 11:36
 * @Description:宝马车(RealSubject)
 */
public class BMWCar implements ICar {
    //开车人名称
    private String name;

    public BMWCar(String name) {
        this.name = name;
    }

    @Override
    public void drive() {
        System.out.println("驾驶宝马的人:"+name);
    }
}
/**
 * @Auther: csh
 * @Date: 2020/6/2 11:38
 * @Description:代驾(Proxy)
 */
public class CarProxy implements ICar {
    //宝马
    private BMWCar bmwCar;
    //代驾名称
    private String proxyName;

    public CarProxy(String proxyName) {
        this.proxyName = proxyName;
    }

    @Override
    public void drive() {
        if(null==bmwCar){
            bmwCar = new BMWCar(proxyName);
        }
        bmwCar.drive();
    }
}
/**
 * @Auther: csh
 * @Date: 2020/6/2 11:39
 * @Description:代驾列子(静态代理)
 */
public class Client {

    public static void main(String[] args) {
        ICar car =new  CarProxy("滴滴代驾");
        car.drive();
    }
}

结果

驾驶宝马的人:滴滴代驾

JDK动态代理:

(jdk动态代理只针对代理接口,并且类中必须要有一个接口,而不能针对这个类进行代理)

public Object invoke(Object proxy, Method method, Object[] args)
    throws Throwable;

此方法的参数含义如下

proxy:代表动态代理对象

method:代表正在执行的方法

args:代表当前执行方法传入的实参

返回值:表示当前执行方法的返回值

实现代码

/**
 * @Auther: csh
 * @Date: 2020/6/2 11:35
 * @Description:抽象车(subject)
 */
public interface ICar {
    //开车
    void drive();
}
/**
 * @Auther: csh
 * @Date: 2020/6/2 11:36
 * @Description:宝马车(RealSubject)
 */
public class BMWCar implements ICar {
    //开车人名称
    private String name;

    public BMWCar(String name) {
        this.name = name;
    }

    @Override
    public void drive() {
        System.out.println("驾驶宝马的人:"+name);
    }
}
/**
 * @Auther: csh
 * @Date: 2020/6/2 16:22
 * @Description:代理(Proxy)
 */
public class ProHandler implements InvocationHandler {

    Object obj = null;


    public ProHandler(Object obj) {
        this.obj = obj;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("我是代理,正在驾驶");
        Object invoke = method.invoke(obj, args);
        System.out.println("到达目的地");
        return invoke;
    }
}
/**
 * @Auther: csh
 * @Date: 2020/6/2 16:25
 * @Description:jdk动态代理
 */
public class Client {
    public static void main(String[] args) {
        //代驾
        BMWCar car = new BMWCar("滴滴代驾");
        InvocationHandler proHandler = new ProHandler(car);
        ICar bmwCar = (ICar) Proxy.newProxyInstance(BMWCar.class.getClassLoader(), BMWCar.class.getInterfaces(), proHandler);
        bmwCar.drive();
    }
}

结果

我是代理,正在驾驶
驾驶宝马的人:滴滴代驾
到达目的地

CGLIB动态代理:

CGLIB即可以针对接口动态代理,又可以针对该类进行代理,并且在运行期间动态扩展类或接口,它的底层使用的是java字节码操作框架ASM实现。

CGLIB缺点:对于final方法,无法进行代理。

引入java包

<!-- https://mvnrepository.com/artifact/cglib/cglib-nodep -->
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib-nodep</artifactId>
    <version>3.3.0</version>
    <scope>test</scope>
</dependency>

实现代码

/**
 * @Auther: csh
 * @Date: 2020/6/2 11:35
 * @Description:抽象车(subject)
 */
public interface ICar {
    //开车
    void drive();
}
/**
 * @Auther: csh
 * @Date: 2020/6/2 11:36
 * @Description:宝马车(RealSubject)
 */
public class BMWCar implements ICar {
    //开车人名称
    private String name;

    public BMWCar() {
    }


    public String getName() {
        return name;
    }

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

    @Override
    public void drive() {
        System.out.println("驾驶宝马的人:"+name);
    }
}
/**
 * @Auther: csh
 * @Date: 2020/6/2 17:31
 * @Description:CGLIB拦截器
 * 在调用目标方法时,CGLib会回调MethodInterceptor接口方法拦截,来实现你自己的代理逻辑,类似于JDK中的InvocationHandler接口
 */
public class CglibProxy implements MethodInterceptor {

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("被拦截");
        Object invoke = methodProxy.invokeSuper(o, objects);
        return invoke;
    }
}
/**
 * @Auther: csh
 * @Date: 2020/6/2 17:35
 * @Description:代理工厂
 */
public class ProxyFacotory {
    public static Object getcglibProxy(Object target){
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(target.getClass());
        enhancer.setCallback(new CglibProxy());
        Object targetProxy = enhancer.create();
        return targetProxy;
    }
}
/**
 * @Auther: csh
 * @Date: 2020/6/2 17:37
 * @Description:cglib动态代理
 * 区别于jdk代理在于
 * 1.CGLIB可代理类,可JDK代理只能代理方法
 * 2.CGLIB可以运行时动态增加类或方法,而JDK代理不能;
 * 3.CGLIB不能代理final的方法,并且比JDK代理快;
 */
public class Client {
    public static void main(String[] args) {
        BMWCar car = (BMWCar)ProxyFacotory.getcglibProxy(new BMWCar());
        car.setName("滴滴代驾");
        car.drive();

    }
}

结果

被拦截
被拦截
驾驶宝马的人:滴滴代驾

最后

代理模式,在各大开源项目中用得非常广泛,比如Spring AOP ,IOC ,当然代理模式的优秀主要在于,通过代理角色,可以隔离开内部信息,通过代理角色来达到想要的结果,并且很好的屏蔽了内部对外暴露的风险,很好的降低了系统的耦合度。