------- android培训、java培训、期待与您交流! ----------
一、代理类:要为已存在的多个具有相同接口的目标类的各个方法增加一些系统功能,如:异常处理、日志、时间的计算方法等事务管理,这就需要编写一个与目标类具有相同接口类,此类的每个方法调用目标类的相同方法,并在调用方法时,加上系统功能代码,这样的类就叫做代理类。
Class A
{
Public void print ()
{
System.out.println(“hi,itcast!”);
}
}
class ProxyA//ProxyA类代理A类实现了相同的功能,同时还实现了系统的功能代码
{
long start=System.currentTimeMillis();
newA().print();
long end=System.currentTimeMillis();
System.out.println("程序运行的时间:"+(end-start));
}
二、 动态代理类:java虚拟机在运行时可以动态的生成出类的字节码,这种动态生成的类往往被用作其他类的代理,即动态代理类。
动态生成代理类的前提:
1) jvm生成的动态类,必须实现了一个或者多个接口,所以,jvm生成的动态类只能用作具有相同接口的目标类的代理。
2) 如果一个类没有要实现的接口,那么,可以调用CGLIB库动态的生成这个类的子类,把这个子类用作此类的动态代理类。
三、创建动态代理类的类:Proxy, 提供用于创建动态代理类和实例的静态方法,它还是由这些方法创建的所有动态代理类的超类。
1) 以Collection为例在Proxy中创建代理类实例的第一种方式:
ClassclassProxy=Collection.getProxyClass(Collection.class.getClassLoader(),Collection.class);//得到collection的代理类的字节码对象
Constructorconstructor=classProxy.getConstructor(InvocationHandlar.class);
CollectionProxy1=(Collection)constructor.newInstance(new MyInvocationHandler());//proxy1为此代理类的实例
class MyInvocationHandler implements InvocationHandler//定义一个InvocationHandlar接口的有名的内部类,作为参数传递给用于创建collection代理类的构造函数
{
public Object invoke(Objectproxy, Method method, Object[] args)
throws Throwable {
// TODO自动生成的方法存根
return null;
}
}
注意想想为什么?:此时Proxy1的toString()方法返回null,调用有返回值的方
法出了异常,调用没有返回值的方法成功
2) 创建Proxy实例的第二种方法:
Collectionproxy2=(Collection)constructor.newInstance(new InvocationHandler(){
@Override
public Object invoke(Objectproxy, Method method, Object[] args)
throws Throwable {
// TODO自动生成的方法存根
return null;
}
});
3)创建Proxy实例的第三种方法:Collection proxy3=(Collection)Proxy.newProxyInstance(
Collection.class.getClassLoader(),
new Class[]{Collection.class},
new InvocationHandler(){
public Object invoke(Objectproxy, Method method, Object[] args)
throws Throwable {
// TODO自动生成的方法存根
ArrayListtarget=newArrayList();//定义要代理的方法的作用目标对象
Longstart=System.currentTimeMillis();//在代码运行前插入系统功能代码
ObjectretVal=method.invoke(target, args);
Longend=System.currentTimeMillis();//在代码后插入系统的功能代码
System.out.println(method.getName()+"running"+(end-start));
return retVal;
}
}
);
proxy3.add("abc");
proxy3.add(123);
proxy3.add(false);
System.out.println(proxy3.size());
}
其运行结果:
addrunning1
addrunning0
addrunning0
sizerunning0
0
思考:程序结果的出现是因为代理实例的方法的每次运行都会调用InvocationLHandler对象中的invoke方法,而由于我们把目标集合定义在了invoke方法的内部,每次的调用都会实例化一个新的集合,所以size()方法返回了0;当然了,如果我们把目标集合定义在了被覆写 的invoke方法的外部,这时size()方法将返回3,这也正是实例化Proxy实例的第一个方法中的“想一想为什么”的答案
addrunning1
addrunning0
addrunning0
sizerunning0
3
总之:从实例化Proxy的几种方法看来,从一到三,我们可以看到jvm在运行的时候动态的生成了代理Collection的Proxy的字节码,进而获得代理collection 的代理类,同时在不影响实现collection接口中的方法的同时,也加入了自己的系统功能代码。