`
kylinsoong
  • 浏览: 240070 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Proxy - 动态代理

    博客分类:
  • JAVA
阅读更多

1. 先看JDK提供代理类Proxy

      根据JDK描述,动态代理类Proxy主要用来创建动态代理类,是一个在创建类时实现指定的接口列表的类,这个指定的接口叫做代理接口,即是创建代理类实现的接口,代理实例是代理类的一个实例。 每个代理实例都有一个关联的调用处理程序对象,这个调用处理程序对象实现接口 InvocationHandler;代理类具有以下属性:

 根据JDK的约定,调用处理对象必须包含一个代理实例,如上private Object target表示代理对象。

  • 代理类是public,final而不是abstract。
  • 未指定代理类的非限定名称。但是,以字符串 "$Proxy" 开头的类名空间应该为代理类保留。
  •  

  • 代理类会按同一顺序准确地实现其创建时指定的接口。
  • 在看JDK中Proxy类产生代理类的一个静态方法:

    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
                                   throws IllegalArgumentException

        该方法返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序,其中interfaces表示代理类要实现的接口列表;h为指派方法调用的调用处理程序;

    java.lang.reflect.InvocationHandler接口定义一个方法如下;

    Object invoke(Object proxy,
                  Method method,

                  Object[] args)
                  throws Throwable 

    proxy为代理实例,Method为代理实例和代理实现的接口中定义的方法;

    2.显然上面的描述是比较抽象,下面我写一个自己的Proxy类和InvocationHanlder接口:

    如下为为我定义的InvocationHanlder:

    package com.learn.proxy;
    import java.lang.reflect.Method;
    public interface InvocationHanlder {
    	public void invoke(Object proxy, Method method);
    }
    

     为了简化我假设我的代理类和代理实现的接口中所定义的方法没有参数,所以和java.lang.reflect.InvocationHandler中的相比缺少方法参数args;

    我的Proxy类:

    package com.learn.proxy;
    
    import java.io.File;
    import java.io.FileWriter;
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Method;
    
    import javax.tools.JavaCompiler;
    import javax.tools.StandardJavaFileManager;
    import javax.tools.ToolProvider;
    import javax.tools.JavaCompiler.CompilationTask;
    
    import com.learn.proxy.test.Moveable;
    
    public class Proxy {
    	
    	public static Object newProxyInstance(ClassLoader loader, Class interfaces, InvocationHanlder h) throws Exception {
    		
    		String rt = "\r\n";
    		String methodStr = "";
    		
    		Method[] methods = Moveable.class.getMethods();
    		for(Method m : methods) {
    			methodStr += "	@Override" + rt +
    			"	public " + m.getGenericReturnType() + " " + m.getName() + "()" + " {" + rt +
    			"		try{" + rt +
    			"			Method md = " + interfaces.getName() + ".class.getMethod(\"" + m.getName() + "\");" + rt +
    			"			hanlder.invoke(this, md);" + rt +
    			"		}catch(Exception e) {e.printStackTrace();}" + rt +
    			"	}" + rt;
    		}
    		
    		String str = 
    			"package com.learn.proxy; " + rt +
    		     rt +
    			"import java.lang.reflect.Method;" + rt +
    		     rt +
    			"public class $Proxy1 implements " + interfaces.getName() +" {   " + rt +
    			"	private com.learn.proxy.InvocationHanlder hanlder;" + rt +
    			"	public $Proxy1(InvocationHanlder hanlder) {" + rt +
    			"		this.hanlder = hanlder;                        " + rt +
    			"	}                                                " + rt +
    					methodStr +
    			"} " + rt ;
    		String fileName = System.getProperty("user.dir") + "/proxy/com/learn/proxy/$Proxy1.java";
    		File file = new File(fileName);
    		FileWriter writer = new FileWriter(file);
    		writer.write(str);
    		writer.flush();
    		writer.close();
    		
    		JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
    		StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
    		Iterable units = fileManager.getJavaFileObjects(fileName);
    		CompilationTask task = compiler.getTask(null, fileManager, null, null, null, units);
    		task.call();
    		fileManager.close();
    		file.delete();
    		
    		Class c = loader.loadClass("com.learn.proxy.$Proxy1");
    		Constructor constructor = c.getDeclaredConstructor(InvocationHanlder.class);
    		Object obj = constructor.newInstance(h);
    		return obj;
    	}
    }
    

     此类包含一个方法newProxyInstance,此方法可以产生一个代理,此代理要实现的接口由参数interfaces指定,loader用来加载动态生成的代理类,h用来处理被代理类要执行方法;

    3.接下来给出一个上面2的应用:

    同样有一个Person对象,它实现了Moveable接口,详细代码如下;

    public interface Moveable {
    	public void move();
    }
    
    public class Person implements Moveable {
    	public void move() {
    		System.out.println("Person remove");
    		try {
    			Thread.sleep(new Random().nextInt(10000));
    		} catch (InterruptedException e) {
    		}
    	}
    }

     现在要求记录Person对象执行move方法的时间,我们可以这样处理;

    Step 1 分析我们首先需要一个调用处理对象,我们定义它的名字为TimeHanlder,当然这里的TimeHanlder实现我们自己定义的InvocationHanlder,如下:

    package com.learn.proxy.test;
    
    import java.lang.reflect.Method;
    import java.util.Date;
    
    import com.learn.proxy.InvocationHanlder;
    
    public class TimeHanlder implements InvocationHanlder {
    	
    	private Object target;
    	
    	public TimeHanlder(Object target) {
    		this.target = target;
    	}
    	
    	@Override
    	public void invoke(Object obj, Method method) {
    		Date start = new Date();
    		System.out.println("START...");   
    		try {
    			method.invoke(target);
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    		System.out.println("END, TOTAL APENT: " + (new Date().getTime() - start.getTime()) + " MILLISECONDS.");
    	}
    }
    

     

     

    Step 2 产生代理类

    有了调用处理对象,我们还需要指定加载我们生成代理类的ClassLoader,根据我们自己的Proxy,我们是将代理类编译好放在一个单独的位置,所以我们不能用程序中的ClassLoader,这里我们用URLClassLoader;

    如下代码:

     

    public static void main(String[] args) throws Exception {
    		
    		URL[] urls = new URL[]{new URL("file://" + System.getProperty("user.dir") + "/proxy/")};
    		URLClassLoader urlClassLoader = new URLClassLoader(urls);
    		
    		TimeHanlder hanlder = new TimeHanlder(new Person());
    		Object proxy = Proxy.newProxyInstance(urlClassLoader,Moveable.class,hanlder);
    		System.out.println(proxy.getClass());
    		System.out.println(proxy.getClass().getInterfaces()[0]);
    		System.out.println(proxy.getClass().getConstructors()[0]);
    		System.out.println(proxy.getClass().getMethods()[0]);
    		Moveable m  = (Moveable) proxy;
    		m.move();
    	}

     

    此段代码运行结果如下:

     

    class com.learn.proxy.$Proxy1
    interface com.learn.proxy.test.Moveable
    public com.learn.proxy.$Proxy1(com.learn.proxy.InvocationHanlder)
    public void com.learn.proxy.$Proxy1.move()
    
    START...
    Person remove
    END, TOTAL APENT: 2562 MILLISECONDS.

     

     Step 3 分析整个运行过程:

    从结果看可以知道:

    我们的代理类名字为com.learn.proxy.$Proxy1;

    它有一个构造方法:public $Proxy1(com.learn.proxy.InvocationHanlder hanlder);

    它实现了接口com.learn.proxy.test.Moveable;

    它有一个方法public void move() ,显然这方法是com.learn.proxy.test.Moveable中定义的方法

    这样我们可以大致还原出我们的代理类代码:

    package com.learn.proxy; 
    
    import java.lang.reflect.Method;
    
    public class $Proxy1 implements com.learn.proxy.test.Moveable {   
    	private com.learn.proxy.InvocationHanlder hanlder;
    	public $Proxy1(InvocationHanlder hanlder) {
    		this.hanlder = hanlder;                        
    	}                                                
    	@Override
    	public void move() {
    		try{
    			Method md = com.learn.proxy.test.Moveable.class.getMethod("move");
    			hanlder.invoke(this, md);
    		}catch(Exception e) {e.printStackTrace();}
    	}
    } 
    

     此类是程序调运Proxy.newProxyInstance方法时动态生成,实际中并不存在;

    接着分析,上述运行结果,Person对象move方法被执行,同时执行move方法的时间被记录,所以整个过程可以用下图描述:



     首先动态生成的代理类中move方法被调用;

     TimeHanlder中invoke方法被调用;

     java.lang.reflect.Method中invoke方法被调用,来完成执行Person中move方法;

     

     

    • 大小: 6.4 KB
    分享到:
    评论

    相关推荐

      Node.js-http-proxy-middleware用于把请求代理转发到其他服务器的中间件

      这个中间件简化了配置过程,使得开发者能够快速设置代理,从而实现数据的动态转发。 `http-proxy-middleware` 的核心功能包括: 1. **代理配置**:你可以通过简单的配置对象来定义代理规则。例如,你可以设定特定...

      goproxy-android snail007/goproxy全能代理服务器安卓版

      《GoProxy-Android:全能代理服务器在安卓平台的应用与实现》 GoProxy-Android是由snail007/goproxy团队开发的一款适用于安卓系统的全能代理服务器应用。此项目旨在为移动设备提供强大的网络代理功能,使得用户能够...

      influx-proxy-2.5.7-linux-amd64.tar.gz

      而"Influx-proxy"则可能是一个针对InfluxDB的代理服务,用于优化数据流入和查询效率。本文将详细探讨InfluxDB的核心特性和Influx-proxy的功能,并结合"Influx-proxy-2.5.7-linux-amd64.tar.gz"这个压缩包文件,解析...

      docker-letsencrypt-nginx-proxy-companion-examples, 结合 Docker gen和 letsencrypt Nginx 代理伙伴的示例.zip

      docker-letsencrypt-nginx-proxy-companion-examples, 结合 Docker gen和 letsencrypt Nginx 代理伙伴的示例 docker-letsencrypt-nginx-proxy-companion-examples这个库是使用 nginx代理插件, docker gen和 docker-...

      node-https-proxy-agent, HTTPS端点的HTTP代理 `http.Agent` 实现.zip

      node-https-proxy-agent, HTTPS端点的HTTP代理 `http.Agent` 实现 https-proxy-agent HTTPS的HTTP代理 http.Agent 实现 这个模块为连接到指定的HTTP或者HTTPS代理服务器提供了 http.Agent 实现,并且可以与内置的...

      Proxy-SwitchyOmega-Chromium-2.5.15.zip

      Proxy-SwitchyOmega是一款强大的网络代理管理插件,专为Chromium浏览器(Chromium是Google Chrome浏览器的基础开源版本)设计。这款插件允许用户在不同的网络环境之间快速切换,包括HTTP、HTTPS、SOCKS4、SOCKS5等...

      ftpproxy-1.2.3.rar_Linux c proxy_ftpproxy_ftp代理_linux ftp_proxy-

      proxy源代码,linux下的ftp 代理的源代码,大家多多支持啊

      mysql-proxy-0.8.5-windows-x86-32bit

      MySQL Proxy 是一个开源工具,由 MySQL AB 公司开发,允许用户在 MySQL 客户端和服务器之间插入一个代理层。这个代理层可以用来监控、分析或者修改通信数据,为数据库管理提供了一种灵活的解决方案。`mysql-proxy-...

      Node.js代理中间件http-proxy-middleware.zip

      http-proxy-middleware 是一套 Node.js 代理中间件 for connect, express 和 browser-sync。安装$ npm install --save-dev http-proxy-middleware配置var proxyMiddleware = require('http-proxy-middleware'...

      Midjourney-Proxy-Plus-3.8.6免授权版

      MJ-PROXY-PLUS-3.8.6免授权,功能和商用版本相同,最新版本,由于MJ官网更新,老版本已无法使用MJ-PROXY-PLUS,安装方法和老版本一样,详细可参考MJ-PROXY-PLUS配置教程。 使用自己的MJ账号,支持GPT3.5自动翻译等。...

      charles-proxy-4.6.2-win64.msi

      Charles(charles-proxy-4.6.2-win64.msi官方下载试用30天Windows 64位版本) 是在您自己的计算机上运行的 Web 代理(HTTP 代理/ HTTP 监视器)。然后,您的网络浏览器(或任何其他互联网应用程序)被配置为通过 ...

      Proxy-SwitchyOmega_v2.5.21.zip

      Proxy-SwitchyOmega是一款针对Chrome浏览器的扩展程序,主要用于网络代理管理和IP地址切换。这款插件对于需要频繁更换网络环境的用户,如开发者、网络测试人员或者需要访问不同地区服务的用户来说,非常实用。它允许...

      win64 charles-proxy-4.6.3-win64.msi

      安装后,按照向导提示设置系统代理,并在Charles Proxy中启用SSL代理(可能需要安装其提供的根证书)。在使用过程中,遇到问题可以查阅官方文档或社区论坛,通常能找到解决方案。 总的来说,Charles Proxy 4.6.3是...

      smiley-http-proxy-servlet-1.7.jar

      java运行依赖jar包

      Node.js-hpts(http-proxy-to-socks)一个nodejs客户端将socks代理转换为http代理

      hpts(http-proxy-to-socks) 一个nodejs客户端将socks代理转换为http代理

      proxy-server-0.1.zip

      《内网服务器映射工具——proxy-server-0.1.zip详解》 在现代网络环境中,内网服务器的访问和管理常常受到外部网络的限制。为了突破这种限制,映射工具应运而生,它们能够将内网服务器暴露给外部网络,实现内外网络...

      Proxy-Go-JUI v6.0

      Proxy-Go-JUI基于高性能代理服务器 Proxy-Go 的 SDK 开发的 GUI 版本,支持 windows 跨平台性,无论你是widows,linux,还是mac,甚至是树莓派,都可以很好的运行proxy. 多协议支持,支持HTTP(S),TCP,UDP,Websocket,SOCKS5...

      browsermob-proxy-2.1.4-bin.zip

      代理服务器 BrowserMob Proxy,BrowserMob Proxy 简称 BMP,可以这么说,BMP 绝对是为 Selenium 为生的,Selenium + BMP 的完美搭配,可以实现很多你绝对想象不出来的功能

      前端开源库-http-proxy-rules

      1. **动态路由**:它可以让你基于URL路径、查询参数或其他请求头信息来动态地设定代理目标。这使得在开发阶段能够灵活地处理API请求,比如将特定的API请求转发到本地模拟服务器,而其他请求则正常发送到远程服务器。...

    Global site tag (gtag.js) - Google Analytics