项目开发中,用户的需求经常是变动的。面对用户多变的需求,系统的灵活性是很重要的。
在原来的系统上,想计算方法执行的时间,作为参评系统性能的数据。
方法一:最直观简单做法就是,在每个方法中都添加计算执行时间的逻辑代码。
/**
* 移动
*/
public interface Moveable {
public void move();
}
/**
* 坦克
*/
public class Tank implements Moveable {
@Override
public void move(){
try {
System.out.println("tank--moving...");
Thread.sleep(new Random().nextInt(10000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* 坦克——计算了方法执行的时间
*/
public class Tank implements Moveable {
@Override
public void move(){
//方法执行前,计算系统时间
Long start = System.currentTimeMillis();
try {
System.out.println("tank--moving...");
Thread.sleep(new Random().nextInt(10000));
} catch (Exception e) {
e.printStackTrace();
}
//方法执行后,计算系统时间
Long end = System.currentTimeMillis();
System.out.println("time=" + (end-start));
}
}
这样的做法明显违背了对修改关闭,扩展开放的原则。
方法二:利用继承重写方法
/**
* Tank2继承Tank
*
*/
public class Tank2 extends Tank {
/**
* 重写move方法,加入计算执行时间代码
*/
public void move(){
Long start = System.currentTimeMillis();
super.move();
Long end = System.currentTimeMillis();
System.out.println("time=" + (start-end));
}
}
这样的做法同样有很大的问题,第一,Tank2继承了Tank就不能再继承其它的类。第二,Tank类一旦做了修改,子类必定跟着改。
方法三:另外添加Moveable接口的实现,利用组合。
/**
* Tank3 实现了Moveable接口
*/
public class Tank3 implements Moveable {
Moveable m;
public Tank3(Moveable m){
this.m = m;
}
/**
* 借助原来的实现,再加工
*/
@Override
public void move() {
Long start = System.currentTimeMillis();
m.move();
Long end = System.currentTimeMillis();
System.out.println("time=" + (start - end));
}
}
这其实就是一个代理模式——静态代理,Tank3代理了Tank,在Tank原有的move方法上添加了自己的逻辑处理,大大增加了系统的灵活性,但是假设一个系统500个类500个方法都需要这样做,那就会添加500个类,工作量还是很大的。
方法四:动态代理。动态代理,其实就是不针对具体的类产生代理类,系统执行期间,动态的为某个类产生代理。
动态代理的实现:
1.借助jdk提供的API:被代理的类必须实现接口,JDK1.5以上才支持动态代理。
2.CGLib:不要求有接口,可以为任意类提供代理
这里介绍的是JDK的动态代理,一步一步的分析:
(1):动态的产生代理类:
/**
* 代理类
* 动态生成代理对象
*/
public class Proxy{
public static Object newInstance() throws Exception{
String rt = "\r\n";
//将代理类拼成字符串
String src =
"package com.tgb.proxy;" + rt +
"public class $Proxy1 implements Moveable{" + rt +
" Moveable m;" + rt +
" public $Proxy1(Moveable m){" + rt +
" this.m = m;" + rt +
" }" + rt +
" public void move(){" + rt +
" Long start = System.currentTimeMillis();" + rt +
" m.move();" + rt +
" Long end = System.currentTimeMillis();" + rt +
" System.out.println(\"time=\" + (start-end));" + rt +
" }" + rt +
"}" ;
//将src写到文件中,生成$Proxy1.java源文件
String filename = "E:/java/com/tgb/proxy/$Proxy1.java";
File f = new File(filename);
FileWriter fw = new FileWriter(f);
fw.write(src);
fw.flush();
fw.close();
//-----利用JDK的编译器,编译文件——就相当于静态编写了代理类,经过了编译----
//编译器
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
//文件管理器
StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null,null);
//通过文件管理器,获得需要编译的文件
Iterable unites = fileMgr.getJavaFileObjects(filename);
//编译器获得编译任务
CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, unites);
//进行编译
t.call();
//关闭文件管理器
fileMgr.close();
//-----利用JDK的编译器,编译文件——就相当于静态编写了代理类,经过了编译----
//读取编译好的文件,放入到内存中
URL[] urls = new URL[] {new URL("file:/" + "E:/java/")};
URLClassLoader ul = new URLClassLoader(urls);
Class c = ul.loadClass("com.tgb.proxy.$Proxy1");
//利用带参数的构造器,生成代理对象,并返回
Constructor ctr = c.getConstructor(Moveable.class);
Moveable m = (Moveable)ctr.newInstance(new Tank());
return m;
}
}
通过代理类(Proxy)的newInstance方法,经过动态编写代理文件,经过编译,再加载的内存,生成真正的代理对象并返回。完成动态代理。
src这个字符串是固定的,所以需要做如下调整:1.动态的代理接口,方法 2.对原来的方法不仅仅是计算执行时间的处理。
(2)调整1:动态的代理接口、方法
/**
* 代理类
* 调整1:动态的代理接口、方法
*/
public class Proxy{
/**
* 将要被代理的接口,作为参数传入
* @param inter
* @return
* @throws Exception
*/
public static Object newInstance(Class inter) throws Exception{
String rt = "\r\n";
//代理类通过接口,动态生成代理方法
String methodStr = "";
Method[] methods = inter.getMethods();
for(Method m : methods){
methodStr +=" @Override" + rt +
" public void " + m.getName() + "(){" + rt +
" Long start = System.currentTimeMillis();" + rt +
" t." + m.getName() + "();" + rt +
" Long end = System.currentTimeMillis();" + rt +
" System.out.println(\"time=\" + (start-end));" + rt +
" }";
}
//代理类的实现接口有传入的参数决定
String src =
"package com.tgb.proxy;" + rt +
"public class $Proxy1 implements " + inter.getName() + "{" + rt +
" Moveable t;" + rt +
" public $Proxy1(Moveable t){" + rt +
" this.t = t;" + rt +
" }" + rt +
methodStr + rt +
"}" ;
//将代码写入文件,生成源文件
String filename = "E:/java/com/tgb/proxy/$Proxy1.java";
File f = new File(filename);
FileWriter fw = new FileWriter(f);
fw.write(src);
fw.flush();
fw.close();
//利用jdk将源文件进行编译
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
Iterable units = fileMgr.getJavaFileObjects(filename);
CompilationTask ct = compiler.getTask(null, fileMgr, null, null, null, units);
ct.call();
fileMgr.close();
//将编译的文件加载到内存中
URL[] urls = new URL[]{new URL("file:/" + "E:/java/")};
URLClassLoader ul = new URLClassLoader(urls);
Class c = ul.loadClass("com.tgb.proxy.$Proxy1");
Constructor ctr = c.getConstructor(Moveable.class);
Object o = (Object)ctr.newInstance(new Tank());
return o;
}
}
这样,基本实现了通过任意的接口,生成对应的代理对象,并返回。
(3):调整2:被代理的方法,不仅仅是做计算时间的处理
/**
* 方法调用的处理器
*/
public interface InvocationHandler {
public void invoke(Object o,Method m);
}
/**
* 对被代理对象,添加 计算方法的执行时间 处理
*/
public class TimeHandler implements InvocationHandler{
//被代理的对象
private Object target;
public TimeHandler(Object target) {
this.target = target;
}
/**
* 处理被代理对象的方法:添加 计算方法执行时间 处理
*/
@Override
public void invoke(Object o,Method m){
try {
Long start = System.currentTimeMillis();
m.invoke(target, new Object[]{});
Long end = System.currentTimeMillis();
System.out.println("time=" + (start-end));
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 代理类
*/
public class Proxy{
/**
* @param inter 代理类实现的接口
* @param h 代理类方法需要做的处理对象
* @return
* @throws Exception
*/
public static Object newInstance(Class inter,InvocationHandler h) throws Exception{
String rt = "\r\n";
//通过接口参数,代理类动态生成代理方法
//代理方法中,通过方法处理参数,动态调用方法的处理类
String methodStr = "";
Method[] methods = inter.getMethods();
for(Method m : methods){
methodStr +=" @Override" + rt +
" public void " + m.getName() + "(){" + rt +
" try {" + rt +
" Method md = " + inter.getName() + ".class.getMethod(\"" + m.getName() + "\");" + rt +
" h.invoke(this, md);" + rt +
" }c atch(Exception e){" + rt +
" e.printStackTrace();" + rt +
" }" + rt +
" }";
}
//通过接口参数,代理类动态决定实现接口
String src =
"package com.tgb.proxy;" + rt +
"import java.lang.reflect.Method;" + rt +
"public class $Proxy1 implements " + inter.getName() + "{" + rt +
" com.tgb.proxy.InvocationHandler h;" + rt +
" public $Proxy1(InvocationHandler h){" + rt +
" this.h = h;" + rt +
" }" + rt +
methodStr + rt +
"}";
String filename = "E:/java/com/tgb/proxy/$Proxy1.java";
File file = new File(filename);
FileWriter fw = new FileWriter(file);
fw.write(src);
fw.flush();
fw.close();
//编译
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
Iterable units = fileMgr.getJavaFileObjects(filename);
CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units);
t.call();
fileMgr.close();
//加载到内存
URL[] urls = new URL[]{new URL("file:/" + "E:/java/")};
URLClassLoader ul = new URLClassLoader(urls);
Class c = ul.loadClass("com.tgb.proxy.$Proxy1");
//生成代理对象,并返回
Constructor ctr = c.getConstructor(InvocationHandler.class);
Object o = (Object)ctr.newInstance(h);
return o;
}
}
客户端:
public class Client {
public static void main(String[] args) throws Exception{
Tank t = new Tank();
InvocationHandler h = new TimeHandler(t);
Moveable m = (Moveable)Proxy.newInstance(Moveable.class,h);
m.move();
}
}
这样,完全实现了动态生成代理对象,并动态添加对方法的处理。