您的位置:首页 > 编程语言 > Java开发

自己实现JDK的Proxy

2017-01-09 16:09 155 查看
1.动态代理,就是在运行期间动态生成一个代理类,通过代理类来调用我们主题类的方法(本文不讲动态代理模式,要自己先了解清楚)。所以首先我们得搞清楚怎么动态生成类,这里我用到的是jdk自带的javax.tools里面的 JavaCompiler等工具。

2.动态生成类,下面是我封装好的一个工具代码:

public class CompilerTool {

/**
* 编译生成class类对象
* @param packageName 包名
* @param className class文件的名称
* @param classContent java代码内容
* @return
*/
public static Class<?> compiler(String packageName , String className , String javaSrcContent){
// 生成源代码的JavaFileObject
SimpleJavaFileObject fileObject = new JavaSourceFromString(
className, javaSrcContent);
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
// 被修改后的JavaFileManager
JavaFileManager fileManager = new ClassFileManager(
compiler.getStandardFileManager(null, null, null));
// 执行编译
JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager,
null, null, null, Arrays.asList(fileObject));
task.call();
// 获得ClassLoader,加载class文件
ClassLoader classLoader = fileManager.getClassLoader(null);
try {
String c = packageName+"."+className ;
return classLoader.loadClass(c);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return null ;
}

static class JavaSourceFromString extends SimpleJavaFileObject {
final String code;
JavaSourceFromString(String name, String code) {
super(URI.create("string:///" + name.replace('.', '/')
+ Kind.SOURCE.extension), Kind.SOURCE);
this.code = code;
}
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return code;
}
}
static class JavaClassFileObject extends SimpleJavaFileObject {
// 用于存储class字节
ByteArrayOutputStream outputStream;

public JavaClassFileObject(String className, Kind kind) {
super(URI.create("string:///" + className.replace('.', '/')
+ kind.extension), kind);
outputStream = new ByteArrayOutputStream();
}

@Override
public OutputStream openOutputStream() throws IOException {
return outputStream;
}

public byte[] getClassBytes() {
return outputStream.toByteArray();
}
}
@SuppressWarnings("rawtypes")
static class ClassFileManager extends ForwardingJavaFileManager {
private JavaClassFileObject classFileObject;
@SuppressWarnings("unchecked")
protected ClassFileManager(JavaFileManager fileManager) {
super(fileManager);
}
@Override
public JavaFileObject getJavaFileForOutput(Location location,
String className, JavaFileObject.Kind kind, FileObject sibling)
throws IOException {
classFileObject = new JavaClassFileObject(className, kind);
return classFileObject;
}
@Override
// 获得一个定制ClassLoader,返回我们保存在内存的类
public ClassLoader getClassLoader(Location location) {
return new ClassLoader() {
@Override
protected Class<?> findClass(String name)
throws ClassNotFoundException {
byte[] classBytes = classFileObject.getClassBytes();
return super
.defineClass(name, classBytes, 0, classBytes.length);
}
};
}
}
}


测试上面工具类:

public static void main(String[] args) throws InstantiationException, IllegalAccessException {
String java = "public class Aa{static{System.out.println(\"aaaa被动态加载了\");}}" ;

Class<?> aa = CompilerTool.compiler("Aa", java) ;
aa.newInstance();
}
//结果:aaaa被动态加载了


3.下面根据jdk的proxy简单实现下,注意是简单实现,不包括jdk的优化代理类缓存等。(ps:jdk动态生成类的技术肯定不是用的上述动态生成类的技术,我们这里只是模拟思想)

首先上jdk的InvocationHandler

public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object... args)
throws Exception;
}


重要角色Proxy类

public class Proxy{
public static Object newProxyInstance(InvocationHandler invocationHandler , Class<?>[] interfaces){
//代理类的随机名称
String targetName = "$Proxy"+new Random().nextInt(10);
// 存储 代理类 的 java代码字符串
StringBuilder proxyContent = new StringBuilder();
//--------- 包名  :  package 抽象主题接口的包 一致
proxyContent.append("package "+interfaces[0].getPackage().getName() + ";");
//--------- import 的一些必须
for(Class<?> clazz : interfaces){
proxyContent.append("import ").append(clazz.getName()).append(';');
}
proxyContent.append("import java.lang.reflect.Method;");
//--------- 头部 :  public class $Proxy implements xxx1,xx2{
proxyContent.append("public class ").append(targetName).append(" implements ");
for(int i=0;i<interfaces.length;i++){
if(i == interfaces.length - 1){ // 最后一个
proxyContent.append(interfaces[i].getSimpleName());
break;
}
proxyContent.append(interfaces[i].getSimpleName()).append(",");
}
proxyContent.append("{") ;
//---------成员变量 InvocationHandler handler ;
proxyContent.append("InvocationHandler handler;");
//---------构造函数
proxyContent.append("public ").append(targetName).append("(InvocationHandler handler){this.handler=handler;}") ;
// 一个接口一个接口找
for(Class<?> inter : interfaces){
//---------要实现的所有的方法,看抽象主题接口,有什么就都要实现
for(Method method :inter.getDeclaredMethods()){
method.setAccessible(true) ;

proxyContent.append("public ").append(method.getReturnType().getName()).append(' ');
proxyContent.append(method.getName()).append('(');
// 构造 方法参数列表
Class<?>[] parameterTypes = method.getParameterTypes() ;
String ps = null ;
for(int i=0;i<parameterTypes.length;i++){
if(i == parameterTypes.length - 1){
proxyContent.append(parameterTypes[i].getName()).append(" p").append(i);
ps += "p"+i;
c454

break;
}
proxyContent.append(parameterTypes[i].getName()).append(" p").append(i).append(",");
ps += "p"+i+",";
}
// 方法里面 得反射调用 客户端 主题接口 的实现类 的 同样的方法
proxyContent.append("){try{").append("Method md="+inter.getName()+".class.getMethod(\""+method.getName()+"\");");
if(ps == null){
proxyContent.append("this.handler.invoke(this,"+"md"+");}catch(Exception e){e.printStackTrace();}}");
}else{
proxyContent.append("this.handler.invoke(this,"+"md"+ps+");}catch(Exception e){e.printStackTrace();}}");
}
}
}
proxyContent.append('}');
System.out.println(proxyContent);
Object proxy = null ;  //代理对象
try {
Class<?> pClass = CompilerTool.compiler(interfaces[0].getPackage().getName(),targetName, proxyContent.toString()) ;
// 调用上面动态 类的 构造器 , 生成代理类对象
proxy = pClass.getDeclaredConstructor(InvocationHandler.class).newInstance(invocationHandler);
} catch (Exception e) {
e.printStackTrace();
}
return proxy;
}
}

//上面字符串拼接的类实际上是生成了类似下面格式的类(假如随机名称是$Proxy5,包名:test.proxy,主题接口名:HttpSubject,主题接口只有一个方法:http,下面会有例子):
package test.proxy;

import test.proxy.HttpSubject;
import java.lang.reflect.Method;

public class $Proxy5 implements HttpSubject {
InvocationHandler handler;

public $Proxy5tt(InvocationHandler handler) {
this.handler = handler;
}

public void http() {
try {
Method md = test.proxy.HttpSubject.class.getMethod("http");
this.handler.invoke(this, md);
} catch (Exception e) {
e.printStackTrace();
}
}
}


好了,jdk的工具实现完了,下面来举个代理模式的例子:

//这个使我们要代理 的 抽象主题
public interface HttpSubject{
public void http();
}
//抽象主题的实现类
class HttpSubjectImpl implements HttpSubject{
@Override
public void http() {
System.out.println("=========http========");
}
}
//动态代理
class DynamicSubject implements InvocationHandler{
Object subject ;
public DynamicSubject(Object subject ) {
this.subject = subject ;
}
@Override
public Object invoke(Object proxy, Method method, Object... args)
throws Exception {
return method.invoke(subject, args);
}
}
//测试
public static void main(String[] args) throws Exception {
HttpSubject subject = new HttpSubjectImpl();
InvocationHandler handler = new DynamicSubject(subject);

long start = System.currentTimeMillis();
HttpSubject proxy = (HttpSubject) Proxy.newProxyInstance(handler, subject.getClass().getInterfaces());
System.out.println("动态生成时间:"+ (System.currentTimeMillis()-start));
proxy.http();
}
//测试结果:(时间太长了)
动态生成时间:641
=========http========


今天就到这里,谢谢大家!

老生常谈:深圳有爱好音乐的会打鼓(吉他,键盘,贝斯等)的程序员和其它职业可以一起交流加入我们乐队一起嗨。我的QQ:657455400
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: