黑马程序员—Java高新技术4

来源:互联网 发布:sql server olap 编辑:程序博客网 时间:2024/06/05 04:51


----------------------android  java培训、期待与您交流! ----------------------

 

  上一次学习完了Java高新技术中的注解和泛型,这次就接着学习下面的类加载和代理,又是一根难啃的骨头,对知识理解的深度不够,导致这部分的学习难度还真的不小,尤其从Java高新技术的反射开始,那就耐着性子多看几遍吧!学习,加油!

11、类加载器

1.类加载器概念

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

2).类加载器也是Java类,因为其他是java类的类加载器本身也要被类加载器加载,显然必须有第一个类加载器不是不是java类,这正是BootStrap。

3).Java虚拟机中的所有类装载器采用具有父子关系的树形结构进行组织,在实例化每个类装载器对象时,需要为其指定一个父级类装载器对象或者默认采用系统类装载器为其父级类加载。
2.类加载器之间的父子关系和管辖范围
1>.BootStrap (JRE/lib/rt.jar)
2>.ExtClassLoader (JRE/lib/ext/*.jar)
3>.AppClassLoader(JRE/lib/ext/*.jar)
AppClassLoader继承ExtClassLoader,ExtClassLoader继承BootStrap。
3.类加载器的委托机制
    每个ClassLoader本身只能分别加载特定位置和目录中的类,但它们可以委托其他的类装载器去加载类,这就是类加载器的委托模式。类装载器一级级委托到BootStrap类加载器,当BootStrap无法加载当前所要加载的类时,然后才一级级回退到子孙类装载器去进行真正的加载。当回退到最初的类装载器时,如果它自己不能完成类的装载,那就应报告ClassNotFoundException异常。

 4.编写自己的类加载器

编写一个对文件内容进行简单加密的程序。编写了一个自己的类装载器,可实现对加密过的类进行装载和解密。编写一个程序调用类加载器加载类,在源程序中不能用该类名定义引用变量,因为编译器无法识别这个类。程序中可以除了使用ClassLoader.load方法之外,还可以使用设置线程的上下文类加载器或者系统类加载器,然后再使用Class.forName。

import java.util.*;

public class MyTest extends Date//类加载器不能加载非public的类{

            @Override

           public String toString() {

              return "hello,黑马训练营!";

          }

}

import java.io.*;

import java.lang.reflect.*;

import java.util.*;

public class MyClassLoader extends ClassLoader{

  private String path = null;

  public MyClassLoader(String path) throws Exception {  //检查文件是否存在

         File f = new File(path);

         if(!f.isDirectory() {

                throw new RuntimeException(path + " is not a directory");

         }

         this.path = path;

  }

 

  public Class findClass(String name)  {   // throws Exception 子类不能抛出比父类更广泛的异常

         try {

                File f = new File(path,name+ ".class");

                FileInputStream fis = new FileInputStream(f);

                ByteArrayOutputStream bos = new ByteArrayOutputStream();

                cypher(fis,bos);//解密

                byte [] buf = bos.toByteArray();

                fis.close();

                bos.close();

                return defineClass(name,buf,0,buf.length);

         }catch(Exception e)

         {

                //throw new ClassNotFoundException(name + " is not found!");

         }

         return null;

  }

 

  public static void cypher(InputStream istream,OutputStream ostream) throws Exception{    

         int b = 0;

         while((b = istream.read()) != -1){

                ostream.write(((byte)b) ^ 0xff);

         }

  }

 

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

         if(!args[0].endsWith("class"))  {

                ClassLoader loader = new MyClassLoader(args[1]);

                Class clazz = loader.loadClass(args[0]);      

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

                System.out.println(d1);

                System.out.println(clazz.getName());

         } else{           

                FileInputStream fis = new FileInputStream(args[0]);         

                File f = new File(args[1], new File(args[0]).getName());

                FileOutputStream fos = new FileOutputStream(f);            

                cypher(fis,fos);//加密

                fis.close();

                fos.close();

         }

  }

}

 

12、代理

代理学前准备:

什么是代理设计模式
 代理模式的主要作用是为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

 代理模式的思想是为了提供额外的处理或者不同的操作而在实际对象与调用者之间插入一个代理对象。这些额外的操作通常需要与实际对象进行通信。
代理模式一般涉及到的角色有
–抽象角色:声明真实对象和代理对象的共同接口
–代理角色:代理对象角色内部含有对真实对象的引用,从而可以操作真实对象,同时代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装
–真实角色:代理角色所代表的真实对象,是我们最终要引用的对象
怎么用代理
  在客户端调用目标的时候,代理和目标都要实现相同的接口,应为他们对外都要提供相同的方法。客户端程序在编程序的时候,并不是只记得引用目标,也不是调用代理,而是用接口来进行引用的这样就可以在代理的方法里面调用目标的对应的方法,在调用的前面或者是后面我们可以添加相应的系统处理方方法。我们的用代理的目标是为了客户端去访问代理而是直接的访问目标。

 

1.代理构架图

2.面向方面的编程(Aspect oriented program ,简称AOP)

    AOP的目标就是要使交叉业务模块化,安全,事务,日志等功能要贯穿到好多个模块中,所以,它们就是交叉业务,代理是实现AOP功能的核心和关键技术。

3.动态代理技术

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

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

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

4.获取代理类Proxy的构造函数和方法列表

     代码示例:

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

        System.out.println(clazzProxy1.getName());

 

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

        Constructor[] constructors = clazzProxy1.getConstructors();

        for (Constructor constructor : constructors) {

            String name = constructor.getName();

            StringBuilder sBuilder = new StringBuilder(name);

            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("----------begin methods list----------");

        Method[] methods = clazzProxy1.getMethods();

        for (Method method : methods) {

            String name = method.getName();

            StringBuilder sBuilder = new StringBuilder(name);

            sBuilder.append('(');

            Class[] clazzParams = method.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());

        }

 

5.创建动态代理类实例的三种形式

   示例1:

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

        class MyInvocationHander1 implements InvocationHandler {

 

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

                    throws Throwable {

                return null;

            }

        }

 Collection proxy1 = (Collection) constructor.newInstance(new MyInvocationHander1());

 示例2:

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

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

                    throws Throwable {

                return null;

            }

        });

示例3:

Collection proxy3 = (Collection) Proxy.newProxyInstance(

                Collection.class.getClassLoader(),

                new Class[]{Collection.class},

                new InvocationHandler() {

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

                            throws Throwable {

                        return null; 

                    }

                });

          proxy3.add("zxx"); 

注:InvocationHandler接口中定义的invoke方法接受的三个参数分别代表代理对象proxy3,方法add(),add()方法中的参数”zxx”。

6.动态代理的工作原理图




代理小型框架示例代码:

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

        Object proxy3 = Proxy.newProxyInstance(

                target.getClass().getClassLoader(),

               

                target.getClass().getInterfaces(),

                new 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 proxy3;

}

 

import java.lang.reflect.Method;

public interface Advice {  //通信的契约

       void beforeMethod(Method method);

       void afterMethod(Method method);

}

import java.lang.reflect.Method;

public class MyAdvice implements Advice {

       long beginTime = 0;

       public void afterMethod(Method method) {

              System.out.println("从黑马毕业上班啦!");

              long endTime = System.currentTimeMillis();

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

 

       }

       public void beforeMethod(Method method) {

              System.out.println("到黑马来学习啦!");

              beginTime = System.currentTimeMillis();

       }

)

7.实现AOP功能的封装与配置

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

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

       #xxx=java.util.ArrayList

       xxx=cn.itcast.ProxyFactoryBean

       xxx.target=java.util.ArrayList

       xxx.advice=cn.itcast.MyAdvice

 

 

----------------------android  java培训、期待与您交流! ----------------------

详细请查看:http://edu.csdn.net/heima

原创粉丝点击