设计模式之代理模式

  • Post author:
  • Post category:其他




什么是代理模式

在我们开发业务代码的过程中,经常会遇到甲方粑粑提出的新需求~要在已经写好的业务模块中要添加一些功能,比如:增加日志记录、或者一些事务的支持等等。

这时候,你肯定不会为了增加一点功能而想去动那个已经写好测试好的模块–毕竟有可能改着改着bug变多(狗头)。

作为一个优秀的程序员肯定不会认同上面的理由,那找个正当的理由:如果有多个模块需要用到这个功能,如果每一个模块都改,那重复代码岂不是很多了?

所以,为了解决上面的问题,聪明的前辈们想出了

代理模式

的设计思想

举个例子:比如电脑本身只有运行的功能,如果要销售电脑,并不是电脑自己能销售,而是要通过一个代理商去销售这部电脑实现电脑的销售过程。

用图表示如下:

在这里插入图片描述



在Java中,有三种方式实现代理模式



1、静态代理

要实现静态代理,前提需要定义接口或者父类,

被代理对象和代理对象


一起实现相同的接口或者是继承相同的父类

引用上面的例子就是电脑对象需要有一个电脑接口,代理商也要实现电脑这个接口,因为只有这样才能

调用代理对象方法

的时候

同时也调用了被代理对象的方法

这三者的关系图是这样的:

在这里插入图片描述

Java代码实例:

电脑接口:Computer

public interface Computer {
    void running();
}

电脑实现类:ComputerImpl

public class ComputerImpl implements Computer {
    @Override
    public void running() {
        System.out.println("Computer is running...");
    }
}

代理类:ComputerProxy

public class ComputerProxy implements Computer {
    //被代理对象
    private ComputerImpl computer;
   
    public ComputerProxy(ComputerImpl computer) {
        this.computer = computer;
    }

    @Override
    public void running() {
        computer.running();
        System.out.println("computer is selling...");
    }
}

总结:静态代理就是将需要被代理的作为代理对象的一个属性,然后在自己的实现方法中调用了被代理对象的方法,再在该方法里面进行功能的增强。

但是静态代理也有它的不足:代理类和被代理类都需要实现同一个接口,也要实现相同的方法,这样会导致大量的重复代码!并且如果有多个模块需要增加同一个功能,这样就会导致需要创建大量的接口。

因此,动态代理也就出现了~



2、动态代理

动态代理的特点主要是通过

反射机制

创建一个实现了一组给定接口的新类。

要在Java中实现动态代理,需要了解一个类和一个接口:



InvocationHandler接口:
public interface InvocationHandler {
public Object invoke(Object proxy,Method method,Object[] args) throws Throwable;
}

参数说明:

Object proxy:指被代理的对象(上面的电脑类)

Method method:要调用的方法(电脑类的run方法)

Object[] args: 方法调用时所需要的参数(run方法的参数)

代理类(代理商)就是实现InvocationHandler接口的类。



Proxy类

Proxy是来自java.lang.reflect包的一个专门完成代理的操作类,可以通过该类为一个或多个接口动态的生成实现类。该类提供的一个newProxyInstance的方法:

Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),new ProxyFactory(target));

参数说明:

target : 被代理对象(电脑类)。

target.getClass().getClassLoader():被代理对象类加载器,固定写法。

target.getClass().getInterfaces():被代理对象接口(相当与电脑接口)

java代码实例:

其中被代理对象和接口和静态代理的一样。

public class ProxyFactory implements InvocationHandler {
   //要被代理的对象,这里指computerImpl
   private Object target;

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

   public Object getProxyInstance(){
       return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),new ProxyFactory(target));
   }

   @Override
   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

       Object invoke = method.invoke(target, args);
       System.out.println("computer is selling...");
       return null;
   }
}

动态代理测试:

public class ComputerTest {
   public static void main(String[] args) {
       System.out.println("Computer without proxy--------------");
       Computer computer = new ComputerImpl();
       computer.running();
       System.out.println("\n");
       System.out.println("Computer with proxy-----------------");
       Computer computerProxy = (Computer) new ProxyFactory(new ComputerImpl()).getProxyInstance();
       computerProxy.running();
   }
}

运行结果如图:

在这里插入图片描述



3、Cglib代理

上面的两种代理的前提都是需要代理的目标对象要实现一个

接口

。但是有时候如果目标对象只是一个单独的对象,并没有实现任何的接口,这时候就可以用这个Cglib代理。

简而言之:Cglib代理,也叫做子类代理,原理是它在内存中构建一个子类对象从而实现对目标功能的扩展。在Java的Spring AOP中就使用到了Cglib代理。

使用Cglib代理,要求是被代理的对象

不能是被final所修饰

,因为它是通过创建子类的方式实现的,如果该类被final修饰,将无法继承!

还要实现MethodInterceptor这个接口,该接口中有一个intercept的方法和动态代理的invoke方法相似。

代码实现:

被代理类Computer

public class Computer {
    public void running(){
        System.out.println("computer is running...");
    }
}

代理类:ProxyFactory(相当代理商)

public class ProxyFactory implements MethodInterceptor {
    private Object target;

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

    public Object getProxyInstance(){
        //工具类
        Enhancer en = new Enhancer();
        //设置父类为被代理类
        en.setSuperclass(target.getClass());
        //设置回调函数
        en.setCallback(this);
        //创建子类(代理对象)
        return en.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        method.invoke(target,objects);
        System.out.println("computer is selling...");
        return null;
    }
}

测试类:

public class ComputerTest {
    public static void main(String[] args) {
        //没有代理,只有电脑的基本功能
        System.out.println("Computer without proxy--------------");
        Computer computer = new Computer();
        computer.running();

        System.out.println("\n");
        //代理后,增加销售功能
        System.out.println("Computer with proxy-----------------");
        Computer computerProxy = (Computer) new ProxyFactory(new Computer()).getProxyInstance();
        computerProxy.running();
    }
}



总结:

Spring中的AOP编程就是用代理模式实现的。

如果需要用到代理模式:

如果被代理对象有实现接口,用动态代理

如果被代理对象没有接口,用Cglib代理

好了,上面三种代理模式就到这~古耐



版权声明:本文为qq_40619228原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。