手动实现一个简单JDK版动态代理

一.实现步骤

  1. 根据目标类的接口类型生成代理类的java文件。
  2. 编译代理类java文件为.class字节码文件。
  3. 将编译好的字节码文件加载到jvm中。
  4. 生成代理类对象并返回。

二.代码实现

1.Proxy类

public class CLProxy   {
    private static  final  String  ENTER= "\r\n";
    private static  final  String  PAKAGE=CLProxy.class.getPackage().toString()+";";
    private static  final  String  CLASS_NAME="$Proxy";
    private static  final  AtomicInteger NUMBER= new AtomicInteger(0);
    public static Object newProxyInstance(CLClassLoader classLoader, Class<?>[] interfaces,CLInvocationHandler h) throws  Exception{
        String className =CLASS_NAME+NUMBER.getAndIncrement();
        //遍历所有的接口生成java 文件
        String javaString = createJavaString(interfaces, className);
        String parentPath = CLProxy.class.getResource("").getPath();
        File file  =new File(parentPath,className+".java" );
        FileWriter writer   = new FileWriter(file);
        writer.write(javaString);
        writer.flush();
        writer.close();
        //System.out.println(file);
        //编译
        JavaCompiler systemJavaCompiler = ToolProvider.getSystemJavaCompiler();
        StandardJavaFileManager standardFileManager = systemJavaCompiler.getStandardFileManager(null, null, null);
        Iterable<? extends JavaFileObject> javaFileObjects = standardFileManager.getJavaFileObjects(file);
        JavaCompiler.CompilationTask task = systemJavaCompiler.getTask(null, standardFileManager, null, null, null, javaFileObjects);
        task.call();
        standardFileManager.close();
        //创建实例
        Class<?> aClass = classLoader.findClass(className);
        Constructor<?> constructor = aClass.getConstructor(CLInvocationHandler.class);
        Object instance = constructor.newInstance(h);
        //file.delete();
        return instance;
    }

    /**
     * 生成java 文件
     * @param interfaces
     * @return
     */
    private static String createJavaString(Class<?>[] interfaces , String className ){

        StringBuffer  buffer   = new StringBuffer();
        buffer.append(PAKAGE+ENTER);
        buffer.append("import java.lang.reflect.Method;"+ ENTER);

        StringBuffer interfaceString= new StringBuffer();
        int  length= interfaces.length;
        for (int i = 0; i<length ;  ++i){
            interfaceString.append(interfaces[i].getName());
            if (i!=length-1){
                interfaceString.append(",");
            }
        }
        buffer.append("public final  class ");
        buffer.append(className);
        buffer.append(" implements ");
        buffer.append(interfaceString);
        buffer.append(" {"+ENTER);
        buffer.append("private CLInvocationHandler handler;"+ENTER);
        buffer.append("public  "+className+"(CLInvocationHandler handler) {"+ENTER);
        buffer.append(" this.handler= handler;"+ENTER);
        buffer.append("}"+ENTER);
        for (int i =0 ;i<length;++i){
            Class<?> clazz= interfaces[i];
            Method[] methods = clazz.getMethods();
            for (Method method  : methods){
                String returnTypeString = method.getReturnType().getName();

                Class<?>[] parameterTypes = method.getParameterTypes();
                StringBuffer paramTypeString  = new StringBuffer();
                StringBuffer methodParamString = new StringBuffer();
                StringBuffer invokeParamString = new StringBuffer();

                paramTypeString.append("new Class[]{");
                int paramLength=  parameterTypes.length;
                for (int j  =0 ; j<paramLength ;++j){
                    Class<?> paramClazz=  parameterTypes[j];
                    paramTypeString.append(paramClazz.getName()+".class");
                    String paramFieldName  = "var"+j;
                    methodParamString.append(paramClazz.getName() +" "+paramFieldName);
                    invokeParamString.append(paramFieldName);
                    if (j!= paramLength-1){
                        paramTypeString.append(",");
                        methodParamString.append(",");
                        invokeParamString.append(",");
                    }
                }
                paramTypeString.append("}");
                int modifiers = method.getModifiers();
                if (Modifier.isPublic(modifiers)){
                    buffer.append("public");
                }else if (Modifier.isPrivate(modifiers)){
                    buffer.append("private");
                }else if (Modifier.isProtected(modifiers)){
                    buffer.append("protected");
                }
                buffer.append(" final "+returnTypeString+" "+ method.getName()+"("+methodParamString+"){"+ ENTER);
                    buffer.append("try{"+ENTER);

                buffer.append("Method method = "+clazz.getName()+".class.getMethod(\""+method.getName()+"\","+paramTypeString+" );"+ENTER);

                    if (!"void".equals(returnTypeString)){
                        buffer.append("return ("+returnTypeString+")");

                    }
                    if (invokeParamString.toString().length()==0){
                        invokeParamString.append("null");
                    }else{
                        invokeParamString = new StringBuffer("new Object[]{"+invokeParamString.toString()+"}");
                    }
                    buffer.append("this.handler.invoke(this,method,"+invokeParamString+");"+ENTER);
                    buffer.append("}catch(Throwable e){"+ENTER);
                    buffer.append("e.printStackTrace();"+ENTER);
                    buffer.append("}"+ENTER);
                    if (!"void".equals(returnTypeString)){
                        buffer.append("return null;"+ENTER);
                    }
                buffer.append("}"+ENTER);
            }
        }

        buffer.append("}");
        return buffer.toString();
    }

    public static void main(String[] args) throws Exception {
        Person person = (Person)CLProxy.newProxyInstance(new CLClassLoader(), XiaoMing.class.getInterfaces(), new CLInvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("before");

                Object result= method.invoke(new XiaoMing(), args);
                System.out.println("after");
                return result;
            }
        });
        String laoxu = person.call("laoxu");
        System.out.println(laoxu);
       /* person.eat();
        Class<?>[] interfaces = person.getClass().getInterfaces();

        for (Class<?> in:interfaces){
            System.out.println(in.getName());
        }
*/
        Person person2= (Person)CLProxy.newProxyInstance(new CLClassLoader(), XiaoMing.class.getInterfaces(), new CLInvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("before");

                Object result= method.invoke(new XiaoMing(), args);
                System.out.println("after");
                return result;
            }
        });

        System.out.println(person2.getClass());

    }
}

2.InvocationHandler接口

public interface CLInvocationHandler {


    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable;

}

3.ClassLoader类加载器

public class CLClassLoader  extends  ClassLoader {

    private File  classPathFile;

    public CLClassLoader(){

        String classPath = CLClassLoader.class.getResource("").getPath();

        this.classPathFile= new File(classPath);
    }


    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {


        String className  = CLClassLoader.class.getPackage().getName()+"."+name;



        if (classPathFile!= null ){


            File classFile = new File(classPathFile, name.replace("\\.", "/") + ".class");


            if (classFile.exists()){


                FileInputStream  inputStream =null;

                ByteArrayOutputStream outputStream  = null;
                try{
                    inputStream=new FileInputStream(classFile);

                    outputStream= new ByteArrayOutputStream();

                    byte[] bytes = new byte[1024];
                    int len;
                    while ((len=inputStream.read(bytes))!=-1){
                        outputStream.write(bytes,0,len);
                    }
                    return defineClass(className,outputStream.toByteArray(),0,outputStream.size());
                }catch (Exception e){
                    e.printStackTrace();
                }finally {
                    if (inputStream!= null){
                        try {
                            inputStream.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                    if (outputStream!=null){
                        try {
                            outputStream.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }



            }




        }
        return super.findClass(name);
    }
}

4.测试使用的接口与目标类

//测试使用的接口
public interface Person {

    void  eat();

    String call(String name);
}


//测试使用目标类
public class XiaoMing  implements  Person {
    @Override
    public void eat() {
        System.out.println("吃东西");
    }

    //@Override
    public String call(String name) {
        return name;
    }
}

注意测试方法在CLProxy 的main 方法中。

猜你喜欢

转载自blog.csdn.net/yuanshangshenghuo/article/details/101169883