动态代理 原理简析(java. 动态编译,动态代理)

动态代理:
  1.动态编译 JavaCompiler.CompilationTask 动态编译想理解自己查API文档
  2.反射被代理类 主要使用Method.invoke(Object o,Object... args);对带有指定参数的指定对象调用由此 Method 对象表示的底层方法。
  3.类的加载 URLClassLoader可以加载硬盘任意位置的.java文件。class.getClassLoader只能加载classPath目录下的类。
动态代理可以理解为 动态生成发射代理的类。这其中可以动态增加逻辑操作。比如日志的打印,事物的处理等。spring的AOP操作也是动态代理的。

假设我们有一个接口GrowAble可成长的。

package com.cn;



public interface GrowAble {

    void growUp();

}

一棵小树苗实现了这个接口

package com.cn;

public class Tree implements GrowAble {

    @Override

    public void growUp() {

        System.out.println("I am a tree , I'm grow up!");

    }



}

这时我们想不在不改变源码的情况下想知道树长了多少这个操作?
我们需要一个转换接口。

package com.cn;

import java.lang.reflect.Method;



public interface InvactionHandle {

    void invoke(Object o,Method m);

}

一个实现接口类。

package com.cn;

import java.lang.reflect.Method;

import java.util.Random;



public class HeightInvactionHandle implements InvactionHandle {

    @Override

    public void invoke(Object c, Method m) {

        try {

            m.invoke(this.o);

            System.out.println("这棵树长了" + new Random().nextInt(9527)+"米!!!" );

        } catch (Exception e) {

            e.printStackTrace();

        }

    }

    private Object o;

    public HeightInvactionHandle(Object o) {

        super();

        this.o = o;

    }

}

现在最重要的Proxy类了。把上述两个接口接口起来。

package com.cn;

import java.io.File;

import java.io.FileWriter;

import java.lang.reflect.Constructor;

import java.lang.reflect.Method;

import java.net.URL;

import java.net.URLClassLoader;

import javax.tools.JavaCompiler;

import javax.tools.JavaFileObject;

import javax.tools.StandardJavaFileManager;

import javax.tools.ToolProvider;

import javax.tools.JavaCompiler.CompilationTask;

/**

 * 动态代理

 * @author 灵台方寸小道士

 */

public class Proxy {

    public static Object getNewInstance(Class<?> c,Object object) throws Exception {

        String path = System.getProperty("user.dir") + File.separator + "mybin"

                + File.separator + "com" + File.separator + "cn"

                + File.separator;

        String fileName = "$Proxy.java";

        String nextLine = System.getProperty("line.separator");

        // create java File

        String fileValue = "package com.cn;"+nextLine+

                           "import com.cn.*;"+nextLine+

                           "import java.lang.reflect.Method;"+nextLine+

                           "public class $Proxy implements "+ c.getName() +"{"+nextLine+

                           "    private InvactionHandle h;"+nextLine+

                           "    public $Proxy(InvactionHandle hin)"+ nextLine+

                           "     {"+nextLine+

                           "           this.h = hin;"+nextLine+

                           "     }"+nextLine;

        Method[] methods = c.getDeclaredMethods();

        for (Method m:methods) {

            fileValue += "    public "+ m.getReturnType()+" "+m.getName()+"()"+nextLine+

                       "     {"+nextLine+

                       "          try{            "+nextLine+

                       //测试方法不带参数  所以new Class<?>[]{}空参数传入

                       "          Method me = "+c.getName()+".class.getDeclaredMethod(\""+m.getName()+"\",  new Class<?>[]{});"+nextLine+

                       "          h.invoke(this,me);"+nextLine+

                       "          }catch(Exception e){ "+nextLine+

                       "           e.printStackTrace(); }"+nextLine+

                       "     }"+nextLine;

        }

        fileValue +="}"+nextLine;

        File f =  new File(path);//是否存在此目录

        if (!f.exists())

            f.mkdirs();

        FileWriter writer = new FileWriter(new File(f,fileName));

        writer.write(fileValue);

        writer.flush();

        writer.close();

        System.out.println("***************     create java file over      ******************");

        // compiler 生成 class文件  调取javac编译

        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();

        StandardJavaFileManager manager = compiler.getStandardFileManager(null,null, null);

        Iterable<? extends JavaFileObject> in = manager.getJavaFileObjects(path+ fileName);

        CompilationTask task = compiler.getTask(null, manager, null, null,null, in);

        task.call();

        System.out.println("***************     complier class file over      ******************");



        // loader 加载class文件 的第一种方法 URLClassLoader可以load任意目录下的类!

        URL[] urls = new URL[] { new URL("file:/" + System.getProperty("user.dir") + File.separator + "mybin"+ File.separator) };

        URLClassLoader loader = new URLClassLoader(urls);

        Class<?> d = loader.loadClass("com.cn.$Proxy");

        System.out.println("***************     loader class file over      ******************");



        // newInstance class JVM

        Constructor<?> con = d.getConstructor(InvactionHandle.class);

        Object o = con.newInstance(object);

        // newInstance...

        /**

         加载class文件 的第二种方法 ClassLoader只能load位于classpath(src目录)下的类

         Class<?> second = Proxy.class.getClassLoader().loadClass("com.cn.$Proxy");

         System.out.println(second.getSimpleName());

         */

        return o;

    }

}

JavaCompiler 是用于编译生成的java代码。在用URLClassLoader将class文件加载进内存。在实例化。

下面一个测试类 Client

package com.cn;

public class Client {

    

    public static void main(String[] args) throws Exception {

        Tree tree = new Tree();

        InvactionHandle handle = new HeightInvactionHandle(tree);

        GrowAble gro = (GrowAble)Proxy.getNewInstance(GrowAble.class, handle);

        gro.growUp();

        System.out.println("测试结束");

    }

}

运行结果

***************     create java file over      ******************

***************     complier class file over      ******************

***************     loader class file over      ******************

I am a tree , I'm grow up!

这棵树长了2174米!!!

测试结束

现在我们在用JDK来做做

package com.cn;

import java.lang.reflect.InvocationHandler;

import java.lang.reflect.Method;

import java.util.Random;



public class JDKInvocationHandle implements InvocationHandler {



    private Object o;

    public JDKInvocationHandle(Object o)

    {

        super();

        this.o = o;

    }

    @Override

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

            throws Throwable {

        Object result =  null;

        try {

            result = method.invoke(o,args);

            System.out.println("这棵树长了" + new Random().nextInt(9527)+"米!!!" );

        } catch (Exception e) {

            e.printStackTrace();

        }

        return result;

    }

}

测试类

package com.cn;

public class Client2 {

    

    public static void main(String[] args) {

        java.lang.reflect.InvocationHandler h = new JDKInvocationHandle(new Tree());

        GrowAble gro = (GrowAble) java.lang.reflect.Proxy.newProxyInstance(

                GrowAble.class.getClassLoader(), 

                new Class[] { GrowAble.class }, 

                h);

        gro.growUp();

        System.out.println("测试结束");

    }

}

运行结果

I am a tree , I'm grow up!

这棵树长了726米!!!

测试结束

文章目标 学习java动态代理原理。仅当抛砖引玉作用。

文章原创。转载请注明http://www.cnblogs.com/stay-9527/p/3689266.html

 

 

 

 

 

 

 

你可能感兴趣的:(java)