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类 ,可能编译不了?