设计模式(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

原文地址:https://www.cnblogs.com/qingruihappy/p/9902959.html

时间: 2024-11-09 02:39:29

设计模式(23)-----代理设计模式-----动态代理的相关文章

常用设计模式之代理(动态代理)

常用设计模式之代理(动态代理) UML Code1 1 interface Subject{void doSth();} 2 class RealSubject implements Subject{ 3 public void doSth(){ 4 System.out.println("RealSubject doSth...."); 5 } 6 } 7 class ProxyHandler implements InvocationHandler 8 { 9 private Ob

设计模式之--静态代理及动态代理模式

原文链接:http://www.studyshare.cn/blog/details/1181/0 一.概念 代理模式:为其他对象提供一种代理类用以控制对这个对象的访问.在某些情况下,一个对象不适合或者不能直接引用 另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用,代理模式属于23中设计模式中的结构型设计模式. 代理模式可分为静态代理和动态代理. 代理结构如下图: 例如:客户(客户类)—>代购(代理类)—>海外下单(海外订单类),普通客户无法直接去海外下单,则向代购公司 下单,代

JAVA设计模式——代理(动态代理)

传送门:JAVA设计模式--代理(静态代理) 序言: 在学习Spring的时候,我们知道Spring主要有两大思想,一个是IoC,另一个就是AOP,对于IoC,依赖注入就不用多说了,而对于Spring的核心AOP来说,我们不但要知道怎么通过AOP来满足的我们的功能,我们更需要学习的是其底层是怎么样的一个原理,而AOP的原理就是java的动态代理机制,所以本篇随笔就是对java的动态机制进行一个回顾. 动态代理模式主要由四个元素共同构成: 1. 接口,接口中的方法是要真正去实现的 2. 被代理类,

Java设计模式学习06——静态代理与动态代理(转)

原地址:http://blog.csdn.net/xu__cg/article/details/52970885 一.代理模式 为某个对象提供一个代理,从而控制这个代理的访问.代理类和委托类具有共同的父类或父接口,这样在任何使用委托类对象的地方都可以使用代理类对象替代.代理类负责请求的预处理.过滤.将请求分配给委托类处理.以及委托类处理完请求的后续处理. 二.代理模式结构 UML类图: 由上图代理模式的结构为: 抽象角色: 真实对象和代理对象的共同接口. 代理角色: 代理对象角色内部含有对真实对

设计模式--5.4 代理模式-动态代理

1.动态代理 (1)动态代理,是实现阶段不关心代理谁,而在运行阶段才指定代理哪一个对象.相对的说,上面两种 普通代理和强制代理,都是通过写代理类来获取代理,这种是静态代理. (2)区别:静态代理,需要写代理类,在代理之前要知道我代理的是哪个类: (3)类图 2.代码 接口类 package com.design.代理模式.动态代理; public interface IGamePlayer { void login(String username , String pwd); void kill

【设计模式】代理模式:静态代理,动态代理,spring aop

代理模式分为静态代理和动态代理.我们拿链家来举例子,我们本人是真实的对象,有真实的业务需求:需要去找房子:链家是中介,是代理类,他来帮我执行找房子的这个操作. 静态代理: 1.实现一个接口 public interface SearchHome { public void search(); } 2.构建实现接口的委托类 public class Master implements SearchHome { @Override public void search() { System.out.

静态代理与动态代理

记得刚接触代理,是在大话设计模式,最近在技术总结和学些java的过程又接触到静态代理和动态代理,尤其是动态代理,在学习AOP时,用到了动态代理,下面我用一些例子来对静态代理和动态代理做个总结. 其实用了代理之后最大的好处就是隐藏了真实类(委托类),这样更加安全,而静态代理和动态代理最大的区别就是,静态代理的代理类是程序员自己写的,在程序运行之前就已经存在的,而动态代理则是在程序运行时动态生成的,而且因为动态代理更加灵活,也常被应用. 首先先用一张UML图来理解一下代理是怎么回事? 其实静态代理就

深入浅出java静态代理和动态代理

首先介绍一下,什么是代理: 代理模式,是常用的设计模式.特征是,代理类与委托类有相同的接口,代理类主要负责为委托类预处理消息.过滤消息.把消息转发给委托类.以及事后处理消息. 代理类和委托类,存在着关联关系.代理类的对象本身并不真正实现服务,知识通过调用委托类的对象的相关方法. 代理类可以分为两种:静态代理和动态代理. 静态代理: 代理类是由程序员创建,或由工具生成的代码 编译成的.在程序运行前,代理类的 *.class文件已经存在了.直接就可以运行 . 动态代理: 动态代理的代理类.没有直接由

静态代理与动态代理的简单使用

代理模式(Proxy Pattern)是一种比较常见的设计模式,在很多场合都会被用到. 所谓代理指的是让其他的类代替完成一些任务(执行一些方法等),在软件开发中代理模式具有非常重要的作用,面向切面编程(AOP)便是基于代理模式运作的编程范式. 下面介绍一下其中的静态代理与动态代理,基于Java语言. 静态代理: 首先由一个HelloWorld接口,其中有一个方法,print public interface HelloWorld { void print(); } 接下来是实现了HelloWor

Java:静态代理and动态代理

代理模式是常用的设计模式,其特征是代理类与委托类具有相同的接口,在具体实现上,有静态代理和动态代理之分.代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务,也就是说代理类主要负责为委托类预处理消息.过滤消息.把消息转发给委托类,以及事后处理消息等. 静态代理和动态代理的一个显著区别: 静态代理:由程序员创建或特定工具自动生成源代码,再对其编译.在程序运行前,代理类的.class文件就