---------------------- ASP.Net+Unity开发、.Net培训、期待与您交流! ---------------------
代理产生的原因,在程序运用过程中,目标类往往无法满足需求,而直接对目标类进行功能上的修改那将是无法想象的问题。那么有没有一种类,让用户不直接调用目标类,而调用该类,使该类具有功能上的增强呢?或者功能上发生一些改变呢?这个类就是代理类了。
代理类的目的是为了改变使用目标类的一些功能。当让在目标类程序内部进行修改那是不可能的。那么我们可以在该类功能调用之前和调用之后,或者在try…catch…功能中添加一些功能,其执行起来的效果与在该类方法中执行起来的效果是一样的。因此,用户虽然在使用代理类,但是,感觉上与用目标类是没有什么分别的。比如,花钱从电脑代理商花钱买了电脑,代理商就从厂商那里买来了电脑,厂商送了鼠标垫,代理商给客户的只有电脑,而没有鼠标垫。客户当然也没有问题,应为买到了电脑。当然,代理商要是只个了客户鼠标垫子了,那就搞笑了。
在类的编程过程中,我们会发现,一个类中具有的某个中功能块一般都会涉及到安全,日志之类的操作。那么将程序放在一个水平面上纵切,可以发现,同一个水平面上的功能块有相似的地方,那么我们可以将这些相似的功能块提取到程序之外,有代理类来完成,这样就可以增强代码的复用性,并且使目标类代码不是那么的臃肿。而这种编程思想又称为AOP编程。
通过上述,可知在编程过程中是需要大量代理类的。但是手动书写这些代理类又是相当痛苦的。比如我们有写一个代理类,我们需要知道,该代理类需要代理目标类的那些方法(类似于代理销售厂商的那些商品),以及怎样调用目标类的方法等等。这样的工作量不亚于重新书写一个目标类。那么如果代理类和目标类都实现同一个接口,那么,就可以很清楚要代理那些方法了,当然java中也有一种类,就是类Proxy代理类,该类中的方法提供了创建代理类的功能
Class Proxy.getProxyClass(ClassLoader cl,Class interface).该方法中有接受连个参数,cl:为生成的代理类指定一个类加载器,interface:为生成的代理类指定一个要实现的接口。
该方法获得了一个Class字节码对象,我们可以同过反射的知识来搞清楚,该字节码对象中的构造函数类表和方法列表。getConstructors和getMethods。获得Constructor[ ]数组和
Method[ ]数组。这样的话,我们就可以通过构造函数来创建代理类了。
以创建Collection类的代理类为为例:通过以上步骤可以获得给类代理类的字节码,而该代理类中的构造函数只有一个,并且要接受一个InvocationHandler对象,那么,我们可以通过三种方式来创建该代理对对象。
1. 通过内部类
Constructorconstructor = clazzProxy.getConstructor(InvocationHandler.class);
//1.获得构造函数字节码
class MyInvocimplements InvocationHandler{//2.创建一个类实现InvocationHandler接口
public Object invoke(Object arg0, Methodarg1, Object[] arg2)throws Throwable {
return null;
}
}
Collectionproxy1 = (Collection) constructor.newInstance(new MyInvoc());
//3通过构造函数字节码来新建一个代理类对象
2.通过匿名内部类
Collectionproxy2 = (Collection) constructor.newInstance(new InvocationHandler(){
public Object invoke(Object arg0, Method arg1, Object[] arg2)
throwsThrowable {
// TODOAuto-generated method stub
return null;
}});
3.通过Proxy类的静态方法newProxyNewInstance方法
Collectionproxy3 = (Collection) Proxy.newProxyInstance(
Collection.class.getClassLoader(),
newClass[]{Collection.class},
newInvocationHandler(){
public Objectinvoke(Object arg0, Method arg1, Object[] arg2)
throwsThrowable {
//TODO Auto-generated method stub
returnnull;
}
});
Proxy.newProxyInstance(arg0,arg1, arg2),该方法要接受三个参数,
arg0:接受一个加载代理类的类加载器,一般该代理类实现那一个接口就用那一个接口的类加载器。
arg1:接受一个该代理类要实现的接口字节码数组即new Class[]{…}
arg2:接受一个IvocationHandler对象。
那么,该代理类又是怎样实现该代理功能的呢?
我们知道,当一个代理类要创建的时候,需要就收一个InvocationHandler对象,而该对象中却只有一个方法,那就是invoke,而该方法有需要接受三个参数
public Object invoke(Object arg0, Method arg1, Object[] arg2){}
Object arg0:接受代理类
Method arg1:接受要对象要操作的方法对象
Ojbect[] arg2:接受要要操作的方法对象所需要的参数数组。
Collection proxy3 = (Collection) Proxy.newProxyInstance(
Collection.class.getClassLoader(),
new Class[]{Collection.class},
new InvocationHandler(){
ArrayList target =new ArrayList();
public Object invoke(Object arg0, Method arg1, Object[] arg2)
throws Throwable {
Object retVal = arg1.invoke(target, arg2);
System.out.println(arg1.getName());
return retVal;
}
});
proxy3.add("aa");
proxy3.add("bb");
proxy3.add("cc");
System.out.println(proxy3.size());
对proxy3.add("aa")调用输出结果分析可知,proxy3调用自身类类中的add方法。而add方法中又调用哪个了InvocationHandler对象的invoke方法,将proxy3对象作为第一个参数,add方法对象作为第二个参数,add接受的参数”aa”作为第三个参数传递进来。
初始默认状态下,该InvocationHandler对象中并没有指定目标类。那么我们就可以指定一个目标类如以上代码中的这一部分:
new InvocationHandler()
{
ArrayList target =new ArrayList();//定义一个Collection实现类
public Objectinvoke(Object arg0, Method arg1, Object[] arg2)
throws Throwable {
Object retVal = arg1.invoke(target, arg2);
//arg1(add方法对象)方法对象调用target对象add方法,并将“aa”参数存入到该目标类对象集合中,并将返回值传递给retVal,通过return返回。
System.out.println(arg1.getName());
return retVal;
}
}
通过以上的学习,我们基本可以了解到该Collection代理类内部代码结构了:
$Proxy0 implements Collection
{
InvocationHandler handler = null;
public Proxy0(InvocationHandler handler){
this.handler = handler;
}
public boolean add(Object arg){
handler.invoke(
this,
this.getClass.getMethod(“add”),
new Object[]{arg});
}
...
}
Tips
代理类的超类当然还是Object类,代理类从超类那里继承过来的方法,只有toString(),equals{}和hashCode()方法交给代理类中的目标类处理,其他方法由代理类自己处理,这就是为什么proxy3.getClass().getName()的返回值为$Proxy0而不是目标类的ArrayList了。
如果我们希望将以上生成(代理和在该代理中要添加的功能)的功能封装成方法,当其他的类要生成代理的时候就可以直接调用该方法,而不用手动繁琐的书写各个代理的生成代码时。通过对该代码的生成代码分析,发现该代理需要接受两个参数一个是目标,一个是添加的代理功能。那么我们就可以对这两部分封装对象,一个是可以Object target,一个可以是Advice advice【Advice是一个接口】。这样就可以生成如下代码了:
public static Object getProxy(finalObject target,final Advice advice) throwsException{
ClassclazzProxy = Proxy.getProxyClass(
target.getClass().getClassLoader(),
target.getClass().getInterfaces());
Constructorconstructor =
clazzProxy.getConstructor(InvocationHandler.class);
Objectclazz = constructor.newInstance(new InvocationHandler(){
publicObject invoke(Object arg0, Method arg1, Object[] arg2)
throwsThrowable {
advice.beginTime(arg1);
ObjectretVal = arg1.invoke(target, arg2);
advice.endTime(arg1);
returnretVal;
}
});
returnclazz;}
程序设计:创建一个AOP简单的框架,要求通过修改配置文件能够达到三个功能
1.是否要生成代理类
2.可修改target类
3.可修改Advice类
需要创建三个类:BeanFactory类,ProxyFactoryBean类,MainClass类,一个配置文件config.properties。
BeanFactory类的作用是,构造函数接收一个InputStream对象,是为了通过Properties类对象的load()方法获得配置文件中的信息,判断是直接使用目标类还是生成一个代理类。如要生一个代理类,则将配置文件中的target和advice通过Class.forName(“…”).newInstance(),创建的target对象和advice对象,传递给创建ProxyFactoryBean的构造方法中,并调用该对象的getProxy()方法来获得代理类对象。
ProxyFactoryBean类的作用是:接受两个对象:target对象和advice对象。并创建一个getProxy()方法,生成该目标类的代理类 。
----------------------- ASP.Net+Unity开发、.Net培训、期待与您交流! ----------------------详细请查看:www.itheima.com