代理
当我们在用别人写的类的时候,如果想在某些方法上附加一些功能。比如在一个方法上添加这个方法运行时间的功能。我们无法更改别人的源代码,这时就用到了代理。
代理的原理如下图所示。
target是原类;
$Proxy1是这个类的代理类,里面具有同target相同的方法;
InvocationHandler是代理类的构造方法需要传入的一个参数对象;
client是客户,它来调用代理类。
当客户实例化代理类时,需要传入一个InvocationHandler对象。当客户调用代理类对象的某个方法时,这个方法中调用InvocationHandler中的invoke方法,invoke方法中再去调用target的对应方法,返回值原路返回。
在InvocationHandler的invoke方法中,我们就可以加入自己想要的附加功能了。
动态代理类:
第一种方式实例化代理类
我们只要指定代理类要实现的接口和类加载器,JVM就会自动给我们生成一个动态代理类。
//创建一个动态类,实现Collection接口
Class clazzProxy =
Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);
然后调用构造方法实例化这个代理类,需要传入InvocationHandler对象,用匿名内部类的方式实现。
下面的例子中加入了方法运行时间附加功能。
Collection colProxy =
(Collection)clazzProxy.getConstructor(InvocationHandler.class).newInstance(new InvocationHandler(){
List target = new ArrayList();
@Override
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
long begintime = System.currentTimeMillis();
Object obj = method.invoke(target, args); //调用target的对应方法
long endtime = System.currentTimeMillis();
System.out.println(method.getName()+"----"+(endtime-begintime));
return obj;
}
});
用代理类对象调用方法:
colProxy.add("abc");
colProxy.add("haha");
System.out.println(colProxy.size());
第二种方式实例化代理类
创建动态类及其实例对象我们需要三个参数:
1. 指定类加载器
2. 需要实现的接口(可以是多个)
3. InvocationHandler对象
所以,java中还有一种实例化代理类的方式
Collection colProxy = (Collection)Proxy.newProxyInstance(
//类加载器参数
Collection.class.getClassLoader(),
//需要实现的接口
new Class[]{Collection.class},
//InvocationHandler对象
new InvocationHandler(){
List target = new ArrayList();
@Override
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
long begintime = System.currentTimeMillis();
Object obj = method.invoke(target, args);
long endtime = System.currentTimeMillis();
System.out.println(method.getName()+"----"+(endtime-begintime));
return obj;
}
});
InvocationHandler有三个传入参数:
1. Object proxy 这是使用InvocationHandler的代理类的对象;
2. Method method 这是用户在调用代理类的哪个方法;
3. Object[] args 这是method中传入的参数。
以上两种方式实现的功能是一样的。第二种更简单。
AOP(Aspect Oriented Program)面向方面编程
public class Advice {
long begintime = 0;
public void methodBefore(Method method){
begintime = System.currentTimeMillis();
}
public void methodAfter(Method method){
long endtime = System.currentTimeMillis()+10;
System.out.println(method.getName()+"----"+(endtime-begintime));
}
}
然后将Advice类的实例对象作为参数传入操作代理类的函数。target是原类,也可以作为参数传入。
public static Object proxydemo4(final Object target, final Advice advice){
Object colProxy = Proxy.newProxyInstance(
//类加载器参数
target.getClass().getClassLoader(),
//需要实现的接口
target.getClass().getInterfaces(),
//InvocationHandler对象
new InvocationHandler(){
@Override
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
//Advice类中定义的附加功能方法
advice.methodBefore(method);
//调用target的对应方法
Object obj = method.invoke(target, args);
//Advice类中定义的附加功能方法
advice.methodAfter(method);
return obj;
}
});
return colProxy;
}
然后我们在主程序里面就可以调用这个函数了
final Advice advice = new Advice();
final List target = new ArrayList();
List reVal = (List)proxydemo4(target, advice);
reVal.add("abc");
reVal.add("haha");
System.out.println(reVal.size());
上面的实现方法是是将原先的定死的代理功能变成可以自定义的功能,用户只要编写Advice类就可以用proxydemo4这个方法。