黑马程序员--类加载器&代理

来源:互联网 发布:淘宝店上传数据包 编辑:程序博客网 时间:2024/04/27 17:09
 ------- android培训、java培训、期待与您交流! ----------

1.类加载器

 Java虚拟机中可以安装多个类加载器,系统默认三个主要类加载器(BootStrap,ExtClassLoader,AppClassLoader),每个类加载器负责加载特定位置的类.除了BootStrap以外的类加载器都是Java类,要被其他类加载器加载,最终的类加载器就是bootStrap.Java虚拟机中的所有类加载器采用具有父子关系的树形结构进行组织,在实例化每个类加载器对象时,需要为其指定一个父级类加载器对象或者默认采用系统类加载器为其父级类加载器.

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

public class ClassLoaderTest {public static void main(String[] args) {//获取当前类的类加载器ClassLoader loader=ClassLoaderTest.class.getClassLoader();//最终的类加载器是BootStrap,不是Java类,不能获取,为nullwhile(loader!=null){System.out.println(loader.getClass().getName());//获取类加载器名loader=loader.getParent();//获取父类}System.out.println(loader);}}

类加载器的委托机制

    1.首先当前线程的类加载器去加载线程的第一个类

    2.如果类A中引用了类B,Java虚拟机将使用加载A的类加载器去加载B

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

    4.每个类加载器加载类时,先委托给其上级加载器,当最终的类加载器没有加载到类时,向发起者类加载器逐级加载,如果直到发起者类加载器都没有加载到类,则抛ClassNotFoundException

编写自己的类加载器

    编程步骤:

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

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

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


练习:编写对class加密的工具类

要加密的类

public class ClassLoaderAttachments extends Date {      @Override      public String toString() {          return "hello,java";      }  } 

自定义加载器

public class MyClassLoader extends ClassLoader {        public static void main(String[] args)throws Exception {          // TODO Auto-generated method stub          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();        }    public static void cypher(InputStream ips,OutputStream ops) throws Exception{          int b = -1;          while((b=ips.read())!=-1){              ops.write(b^0xff);    //文件加密                    }       }      private String classDir;      @Override      protected Class<?> findClass(String name) throws ClassNotFoundException {          // 获取文件路径并解密        String classFileName = classDir+"\\"+name+".class";          try {              FileInputStream fis = new FileInputStream(classFileName);              ByteArrayOutputStream bos = new ByteArrayOutputStream();              cypher(fis,bos);              byte[] b = bos.toByteArray();                         return defineClass(b, 0, b.length);                  } catch (Exception e) {              // TODO Auto-generated catch block              e.printStackTrace();          }              return super.findClass(name);      }            public MyClassLoader(){                }      public MyClassLoader(String classDir){          this.classDir = classDir;                }   }

调用自己定义的加载器

Class clazz = new MyClassLoader("itcastlib").loadClass("ClassLoaderAttachments");  Date d = (Date)clazz.newInstance();  System.out.println(d.toString()); 

2.代理

    代理的作用:为已存在的多个具有相同接口的目标类的各个方法增加一些系统功能,如异常处理,计算运行时间等

动态代理技术
    1.JVM可以在运行期动态生成该类的字节码,这种动态生成的类往往被用作代理类,即动态代理类

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

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

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

a.在调用目标方法之前

b.在调用目标方法之后

c.在调用目标方法前后

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


利用InvocationHandler接口创建代理类的三种形式

//1.实现InvocationHandler接口class MyInvocationHandler1 implements InvocationHandler{              public Object invoke(Object proxy, Method method, Object[] args)              throws Throwable {                    return null;      }                 }         Collection proxy1 = (Collection)constructor.newInstance(new MyInvocationHandler1());  System.out.println(proxy1.toString());  //2.通过匿名内部的形式实现Collection proxy2 = (Collection)constructor.newInstance(new InvocationHandler(){          public Object invoke(Object proxy, Method method, Object[] args)              throws Throwable {                        return null;      }             });  //3.用Proxy的静态方法newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)Collection proxy3=(Collection)Proxy.newProxyInstance(          Collection.class.getClassLoader(),           new Class[]{Collection.class},          new InvocationHandler(){              ArrayList target= new ArrayList();              public Object invoke(Object proxy, Method method,                      Object[] args) throws Throwable {                  long startTime = System.currentTimeMillis();                  Object retVal = method.invoke(target, args);                  long endTime = System.currentTimeMillis();                  System.out.println(method.getName()+"  running time:"+(endTime - startTime));                  return retVal;                                    }                        });  proxy3.add("123");  proxy3.add("453");  proxy3.add("789");  System.out.println(proxy3.size());  

练习:编写一个代理

创建接口

public interface Advice {      void beforeMethod(Method method);      void afterMethod(Method method);  }

实现接口

public class MyAdvice implements Advice {      long startTime=0;      @Override      public void beforeMethod(Method method) {           startTime = System.currentTimeMillis();      }        @Override      public void afterMethod(Method method) {            long endTime = System.currentTimeMillis();          System.out.println(method.getName()+"  running time:"+(endTime - startTime));      }    }  

创建代理

public static Object getProxy(final Object target,final Advice advice) {      Object proxy3 =Proxy.newProxyInstance(              target.getClass().getClassLoader(),  //类加载器            target.getClass().getInterfaces(),//实现的接口            new InvocationHandler() {                             @Override              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 proxy3;  }  

学习感悟

    对于类加载器,感觉比较简单.对class文件代码进行加密,再定义一个可以进行解密加载的加载器,从而达到脱离该加载器则无法看懂使用该class文件的目的,即达到了加密效果.对于代理,感觉比较难懂,看了好几遍才理解个大概.代理的主要作用是为已存在的多个具有相同接口的目标类的各个方法增加一些系统功能,特别注意代理必须是与目标类具有相同的接口,这是代理的关键所在.