代理类

来源:互联网 发布:c语言的创始人 编辑:程序博客网 时间:2024/06/06 03:20
------- android培训、java培训、期待与您交流! ----------

代理类

分析代理类的作用与原理AOP概念

代理的概念与作用

要为已存在的多个具有相同接口的目标类的各个方法增加一些系统功能,例如,异常处理、日志、计算方法的运行时间、事物管理、等等。

编写一个与目标类具有相同接口的代理类,代理类的每个方法调用目标类的相同方法,并在调用方法时加上系统功能的代码。

如果采用工厂模式和配置文件的方式进行管理,则不需要修改客户端程序,在配置文件中配置是使用目标类、还是代理类,这样以后很容易切换,比如,想要日志功能时就配置代理类,否则配置目标类,这样,增加系统功能很容易,以后运行一段时间后,又想去掉系统功能也很容易。

代码架构图:


AOP概念

系统中存在交叉业务,一个交叉业务就是要切入到系统中的一个方面,如以下所示:

用具体的程序代码描述交叉业务:

交叉业务的编程问题即为面向方面的编程(Aspect oriented program,简称AOP),AOP的目标就是要使交叉业务模块化。可以采用将切面代码移动到原始方法的周围,这与直接在方法中编写切面代码的运行效果是一样的,如下所示:

使用代理技术正好可以解决这种问题,代理是实现AOP功能的核心和关键技术。

动态代理技术?

●要为系统中的各种接口的类增加代理功能,那将需要太多的代理类,全部采用静态代理方式,将是一件非常麻烦的事情!写成百上千个代理类,是不是太累!

●JVM可以在运行期动态生成类的字节码,这种动态生成的类往往被用作代理类,即动态代理类。

●JVM生成的动态类必须实现一个或多个接口,所以JVM生成的动态类只能用作具有相同接口的目标类的代理。

●CGLIB库可以动态生成一个类的子类,一个类的子类也可以用作该类的代理,所以,如果要为一个没有实现接口的类生成动态代理类,那么可以使用CGLIB库。

●代理类的各个方法中通常除了要调用目标的相应方法和对外返回目标返回的结果类,还可以在代理方法中的如下四个位置加上系统功能代码:

1.在调用目标方法之前

2.在调用目标方法之后

3.在调用目标方法前后

4.在处理目标方法异常的catch块中

void sayHello(){

    ...............

    try{

    target.sayHello();

}catch(Exception e){

    .............

}

.................

}

 

创建动态类及查看其方法列表信息

分析JVM动态生成的类

创建实现了Collection接口的动态类和查看其名称,分析Proxy.getProxyClass方法的各个参数。

编写列出动态类中的所有构造方法和参数签名。

编码列出动态类中的所有方法和参数签名。

创建动态类的实例对象:

     用反射获得构造方法

     编写一个最简单的InvocationHandler类

      调用构造方法创建动态类的实例对象,并将编写的InvocationHandler类的实例对象穿进去

打印创建的对象和调用对象的没有返回值的方法和getClass方法,演示调用其他有返回值的方法报告了异常。

     将创建动态类的实例对象的代理改成匿名内部类的形式编写。

总结思考:让jvm创建动态类,需要给它提供哪些信息?

   三个方面:

演示代码:

package day3;

import java.lang.reflect.Constructor;

import java.lang.reflect.Method;

import java.lang.reflect.Proxy;

import java.util.Collection;

//动态类

public class ProxyTest {

 

    /**

     * @param args

     */

    public static void main(String[] args) {

       //

       Class clazzProxy1 = Proxy.getProxyClass(Collection.class.getClassLoader(),Collection.class);

   

       System.out.println(clazzProxy1.getName());//输出$Proxy0

      

/*     System.out.println("------------begin constructorslist--------------");

       //希望打印出$Proxy0()形式和$Proxy0(InvocationHandler x)形式

       //获取所有的构造方法存到数组中

       Constructor[] constructors = clazzProxy1.getConstructors();

      

       for(Constructor constructor : constructors){

           //获取构造方法的名字

           String name = constructor.getName();

           //给字符串一个初始值$Proxy0

           StringBuilder sBuilder = new StringBuilder(name);

          

           //看到StringBuilder后想到它和StringBuffer的区别?

           //它们在运用上基本一样,都是往字符串上动态的添加内容,

           //在单线程情况下StringBuilder的效率要高一点,如果在

           //多线程时就要用StringBuffer

           //增加参数列表

           sBuilder.append('(');

           //获取构造方法中的参数返回的数组

           Class[] clazzParams = constructor.getParameterTypes();

           //取出每一个参数

           for(Class clazzParam : clazzParams){

              sBuilder.append(clazzParam.getName()).append(',');

           }

           if(clazzParams!=null && clazzParams.length == 0)

              //去掉最后一个","

              sBuilder.deleteCharAt(sBuilder.length()-1);

          

           sBuilder.append(')');

           System.out.println(sBuilder.toString());

          

          

       }*/

       System.out.println("------------beginmethods list--------------");

       //希望打印出$Proxy0()形式和$Proxy0(InvocationHandler x)形式

       //获取所有的构造方法存到数组中

       Method[] methods = clazzProxy1.getMethods();

      

       for(Method constructor : methods){

           //获取构造方法的名字

           String name = constructor.getName();

           //给字符串一个初始值$Proxy0

           StringBuilder sBuilder = new StringBuilder(name);

          

           //看到StringBuilder后想到它和StringBuffer的区别?

           //它们在运用上基本一样,都是往字符串上动态的添加内容,

           //在单线程情况下StringBuilder的效率要高一点,如果在

           //多线程时就要用StringBuffer

           //增加参数列表

           sBuilder.append('(');

           //获取构造方法中的参数返回的数组

           Class[] clazzParams = constructor.getParameterTypes();

           //取出每一个参数

           for(Class clazzParam : clazzParams){

              sBuilder.append(clazzParam.getName()).append(',');

           }

           if(clazzParams!=null && clazzParams.length == 0)

              //去掉最后一个","

              sBuilder.deleteCharAt(sBuilder.length()-1);

          

           sBuilder.append(')');

           System.out.println(sBuilder.toString());

          

       }

    }

}

创建动态类的实例对象及调用其方法

代码示例:

package day3;

 

import java.lang.reflect.Constructor;

import java.lang.reflect.InvocationHandler;

import java.lang.reflect.Method;

import java.lang.reflect.Proxy;

import java.util.Collection;

//动态类

public class ProxyTest {

 

    public static void main(String[] args)throws Exception {

       Class clazzProxy1 = Proxy.getProxyClass(Collection.class.getClassLoader(),Collection.class);

      

       System.out.println("-----------begincreate instance object------------------");

       //注意:clazzProxy1.newInstance();是不可以的,因为它没有不带参数的构造方法

       //找到构造方法

       Constructor constructor =clazzProxy1.getConstructor(InvocationHandler.class);

       //创建对象匿名内部类的应用

       Collection proxy1 = (Collection)constructor.newInstance(newInvocationHandler(){

           @Override

           public Object invoke(Object proxy, Method method, Object[]args)

                  throws Throwable {

              // TODO Auto-generated method stub

              return null;

           }

          

       });

       System.out.println(proxy1);//结果为:null

       proxy1.clear();

       //System.out.println(proxy1.size());//结果为:null

      

    }

}

完成InvocationHandler对象的内部功能

package day3;

 

import java.lang.reflect.Constructor;

import java.lang.reflect.InvocationHandler;

import java.lang.reflect.Method;

import java.lang.reflect.Proxy;

import java.util.ArrayList;

import java.util.Collection;

//动态类

public class ProxyTest {

 

    /**

     * @param args

     */

    publicstatic void main(String[] args)throws Exception {

 

       //第一种方式创建动态类

       System.out.println("-----------begincreate instance object------------------");

       ClassclazzProxy1 = Proxy.getProxyClass(Collection.class.getClassLoader(),Collection.class);

       //注意:clazzProxy1.newInstance();是不可以的,因为它没有不带参数的构造方法

       //找到构造方法

       Constructorconstructor = clazzProxy1.getConstructor(InvocationHandler.class);

       //创建对象匿名内部类的应用

       Collectionproxy1 = (Collection)constructor.newInstance(new InvocationHandler(){

 

           @Override

           publicObject invoke(Object proxy, Method method, Object[] args)

                  throwsThrowable {

              //TODO Auto-generated method stub

              returnnull;

           }

          

       });

       System.out.println(proxy1);//结果为:null

       proxy1.clear();

       //System.out.println(proxy1.size());//结果为:null

      

       //第二种方式创建动态类及实例对象

       Collectionproxy3 = (Collection)Proxy.newProxyInstance(

              Collection.class.getClassLoader(),

              newClass[]{Collection.class},

              newInvocationHandler() {

                  //ArrayListtarget = new ArrayList();打印长度为3

                  //指定一个目标ArrayList

                  publicObject invoke(Object proxy, Method method, Object[] args)

                         throwsThrowable {

                     ArrayListtarget = new ArrayList();

                     //获取开始毫秒

                     longbeginTime = System.currentTimeMillis();

                     ObjectretVal = method.invoke(target, args);

                     longendTime = System.currentTimeMillis();

                     System.out.println(method.getName()+"运行消耗时间"+(endTime-beginTime));

                     returnretVal;

                  }

              }

              );

       proxy3.add("zxx");

       proxy3.add("lug");

       proxy3.add("r3");

       System.out.println(proxy3.size());

       /*

       add了三个为什么打印的长度还是0呢?

       每调用一次add方法都会去找InvocationHandler这个对象的invoke方法,每次调用add

       方法都会处理一个新的target,所以出来的都是0.

       */

    }

}

分析InvocationHandler对象的运行原理

猜想分析动态生成的类的内部代码:

动态生成的类实现了Collection接口(可以实现若干接口)

Collection接口中的所有方法和一个如下接受InvocationHandler参数的构造方法。

构造方法接受一个InvocationHandler对象,接受对象了要干什么用呢?该方法内部的代码会怎样的呢?

    InvocationHandler handler;

    public $Proxy0(InvocationHandler handler){

       this.handler = handler;

    }

实现Collection接口的动态类中的各个方法的代码又是怎样实现的呢?

    int size(){

       returnhandler.invoke(this,this.getClass().getMethod("size"),null);

    }

    void clear(){

       handler.invoke(this,this.getClass().getMethod("clear"),null);

    }

InvocationHandler接口中定义的invoke方法接受的三个参数又是什么意思?图解说明如下:

总结分析动态代理类的设计原理与结构

让动态类生成的类称为目标类的代理

分析动态代理的工作原理图

怎样将目标类传进去?

直接在InvocationHandler实现类中创建目标类的实例对象,可以看出运行效果和加入日志代码,但没有实际意义。

为InvocationHandler实现类注入目标类的实例对象,不能采用匿名内部类的形式

让匿名的InvocationHandler实现类访问外面方法中的目标类实例对象的final类型的引用变量。

编写可生成代理和插入通告的通用方法

package day3;

 

import java.lang.reflect.InvocationHandler;

import java.lang.reflect.Method;

import java.lang.reflect.Proxy;

import java.util.ArrayList;

import java.util.Collection;

//动态类

public class ProxyTest {

 

    /**

     * @param args

     */

    public static void main(String[] args)throws Exception {

 

       final ArrayList target = new ArrayList();

      

       Collection proxy3 = (Collection)getProxy(target,new MyAdvice());

       proxy3.add("路国强");

       proxy3.add("毕向东");

       System.out.println(proxy3.size()+"iiiii");

    }

 

    private static Object getProxy(final Object target,final Advice advice) {

       Object proxy3 = Proxy.newProxyInstance(

              /*Collection.class.getClassLoader(),*/

              target.getClass().getClassLoader(),

              /*newClass[]{Collection.class},*/

              target.getClass().getInterfaces(),

              new InvocationHandler() {

                 

                  //ArrayListtarget = new ArrayList();

                  public Object invoke(Object proxy, Method method, Object[]args)

                         throws Throwable {

                     /*long beginTime= System.currentTimeMillis();

                     Object retVal = method.invoke(target,args);

                     long endTime = System.currentTimeMillis();

                     System.out.println(method.getName() + "运行的时间:"+(endTime-beginTime));

                     returnretVal;*/

                    

                     advice.beforeMethod(method);

                     Object retVal = method.invoke(target, args);

                     advice.affterMethod(method);

                     return retVal;

                  }

              }

              );

       return proxy3;

    }

}

package day3;

 

import java.lang.reflect.Method;

 

public interface Advice {

    void beforeMethod(Method method);

    void affterMethod(Methodmethod);

}

package day3;

 

import java.lang.reflect.Method;

 

public class MyAdvice implements Advice {

    long beginTime = 0;

    @Override

    public void affterMethod(Method method) {

       System.out.println("我从黑马出来了!");

       long endTime = System.currentTimeMillis();

       System.out.println(method.getName() +"运行的时间:"+(endTime-beginTime));

    }

 

    @Override

    public void beforeMethod(Method method) {

       System.out.println("我要去黑马!");

       beginTime = System.currentTimeMillis();

    }

}

------- android培训、java培训、期待与您交流! ----------
0 0
原创粉丝点击