设计模式(23)-----代理设计模式-----动态代理

 一:自己手写动态代理

1.1,把一串静态的字符串(代理类)加载输出,编译,加载到内存,并且反射获取这个对象

public interface Moveable {
    public void move();
}
 1 package com.DesignPatterns.al.Dynamic5;
 2 
 3 import java.util.Random;
 4 
 5 public class Tank implements Moveable {
 6 
 7     @Override
 8     public void move() {
 9         
10         System.out.println("坦克在移动");
11         try {
12             Thread.sleep(new Random().nextInt(1000));
13         } catch (InterruptedException e) {
14             e.printStackTrace();
15         }
16         
17     }
18 }
package com.DesignPatterns.al.Dynamic5;

import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Constructor;
import java.net.URL;
import java.net.URLClassLoader;

import javax.tools.JavaCompiler;
import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
public class Test {
    public static void main(String[] args) throws Exception {
        String rt="\r\n";
        String src=
                "package com.DesignPatterns.al.Dynamic5;"+rt+

                "public class TankTime implements Moveable {"+rt+
                "    Moveable t;"+rt+

                "    public TankTime(Moveable t) {"+rt+
                "        super();"+rt+
                "        this.t = t;"+rt+
                "    }"+rt+

                "    @Override"+rt+
                "    public void move() {"+rt+

                "        long startTime = System.currentTimeMillis();"+rt+
                "        System.out.println(\"tank开始打印开始时间\"+startTime);"+rt+
                "        t.move();"+rt+
                "        long endTime = System.currentTimeMillis();"+rt+
                "        System.out.println(\"tank总共花费时间是\" + (endTime - startTime));"+rt+
                "    }"+rt+

                "}";
        //1,生成代理类
        String fileName = System.getProperty("user.dir");
        System.out.println("fileName是项目的根路径:"+fileName);
        String fileNameEnd=fileName+"/src/com/DesignPatterns/al/Dynamic5/TankTime.java";
        
        File f=new File(fileNameEnd);
        FileWriter fw=new FileWriter(f);
        fw.write(src);
        fw.flush();
        fw.close();
        
        //2,开始生成class文件,进行编译
        //2.1,获取java默认的编译器,说白了就是javac
        JavaCompiler compiler=ToolProvider.getSystemJavaCompiler();
        System.out.println("现在我的编译器是"+compiler.getClass().getName());
        StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
        Iterable units = fileMgr.getJavaFileObjects(fileNameEnd);
        CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units);
        //2.2,生成class文件这里不是重点,我们知道通过上面的代码就可以把java代码生成到指定的目录下面的class文件
        t.call();
        fileMgr.close();
        
        
        //3,把class文件加载到内存中去
        URL[] urls = new URL[] {new URL("file:/" + System.getProperty("user.dir") +"/src")};
        URLClassLoader ul = new URLClassLoader(urls);
        Class c = ul.loadClass("com.DesignPatterns.al.Dynamic5.TankTime");
        System.out.println("加载我的class文件是"+c);
        
        //4,从内存中生成对象,因为这个时候我们没有这个java类,所以只有从内存中来区这个值。
        //换句话就是说,我们现在没有这个对象,只有通过反射(通过构造器反射来获取这个对象)
        //4.1,我们调用的是构造方法的参数为Moveable(上面的参数是它的实现也可以的)类型的构造方法
        Constructor ctr = c.getConstructor(Moveable.class);
        Moveable m = (Moveable)ctr.newInstance(new Tank());
        m.move();
        }
}

通过运行,我们得到动态获取下面的代码:

package com.DesignPatterns.al.Dynamic5;
public class TankTime implements Moveable {
    Moveable t;
    public TankTime(Moveable t) {
        super();
        this.t = t;
    }
    @Override
    public void move() {
        long startTime = System.currentTimeMillis();
        System.out.println("tank开始打印开始时间"+startTime);
        t.move();
        long endTime = System.currentTimeMillis();
        System.out.println("tank总共花费时间是" + (endTime - startTime));
    }
}

控制台打印出下面的

fileName是项目的根路径:D:\data\eclipse-workspace\DesignPatterns
现在我的编译器是com.sun.tools.javac.api.JavacTool
加载我的class文件是class com.DesignPatterns.al.Dynamic5.TankTime
tank开始打印开始时间1541264914598
坦克在移动
tank总共花费时间是531

1.2,升级------ 静态的字符串(代理类)------里面的接口变成动态的

package com.DesignPatterns.al.Dynamic6;

import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.net.URLClassLoader;

import javax.tools.JavaCompiler;
import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;

public class Proxy {
public static Object newProxyInstance(Class inferance) throws Exception {
    String rt="\r\n";
    String src=
            "package com.DesignPatterns.al.Dynamic6;"+rt+

            "public class TankTime implements "+inferance.getName()+ "{"+rt+
            "    Moveable t;"+rt+

            "    public TankTime(Moveable t) {"+rt+
            "        super();"+rt+
            "        this.t = t;"+rt+
            "    }"+rt+

            "    @Override"+rt+
            "    public void move() {"+rt+

            "        long startTime = System.currentTimeMillis();"+rt+
            "        System.out.println(\"tank开始打印开始时间\"+startTime);"+rt+
            "        t.move();"+rt+
            "        long endTime = System.currentTimeMillis();"+rt+
            "        System.out.println(\"tank总共花费时间是\" + (endTime - startTime));"+rt+
            "    }"+rt+

            "}";
    //1,生成代理类
    String fileName = System.getProperty("user.dir");
    System.out.println("fileName是项目的根路径:"+fileName);
    String fileNameEnd=fileName+"/src/com/DesignPatterns/al/Dynamic6/TankTime.java";
    
    File f=new File(fileNameEnd);
    FileWriter fw=new FileWriter(f);
    fw.write(src);
    fw.flush();
    fw.close();
    
    //2,开始生成class文件,进行编译
    //2.1,获取java默认的编译器,说白了就是javac
    JavaCompiler compiler=ToolProvider.getSystemJavaCompiler();
    System.out.println("现在我的编译器是"+compiler.getClass().getName());
    StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
    Iterable units = fileMgr.getJavaFileObjects(fileNameEnd);
    CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units);
    //2.2,生成class文件这里不是重点,我们知道通过上面的代码就可以把java代码生成到指定的目录下面的class文件
    t.call();
    fileMgr.close();
    
    
    //3,把class文件加载到内存中去
    URL[] urls = new URL[] {new URL("file:/" + System.getProperty("user.dir") +"/src")};
    URLClassLoader ul = new URLClassLoader(urls);
    Class c = ul.loadClass("com.DesignPatterns.al.Dynamic6.TankTime");
    System.out.println("加载我的class文件是"+c);
    
    //4,从内存中生成对象
    //4.1,我们调用的是构造方法的参数为Moveable(上面的参数是它的实现也可以的)类型的构造方法
    Constructor ctr = c.getConstructor(Moveable.class);
    Moveable m = (Moveable)ctr.newInstance(new Tank());
    m.move();
    return null;
    
}
}

1.3,升级------ 静态的字符串(代理类)------里面的接口变成动态的----里面的方法变成动态的

 

public interface Moveable {
    public void move();
}
public class Test {
    public static void main(String[] args) {
        Method[] methods = Moveable.class.getMethods();
        for(Method m : methods) {
            System.out.println(m.getName());
        }
    }
}

上面的代码是通过发射动态的来获取方法的实例。看下面真正的改造

package com.DesignPatterns.al.Dynamic8;

public interface Moveable {
    public void move();
}
package com.DesignPatterns.al.Dynamic8;

import java.util.Random;

public class Tank implements Moveable {

    @Override
    public void move() {
        
        System.out.println("坦克在移动");
        try {
            Thread.sleep(new Random().nextInt(1000));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
    }
}
package com.DesignPatterns.al.Dynamic8;

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.JavaCompiler.CompilationTask;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;

public class Proxy {
public static Object newProxyInstance(Class inferance) throws Exception {
    String methodStr = "";
    Method[] methods = inferance.getMethods();
    String rt="\r\n";
    
    for(Method m : methods) {
        methodStr += "@Override" + rt + 
                     "public void " + m.getName() + "() {" + rt +
                         "   long start = System.currentTimeMillis();" + rt +
                        "   System.out.println(\"starttime:\" + start);" + rt +
                        "   t." + m.getName() + "();" + rt +
                        "   long end = System.currentTimeMillis();" + rt +
                        "   System.out.println(\"time:\" + (end-start));" + rt +
                     "}";
    }
    String src=
            "package com.DesignPatterns.al.Dynamic8;"+rt+

            "public class TankTime implements "+inferance.getName()+ "{"+rt+
            "    Moveable t;"+rt+

            "    public TankTime(Moveable t) {"+rt+
            "        super();"+rt+
            "        this.t = t;"+rt+
            "    }"+rt+

            methodStr +rt+

            "}";
    //1,生成代理类
    String fileName = System.getProperty("user.dir");
    System.out.println("fileName是项目的根路径:"+fileName);
    String fileNameEnd=fileName+"/src/com/DesignPatterns/al/Dynamic8/TankTime.java";
    
    File f=new File(fileNameEnd);
    FileWriter fw=new FileWriter(f);
    fw.write(src);
    fw.flush();
    fw.close();
    
    //2,开始生成class文件,进行编译
    //2.1,获取java默认的编译器,说白了就是javac
    JavaCompiler compiler=ToolProvider.getSystemJavaCompiler();
    System.out.println("现在我的编译器是"+compiler.getClass().getName());
    StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
    Iterable units = fileMgr.getJavaFileObjects(fileNameEnd);
    CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units);
    //2.2,生成class文件这里不是重点,我们知道通过上面的代码就可以把java代码生成到指定的目录下面的class文件
    t.call();
    fileMgr.close();
    
    
    //3,把class文件加载到内存中去
    URL[] urls = new URL[] {new URL("file:/" + System.getProperty("user.dir") +"/src")};
    URLClassLoader ul = new URLClassLoader(urls);
    Class c = ul.loadClass("com.DesignPatterns.al.Dynamic8.TankTime");
    System.out.println("加载我的class文件是"+c);
    
    //4,从内存中生成对象
    //4.1,我们调用的是构造方法的参数为Moveable(上面的参数是它的实现也可以的)类型的构造方法
    Constructor ctr = c.getConstructor(Moveable.class);
    Object o = ctr.newInstance(new Tank());
    return o;
    
}
}
package com.DesignPatterns.al.Dynamic8;

/**
 * 
 * @author qingruihappy
 * @data 2018年11月1日 上午12:39:35
 * @说明:现在模拟动态的方法并且打印主方法的内容
 */
public class Test {
    public static void main(String[] args) throws Exception {
        Moveable newProxyInstance = (Moveable) Proxy.newProxyInstance(Moveable.class);
        // 是不是很神奇啊,现在假如我们现在没有看到代理类,我们只有接口类,tank类,还有测试类,
        // 我们直观的来看现在的方法就是,我们把接口传入进入了newProxy这个方法中,它居然动态的给我
        // 打印出了日志还有主方法。
        // 我们不看代理类里面的代码我们闭着眼来想一下它是怎么实现的。
        // 1,首先把不用变的代码以静态string的形式写入进了动态类中
        // 2,把动态变化的东西比如说接口,通过传入的接口来获取,
        // 3,方法,因为接口中就有了实现类的方法,所以通过接口也能获取,其中的方法,
        // 4,那它是怎么来确定实现类就是tank呢而不是其他的呢。往下面看
        // 5,写完了之后生成java文件
        // 6,编译生成class文件
        // 7,加载到内存中来
        // 8,从内从中反射生成对象。(注意这这里传入了tank确定了是tank对象,所以当下面调用)
        // 9,执行下面的方法,就相当于执行刚生成的tanktime里面的方法是一样的。
        newProxyInstance.move();
        // 我们来看看还有什么问题就是动态切入进来的代码不是随机变化的,下面就是让切入进来的代码变成随机变化的。

    }

}

上面的代码运行之后动态生成的java代理类是:

package com.DesignPatterns.al.Dynamic8;
public class TankTime implements com.DesignPatterns.al.Dynamic8.Moveable{
    Moveable t;
    public TankTime(Moveable t) {
        super();
        this.t = t;
    }
@Override
public void move() {
   long start = System.currentTimeMillis();
   System.out.println("starttime:" + start);
   t.move();
   long end = System.currentTimeMillis();
   System.out.println("time:" + (end-start));
}
}

控制台打印出来的是:

fileName是项目的根路径:D:\data\eclipse-workspace\DesignPatterns
现在我的编译器是com.sun.tools.javac.api.JavacTool
加载我的class文件是class com.DesignPatterns.al.Dynamic8.TankTime
starttime:1541265534048
坦克在移动
time:877

1.4,升级------ 静态的字符串(代理类)------里面的接口变成动态的----里面的方法变成动态的-----非业务代码的抽取(关键)

package com.DesignPatterns.al.Dynamic9;

import java.lang.reflect.Method;

public interface InvocationHandler {
    public void invoke(Object o, Method m);
}
package com.DesignPatterns.al.Dynamic9;

import java.lang.reflect.Method;

public class TimeHandler implements InvocationHandler{
    //这个target在test类中传参的时候就已经传入进来了
    private Object target;



    public TimeHandler(Object target) {
        super();
        this.target = target;
    }



    @Override
    public void invoke(Object o, Method m) {
        //Object o这里的o是代理对象的实例,这里我们没用,并不代表以后我们不用
        System.out.println("代理类是谁呢?"+o.getClass().getName());
        long start = System.currentTimeMillis();
        System.out.println("starttime:" + start);
        try {
            //target是目标类不是代理类。目标类的方法,而不是代理类的方法。这里invoke是反射特有的方法,就是类(target).方法(m)
            m.invoke(target);
        } catch (Exception e) {
            e.printStackTrace();
        }
        long end = System.currentTimeMillis();
        System.out.println("time:" + (end-start));
    }

}
package com.DesignPatterns.al.Dynamic9;

public interface Moveable {
    public void move();
}
package com.DesignPatterns.al.Dynamic9;

import java.util.Random;

public class Tank implements Moveable {

    @Override
    public void move() {
        
        System.out.println("坦克在移动");
        try {
            Thread.sleep(new Random().nextInt(1000));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
    }
}
package com.DesignPatterns.al.Dynamic9;

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.JavaCompiler.CompilationTask;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;

/**
 * jdk中的类名是$Proxy1,可以把下面的TankTime替换掉,其实就是一个名字意义不大,都一样
 * 
 * @author qingruihappy
 * @data 2018年11月2日 上午1:24:30
 * @说明:其实可以这样理解对inferance接口里面的方法实行的是什么样(InvocationHandler)日志,时间,事物。。。等等的代理
 */
public class Proxy {
    public static Object newProxyInstance(Class inferance, InvocationHandler h) throws Exception {
        String methodStr = "";
        Method[] methods = inferance.getMethods();
        String rt = "\r\n";

        for (Method m : methods) {
            methodStr += "@Override" + rt + "public void " + m.getName() + "() {" + rt + "    try {" + rt
                    + "    Method md = " + inferance.getName() + ".class.getMethod(\"" + m.getName() + "\");" + rt
                    + "    h.invoke(this, md);" + rt + "    }catch(Exception e) {e.printStackTrace();}" + rt +

                    "}";
        }
        String src = "package com.DesignPatterns.al.Dynamic9;" + rt + "import java.lang.reflect.Method;" + rt
                + "public class TankTime implements " + inferance.getName() + "{" + rt +

                "    public TankTime(InvocationHandler h) {" + rt + "        super();" + rt + "        this.h = h;" + rt
                + "    }" + rt + "    com.DesignPatterns.al.Dynamic9.InvocationHandler h;" + rt + methodStr + rt +

                "}";
        // 1,生成代理类
        String fileName = System.getProperty("user.dir");
        System.out.println("fileName是项目的根路径:" + fileName);
        String fileNameEnd = fileName + "/src/com/DesignPatterns/al/Dynamic9/TankTime.java";

        File f = new File(fileNameEnd);
        FileWriter fw = new FileWriter(f);
        fw.write(src);
        fw.flush();
        fw.close();

        // 2,开始生成class文件,进行编译
        // 2.1,获取java默认的编译器,说白了就是javac
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        System.out.println("现在我的编译器是" + compiler.getClass().getName());
        StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
        Iterable units = fileMgr.getJavaFileObjects(fileNameEnd);
        CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units);
        // 2.2,生成class文件这里不是重点,我们知道通过上面的代码就可以把java代码生成到指定的目录下面的class文件
        t.call();
        fileMgr.close();

        // 3,把class文件加载到内存中去
        URL[] urls = new URL[] { new URL("file:/" + System.getProperty("user.dir") + "/src") };
        URLClassLoader ul = new URLClassLoader(urls);
        Class c = ul.loadClass("com.DesignPatterns.al.Dynamic9.TankTime");
        System.out.println("加载我的class文件是" + c);

        // 4,从内存中生成对象
        // 4.1,我们调用的是构造方法的参数为Moveable(上面的参数是它的实现也可以的)类型的构造方法
        // 其实在这里我们可以更好的理解反射,传统意义上的代码我们都是现有java实体类,让后我们在去new的方法创造内存对象
        // 但是假如现在我们有了使我们动态的java文件,并且已经生成class文件加载到内存中了
        // 但是由于java文件不是我们自己写的,所以我们现在我们看不到java文件,所以就没有办法new对象了
        // 但是我们在内存中已经有了这个对象了,现在我们要用这个对象,这个时候就需要用发射了

        // 下面的代码就是实现了InvocationHandler接口的构造器反射获取构造器,然后再通过newInstance获取对象,h是接口具体的实现类
        Constructor ctr = c.getConstructor(InvocationHandler.class);
        Object o = ctr.newInstance(h);
        return o;

    }
}
package com.DesignPatterns.al.Dynamic9;

/**
 * 
 * @author qingruihappy
 * @data 2018年11月1日 上午12:39:35
 * @说明:现在模拟配置类随意切换的问题
 * 
 * 现在可以对任意的对象,任意的接口方法,实现任意的代理
 *现在我们写的差不多了,我们来捋一下思路
 *1,我们首先要知道动态代理要干啥呢?
 *动态代理就是类似于面向切面编程,我们在不知不觉中间就把类似于日志,事物,权限,加解密等非业务代码逻辑加到了目标函数的前后。
 *2,我们来看一下,我们平常遇到的jdk动态代理是怎么用的。
 *2.1,写出目标(业务)函数,并且必须让它实现一个接口,
 *2.2,写出非业务代码的接口,并且让子类来实现非业务代码
 *2.3,在非业务代码中又通过发射来调用业务代码,(注意这里面无非就是目标类.目标方法,反射特有的invoke方法)
 *2.4,通过(Proxy)动态的获取变化的代理类
 *3,动态的获取变化的代理才是动态代理的难点,因为,生成代理类的proxy是封装过得,而生成的代理的java代码我们也是看不到的
 *这个也就是难点
 *3.1,至于如何获取请看下面
 *        // 1,首先把不用变的代码以静态string的形式写入进了动态类中
        // 2,接口:把动态变化的东西比如说接口,通过传入的接口来获取,
        // 3,方法:因为接口中就有了实现类的方法,所以通过接口也能获取,其中的方法,
        // 4,回调:把非业务代码的实现了传入到Proxy中来,当调用第三步已经确定了的方法额时候,就明确的调用在非业务代码中的invoke方法
        //4.1,目标:在非业务代码的中间反射调用业务代码
        // 5,写出:写完了之后生成java文件
        // 6,编译:编译生成class文件
        // 7,内存:加载到内存中来
        // 8,反射:从内从中反射生成对象。(注意这这里传入了tank确定了是tank对象,所以当下面调用)
        //9,对象:获取代理对象
 */
public class Test {
    public static void main(String[] args) throws Exception {
        Tank t = new Tank();
        InvocationHandler h = new TimeHandler(t);
        Moveable m = (Moveable) Proxy.newProxyInstance(Moveable.class, h);
        m.move();
    }

}

生成的代理类是:

package com.DesignPatterns.al.Dynamic9;
import java.lang.reflect.Method;
public class TankTime implements com.DesignPatterns.al.Dynamic9.Moveable{
    public TankTime(InvocationHandler h) {
        super();
        this.h = h;
    }
    com.DesignPatterns.al.Dynamic9.InvocationHandler h;
@Override
public void move() {
    try {
    Method md = com.DesignPatterns.al.Dynamic9.Moveable.class.getMethod("move");
    h.invoke(this, md);
    }catch(Exception e) {e.printStackTrace();}
}
}

控制台

fileName是项目的根路径:D:\data\eclipse-workspace\DesignPatterns
现在我的编译器是com.sun.tools.javac.api.JavacTool
加载我的class文件是class com.DesignPatterns.al.Dynamic9.TankTime
代理类是谁呢?com.DesignPatterns.al.Dynamic9.TankTime
starttime:1541265881417
坦克在移动
time:698

1.5,升级------ 静态的字符串(代理类)------里面的接口变成动态的----里面的方法变成动态的-----非业务代码的抽取----用我们自己写的动态代理测试在代码前后加事物

package com.DesignPatterns.al.Dynamic91;

import java.lang.reflect.Method;

public interface InvocationHandler {
    public void invoke(Object o, Method m);
}
package com.DesignPatterns.al.Dynamic91;

import java.lang.reflect.Method;

public class TransactionHandler implements InvocationHandler {
    
    private Object target;
    
    public TransactionHandler(Object target) {
        super();
        this.target = target;
    }

    @Override
    public void invoke(Object o, Method m) {
        System.out.println("Transaction Start");
        try {
            m.invoke(target);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("Transaction Commit");
    }

}
package com.DesignPatterns.al.Dynamic91;

public interface UserMgr {
    void addUser();
}
package com.DesignPatterns.al.Dynamic91;

public class UserMgrImpl implements UserMgr {

    @Override
    public void addUser() {
        System.out.println("1: 插入记录到user表");
        System.out.println("2: 做日志在另外一张表");
    }
    
}
package com.DesignPatterns.al.Dynamic91;

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.JavaCompiler.CompilationTask;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
/**
 * jdk中的类名是$Proxy1,可以把下面的TankTime替换掉,其实就是一个名字意义不大,都一样
 * @author qingruihappy
 * @data   2018年11月2日 上午1:24:30
 * @说明:
 */
public class Proxy {
public static Object newProxyInstance(Class inferance,InvocationHandler h) throws Exception {
    String methodStr = "";
    Method[] methods = inferance.getMethods();
    String rt="\r\n";
    
    for(Method m : methods) {
        methodStr += "@Override" + rt + 
                     "public void " + m.getName() + "() {" + rt +
                     "    try {" + rt +
                     "    Method md = " + inferance.getName() + ".class.getMethod(\"" + m.getName() + "\");" + rt +
                     "    h.invoke(this, md);" + rt +
                     "    }catch(Exception e) {e.printStackTrace();}" + rt +
                    
                     "}";
    }
    String src=
            "package com.DesignPatterns.al.Dynamic91;"+rt+
            "import java.lang.reflect.Method;" + rt +
            "public class TankTime implements "+inferance.getName()+ "{"+rt+

            "    public TankTime(InvocationHandler h) {"+rt+
            "        super();"+rt+
            "        this.h = h;"+rt+
            "    }"+rt+
            "    com.DesignPatterns.al.Dynamic91.InvocationHandler h;" + rt +
            methodStr +rt+

            "}";
    //1,生成代理类
    String fileName = System.getProperty("user.dir");
    System.out.println("fileName是项目的根路径:"+fileName);
    String fileNameEnd=fileName+"/src/com/DesignPatterns/al/Dynamic91/TankTime.java";
    
    File f=new File(fileNameEnd);
    FileWriter fw=new FileWriter(f);
    fw.write(src);
    fw.flush();
    fw.close();
    
    //2,开始生成class文件,进行编译
    //2.1,获取java默认的编译器,说白了就是javac
    JavaCompiler compiler=ToolProvider.getSystemJavaCompiler();
    System.out.println("现在我的编译器是"+compiler.getClass().getName());
    StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
    Iterable units = fileMgr.getJavaFileObjects(fileNameEnd);
    CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units);
    //2.2,生成class文件这里不是重点,我们知道通过上面的代码就可以把java代码生成到指定的目录下面的class文件
    t.call();
    fileMgr.close();
    
    
    //3,把class文件加载到内存中去
    URL[] urls = new URL[] {new URL("file:/" + System.getProperty("user.dir") +"/src")};
    URLClassLoader ul = new URLClassLoader(urls);
    Class c = ul.loadClass("com.DesignPatterns.al.Dynamic91.TankTime");
    System.out.println("加载我的class文件是"+c);
    
    //4,从内存中生成对象
    //4.1,我们调用的是构造方法的参数为Moveable(上面的参数是它的实现也可以的)类型的构造方法
    Constructor ctr = c.getConstructor(InvocationHandler.class);
    Object o = ctr.newInstance(h);
    return o;
    
}
}
package com.DesignPatterns.al.Dynamic91;

public class Client {
    public static void main(String[] args) throws Exception {
        UserMgr mgr = new UserMgrImpl();
        InvocationHandler h = new TransactionHandler(mgr);
        UserMgr u = (UserMgr)Proxy.newProxyInstance(UserMgr.class,h);
        u.addUser();
    }
}

动态生成的代理类是:

package com.DesignPatterns.al.Dynamic91;
import java.lang.reflect.Method;
public class TankTime implements com.DesignPatterns.al.Dynamic91.UserMgr{
    public TankTime(InvocationHandler h) {
        super();
        this.h = h;
    }
    com.DesignPatterns.al.Dynamic91.InvocationHandler h;
@Override
public void addUser() {
    try {
    Method md = com.DesignPatterns.al.Dynamic91.UserMgr.class.getMethod("addUser");
    h.invoke(this, md);
    }catch(Exception e) {e.printStackTrace();}
}
}

控制台:

fileName是项目的根路径:D:\data\eclipse-workspace\DesignPatterns
现在我的编译器是com.sun.tools.javac.api.JavacTool
加载我的class文件是class com.DesignPatterns.al.Dynamic91.TankTime
Transaction Start
1: 插入记录到user表
2: 做日志在另外一张表
Transaction Commit

 二,我们来看jdk自带的动态代理

案例一:

package com.DesignPatterns.al.Dynamic92;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * @Author: feiweiwei
 * @Description:
 * @Created Date: 09:50 17/9/22.
 * @Modify by:
 */
public class DynamicProxyAOP implements InvocationHandler {
    private Object subject;

    public DynamicProxyAOP(Object subject) {
        this.subject = subject;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //在执行真实subject执行的方法
        System.out.println("before do something");
        //执行真实subject方法    //方法,对象,参数都有了就可以执行了。
        Object rtn = method.invoke(subject, args);
        //在执行结束后再执行的方法
        System.out.println("after do something");

        return rtn;
    }
}
package com.DesignPatterns.al.Dynamic92;

/**
 * @Author: feiweiwei
 * @Description:
 * @Created Date: 09:47 17/9/22.
 * @Modify by:
 */
public interface Subject {
    public String doSomething(String name);
}
package com.DesignPatterns.al.Dynamic92;

/**
 * @Author: feiweiwei
 * @Description:
 * @Created Date: 09:49 17/9/22.
 * @Modify by:
 */
public class RealSubject implements Subject {
    @Override
    public String doSomething(String name) {
        System.out.println(name + " do something!");
        return name + " do something!";
    }
}
package com.DesignPatterns.al.Dynamic92;

/**
 * @Author: feiweiwei
 * @Description:
 * @Created Date: 09:56 17/9/22.
 * @Modify by:
 */
public class RealSubject2 implements Subject {
    @Override
    public String doSomething(String name) {
        System.out.println(name + " do something2!");
        return name + "do something2!";
    }
}
package com.DesignPatterns.al.Dynamic92;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

import sun.misc.ProxyGenerator;

/**
 * @Author: feiweiwei
 * @Description:
 * @Created Date: 10:00 17/9/22.
 * @Modify by:
 */
public class DynamicProxyMain {

    public static void main(String[] args) throws IOException {
        // 目标类1
        Subject realSubject = new RealSubject();
        // 处理器类
        InvocationHandler handler = new DynamicProxyAOP(realSubject);

        Subject subject = (Subject) Proxy.newProxyInstance(realSubject.getClass().getClassLoader(), realSubject
                .getClass().getInterfaces(), handler);
        subject.doSomething("tester ");
        /*
         * // 目标类2 Subject realSubject2 = new RealSubject2();
         */

        // 多态指向DynamicProxyAOP的接口类InvocationHandler

        // 多态指向DynamicProxyAOP的接口类InvocationHandler
        /* InvocationHandler handler2 = new DynamicProxyAOP(realSubject2); */

        /*
         * Subject subject2 = (Subject) Proxy.newProxyInstance(realSubject2.getClass().getClassLoader(), realSubject2
         * .getClass().getInterfaces(), handler2);
         */

        // subject2.doSomething("tester ");

        String path = "E:/$Proxy0.class";
        byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", RealSubject.class.getInterfaces());
        FileOutputStream out = null;
        try {
            out = new FileOutputStream(path);
            out.write(classFile);
            out.flush();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

}

生成的代理类

import com.DesignPatterns.al.Dynamic92.Subject;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy0
  extends Proxy
  implements Subject
{
  private static Method m1;
  private static Method m3;
  private static Method m0;
  private static Method m2;
  
  public $Proxy0(InvocationHandler paramInvocationHandler)
    throws 
  {
    super(paramInvocationHandler);
  }
  
  public final boolean equals(Object paramObject)
    throws 
  {
    try
    {
      return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }
  
  public final String doSomething(String paramString)
    throws 
  {
    try
    {
      return (String)this.h.invoke(this, m3, new Object[] { paramString });
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }
  
  public final int hashCode()
    throws 
  {
    try
    {
      return ((Integer)this.h.invoke(this, m0, null)).intValue();
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }
  
  public final String toString()
    throws 
  {
    try
    {
      return (String)this.h.invoke(this, m2, null);
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }
  
  static
  {
    try
    {
      m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
      m3 = Class.forName("com.DesignPatterns.al.Dynamic92.Subject").getMethod("doSomething", new Class[] { Class.forName("java.lang.String") });
      m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
      m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
      return;
    }
    catch (NoSuchMethodException localNoSuchMethodException)
    {
      throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
    }
    catch (ClassNotFoundException localClassNotFoundException)
    {
      throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
    }
  }
}

控制台:

before do something
tester  do something!
after do something

是不是很像啊。

有兴趣的可以研究一下它的源码

下面是源码的链接文档:

https://blog.csdn.net/sum__mer/article/details/53179662
https://www.jianshu.com/p/269afd0a52e6
https://www.jianshu.com/p/471c80a7e831

 kk

猜你喜欢

转载自www.cnblogs.com/qingruihappy/p/9902959.html