代理模式-ProxyPattern


前言:

代理模式与装饰者模式:

  • 相同点:
    1. 前提条件:装饰者(代理者)与被装饰者(委托者)要继承相同的父类或实现相同的接口。
    2. 都可以增强某个类的方法,对程序进行扩展。
  • 不同点:
    1. 代理者是为某个对象添加一个代理,以控制对这个对象的访问。而装饰者增强某个类的方法。
    2. 代理可以在调用核心方法之前做前置处理,在调用核心方法之后做后置处理。

代理模式

  • 定义:
    1. 为对象提供一种代理,加以控制对象的访问。
    2. 在某些特定的情况下一个对象不适合或者不能直接引用另一个对象,而代理对象可以在目标对象与客户直接起到中介作用。

代理模式的组成部分

  • 代理接口:代理类和委托类实现的接口
  • 代理类
    • 负责对请求的预处理、过滤,将请求分派给委托类处理以及委托了执行完后的后续处理
  • 委托类:真正处理核心逻辑的类

静态代理

  • 定义:由程序员创建或工具生成代理类的源码,再编译代理类。所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了
  • 静态代理的缺点
  • 代理对象的一个接口只服务于一种类型的对象,如果要代理的方法很多,势必要为每一种方法都进行代理,静态代理在程序规模稍大时就无法胜任了。
  • 如果接口增加一个方法,除了所有委托类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。

动态代理

定义

  • 动态代理它可以直接给某一个目标对象(委托对象)生成一个代理对象,而不需要代理类存在
  • 动态代理与静态代理模式原理是一样的,只是动态代理类的源码是在程序运行期间由JVM根据反射等机制动态的生成,所以不存在代理类的字节码文件。代理者和委托者的关系是在程序运行时确定。

动态代理常用类详解

java.lang.reflect.Proxy
这是 Java 动态代理机制生成的所有动态代理类的父类,它提供了一组静态方法来为一组接口动态地生成代理对象。

1
2
3
4
5
6
public static Object newProxyInstance(ClassLoader loader, 
Class<?>[] interfaces, InvocationHandler h)
loader:指定类的装载器
Interfaces:一组接口,委托类实现的所有接口
H:处理器生成代理类实例
@return:返回一个代理对象

java.lang.reflect.InvocationHandler
这是调用处理器接口,它自定义了一个 invoke 方法,用于集中处理在动态代理类对象上的方法调用,通常在该方法中实现对委托类的代理访问。每次生成动态代理类对象时都要指定一个对应的调用处理器对象。

1
2
3
4
5
public Object invoke(Object proxy, Method method, Object[] args)
proxy:动态创建的代理者实例, 一般不要调用, 会发生递归
method:代理对象当前所调用的方法, 只要代理调用任何方法都会走到invoke方法
args:参数列表
@return:返回当前被调用方法的返回值

java.lang.ClassLoader

  • 这是类装载器类负责将类的字节码装载到Java虚拟机(JVM)中并为其定义类对象,然后该类才能被使用。
  • Proxy 静态方法生成动态代理类同样需要通过类装载器来进行装载才能使用,它与普通类的唯一区别就是其字节码是由 JVM在运行时动态生成的而非预存在于任何一个 .class 文件中。
  • 每次生成动态代理类对象时都需要指定一个类装载器对象。
1
ClassLoader loader = jay.getClass().getClassLoader();

Dynamic Proxy Example

  • 明星接口- 代理类和委托类实现的接口

    1
    2
    3
    4
    5
    6
    public interface Star {
    // 发布会
    public void conference();
    // 代言广告
    public void endorsementAD();
    }
  • 明星类 - 委托者

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public class JayStar implements Star{
    @Override
    public void conference() {
    System.out.println("周杰棍正在开发布会.....");
    }

    @Override
    public void endorsementAD() {
    System.out.println("周杰棍正在帮百雀羚打广告.....");
    }
    }
  • 客户与动态代理

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;

    public class Client {

    public static void main(String[] args) {
    // 实例化一个JayStar对象
    JayStar jay = new JayStar();
    // 获取字节码文件对象
    Class<? extends JayStar> clazz = jay.getClass();
    // 获取当前类的类加载器
    ClassLoader loader = Client.class.getClassLoader();
    // 委托者所实现的所有接口
    Class<?>[] interfaces = clazz.getInterfaces();
    /**
    * newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
    * loader:指定的类加载器
    * interfaces:委托者所实现的所有接口
    * h:处理器回调,动态设置代理
    */
    Star star = (Star) Proxy.newProxyInstance(loader, interfaces, new InvocationHandler() {
    // 动态代理的前置处理
    private void will() {
    System.out.println("发布会开始了。。。。");
    }
    // 动态代理的后置处理
    private void end() {
    System.out.println("发布会结束了。。。。");
    }
    /**
    * Object invoke(Object proxy, Method method, Object[] args)
    * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])
    * proxy:第一个proxy指的是生成的动态代理对象
    * method:被调用的方法
    * args:调用该方法传入的参数
    */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    String name = method.getName();
    if (name.equals("fbh")) {
    will();
    jay.conference();
    end();
    }
    if (name.equals("ad")) {
    jay.endorsementAD();
    }
    return null;
    }
    });
    star.conference();
    star.endorsementAD();
    }
    }
0%