您的位置:首页 > 职场人生

黑马程序员 (高新技术)  动态代理学习总结

2014-05-23 14:23 232 查看
----------------------
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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  黑马程序员