设计模式之代理 proxy

AOP面向切面编程是动态代理的应用。

首先 什么是聚合:聚合就是在一个类A中有使用另一个B类作为成员变量,那在A类中就可以调用B类中的所有成员变量以及成员函数(而继承只能调用父类的成员函数)。
静态代理:
静态代理就是我们明确的知道想要实现的是什么代理,我们就可以将该代理继承自某一个接口。
那么我们可以使得LogProxy和TimeProxy等被代理的类都继承自一个接口,实现代理的生成。
//当两个代理都实现的是Movable接口时,我们就可以实现一个个代理之间的互相嵌套,并且可以直接在我们的调用类中实现改变。灵活性较高。

 public class TankLogProxy implements Movable {
    Movable t;

    public TankLogProxy(Movable t) {
        this.t = t;
    }

    @Override
    public void move() {
        System.out.println("Tank is started....");
        t.move();
        System.out.println("Tank is stopped....");
    }
}

在这里插入图片描述
在这里插入图片描述
将时间和日志代理顺序交换。
在这里插入图片描述
在这里插入图片描述

动态代理就是我们是不知道具体有哪些类需要实现代理。
Spring是直接使用JDK中的动态代理进行生成的

我们新建一个Test类,将需要生成的信息指定文件夹的位置,运行下面程序,即可在对应的位置生成代理类文件。

public class Test1 {
    public static void main(String[] args) throws Exception{
        String rt = "\r\n";
        String src =
                "package martina.proxy;" +  rt +
                        "public class **TankTimeProxy** implements **Moveable** {" + rt +
                        "    public TankTimeProxy(Moveable t) {" + rt +
                        "        super();" + rt +
                        "        this.t = t;" + rt +
                        "    }" + rt +

                        "    Moveable t;" + rt +

                        "    @Override" + rt +
                        "    public void move() {" + rt +
                        "        long start = System.currentTimeMillis();" + rt +
                        "        System.out.println(\"starttime:\" + start);" + rt +
                        "        t.move();" + rt +
                        "        long end = System.currentTimeMillis();" + rt +
                        "        System.out.println(\"time:\" + (end-start));" + rt +
                        "    }" + rt +
                        "}";
        String fileName = System.getProperty("user.dir")
                + "/src/martina/proxy/TankTimeProxy.java";
        File f = new File(fileName);
        FileWriter fw = new FileWriter(f);
        fw.write(src);
        fw.flush();
        fw.close();
        }
 }

在这里插入图片描述
生成java文件之后,我们还需要将其编译并载入内存。

//compile
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
        Iterable units = fileMgr.getJavaFileObjects(fileName);
        //根据fileMgr和要编译的内容 拿到编译任务
        JavaCompiler.CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units);
        t.call();
        fileMgr.close();

         // 将硬盘上的java文件加载入内存
        //load into memory and create an instance
        //之所以使用URLClassLoader的原因是我们生成的编译文件(class文件)是在源码的文件夹下的,而不是在bin的classPatch下。
        //根据目录 找到class文件
        URL[] urls = new URL[] {new URL("file:/" + System.getProperty("user.dir") +"/src")};
        URLClassLoader ul = new URLClassLoader(urls);
        Class c = ul.loadClass("martina.proxy.TankTimeProxy");
        System.out.println(c);

        //拿到Movable的构造器从而实例化出Movable对象,然后调用
        Constructor ctr = c.getConstructor(Movable.class);
        Movable m = (Movable)ctr.newInstance(new Tank());
        m.move();

运行main函数就可以得到输出结果:
在这里插入图片描述
那么其实我们也可以通过传参的方式将要实现的接口传入,(上文实现的是接口是固定的Movable接口)
那么我们就需要将src修改一下,将Movable修改为形参传过来的接口名,并且将传来的接口中所有的方法都实现。
当然,实现了接口的传递还远远不够,在接口的方法中该怎么实现处理我们不能直接放在src中,所以我们还需要传递进来一个Handler,在实现Handler的各个接口中定义不同的操作,在src中调用即可。
所以我们需要定义一个InnovationHandler(),其中定义一个invoke方法即可。
未编译的类如果 没有导入reflect类 ,可能编译不了?

猜你喜欢

转载自blog.csdn.net/mulinsen77/article/details/84308964