java动态代理(下)

好的,接下来我们接着上一篇博客接着继续给大家介绍java动态代理技术,上一篇博客的结尾我们讲到我们要用自己的模拟的Proxy类来实现跟JDK的Proxy类差不多的功能,即返回一个代理类。以下内容需要读者有一定java反射基础,如不了解,可先去看下我的另外一篇博文:java反射技术

我们首先分析下,JDK的Proxy类的newProxyInstance接收三个参数,第一个是ClassLoader,我们为了简单起见,省去这个参数,ClassLoader的具体作用读者感兴趣可上网了解,第二个参数是被代理类实现的接口列表,我们为简单起见,默认被代理类只实现一个接口。

先简单说下JDK的Proxy类干了什么,然后再模拟它,它接收了被代理类实现的接口列表目的是创造一个同样实现这些接口的代理类,这样他就能反射出这些接口中的方法,也就是他可以得到被代理类中的方法列表,然后它还接收一个InvocationHandler的实现类h,在反射出的每一个方法中,会调用h的invoke方法,在h的invoke方法中,先加入自己的逻辑,然后再调用被代理类中的同样方法(注意,h中持有一个被代理对象),将所有的方法反射完成之后,动态编译这个代理类,返回一个代理类的对象。然后我们在外面就可以直接拿接口类型的引用接收这个对象,当调用这个代理类的方法的时候,我们自己加的逻辑自然也就加进去了。

上面只是说的是基本原理,当然JDK的Proxy类在实际执行过程中远比以上描述要复杂,不知各位读者,你们明白了么?如果还不明白的话,我们直接上代码把。


package com.proxy;

import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Constructor;
import com.proxy.InvocationHandler;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import javax.tools.JavaCompiler.CompilationTask;

public class Proxy {
	//接收一个接口的Class对象,然后接收一个InvocationHandler的对象 h
	public static Object newProxyInstance(Class infce, InvocationHandler h) throws Exception { 
		String methodStr = "";
		String rt = "\r\n";
		
		//反射出接口中的所有方法
		Method[] methods = infce.getMethods();
		for(Method m : methods) {
			//对每个方法,我们构造他们的方法字符串,为简单起见,我们默认每个方法都没有返回值
			methodStr += "@Override" + rt + 
						 "public void " + m.getName() + "() {" + rt +
						 "    try {" + rt +
						 //得到该方法的名字
						 "    Method md = " + infce.getName() + ".class.getMethod(\"" + m.getName() + "\");" + rt +
						 //调用h的invoke方法,至于invoke方法内部执行了什么,可去h中看下
						 "    h.invoke(this, md);" + rt +
						 "    }catch(Exception e) {e.printStackTrace();}" + rt +
						
						 "}";
		}
		
		String src = 
			"package com.proxy;" +  rt +
			"import java.lang.reflect.Method;" + rt +
			"import com.proxy.InvocationHandler;" + rt +
			//代理类和被代理类实现同样的接口
			"public class $Proxy1 implements " + infce.getName() + "{" + rt +
			//代理类持有一个InvocationHandler对象,为简单起见,我们自己重新定义一个InvocationHandler接口和实现类
			"    public $Proxy1(InvocationHandler h) {" + rt +
			"        this.h = h;" + rt +
			"    }" + rt +
			
			
			"    InvocationHandler h;" + rt +
							
			methodStr +
			"}";
		
		//以下代码为动态编译过程,将以上代码写到一个java文件中,然后编译此文件,在根据编译生成的class文件反射出对象
		String fileName = 
			"d:/src/com/proxy/$Proxy1.java";
		File f = new File(fileName);
		FileWriter fw = new FileWriter(f);
		fw.write(src);
		fw.flush();
		fw.close();
		
		JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
		StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
		Iterable units = fileMgr.getJavaFileObjects(fileName);
		CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units);
		t.call();
		fileMgr.close();
		
		URL[] urls = new URL[] {new URL("file:/" + "d:/src/")};
		URLClassLoader ul = new URLClassLoader(urls);
		Class c = ul.loadClass("com.proxy.$Proxy1");
		
		//反射出那个接受一个InvocationHandler对象为参数的构造方法
		Constructor ctr = c.getConstructor(InvocationHandler.class);
		//得到一个代理对象,然后返回,至此,大功告成
		Object m = ctr.newInstance(h);
		//m.move();

		return m;
	}
}

为简单起见,我们重写了InvocationHandler接口(上面代码中用到的),如下所示:

package com.proxy;

import java.lang.reflect.Method;

public interface InvocationHandler {
	public void invoke(Object o, Method m);
}

我们重新定义了InvocationHandler的实现类,如下所示:

package com.proxy;

import com.proxy.InvocationHandler;

import java.lang.reflect.Method;

public class HelloWorldHandler1 implements InvocationHandler {

	// 持有一个Object对象,用来保存被代理对象
	Object obj = null;

	public HelloWorldHandler1(Object obj) {
		this.obj = obj;
	}

	@Override
	public void invoke(Object proxy, Method method) {
		// 在方法执行之前加上自己的逻辑
		this.before();
		// 执行被代理对象的方法
		try {
			Object resultObject = method.invoke(obj, null);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		// 在方法执行之后加上自己的逻辑
		this.after();
		// 返回方法执行的结果,当然可能为null

	}

	public void before() {
		System.out.println("Method sayHello start");
	}

	public void after() {
		System.out.println("Method sayHello end");
	}
}

测试类如下:

package com.proxy;

import com.proxy.Proxy;

public class HelloWorldTest3 {
	public static void main(String[] args) throws Exception {
		HelloWorld helloWorld = new HelloWorldImpl();
		HelloWorldHandler1 helloWorldHandler = new HelloWorldHandler1(helloWorld);

		// 调用Proxy的静态方法newProxyInstance创建一个代理类
		HelloWorld helloWorld2 = (HelloWorld) Proxy.newProxyInstance(HelloWorld.class,
				 helloWorldHandler);
		// 调用代理类的同名方法sayHello
		helloWorld2.sayHello();
	}
}
以上代码也实现了对HelloWorldImpl的动态代理,但是由于简化了结构,所以功能还是很有限,但是基本的原理就是这个样子滴,大家可以自己研究下我们自己模拟的Proxy类,有什么疑问或者问题,欢迎在评论中提出,我将尽可能回复大家。

我上面讲到了,阅读上面那个Proxy类需要一定的java反射基础,理解了上面那个类的原理,也就基本上理解JDK的Proxy的基本原理了。

希望以上内容对大家学习java动态代理技术有一定的帮助,谢谢。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
本系统的研发具有重大的意义,在安全性方面,用户使用浏览器访问网站时,采用注册和密码等相关的保护措施,提高系统的可靠性,维护用户的个人信息和财产的安全。在方便性方面,促进了校园失物招领网站的信息化建设,极大的方便了相关的工作人员对校园失物招领网站信息进行管理。 本系统主要通过使用Java语言编码设计系统功能,MySQL数据库管理数据,AJAX技术设计简洁的、友好的网址页面,然后在IDEA开发平台中,编写相关的Java代码文件,接着通过连接语言完成与数据库的搭建工作,再通过平台提供的Tomcat插件完成信息的交互,最后在浏览器中打开系统网址便可使用本系统。本系统的使用角色可以被分为用户和管理员,用户具有注册、查看信息、留言信息等功能,管理员具有修改用户信息,发布寻物启事等功能。 管理员可以选择任一浏览器打开网址,输入信息无误后,以管理员的身份行使相关的管理权限。管理员可以通过选择失物招领管理,管理相关的失物招领信息记录,比如进行查看失物招领信息标题,修改失物招领信息来源等操作。管理员可以通过选择公告管理,管理相关的公告信息记录,比如进行查看公告详情,删除错误的公告信息,发布公告等操作。管理员可以通过选择公告类型管理,管理相关的公告类型信息,比如查看所有公告类型,删除无用公告类型,修改公告类型,添加公告类型等操作。寻物启事管理页面,此页面提供给管理员的功能有:新增寻物启事,修改寻物启事,删除寻物启事。物品类型管理页面,此页面提供给管理员的功能有:新增物品类型,修改物品类型,删除物品类型。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值