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异常。