自己实现JDK的Proxy

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;
                        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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值