java--加强之 类加载器,动态代理


转载请申明出处:http://blog.csdn.net/xmxkf/article/details/9944561

********************************************************类加载器的升入讲解及应用*******************************************************

44.类加载器及其委托机制的深入分析

              System.out.println(

                            ClassLoaderTest.class.getClassLoader().getClass().getName());

              //sun.misc.Launcher$AppClassLoader

             

              //System是由BootStrap加载的,它不是java类,所以获取到的加载器对象为null

              System.out.println(System.class.getClassLoader()); //null

             

              ClassLoader loader = ClassLoaderTest.class.getClassLoader();

              while(loader !=null)

              {

                     System.out.println(loader.getClass().getName());

                     //获取父级类加载器sun.misc.Launcher$ExtClassLoader

                     loader = loader.getParent();    

              }

              System.out.println(loader); //null

 

1、类加载器:

   简要介绍什么是类加载器,和类加载器的作用;

   Java虚拟机中可以安装多个类加载器,系统默认的三个主要类加载器,每个类负责加载特定位置的类:BootStrap   ExtClassLoader   AppClassLoader

   类加载器也是Java类,因为其他是java类的类加载器本身也要被类加载器加载,显然必须有第一个类加载器不是java类,不需要被加载,它是嵌套在虚拟机内核中的,虚拟机启动时他就已经加载到内存,这就是BootStrap

   Java虚拟机中的所有类装载器采用具有父子关系的树状结构进行组织,在实例化每个类装载器对象时,需要为其指定一个父级类装载器对象或者默认采用系统类装载器为其父级类加载。

 

2、类加载器之间的父子关系和管辖范围图:

        BootStrap    --------------------------à  JRE/lib/rt.jar

             |

            ExtClassLoader ---------------- --à  JRE/lib/ext/*.jar

                       |

             AppClassLoader --------------à  ClassPath指定的所有jar或目录

                              /   \

     MyClassLoader  ItcastClassLoader------à传智播客指定的特殊目录

 

3、类加载器的委托机制

  1)当java虚拟机要加载一个类时,到底派那个类加载器去加载呢?

     A 首先当前线程的类加载器去加载线程中的第一个类;

     B 如果类A中引用了Bjava虚拟机将使用加载类A的类装载器加载类B

(但是过程是反的,这个类加载器会委托他的父类以及父类的父类去加载,如果父类加载到了B,子类就不用加载);

     C 还可以直接调用ClassLoader.loadClass()方法来指定某个类加载器去加载某个类。

 

   2)每个类加载器加载类时,又先委托给其上级类加载器:

      A 当所有祖宗类加载器没有加载到类,回到发起者类加载器,还记载不了,则抛出ClassNotFoundException,不是再去找发起者类加载器的儿子,因为没有getChild方法,即使有,那么多儿子也不知道找那个;

       B 对着类加载器的层次结构图和委托加载原理,解释先前将ClassLoaderTest输出成jre/lib/ext目录下的itcast.jar包中之后,运行结果为ExtClassLoader的原因:

(用eclipse的打包工具将ClassLoaderTest输出成jre/lib/ext目录下的itcast.jar包,再在eclipse中运行这个类,运行结果显示为ExtClassLoader。此时的环境状态是classpath目录有ClassLoaderTest.classext/itcast.jar包中也有ClassLoaderTest.class。这时我们就需要了解类加载器具体过程和原理了(首先祖宗类加载器在自己指定范围类找class文件,如果没有找到就由子类加载器去加载,如果父类加载到了,子类加载器就不会加载了)。)

 

 面试题:能不能自己写一个叫做java.lang.System的类?

为了不让我们写System类,类加载采用委托机制,这样可以保证爸爸们优先,也就是总是使用爸爸们找到的类,这样总是使用java系统提供的System类。我们即使写了也不会用到我写的System,除非自己写一个类加载器,摆脱类加载委托机制。

 

45.自定义类加载器的编写原理分析

  编写自己的类加载器:

1、知识讲解:

   自定义的类加载器必须继承ClassLoader

loadClass方法不需要覆盖:因为它内部就是委托机制,委托父类加载器加载调用findClass()方法;如果覆盖了就没有委托机制了,也就是不会找父类加载。

findClass方法:覆盖此方法,当父类加载器没有加载时,自己加载;

defineClass方法:将一个类的二进制代码传入,转成(返回)Class对象(字节码)

这几个方法的关系:

父类—>loadClass//findClass/得到class文件转换成字节码—>definClass()

class NetworkClassLoader extends ClassLoader {
         String host;
         int port;
 
         public Class findClass(String name) {
             byte[] b = loadClassData(name);
             return defineClass(name, b, 0, b.length);
         }
 
         private byte[] loadClassData(String name) {
             // load the class data from the connection
              . . .
         }
     }

 

2、编程步骤:

   编写一个对文件内容进行简单加密的程序;

   编写了一个自己的类加载器,可以实现对加密过的类进行装载和解密;

   编写一个程序调用类加载器加载类,在源程序中不能用该类名定义引用变量,因为编译器无法识别这个类,程序中可以除了使用ClassLoader.load()方法之外,还可以使用设置线程的上下文类加载器或者系统类加载器,然后再使用Class.forName()

 

3、试验步骤:

   对不带包名的class文件进行加密,加密结果存放到另一个目录,

例如:java MyClassLoader MyTest.class F:\itcast

   运行加载器的程序,结果能够被正常加载,但是打印出来的类装载器名称为AppClassLoaderjava MyClassLoader MyTest F:\itcasts

   用加密后的类文件替换ClassPath环境下的类文件,在执行上一步操作就出现问题了,错误说明是AppClassLoader类装载器装载失败。

 

46.编写对class文件进行加密的工具类

package cn.itcast.day2;

import java.io.*;

/**

 * 类加载器兼加密工具

 */

publicclass MyClassLoader extends ClassLoader

{

       private StringclassDir;

 

       public MyClassLoader()

       {

             

       }

       public MyClassLoader(String classDir)

       {

              this.classDir = classDir;

       }

      

       //主方法将指定class文件加密,并放在另一文件夹中

       publicstaticvoid main(String[] args)throws Exception

       {

              String srcPath = args[0];

              String destDir = args[1];

              FileInputStream fis =new FileInputStream(srcPath);

              String destFileName = srcPath.substring(srcPath.lastIndexOf('\\')+1);

              String destPath = destDir+"\\"+destFileName;

              FileOutputStream fos =new FileOutputStream(destPath);

              cypher(fis,fos);

              fis.close();

              fos.close();

             

       }

 

       //加密文件的方法

       publicstaticvoid cypher(InputStream ips, OutputStream ops) throws Exception

       {

              int b = -1;

              while((b=ips.read())!=-1)

              {

                     ops.write(b^0xff);   //加密写入,异或255

              }

       }

      

       @Override

       protected Class findClass(String name)throws ClassNotFoundException {

              String ClassFileName =classDir +"\\"+name.substring(name.lastIndexOf('.')+1)+".class";

              try

              {

                     FileInputStream fis =new FileInputStream(ClassFileName);

                     ByteArrayOutputStream bos =new ByteArrayOutputStream();

                     cypher(fis,bos);

                     fis.close();

                     byte[] bytes = bos.toByteArray();

                     returndefineClass(bytes, 0 , bytes.length);

              }

              catch (Exception e)

              {

                     e.printStackTrace();

              }

              returnnull;

       }

      

}

47.编写和测试自己编写的解密类加载器

 

package cn.itcast.day2;

import java.util.Date;

 

publicclass ClassLoaderTest

{

       publicstaticvoid main(String[] args) throws Exception

       {

              //System.out.println(new ClassLoaderAttchment().toString());

/*           调用loadClass方法时,首先会让MyClassLoader的父类加载器在ClassPath指定目录去加载。

              如果没有加载到,即bin目录下不存在class文件,就会调用自己的findClass方法,去加载*/

              Class clazz= new MyClassLoader("itcastlib").loadClass("cn.itcast.day2.ClassLoaderAttchment");              

System.out.println( new MyClassLoader("itcastlib").getParent());

//sun.misc.Launcher$AppClassLoader@1016632

        Date d1 = (Date)clazz.newInstance();

              System.out.println(d1);

             

       }

}

48.类加载器的一个高级问题的实验分析

步骤:

编写一个能打印出自己的类加载器名称和当前类加载器的父子结构关系链的MyServlet,正常发布后,看到打印结果为WeAppClassLoader

MyServlet.class文件打jar包,放到ext目录中,重启tomcat,发现找不到HttpServlet的错误。

servlet.jar也放到ext目录中,问题解决了,打印结果是ExtClassLoader

 

父级类加载器的类无法引用只能被子级类加载器加载的类,原理如下:
       当加载MyServlet时,是由WebAppClassLoader加载的,由于继承了HttpServlet,所以WebAppClassLoader去加载它(由上级先加载,没加载到再由WebAppClassLoader加载)。

MyServlet放到ext目录中后,WebAppClassLoader的父级ExtClassLoader加载到了,然后由它去加载HttpServlet,它只会交给ExtClassLoader的父类去加载,所以加载不到

 

 

**********************************************************************动态代理技术的深入讲解:***************************************************************************

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

代理的概念与作用:

1、  生活中的代理

武汉人从代理商手中买联想电脑和直接跑到北京传智播客旁边来找联想总部买电脑,你觉得最终的主体业务目标有什么区别吗?基本上一样吧,都解决了核心问题,但是一点区别都没有吗?从代理商那里真的一点好处都没有吗?批发进货成本和运费优势比自己直接到北京总部买的总成本要低吧。

2、  程序中的代理

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

编写一个与目标类具有相同接口的代理类,代理类的每个方法调用目标类的相同方法,并在调用方法时加上系统功能的代码(参看下页的原理图)

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

 

AOP(Aspect  oriented  program )面向方面的编程  OOP)面向对象的编程

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

                       安全        事务       日志

      StudentService  --------|------------------|------------------|----------

     CourseService  --------|------------------|------------------|----------

      MiscService    --------|------------------|------------------|----------

  2、安全,事务,日志等功能要贯穿到好多个模块中,所以,它们就是交叉业务

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

      method1        method2       method3

      {              {             {

       ---------------------------------------------------切面

       ……          ……         ……

       ---------------------------------------------------切面

       }              }             }

  3、交叉业务的编程问题即为面向方面的编程(AOP),AOP的目标就是要使交叉业务模块化。

  可以采用将切面代码移动到原始方法的周围,这与直接在方法中编写切面代码的运行效果是一样的,如下所示:

---------------------------------------------------切面

       method1        method2       method3

      {              {             {

       ……          ……         ……

      }              }             }

 - --------------------------------------------------切面

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

 

动态代理技术:

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

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

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

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

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

        A、在调用目标方法之前

        B、在调用目标方法之后

        C、在调用目标方法前后

        D、在处理目标方法异常的catch块中

 

50.创建动态类及查看其方法列表信息(相关代码在下面)

Proxy  提供用于创建动态代理类和实例的静态方法,它还是由这些方法创建的所有动态代理类的超类

 

         //获取指定的类加载器定义的代理类,它可以实现指定的接口

              Class clazzProxy1 =

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

              System.out.println(clazzProxy1.getName());    //sun.proxy.$Proxy0

             

         System.out.println("-------begin constructors list----------");

              //获取此代理类所有构造方法的数组

              Constructor[] constructors = clazzProxy1.getConstructors();

              for(Constructor constructor : constructors)

              {

                     String name = constructor.getName();

                     //System.out.println(constructor);

                     StringBuilder sBuilder =new StringBuilder(name);

                     sBuilder.append("(");

                     Class[] clazzParams = constructor.getParameterTypes();

                     for(Class clazzParam : clazzParams)

                     {

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

                     }

                     //当参数列表不为空,那么就会执行for,才需要删掉最后一个,

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

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

                     sBuilder.append(")");

                     //sun.proxy.$Proxy0(java.lang.reflect.InvocationHandler)

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

              }

                    

              System.out.println("-------begin methods list----------");

              //获取此代理类所有构造方法的数组

              Method[] methods = clazzProxy1.getMethods();

              for(Method method : methods)

              {

                     String name = method.getName();

                     //System.out.println(constructor);

                     StringBuilder sBuilder =new StringBuilder(name);

                     sBuilder.append("(");

                     Class[] clazzParams = method.getParameterTypes();

                     for(Class clazzParam : clazzParams)

                     {

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

                     }

                     //当参数列表不为空,那么就会执行for,才需要删掉最后一个,

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

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

                     sBuilder.append(")");

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

 }

 

 

51.创建动态类的实例对象及调用其方法(相关代码在下面)

接口 InvocationHandler 是代理实例的调用处理程序实现的接口

System.out.println("-------begin create instance object----------");

              //Object obj = clazzProxy1.newInstance();没有空参数的构造方法,出异常

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

              class MyInvocationHandler1implements InvocationHandler

              {

                     public Object invoke(Object arg0, Method arg1, Object[] arg2)

                                   throws Throwable {

                            //TODO Auto-generated method stub

                            returnnull;

                     }

              }

              Collection proxy1 =

 (Collection)constructor.newInstance(new MyInvocationHandler1());

              System.out.println(proxy1); //null

              proxy1.clear();    //通过,说明不是对象为null

              proxy1.size();   //方法有返回值,会发生 java.lang.NullPointerException

 

52.完成InvocationHandler对象的内部功能

分析JVM动态生成的类:

1、创建实现了Collection接口的动态类和查看其名称,

分析Proxy.getClass()方法的各个参数;

2、编码列出动态类的所有构造方法和参数签名

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

3、创建动态类的实例对象

     A 通过反射获得构造方法;

     B 编写一个最简单的InvocationHandler类;

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

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

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

//用匿名类(InvocationHandler的实现类)作为构造方法的参数

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

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

                                   throws Throwable {

                            //TODO Auto-generated method stub

                            returnnull;

                     }

              });

 

总结思考:让JVM创建动态类,需要给他提供那些信息?

三个方面:

   A 生成的类中有哪些方法,通过让其实现那些接口的方式进行告知;

   B 产生的类字节码必须有一个关联的类加载器对象;

   C 生成的类中的方法的代码是怎样的,也得有我们提供。把我们的代码写在一个约定好了接口的方法中,把对象传给它,它调用我的方法,即相当于插入了我的代码。提供执行代码的对象就是那个InvocationHandler对象,它是在创建动态类的实例对象的构造方法时传递进去的。在上面的InvocationHandler对象的invoke方法中加一点代码,就可以看到这些代码被调用了。

 

Proxy.newInstance方法直接一步就创建出代理对象。

static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)

    返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。

                 //用Proxy的静态方法直接创建代理类的实例对象

              Collection proxy3 = (Collection)Proxy.newProxyInstance(

                            Collection.class.getClassLoader(),  //第一个参数,类加载器

                            new Class[]{Collection.class},   //第二个参数,实现的接口的字节码对象数组

                            new InvocationHandler(){     //第三个参数InvocationHandler实现类对象

                                   ArrayList targrt = new ArrayList();

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

                                                throws Throwable {

                                   //如果写在这个方法内,每次调用方法时,都会new新的对象,导致size为0

                                         //ArrayList targrt = new ArrayList();

                                         long beginTime = System.currentTimeMillis();

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

                                   long endTime = System.currentTimeMillis();

                                    System.out.println(

method.getName()+running time of  "+(endTime-beginTime));

                                         return retVal;

                                   }

                                   });

              proxy3.add("zxx");

              proxy3.add("lhm");

              proxy3.add("bxd");

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

/*           输出结果

        add  running time of  0

              add  running time of  0

              add  running time of  0

              size  running time of  0

              3*/

 

53.分析InvocationHandler对象的运行原理

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

     1 动态生成的类实现了Collection接口(可以实现若干个接口),生成的类有Collection接口中的所有方法和一个如下接受InvocationHandler参数的构造方法;

        $Proxy0 implements Collection

{

InvocationHandler handler;

public $Proxy0(InvocationHandler handler)

{

    this.handler = handler;

}
}

2 构造方法接受一个InvocationHandler对象,有什么作用呢?该方法内部代码又是怎样的呢?(记住它,在其他的地方用它)

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

           生成的Collection接口中的方法的运行原理:

           int size()

{

    return handler.invoke(this, this.getClass().getMethod(“size”), null);

}

int clear()

{

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

}

 

      4  InvocationHandler接口中定义的invoke方法接受的三个参数又是什么意思?       Client程序调用objProxy.add(“abc”)方法时,设计三个要素:

objProxy对象, add方法,“abc”参数

            Class Proxy${

                add(Object objct){

        return handler.invoke(Object proxy, Method method, Object[] args);

     }

}

       5 分析为什么动态类的实例对象的getClass()方法返回了正确结果呢?

         调用代理(Proxy)对象的从Object类继承的hashCodeequalstoString这几个方法时,代理对象将调用请求转发给InvocationHandler对象,对于其他方法,则不会转发调用请求,直接执行Proxy类从Object继承并复写的方法。

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

                System.out.println(proxy3.getClass().getName());

输出:

toString  running time of  0

[zxx, lhm, bxd]

sun.proxy.$Proxy0

 

总结:调用代理类的add方法时,add方法内部会去调用获取此代理类的实例对象时接受的

InvocationHandler对象中的invoke()方法,invoke方法中又会对代理对象调用该方法的底层字节码,然后该方法返回的值(包括void)会返回给invoke方法,再由invoke方法返回给调用的方法;每调用一次代理类对象的方法时,invoke方法都会执行一次。invoke方法返回的Object对象会传到add方法,作为add方法的返回值。如果重写invoke方法时返回的是null,当代理对象调用有返回值的方法时,返回的是null,而此有返回值的方法又要将null转换成相应的返回值(对象),所以会报异常(这就是为什么前两次调用size方法时发生NullPointerException)。

例子:add(“abc”)

      {

            return handler.invoke(Object proxy, Method method, Object[] args)

            {

                 return method.invoke(proxy, args);

             }

       }

 

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

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

   1、分析动态代理的工作原理图(见下页)

     

   2、怎样将目标类传出去?

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

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

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

3、将创建代理的过程改为一种更优雅的方式,eclipse重构出一个getProxy方法绑定接受目标同时返回代理对象,让调用者更懒惰,更方便,调用者甚至不用接触任何代理的API

   

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

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

       privatestatic Object getProxy(final Object target, final Advice advice) {

              Object proxy = Proxy.newProxyInstance(

                   target.getClass().getClassLoader(), 

                     /*new Class[]{Collection.class}, */

                     target.getClass().getInterfaces(),  //目标类实现的接口

                     new InvocationHandler(){   //第三个参数InvocationHandler实现类对象

                                                

                            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()+"  running time of  "+(endTime-beginTime));

                                   return retVal;*/

                                 advice.beforeMethod(method);    //功能对象调用功能方法

                                 Object retVal = method.invoke(target, args); //对目标类对象调用底层方法

                                 advice.afterMethod(method);     //功能对象调用功能方法

                                 return retVal;

                           }

                      });

              return proxy;

       }

 

附上系统功能类:

  import java.lang.reflect.Method;

 

publicclass MyAdviceimplements Advice

{

        longbeginTime;

 

       publicvoid beforeMethod(Method method)

       {

              System.out.println("到传智播客来学习了!");

              beginTime = System.currentTimeMillis();

       }

 

       publicvoid afterMethod(Method method)

       {

              System.out.println("从传智播客毕业工作了!");

              long endTime = System.currentTimeMillis();

              System.out.println(method.getName()+running time of  "+(endTime-beginTime));

       }

}

 

56.实现类似spring的可配置的AOP框架

实现AOP功能的封装与配置:

1、  工厂类BeanFactory负责创建目标类或代理类的实例对象,并通过文件实现切换。其getBean方法根据参数字符串返回一个相应的实例对象,如果参数字符串在配置文件中对  应的类名不是ProxyFactoryBean,则直接返回该类的实例对象,否则,返回该类实例对象的getProxy方法返回的对象(代理类对象)。

2、  BeanFactory的构造方法接收代表配置文件的输入流对象,配置文件格式如下:

         #xxx=java.util.ArrayList

xxx= cn.itcast.day3.aopframework.ProxyFactoryBean

xxx.advice=cn.itcast.day3.MyAdvice

xxx.target=java.util.ArrayList

3、  ProxyFactoryBean充当封装生成动态代理的工厂,需要为工厂类提供哪些配置参数信息呢?(目标也就是目标类,通知也就是系统功能)

4、  编写客户端的应用:AopFrameworkTest
       编写实现Advice接口的类和在配置文件中进行配置;

       调用BeanFactory获取对象。

 

附上代码:

BeanFactory

publicclass BeanFactory

{

              Properties props = new Properties();

             

              public BeanFactory(InputStream ips)

              {

                     try {

                            props.load(ips);  //从输入流中读取属性列表(次输入流关联了一个配置文件)

                     } catch (IOException e) {

                            e.printStackTrace();

                     }

              }

             

              //可能返回代理,也可以返回目标对象 。就要看配置文件里面怎么配置

              public Object getBean(String name)

              {

                     String className =props.getProperty(name);

                     Object bean =null;

                     try {

                            Class clazz = Class.forName(className);

                            bean = clazz.newInstance();

                     }

                     catch (Exception e)

                     {                          

                            e.printStackTrace();

                     }

                      //如果获得的对象是ProxyFactoryBean的对象,说明要返回的是代理类的对象

                     if(beaninstanceof ProxyFactoryBean) 

                     {

                            Object proxy =null;

                            ProxyFactoryBean proxyFactoryBean = (ProxyFactoryBean)bean;

                            try {

                                   //获取功能类对象及目标类对象(通过配置文件)

                                   Advice advice = (Advice)Class.forName(props.getProperty(name+".advice")).newInstance();

                                   Object target = Class.forName(props.getProperty(name+".target")).newInstance();

                                   //设置proxyFactoryBean对象的两个属性值

                                   proxyFactoryBean.setAdvice(advice);

                                   proxyFactoryBean.setTarget(target);

                                   //调用ProxyFactoryBean类的getProxy()方法,获取代理类对象

                                   proxy = proxyFactoryBean.getProxy();

                            }

                            catch (Exception e)

                            {

                                   e.printStackTrace();

                            }

                            return proxy;

                     }

                     return bean;

              }

}

 

ProxyFactoryBean

publicclass ProxyFactoryBean

{

 

       private Adviceadvice;

       private Objecttarget;

      

       public Advice getAdvice() {

              returnadvice;

       }

       publicvoid setAdvice(Advice advice) {

              this.advice = advice;

       }

       public Object getTarget() {

              returntarget;

       }

       publicvoid setTarget(Object target) {

              this.target = target;

       }

 

       //获取代理类对象的方法

       //(本来要传递两个参数,这里当做全局变量,这两个参数需要对象代用set方法设置)

       public Object getProxy()

       {

              Object proxy = Proxy.newProxyInstance(

                            target.getClass().getClassLoader(), 

                            /*new Class[]{Collection.class}, */

                            target.getClass().getInterfaces(),  //目标类实现的接口

                            new InvocationHandler(){   //第三个参数InvocationHandler实现类对象

                                                

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

                                                throws Throwable {

                                              advice.beforeMethod(method);

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

                                        advice.afterMethod(method);

                                               return retVal;

                                                }

                                                });

              return proxy;

       }

}

 

AopFrameworkTest

publicclass AopFrameworkTest

{

       publicstaticvoid main(String[] args) throws Exception

       {

              //根据class对象的方法获取一个跟配置文件关联的读取流

              InputStream ips = AopFrameworkTest.class.getResourceAsStream("config.properties");

              //获取对象(还不知道是目标类对象还是代理类对象)

              Object bean= new BeanFactory(ips).getBean("xxx");

              //检验是什么的对象

              System.out.println(bean.getClass().getName());

              ((Collection)bean).clear();    //将代理类对象转成Collection之后调用它的方法

       }

}

 

-------------------- ASP.Net+Android+IOS开发、.Net培训、期待与您交流! ----------------------

你可能感兴趣的:(java)