java动态代理_Java 动态代理 原理解析

示例

需要代理的接口

public interface IHello {
    public void sayHello();
}

需要代理的类

public class HelloImpl implements IHello {
    public void sayHello() {
        System.out.println("Hello World...");
    }
}

调用处理器实现类

public class ProxyHandler implements InvocationHandler {
    private Object target;
    public ProxyHandler(Object target) {
        this.target = target;
    }
    public Object proxyInstance() {
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(), this);
    }
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        System.out.println("aspect before ... ");
        Object result = method.invoke(this.target, args);
        System.out.println("aspect after ... ");
        return result;
    }
}

测试类入口

public class Main {
    public static void main(String[] args) {
        ProxyHandler proxy = new ProxyHandler(new HelloImpl());
        IHello hello = (IHello) proxy.proxyInstance();
        hello.sayHello();
    }
}

Proxy 源码解析

newProxyInstance() 方法

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,  InvocationHandler h) 
    throws IllegalArgumentException {
 
    Class<?> cl = getProxyClass0(loader, intfs);
    try {
        final Constructor<?> cons = cl.getConstructor(constructorParams);
        cons.setAccessible(true);
        return cons.newInstance(new Object[]{h});
    } catch (Exception e) {
        ......
    } 
}
  1. 调用 getProxyClass0() 方法获取代理类的 Class 对象
  2. 通过反射生成 代理类的实例,并返回。

getProxyClass0() 方法

private static Class<?> getProxyClass0(ClassLoader loader, Class<?>... interfaces) {
    if (interfaces.length > 65535) {
        throw new IllegalArgumentException("interface limit exceeded");
    }
    return proxyClassCache.get(loader, interfaces);
}
  1. 检查需要代理的类接口数量是否超过65535个,接口个数用2个byte存储,最大支持65535个。
  2. 把获取代理类的Class对象委托给 proxyClassCache() 方法
private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
        proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

proxyClassCache.get() 方法

37aa55a4effca486b42f8f3ea4f7762e.png
  1. 首先通过 classLoader 获取该classLoader下所有的代理类缓存map。
  2. 从该代理类缓存map中根据代理类所有实现的所有接口进行查找代理类。

如果都没有找到对应的代理类,则使用ProxyClassFactory.apply() 方法生成代理类

ProxyClassFactory.apply()

43000a5023d19494954587115f85c56c.png
  1. 生成代理类的包名和类名
  2. 通过ProxyGenerator.generateProxyClass() 方法动态生成代理类字节码。
  3. 通过classloader 动态加载 字节码,并生成动态代理类的Class实例,并返回

ProxyGenerator.generateProxyClass()

简单的列出 生成字节码的片段

d36aabac7d9a011fde658aaff53f68cd.png

添加默认的代理方法 hashCode、equals、toString 三个代理方法

6c1c53907265509e25aee65f38db790c.png

循环迭代所有的接口,把接口中所有的方法都生成代理方法。

6c74bf94ab4b3fec93f3c6bca40e9150.png

生成字节码: 从代码中我们可以看到,有class文件中的 魔术头、小版本号、主版本号、类方法标志、当前类、超类、接口个数、每个实现接口、属性个数、每个属性、方法个数、每个方法等信息。

动态生成代理类class文件

下面我们调用JDK内部提供的ProxyGenerator.generateProxyClass 方法把动态生成的代理类持久化到磁盘上。代码如下:

public static void main(String[] args) {
    String name = "ProxyHello";
    byte[] data = ProxyGenerator.generateProxyClass(name, new Class[] { IHello.class });
    try {
        FileOutputStream out = new FileOutputStream("d:"+name + ".class");
        out.write(data);
        out.close();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

在D盘根目录下生成ProxyHello.class文件。通过反编译工具,我们得到如下代码:

import cn.com.infcn.proxy.IHello;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class ProxyHello extends Proxy
  implements IHello
{
  private static Method m1;
  private static Method m3;
  private static Method m2;
  private static Method m0;

  public ProxyHello(InvocationHandler paramInvocationHandler)
    throws 
  {
    super(paramInvocationHandler);
  }

  public final boolean equals(Object paramObject)
    throws 
  {
    try
    {
      return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
    }
    catch (RuntimeException localRuntimeException)
    {
      throw localRuntimeException;
    }
    catch (Throwable localThrowable)
    {
    }
    throw new UndeclaredThrowableException(localThrowable);
  }

  public final void sayHello()
    throws 
  {
    try
    {
      this.h.invoke(this, m3, null);
      return;
    }
    catch (RuntimeException localRuntimeException)
    {
      throw localRuntimeException;
    }
    catch (Throwable localThrowable)
    {
    }
    throw new UndeclaredThrowableException(localThrowable);
  }

  public final String toString()
    throws 
  {
    try
    {
      return (String)this.h.invoke(this, m2, null);
    }
    catch (RuntimeException localRuntimeException)
    {
      throw localRuntimeException;
    }
    catch (Throwable localThrowable)
    {
    }
    throw new UndeclaredThrowableException(localThrowable);
  }

  public final int hashCode()
    throws 
  {
    try
    {
      return ((Integer)this.h.invoke(this, m0, null)).intValue();
    }
    catch (RuntimeException localRuntimeException)
    {
      throw localRuntimeException;
    }
    catch (Throwable localThrowable)
    {
    }
    throw new UndeclaredThrowableException(localThrowable);
  }

  static
  {
    try
    {
      m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
      m3 = Class.forName("cn.com.infcn.proxy.IHello").getMethod("sayHello", new Class[0]);
      m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
      m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
      return;
    }
    catch (NoSuchMethodException localNoSuchMethodException)
    {
      throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
    }
    catch (ClassNotFoundException localClassNotFoundException)
    {
    }
    throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
  }
}

通过反编译后的代码我们可以看到,他内部包含4个代理方法: equals、toString、hashCode 还有 IHello接口中的 sayHello 方法。

该代理类继承了Proxy,并把我们创建的ProxyHandler 类通过构造方法自动注入到 代理类中。

当我们调用代理类的sayHello方法时,sayHello方法就会调用我们实现的ProxyHandler 类中的invoke() 方法,ProxyHandler.invoke() 方法调用我们真正的目标类的sayHello方法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java中实现代理服务器的方法主要有两种,一种是使用Java原生的Socket类实现代理服务器,另一种是使用第三方库实现代理服务器。 使用Java原生的Socket类实现代理服务器的步骤如下: 1. 创建一个ServerSocket对象,并指定代理服务器的监听端口号。 2. 使用while循环等待客户端连接,并使用accept()方法接受客户端的连接请求。 3. 在连接建立之后,读取客户端发送的请求,解析请求,获取要访问的目标服务器的地址和端口号。 4. 创建一个Socket对象,连接到目标服务器,并将客户端发送的请求转发给目标服务器。 5. 接收目标服务器的响应,将响应转发给客户端。 6. 关闭Socket和ServerSocket对象,结束连接。 以下是一个使用Java原生的Socket类实现代理服务器的示例代码: ``` import java.net.*; import java.io.*; public class ProxyServer { public static void main(String[] args) throws IOException { ServerSocket serverSocket = new ServerSocket(8080); while (true) { Socket clientSocket = serverSocket.accept(); new Thread(() -> { try { BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); String requestLine = in.readLine(); String[] requestLineParts = requestLine.split("\\s+"); String method = requestLineParts[0]; String url = requestLineParts[1]; String version = requestLineParts[2]; URL targetUrl = new URL(url); Socket serverSocket = new Socket(targetUrl.getHost(), targetUrl.getPort()); OutputStream serverOut = serverSocket.getOutputStream(); serverOut.write(requestLine.getBytes()); serverOut.flush(); InputStream serverIn = serverSocket.getInputStream(); byte[] buffer = new byte[1024]; int length; while ((length = serverIn.read(buffer)) != -1) { clientSocket.getOutputStream().write(buffer, 0, length); clientSocket.getOutputStream().flush(); } in.close(); serverOut.close(); serverIn.close(); clientSocket.close(); } catch (IOException e) { e.printStackTrace(); } }).start(); } } } ``` 以上代码中,代理服务器监听8080端口,当有客户端连接时,会在一个新的线程中处理客户端的请求,读取请求行并解析出要访问的目标服务器地址和端口号,然后连接到目标服务器并将请求转发给目标服务器。接收到目标服务器的响应后,将响应转发给客户端。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值