设计模式之代理模式

1. 定义

       代理模式属于结构型模式,其本质就是给某个对象创建一个代理,通过代理对象从而实现对于被代理对象的访问和控制,即所谓的“中介”。关于代理模式的形象表述,比较典型的一个例子就是房东通过中介进行房子的出租,而租客通过中金进行租房子,租客不与房东直接接触,而是通过中介,中介在中间起到穿针引线的作用。其中,动态代理可以分为静态代理和动态代理。

2. 原则

       从设计原则的角度出发,代理模式的使用主要遵循了中介隔离原则和开闭原则。

       2.1 中介原则

       顾名思义,“中介原则”即与“中介”的含义一致,指的是客户类与被代理类不直接接触,通过中介作用的委托类来实现,其主要特征是代理类与委托类都实现相同的接口。

       2.2 开闭原则

       所谓“开闭原则”,指的是对于扩展开放,对于修改关闭。充当“中介”作用的代理类除了实现委托类的基本功能以外,还会对于委托类增加额外的功能,即对委托对象进行扩展;但是代理类只是增加额外功能,并不会对于委托类进行修改,即没有对委托对象进行修改。代理类只是在委托类执行的前后增加额外的功能,比如加入缓存、日志等功能。

3. 静态代理

       所谓静态代理,指的是在使用时,需要定义接口或者父类,静态代理类只是实现委托对象的接口或者继承委托对象的父类。

       3.1 优缺点

  其优点是可以在符合中介原则和开闭原则的基础上对于委托对象的功能进行扩展,并且不会改变委托类的功能。

  其缺点是必须针对每一个服务都需要创建代理类,伴随着业务的发展,服务数量的增多,代理类的数量也会剧增;并且当某个服务需要更改时,所有该服务的代理类都需要进行修改,成本较大。

  3.2   实现

  3.2.1   首先创建一个接口

/**
 * 定义接口: 卖商品的接口
 */
public interface SaleGood {

    public void saleGoodMethod();
}

  3.2.2    创建这个接口的实现类作为被代理类

/**
 * 接口的实现类
 */
public class Sale implements SaleGood {

    private String product;

    public Sale() {
    }

    public Sale(String product) {
        this.product = product;
    }

    @Override
    public void saleGoodMethod() {
        System.out.println("卖的商品是: " + product);
    }
}

  3.2.3    创建这个接口的另外一个实现类作为代理类

/**
 * 静态代理
 * 静态代理类需要实现 核心的接口,并且需要调用核心接口的方法
 */
public class SaleProxy implements SaleGood {

    private SaleGood saleGood;
    private String product;

    public SaleProxy(String product) {
        this.product = product;
    }

    @Override
    public void saleGoodMethod() {
        if (saleGood == null){
            saleGood = new Sale(product);
        }
        System.out.println("这是静态代理");
        saleGood.saleGoodMethod();

    }
}

  3.2.4    测试方法

/**
 * 静态代理的测试类
 * 直接使用  静态代理  来创建被代理类
 * 这样就可以直接使用被代理类的方法
 */
public class staticProxyTest {

    public static void main(String[] args) {
        SaleGood saleGood = new SaleProxy("apple");
        saleGood.saleGoodMethod();
    }
}

  3.2.5    运行结果

  

4. 动态代理

  动态代理中不需要在手动创建代理类,只是需要一个动态处理器来实现。需要注意的是不是创建动态代理类,而是直接创建动态代理对象。动态代理不需要实现接口,但是需要指定接口的类型。实现动态代理方式主要有两种:JDK动态代理和Cglib代理。

       4.1 JDK动态代理

       JDK动态代理,即使用JDK 中自带生成代理类的API,通过调用       Proxy的newProxyInstance方法,来生成代理对象。其底层是利用了Java的反射机智,因此使用JDK动态代理需要被代理对象实现接口。

       4.1.1 实现的动态代理器

/**
 * 动态代理---动态代理器
 * 使用动态处理器可以减少代码的冗余,避免多个静态代理对象的复杂
 * 直接使用动态代理器来拦截被代理类的方法,从而对方法进行控制
 */
public class ProxyHandler implements InvocationHandler {

    //被代理对象
    private Object object;

    public ProxyHandler(Object object) {
        this.object = object;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("动态代理start");
        Object res = method.invoke(object, args);
        return res;
    }
}

       4.1.2 测试方法

import java.lang.reflect.Proxy;

/**
 * 动态代理的测试类,测试的 jdk 代理,即实现接口的被代理类
 * 通过动态代理可以调用被代理类的方法
 */
public class dynamicProxyTest {

    public static void main(String[] args) {
        SaleGood saleGood = (SaleGood)Proxy.newProxyInstance(dynamicProxyTest.class.getClassLoader(),
                new Class[]{SaleGood.class},
                new ProxyHandler(new Sale("watermelon")));
        saleGood.saleGoodMethod();

    }
}

       4.1.3 测试结果

   

  4.2   Cglib动态代理

  由于JDK动态代理需要被代理对象实现相应的接口,对于没有实现接口的类,要想实现代理,就需要使用Cglib动态代理了。其使用了底层的字节码,通过字节码技术为被代理对象创建一个子类,在子类中通过方法拦截对父类的方法进行拦截,织入相应的逻辑。因此其实Spring AOP的基础。

  4.2.1   创建被代理类

/**
 * 动态代理之 cglib动态代理
 * 针对的被代理类是 没有实现接口,在代理时只能通过其子类进行拦截
 */
public class CglibSale {

    private String product;

    public CglibSale() {
    }

    public CglibSale(String product) {
        this.product = product;
    }

    public void saleGoodMethod(){
        System.out.println("卖的商品是: " + product);
    }
}

  4.2.2   创建Cglib代理类

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * cglib 的动态代理器
 * 底层原理是 方法拦截器
 */
public class CglibProxy implements MethodInterceptor {

    //被代理类
    private Object object;

    //获取被代理对象
    public Object getProxyInstance(Object object){
        this.object = object;
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(object.getClass());
        enhancer.setCallback(this);
        return enhancer.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {

        System.out.println("动态代理start");
        Object invoke = method.invoke(this.object, objects);
        System.out.println("动态代理end");
        return invoke;
    }
}

  4.2.3   测试方法

/**
 * cglib 动态代理的测试类
 */
public class CglibProxyTest {

    public static void main(String[] args) {
        Sale sale = (Sale)new CglibProxy().getProxyInstance(new Sale("apple"));
        sale.saleGoodMethod();
    }
}

  4.2.4   运行结果

   

5.与其他设计模式的区别

       在结构型模式中,与代理模式相近的有适配器模式和装饰者模式,其与代理模式既有相同之处,又有各自区别。

       适配器模式主要是对于不兼容的接口进行修改,以方便接口的调用,其对于接口本身是有修改的。

       装饰者模式是在不改变接口的前提下,动态扩展对象的功能,其主要是增强现有接口的功能。

       代理模式是在不改变接口的前提下,通过代理对象控制对被代理对象的访问,这样被代理类的实现细节就不会暴露。

原文地址:https://www.cnblogs.com/Demrystv/p/11997010.html