JAVA代理模式学习

Contents

0x0f什么是代理?

代理模式是一种比较好理解的设计模式。简单来说就是 我们使用代理对象来代替对真实对象(real object)的访问,这样就可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标对象的功能。

代理模式的主要作用是扩展目标对象的功能,比如说在目标对象的某个方法执行前后你可以增加一些自定义的操作。

代理主要分为静态代理和动态代理

0x01静态代理

静态,顾名思义,就拿静态页面和动态页面来对比,静态页面每次做修改都需要你改变源代码然后再重新解析,但是动态页面就不需要了,非常的不灵活,所以在开发中一般也不会使用

从 JVM 层面来说, 静态代理在编译时就将接口、实现类、代理类这些都变成了一个个实际的 class 文件。

实现步骤:

  • 定义一个接口及其实现接口的类
  • 创建一个代理同样的实现这个接口
  • 将目标对象注入进代理类,然后在代理类的对应方法调用目标类中的对应方法。 举个栗子 server.java,写一个接口
1
2
3
public interface Server {  
    String send(String Message);  
}

serverImp,实现这个接口

1
2
3
4
5
6
7
public class ServerImp implements Server{  
    public String send(String Meassage){  
        System.out.println("send Message:"+Meassage);  
        return Meassage;  
    }  
  
}

Proxy,写一个静态的代理

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
public class Proxy implements Server{  
    private final Server server;  
    public Proxy(Server server){  
    this.server = server;  
    }  
@Override  
    public String send(String Message){  
    //调用方法之前,我们可以添加自己的操作  
    System.out.println("before method send()");  
    server.send(Message);  
    //调用方法之后,我们同样可以添加自己的操作  
    System.out.println("after method send()");  
    return null;  
  
  
}  
}

这个很好理解,其实就是实现了接口并且重写了其方法而已。

实现:

1
2
3
4
5
6
7
8
9

public class Main {  
    public static void main(String[] args) {  
    Server server = new ServerImp();  
    Proxy proxy = new Proxy(server);  
    proxy.send("DELETE");  
  
    }  
}

输出:

/img/JAVA代理模式学习/1.png
具体的逻辑:

  • 利用接口实现了类ServerImp,并且我们往Proxy传入其对象,然后在Proxy内部的send再调用原本的实现方法即可。

0x02动态代理

相比于静态代理来说,动态代理更加灵活。我们不需要针对每个目标类都单独创建一个代理类,并且也不需要我们必须实现接口,我们可以直接代理实现类( CGLIB 动态代理机制)。

从 JVM 角度来说,动态代理是在运行时动态生成类字节码,并加载到 JVM 中的。

在 Java 动态代理机制中 InvocationHandler 接口和 Proxy 类是核心。

proxy中newProxyInstance()这个方法有三个参数:

  • loader :类加载器,用于加载代理对象。
  • interfaces : 被代理类实现的一些接口;
  • h : 实现了 InvocationHandler 接口的对象;

所以我们这里还需要传入一个h也就是实现了InvocationHandler的对象 当我们的动态代理对象调用一个方法时,这个方法的调用就会被转发到实现InvocationHandler 接口类的 invoke 方法来调用。 这里我们可以跟进一下这个类

/img/JAVA代理模式学习/2.png
可以看到InvocationHandler的接口中只有这个invoke方法。 然后这里也有接收三个参数

  • Proxy 动态生成的代理类
  • method 代理类的方法
  • args 要传入method的参数

也就是说:你通过Proxy 类的 newProxyInstance() 创建的代理对象在调用方法的时候,实际会调用到实现InvocationHandler 接口的类的 invoke()方法。 你可以在 invoke() 方法中自定义处理逻辑,比如在方法执行前后做什么事情。

步骤:

  1. 定义一个接口及其实现类;
  2. 自定义 InvocationHandler 并重写invoke方法,在 invoke 方法中我们会调用原生方法(被代理类的方法)并自定义一些处理逻辑;
  3. 通过 Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) 方法创建代理对象;

还是利用前面的例子 server.java,写一个接口

1
2
3
public interface Server {  
    String send(String Message);  
}

serverImp,实现这个接口

1
2
3
4
5
6
7
public class ServerImp implements Server{  
    public String send(String Meassage){  
        System.out.println("send Message:"+Meassage);  
        return Meassage;  
    }  
  
}

JDK动态代理类DynamicProxy

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
import java.lang.reflect.InvocationHandler;  
import java.lang.reflect.InvocationTargetException;  
import  java.lang.reflect.Method;  
public class DynamicProxy implements InvocationHandler  {  
private final Object target;  
  
public DynamicProxy(Object target){  
    this.target = target;  
}  
@Override  
    public Object invoke(Object proxy, Method method, Object[] args){  
    System.out.println("before method"+method.getName());  
    Object res = invoke(target,args);  
    System.out.println("Method had done");  
    return  res;  
  
}  
  
  
}

GetProxyIns获取代理类对象

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import java.lang.reflect.InvocationTargetException;  
import java.lang.reflect.Method;  
import java.lang.reflect.Proxy;  
  
public class GetProxyIns {  
    public static Object getProxy(Object target) {  
        return Proxy.newProxyInstance(  
                target.getClass().getClassLoader(), // 目标类的类加载器  
                target.getClass().getInterfaces(),  // 代理需要实现的接口,可指定多个  
                new DynamicProxy(target)   // 代理对象对应的自定义 InvocationHandler        );  
    }  
  
}

实现

1
2
3
4
5
6
7
8
import java.lang.reflect.InvocationHandler;  
public class Main {  
    public static void main(String[] args) {  
        Server server = (Server) GetProxyIns.getProxy(new ServerImp());  
        server.send("DELETE");  
  
    }  
}

/img/JAVA代理模式学习/3.png
运行结果 GGLIB就不写了hh,因为chu0师傅说只用知道invoke那个地方就好了

reference: https://javaguide.cn/java/basis/proxy.html#_3-2-cglib-%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86%E6%9C%BA%E5%88%B6