黑马程序员-----java提高之类加载器&代理

来源:互联网 发布:同学app软件下载 编辑:程序博客网 时间:2024/05/16 01:40


一类加载器

 类加载器的作用:

  当程序中用到一个类时,类加载器就会把该类的字节码文件加载到虚拟机中。当然我们也可以用类记载器加载一些资源:如配置文件等

类加载器的分类:

java虚拟机可以安装多个类加载器,系统默认的加载器有三个:

  BootsrapClassLoder  ------------------------------------------------>:负责加载JDK安装目录下jre/lib/rt.jar 常见的如:System类就是由其加载的

     |---------ExtClassLoder --------------------------------------------->:负责加载jre/lib/ext/*.jar

                    |--------------AppClassLoader -------------------------->:负责加载classpath指定的所有jar或目录

                                          |MyClassLoder(我们自定义的类加载器):可加载我们指定目录下的.jar

注意我们自定义的类加载器必须挂靠在系统的类加载器体系上:即我们自定义的类加载器必须时AppClassLoder的子类

二 类加载器的委托机制

  当java虚拟机需要一个类的字节码文件时,到底该由那个类加载器去加载呢?

  java类加载器在加载类时有如下几个规律:

 1 首先当前线程的类加载器去加载线程中的第一个类:main()函数所在的类

 2 如果类A中用到了类B那么B类由加载A类的加载器负责加载。

 3 每个类加载器在加载类时,先委托给上一级父类加载器加载,如果父类加载器加载类成功(找到),则子类加载器不在继续对该类进行加载;

   如果父类加载器加载类没成功(没找到),则下放给下一级子类加载器去加载,如果直到发起类加载请求的类加载器都没加载到该类时就抛出:ClassNotFoundException

  不再下放加载请求。(类加载器只有:getParent()方法,没有getchild());

三 自定义类加载器

   当我有需要时可以自定义类加载器,创建自己的类加载器有如下步骤:

     1: 定义一个类继承ClassLoder

     2:复写 findClass(name);

     3:调用父类的defineClass();方法将字节码转换成class文件的字节码

    因为:各个类加载器委托父类夹杂的类的操作都是一样的 所以不必复写:LoadClass(name);只有当父类不能加载类成功时,才会由子类去加载,而每个子类加载类的

细节上可能有不同,需自己复写父类的findClass(name);

----------------------------------------------------------------------------------------------------------

代理

1 什么叫代理?

当我们需要对一些已经存在的类(在不知道源文件的情况下),进行功能扩充,增加异常处理,日志,事物管理等功能时

可以编写一个实现了相同接口的代理类,在代理类的每个方法中调用目标类的相同方法,并在代理类的方法中增加上述系统功能。

当然有人会问可以通过继承类复写父类的方法实现对类功能的扩展啊!

如果只是为了获得某个类的功能而去继承复写父类的方法存在一定弊端:破坏目标类的原有继承体系。

代理原理:

class X
{
   void sayHello()
   {
      syso:hello
    }

 }
--------------
XProxy
{

   void sayHello()
   {
   
       cod........  // 系统功能实现1
       X.sayHello();  //目标类的方法   
      cod.........  // 系统功能实现2
       
    }
}

代理原理图解


1  静态代理:


Interface inter    // 定义一个接口

{

   public  void sayHello();

}


class B implents inter   //  定义一个类实现该接口

{

     public void sayHello()

        {  System.out.println("hello");

        }

}

Class BProxy implements inter  // 定义代理类实现相同的接口

{

      public void sayHello()

         {

             System.out.println("welcome");//扩展功能

                 B  b = new B();

                 b.sayHello();

         }

}


calss  Test   // 定义测试类 (模拟客户端)

{

       BProxy bproxy   new  BProxy();//   客户端不再对B类对象直接调用而是调用代理类

      bproxy.sayHello();   //  增加了sayHello() 的功能

}

显然 静态代理存在一定的局限性:

  1  加入在接口中增加了新的方法,在目标类和代理类中都得做相应的修改。

  2  加入需要扩展新的系统功能得修改代理类的源文件, 静态代理类将系统功能代码写死了;


2 动态代理

   java虚拟机在运行期间会动态的生成类的字节码,可用这种动态生成的类作为代理类。

   java虚拟机生成的动态类必须实现一个或多个接口,所有动态代理类只能代理实现相同接口的目标类。

   代理类的各个方法的内容有:

       1  通常要调用目标类的方法,并对外返回目标类该方法的返回值。

       2 在调用目标方法的前面添加自己的扩展功能代码

       3 在调用目标方法的后面添加自己的扩展代码

       4 在目标功能的前后添加扩展功能代码

       5 在异常处理catch()块中添加自己的扩展代码

  动态代理运行原理图:

 



如何实现动态代理?(如何获取动态代理对象?)

1 Proxy类
    static  getProxyClass(ClassLoader loader, Class<?>.....interface);// 获得代理类的字节码

如何创建一个动态代理类?

Class clazzProxy1 = Proxy.getProxyClass(Collection.class.getClassLoader(), // 通过获取的字节码穿件对象
                                  Collection.class);
System.out.println(clazzProxy1.getName());//$Proxy0  ----->动态代理类的类名

注意:
/*
 * 1获得的动态代理类:没有无惨构造函数
 * 2成员方法为:参数中接口中的方法+Object类中的方法
   3 构造函数的参数为:java.lang.reflect.InvocationHandler



// 方式一:    public static void getProxy() throws Exception    {    Class clazzproxy = Proxy.getProxyClass(Collection.class.getClassLoader(),                                   Collection.class);    Constructor consturctor = clazzproxy.getConstructor(InvocationHandler.class);    Collection proxy = (Collection)consturctor.newInstance(new InvocationHandler(){@Overridepublic Object invoke(Object proxy, Method method, Object[] args)throws Throwable {// TODO Auto-generated method stubreturn null;}});    }

方式二:

  public static void getProxy2() throws Exception    {    Collection prxoy = (Collection)Proxy.newProxyInstance(                    Collection.class.getClassLoader(),                     new Class[]{Collection.class},                     new InvocationHandler() {ArrayList target = new ArrayList();                    @Overridepublic Object invoke(Object proxy, Method method, Object[] args)throws Throwable {// TODO Auto-generated method stub                    //可在此处添加系统功能的额外代码                    Object retval = method.invoke(target, args);//调用目标的方法                    //可在此处添加系统功能的额外代码return retval;}});                }
方式二的改进1
 //方式2 优化 动态的指定目标,和系统功能    public static Object getProxy3(Object target,Advice advice) throws Exception    {    Collection prxoy = (Collection)Proxy.newProxyInstance(                    Collection.class.getClassLoader(),                     new Class[]{Collection.class},                     new InvocationHandler() {                    @Overridepublic Object invoke(Object proxy, Method method, Object[] args)throws Throwable {// TODO Auto-generated method stub                    //可在此处添加系统功能的额外代码                     advice.methodBefor();                    Object retval = method.invoke(target, args);//调用目标的方法                    //可在此处添加系统功能的额外代码                    advice.methodAfter();return retval;}});      return prxoy;    }

方式2的改进2

//方式3 优化 可代理多种类型而非Collection类型    public static Object getProxy4(Object target,Advice advice) throws Exception    {    Object prxoy = Proxy.newProxyInstance(                    target.getClass().getClassLoader(), //获得对象的类加载器                    target.getClass().getInterfaces(), //获取对象上的所有接口                    new InvocationHandler() {                    @Overridepublic Object invoke(Object proxy, Method method, Object[] args)throws Throwable {// TODO Auto-generated method stub                    //可在此处添加系统功能的额外代码                     advice.methodBefor();                     Object retval = method.invoke(target, args);//调用目标的方法                    //可在此处添加系统功能的额外代码                    advice.methodAfter();return retval;}});      return prxoy;    }    

系统功能扩展接口

系统功能扩展接口在的方法通常是安位置命名的(相对目标类方法调用的位置);如:beforeMthod :在目标方法之前扩展的功能   aftermethod :在目标方法之后的运行的功能方法。。。。。。

public interface Advice {public void methodBefor();public void methodAfter();}

功能接口实现类:

当我们需要修改对应的扩展功能时只需在实现类修改

public class MyAdvice implements Advice {@Overridepublic void methodBefor() {// TODO Auto-generated method stub       System.out.println("目标代码--前--的系统功能方法");}@Overridepublic void methodAfter() {// TODO Auto-generated method stub System.out.println("目标代码--后--的系统功能方法");}}


调用代理类对象时注意事项:必须强转为目标类实现的接口类型,而不不是实现类的类型。

如:

<span style="color:#CC0000;">HashSet</span> target = new HashSet();<span style="color:#FF0000;">Set</span> myProxy = (<span style="color:#FF0000;">Set</span>)getProxy4(target, mydvice);//必须是最顶层的父类?<strong><em><span style="color:#FF0000;">不能是</span></em></strong>:<span style="color:#FF0000;">HashSet</span> myProxy = (<span style="color:#FF0000;">Hashset</span>)getProxy4(target, mydvice)myProxy.add("dasa");System.out.println(myProxy.contains("dasa"));

相比静态代理,动态代理的灵活性有了非常大的提高:1 可动态的指定目标类, 2 可动态的修改系统扩展功能。

动态代理结合读取取配置文件生成相应的类对象:来完成代理对不同类对象的代理,是一些框架开发技术的基石

0 0
原创粉丝点击