代理模式和装饰模式 (委托模式和代理模式)

简介

代理模式的定义:由于某些原因需要给某对象提供一个代理以控制对该对象的访问。这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介,代理模式也叫做委托模式。

为什么使用代理模式

  • 中介隔离作用: 在某些情况下,一个客户类不想或者不能直接引用一个委托对象,而代理类对象可以在客户类和委托对象之间起到中介的作用,其特征是 代理类和委托类实现相同的接口
  • 开闭原则,增加功能: 代理类除了是客户类和委托类的中介之外,我们还可以通过给代理类增加额外的功能来扩展委托类的功能,这样做我们只需要修改代理类而不需要再修改委托类,符合代码设计的开闭原则。代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后对返回结果的处理等。 代理类本身并不真正实现服务,而是同过调用委托类的相关方法,来提供特定的服务 。真正的业务功能还是由委托类来实现,但是可以在业务功能执行的前后加入一些公共的服务。例如我们想给项目加入缓存、日志这些功能,我们就可以使用代理类来完成,而没必要打开已经封装好的委托类。

代理模式优缺点

代理模式的主要优点有:

  • 代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用;
  • 代理对象可以扩展目标对象的功能;
  • 代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度;

  代理模式的主要缺点是:

  • 在客户端和目标对象之间增加一个代理对象,会造成请求处理速度变慢;
  • 增加了系统的复杂度;

代码实现

静态代理

静态代理模式的结构比较简单,主要是通过定义一个继承抽象主题的代理来包含真实主题,从而实现对真实主题的访问。代理模式的主要角色如下:

  • 抽象主题(Subject)类:通过接口或抽象类声明真实主题和代理对象实现的业务方法。
  • 真实主题(Real Subject)类:实现了抽象主题中的具体业务,是代理对象所代表的真实对象,是最终要引用的对象。
  • 代理(Proxy)类:提供了与真实主题相同的接口,其内部含有对真实主题的引用,它可以访问、控制或扩展真实主题的功能。

  其结构图如图所示。

设计模式代理模式和策略模式区别,设计模式动态代理原理

接口

package proxy.staticproxy;

/**
 * @author zyp
 * @create 2022/4/28
 */
public interface ITeacher {
    void teach();
}

具体实现

package proxy.staticproxy;

/**
 * @author zyp
 * @create 2022/4/28
 */
public class Teacher implements ITeacher{
    @Override
    public void teach() {
        System.out.println("老师正在讲课");
    }
}

代理类

package proxy.staticproxy;

/**
 * @author zyp
 * @create 2022/4/28
 */
public class ProxyFactory implements ITeacher{
    private ITeacher iTeacher;

    public ProxyFactory(ITeacher iTeacher){
        this.iTeacher = iTeacher;
    }


    @Override
    public void teach() {
        System.out.println("静态代理开始");
        iTeacher.teach();
        System.out.println("静态代理结束");
    }
}

静态代理测试类

package proxy.staticproxy;

/**
 * 静态代理测试类
 * @author zyp
 * @create 2022/4/28
 */
public class StaticProxyTest {
    public static void main(String[] args){
        ProxyFactory proxyFactory = new ProxyFactory(new Teacher());
        proxyFactory.teach();
    }
}

静态代理优缺点

  • 优点:可以做到在不修改目标对象的功能前提下,对目标功能扩展。
  • 缺点:因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类,类太多.同时,一旦接口增加方法,目标对象与代理对象都要维护。

JDK动态代理

静态代理会手动创建很多代理类的问题,动态代理就解决了这个问题,其中一种就是JDK自带动态代理。其通过自己实现InvocationHandler来实现动态代理,真正的代理对象由JDK再运行时为我们动态的来创建。其结构图如下:

设计模式代理模式和策略模式区别,设计模式动态代理原理

接口

package proxy.jdk;

/**
 * @author zyp
 * @create 2022/4/28
 */
public interface ITeacher {
    void teach();
}

实现类

package proxy.jdk;


/**
 * @author zyp
 * @create 2022/4/28
 */
public class Teacher implements ITeacher{
    @Override
    public void teach() {
        System.out.println("老师正在讲课");
    }
}

代理对象生成类

package proxy.jdk;

import java.lang.reflect.Proxy;

/**
 * 动态代理
 * @author zyp
 * @create 2022/4/29
 */
public class ProxyFactory {
    //目标对象
    private Object target;

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

    /**
     * 获取代理对象
     * @return
     */
    public Object getInstance(){
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(), (proxy, method, args) -> {
            System.out.println("jdk动态代理开始");
            Object invoke = method.invoke(target, args);
            System.out.println("jdk动态代理结束");
            return invoke;
        });
    }

}

JDK动态代理测试类

package proxy.jdk;


/**
 * jdk动态代理测试类
 * @author zyp
 * @create 2022/4/29
 */
public class JdkProxyTest {
    public static void main(String[] args){
        ITeacher teacher = new Teacher();
        ProxyFactory proxyFactory = new ProxyFactory(teacher);
        //代理对象
        ITeacher  iTeacher = (ITeacher)proxyFactory.getInstance();
        iTeacher.teach();
    }
}

JDK动态代理总结: 虽然相对于静态代理,动态代理大大减少了我们的开发任务,同时减少了对业务接口的依赖,降低了耦合度。但是JDK自带动态代理只能支持实现了Interface的类。

cglib动态代理

JDK实现动态代理需要实现类通过接口定义业务方法,对于没有接口的类,如何实现动态代理呢,这就需要CGLIB了。CGLIB采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。但因为采用的是继承,所以不能对final修饰的类进行代理。

设计模式代理模式和策略模式区别,设计模式动态代理原理

目标类

package proxy.cglib;

/**
 * 被代理类
 * @author zyp
 * @create 2022/4/28
 */
public class Teacher{
    public void teach() {
        System.out.println("老师正在讲课");
    }
}

代理对象生成类

package proxy.cglib;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * @author zyp
 * @create 2022/4/29
 */
public class ProxyFactory implements MethodInterceptor {
    //目标对象
    private Object target;
    public ProxyFactory(Object target){
        this.target = target;
    }

    //创建Enhancer对象,类似于JDK动态代理的Proxy类
    public Enhancer getEnhancer(){
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(target.getClass());
        //设置回调函数
        enhancer.setCallback(this);
        return enhancer;
    }


    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("cglib动态代理开始");
        Object invoke = method.invoke(target, objects);
        System.out.println("cglib动态代理结束");
        return invoke;
    }
}

cglib动态代理测试类

package proxy.cglib;

import net.sf.cglib.proxy.Enhancer;

/**
 * cglib动态代理
 * @author zyp
 * @create 2022/4/29
 */
public class CglibProxyTest {
    public static void main(String[] args){
        ProxyFactory proxyFactory = new ProxyFactory(new Teacher());
        Enhancer enhancer = proxyFactory.getEnhancer();
        //返回代理类
        Teacher teacher = (Teacher)enhancer.create();
        teacher.teach();
    }
}