------- android培训、java培训、期待与您交流!----------
如果要求计算ArrayList的add方法的运行时间,该如何做呢?
通常的做法就是获得运行前的时间和运行后的时间,然后打印出两者的差值。
但是,为了提高这段代码的复用性,我们可以使用代理。
代理其实就是为一些已存在的多个具有相同接口的目标类的各个方法增加一些系统功能。
编写一个与目标类具有相同接口的代理类,代理类的每个方法调用目标类的相同方法,并在调用方法时加上额外的增加的方法。
目标类: 代理类:
class X{ Xproxy{
void sayHello()
{ void sayHello(){
syso:Hello; startTime
} X. sayHello();
} endTime;}
}
代理类的优点:
如果采用工厂模式和配置文件的方式进行管理,则不需要修改客户端程序,在配置文件中配置是使用目标类、还是代理类,这样以后很容易切换,譬如,想要日志功能时就配置代理类,否则配置目标类,这样,增加系统功能很容易,以后运行一段时间后,又想去掉系统功能也很容易。
举个例子,介绍一下代理的使用方法
写一个ArrayList类的代理,实现和ArrayList中完全相同的功能,并可以计算每个方法运行的时间。
1.使用硬编码形式:
实例化一个代理类对象其实使用的就是代理类Proxy的一个静态方法
static Object | newProxyInstance(ClassLoader loader,Class<?>[] interfaces, InvocationHandler h) 返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。 |
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;
public class ProxyDemo {
public static void main(String[] args) {
//首先要有一个目标类对象
final ArrayList target = new ArrayList();
//实例化一个指定目标类代理对象,使用newProxyInstance,他又三个参数,点三个参数使用了匿名类
List proxy = (List)Proxy.newProxyInstance(
List.class.getClassLoader(), //目标类使用的类加载器,代理类要使用这个指定的加载器
ArrayList.class.getInterfaces(),//目标类实现的接口,代理类要使用这个指定的接口
new InvocationHandler() //代理类要使用的方法,就是新建一个InvocationHandler对象,
// 要覆写invoke方法
{
@Override
/*
* 在使用一个方法p.add("AD")时,要有三个要素,对象p,方法add,参数"AD",正好对应invoke
* 方法的三个参数 invoke(Object proxy, Method method, Object[] args)
* */
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
//增加一个功能方法
long startTime = System.currentTimeMillis();
Thread.sleep(100);
//使用目标类原有的方法,要用一个Object类型接受方法返回的参数
Object reVal = method.invoke(target, args);
//增加一个功能方法
long endTime = System.currentTimeMillis();
System.out.println( method.getName()+"程序使用时间为"+(endTime - startTime)+"ms");
//注意不要忘记返回给proxy.add("aaaa"),ArrayList.add返回的值
return reVal;
}
} );
proxy.add("aaaa");
proxy.add("bbbb");
proxy.add("cccc");
System.out.println(proxy.toString());
}
}
打印结果:
add程序使用时间为101ms
add程序使用时间为100ms
add程序使用时间为100ms
toString程序使用时间为100ms
[aaaa, bbbb, cccc]
根据上面的代码讲述一下代理类的工作原理:
1.我们(客户端)调用代理,再创建一个代理对象的时候,代理的构造方法接受了一个invocationHandler对象
2.客户端调用代理方法的时候,这些请求会发送给InvocationHandler的invoke方法
3.在invike方法内调用与目标类同名的方法,并执行了我们额外增加的方法。
需要注意的是有三个方法时直接调用目标类的方法的而不访问invoke:
toString,hashCode,equals
2.提高扩展性
为了提高上述代码的扩展性,我们要把所要使用的目标类和所要增加的功能代码抽取出来,这样的话代理类的使用
范围就扩大了很多。
我们可以把上述代码变成一个方法,方法的参数应该包括2个参数,目标类和添加功能的类(也可以叫做插入通告),
添加功能的类实现了Advice接口。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;
public class abStract_ProxyDemo {
public static void main(String[] args)throws Exception
{
final ArrayList target = new ArrayList();
List p =(List) getProxy(target, new myAdvice());
p.add("sss");
//ArrayList p1 = (ArrayList)getProxy(target, new myAdvice());这样会错误
// xception in thread "main" java.lang.ClassCastException: com.sun.proxy.
// $Proxy0 cannot be cast to java.util.ArrayList
}
/*
* 因为要向里面添加功能方法,只好向里面添加一个功能方法都实现的接口对象
* */
public static Object getProxy(final Object target, final Advice advice) throws Exception
{
Object p = Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
//抽象化的添加功能
advice.startRun();
//调用目标类的功能
Object reVal = method.invoke(target, args);
advice.endRun(method);
return reVal;
}
});
return p;
}
}
import java.lang.reflect.Method;
public interface Advice
{
void startRun();
void endRun(Method method);
}
import java.lang.reflect.Method;
public class myAdvice implements Advice {
long startTime ;
@Override
public void startRun() {
startTime = System.currentTimeMillis();
}
@Override
//method就是调用的目标类的那个方法
public void endRun(Method method) {
long endTime = System.currentTimeMillis();
System.out.println(method.getName()+"运行了"+ (endTime - startTime)+ "ms");
}</p><p>}
</p>
import java.lang.reflect.Method;
public interface Advice
{
void startRun();
void endRun(Method method);
}