23种常用设计模式之代理模式

说明

代理模式为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用

实现方式

  • 静态代理:由程序员创建或特定工具自动生成源代码,在对其编译。在程序员运行之前,代理类.class文件就已经被创建了
  • JDK动态代理:是在程序运行时通过反射机制动态创建的
  • CGLIB动态代理:CGLib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。但因为采用的是继承,所以不能对final修饰的类进行代理

模式特征

角色 说明 举栗
Component 统一接口,也是代理类和被代理类的基本类型 BuyComputer
ConcreteComponent 具体实现类,也是被代理类 BuyComputerImpl
Proxy 代理类 BuyComputerProxy、BuyComputerHandler、BuyComputerInterceptor

代码实现

公用类
  • 统一接口:买电脑
public interface BuyComputer {
    void buyComputer();
}
  • 具体实现类
public class BuyComputerImpl implements BuyComputer {
    @Override
    public void buyComputer() {
        System.out.println("我买了windows电脑");
    }
}
静态代理
  • 静态代理类
public class BuyComputerProxy implements BuyComputer {

    private BuyComputer buyComputer;

    public BuyComputerProxy() {
        //由代理类主动new出的被代理对象
        this.buyComputer = new BuyComputerImpl();
    }

    @Override
    public void buyComputer() {
        System.out.println("推荐一款华为电脑");
        buyComputer.buyComputer();
        System.out.println("华为提供售后服务");
    }
}
JDK动态代理
  • JDK动态代理处理类
public class BuyComputerHandler implements InvocationHandler {

    private Object object;

    public Object newProxyInstance(Object object) {
        this.object = object;
        //注意Proxy.newProxyInstance()方法接受三个参数:
        //ClassLoader loader:指定当前目标对象使用的类加载器,获取加载器的方法是固定的
        //Class<?>[] interfaces:指定目标对象实现的接口的类型,使用泛型方式确认类型
        //InvocationHandler:指定动态处理器,执行目标对象的方法时,会触发事件处理器的方法
        return Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(), this);
    }

    /**
     * @param proxy 指代我们所代理的那个真实对象
     * @param method 指代的是我们所要调用真实对象的某个方法的Method对象
     * @param args 指代的是调用真实对象某个方法时接受的参数
     * @return 结果
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("推荐一款华为电脑");
        Object result = method.invoke(object, args);
        System.out.println("华为提供售后服务");
        return result;
    }
}
CGLIB动态代理
  • CGLIB动态代理拦截器
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;
public class BuyComputerInterceptor implements MethodInterceptor {

    //代理的目标对象
    private Object object;

    public Object getInstance(Object object){
        this.object = object;
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(this.object.getClass());
        enhancer.setCallback(this);
        return enhancer.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println("推荐一款华为电脑");
        Object result = methodProxy.invoke(object, args);
        System.out.println("华为提供售后服务");
        return result;
    }

}
测试
  • 客户端测试
public class Client {
    public static void main(String[] args) {
        //静态代理
        BuyComputerProxy proxy = new BuyComputerProxy();
        proxy.buyComputer();

        System.out.println("--------分割线---------");

        //JDK动态代理
        BuyComputerHandler handler = new BuyComputerHandler();
        //这里是基于接口的代理
        BuyComputer buyComputer = (BuyComputer) handler.newProxyInstance(new BuyComputerImpl());
        buyComputer.buyComputer();

        System.out.println("--------分割线---------");

        //CGLIB动态代理
        BuyComputerInterceptor interceptor = new BuyComputerInterceptor();
        //这里代理的是BuyComputerImpl这个实现类,而非接口BuyComputer
        BuyComputerImpl buyComputer1 = (BuyComputerImpl) interceptor.getInstance(new BuyComputerImpl());
        buyComputer1.buyComputer();
    }
}
  • 结果
推荐一款华为电脑
我买了windows电脑
华为提供售后服务
--------分割线---------
推荐一款华为电脑
我买了windows电脑
华为提供售后服务
--------分割线---------
推荐一款华为电脑
我买了windows电脑
华为提供售后服务

优缺点

静态代理
  • 优点:可以做到在符合开闭原则的情况下对目标对象进行功能扩展。
  • 缺点:我们得为每一个服务都得创建代理类,工作量太大,不易管理。同时接口一旦发生改变,代理类也得相应修改。
JDK动态代理
  • 优点:虽然相对于静态代理,动态代理大大减少了我们的开发任务,同时减少了对业务接口的依赖,降低了耦合度,实现无侵入式的代码扩展
  • 缺点:使用JDK动态代理,目标类必须实现的某个接口,如果某个类没有实现接口则不能生成代理对象。
CGLIB动态代理
  • 优点:CGLIB创建的动态代理对象比JDK创建的动态代理对象的性能更高
  • 缺点:CGLIB创建代理对象时所花费的时间却比JDK多得多

对于单例的对象,因为无需频繁创建对象,用CGLIB合适,反之使用JDK方式要更为合适一些。同时由于CGLib由于是采用动态创建子类的方法,对于final修饰的方法无法进行代理,JDK动态代理与CGLib动态代理均是实现Spring AOP的基础。

模式对比

很多刚刚接触设计模式的同学,很容易将装饰器模式和静态代理模式混淆,下面简单说说他们之间的区别,需要用心体会。

  • 对装饰器模式来说,装饰者和被装饰者都实现一个接口。对代理模式来说,代理类和真实处理的类也都实现同一个接口。此外,不论我们使用哪一个模式,都可以很容易地在真实对象的方法前面或者后面加上自定义的方法.
  • 装饰器模式关注于在一个对象上动态地添加方法,而代理模式关注于控制对对象的访问。换句话说,用代理模式,代理类可以对它的客户隐藏一个对象的具体信息。因此当使用代理模式的时候,我们常常在一个代理类中创建一个对象的实例;当使用装饰器模式的时候,我们通常的做法是将原始对象作为一个参数传给装饰器的构造器,简单的说,就是调用者主动将一个对象传入装饰器的构造器,来主动装饰它,调用者知道原始对象的存在。
原创文章 67 获赞 31 访问量 5万+

猜你喜欢

转载自blog.csdn.net/u012534326/article/details/102563286
今日推荐