深入理解Java的动态代理

      java要实现代理可以通过继承和聚合来实现,个人不推荐用继承实现,用继承实现可能使继承层次很多,这样结构就不好看,也不符合设计模式中的建议(尽量使用聚合而代替使用继承)。 看看java的动态代理是怎么实现的: 下面通过模拟java的动态代理写一个例子:

public interface Sale {

	public void sale();
}

 

package com.KingXt.proxy;

public class ComputerSales implements Sale{

	@Override
	public void sale() {
		System.out.println("--------ComputerSales--------");
	}
}

 通过实现下面的接口,用户就可以自定义代理方式(例如:方法执行前后打印出当前时间)

package com.KingXt.proxy;

import java.lang.reflect.Method;


public interface InvocationHandler{
	 public void invoke(Object proxy, Method method);
}

 简单写了一个类来测试按此类实现代理

package com.KingXt.proxy;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * 赠送代理
 * @author KingXt
 *
 */
public class PresentInvocationHandler implements InvocationHandler{

	private Object object;

	public PresentInvocationHandler(Object object) {
		super();
		this.object = object;
	}


	@Override
	public void invoke(Object proxy, Method method){
		System.out.println("赠送鼠标");
		try {
			method.invoke(object, new Object[]{});
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

}

 这是代理生成类,通过动态生成java文件,调用jdk6带来的特性将文件编译成class,然后通过反射生成代理类

package com.KingXt.proxy;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
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;

/**
 * 代理类实现
 * @author KingXt
 *
 */
@SuppressWarnings("unchecked")
public class Proxy {
	
	//得到项目根目录
	public static final String baseDir = System.getProperty("user.dir");
	
	public static Object newProxyInstance(Class intface, InvocationHandler h){
		if (h == null) {
		    throw new NullPointerException();
		}
		String methodString = Proxy.getMethodString(intface);
		String src = "package com.KingXt.proxy; "  +
					 "import java.lang.reflect.Method; " + 
					 "public class ProxyAny implements " + intface.getName()+ "{" + 
					 		"com.KingXt.proxy.InvocationHandler h;" + 
					  		"public ProxyAny(InvocationHandler ih){" + 
					  				"this.h = ih;" + 
					  		 "}" + 				
					  		 methodString + 
					  		 "}"+
					"}";
		String fileName = baseDir + "/src/com/KingXt/proxy/ProxyAny.java";
		File file = new File(fileName);
		//将字符串写入java文件
		try {
			FileWriter fw = new FileWriter(file);
			fw.write(src);
			fw.flush();
			fw.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return getObjectWithFile(fileName, h);
	}
	
	/**
	 * 拼接一个类的方法字符串
	 * @param intface 接口
	 * @return 方法字符串
	 */
	private static String getMethodString(Class intface){
		StringBuffer sb = new StringBuffer();
		//得到接口中的所有方法
		Method []ms = intface.getMethods();
		for(Method m : ms){
			sb.append("@Override ");
			sb.append("public void "+ m.getName() +"(){ ");
			sb.append("try { ");		  	
			sb.append("Method m = " + intface.getName() + ".class.getMethod(\"" + m.getName() + "\");");		  	
			sb.append("h.invoke(this, m);");		  	
			sb.append("}catch(Exception e){e.printStackTrace();}");
		}
		return sb.toString();
	}
	
	/**
	 * 首先将java文件动态编译,然后将编译后的class的文件load进内存,再通过反射机制生产java类
	 * @param filePath  java文件的位置
	 * @param ih		要生产java类,通过调用构造函数构建,这就必须知道构造函数的参数
	 * @return			调用反射生成的类
	 */
	private static Object getObjectWithFile(String filePath, InvocationHandler ih){
		Object o = null;
		//得到java本地编译器
		JavaCompiler jc = ToolProvider.getSystemJavaCompiler();
		//通过java编译器得到文件管理器
		StandardJavaFileManager javaFileManager = jc.getStandardFileManager(null, null, null);
		Iterable files = javaFileManager.getJavaFileObjects(filePath);
		CompilationTask ct = null;
		//调用java编译器编译java文件
		ct = jc.getTask(null, javaFileManager, null, null, null, files);
		 
		ct.call();
		try {
			javaFileManager.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		//通过URLClassLoader加载class文件
		URL urls[] = null;
		try {
			/*
			 * 这里要重点注意:
			 * 如果file:/" + baseDir + "/src这个字符串最后没有"/", URLClassLoader加载的是jar文件
			 * 否则加载的是一个目录
			 */
			urls = new URL[]{new URL("file:/" + baseDir + "/src/")};
		} catch (MalformedURLException e) {
			e.printStackTrace();
		}
		URLClassLoader ul = new URLClassLoader(urls, Thread.currentThread().getContextClassLoader());
		Class c = null;
		try {
			c = ul.loadClass("com.KingXt.proxy.ProxyAny");
			Constructor constr = c.getConstructor(InvocationHandler.class);
			o = constr.newInstance(ih);
		} catch (SecurityException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InstantiationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return o;
	}
	
}

 

package com.KingXt.proxy;

public class Test {

	public static void main(String[] args) {
		
		//创一个电脑销售商
		ComputerSales computerSales = new ComputerSales();
		//给出销售前后可以处理的动作,例如:送鼠标
		InvocationHandler ih = new PresentInvocationHandler(computerSales);
		//创建代理
		Sale sale = (Sale) Proxy.newProxyInstance(Sale.class, ih);
		//根据代理产生的对象执行动作
		sale.sale();
	}
	
}

  

 通过上面的代码,基本上模拟了java的动态代理。但是还有很多细节性东西没实现,比如invoke函数的返回值不应该是void、同步等等。要想知道jdk具体实现可以看看jdk的源代码。还有一点要强调的是URLClassLoader的使用,很容易产生ClassNotFoundException异常。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值