[Java35] class loader, dynamic proxy


1. Introduction to Class Loaders

package com.itheima01.loader;
import org.junit.Test;
import sun.net.spi.nameservice.dns.DNSNameService;
/*
*       0. 类  .java文件   -> .class文件  -> Class对象(内存中)
*                源码           编译后         运行时
*               当.class文件被加载进内存,JVM会在堆中创建一个Class对象
*       1. 类加载器 : 将.class文件加载进内存, 随之产生Class对象,看成输入流(读档)
* 
*       2. 三种类加载器:加载不同包下的类,像前面讲的不同的输入流
*           1. 引导类加载器 bootstrap (用c写的)。核心包下的类(rt.jar)
*           2. 扩展类加载器 extension。ext包下
*           3. 应用/系统 类加载器 application。 第三方编写的类
*       Class对象.getClassLoader(); // 可以获取加载这个Class对象的类加载器
* 
*       补充: 
*           1. 三种类加载器存在继承关系的
*           2. 不叫继承 (叫组合 composition,因为引导类加载器用C写的)
*           3. 为什么一般情况下, 一个.class文件的Class对象只会有一个?
*                  类加载器: 双亲委派机制(这机制保证一个类只会被一个类加载器加载,双亲:父类的父类)
*              什么情况下, 一个.class文件的Class对象会有多个?
*                  多个类加载器去加载同一个.class文件(程序员自定义类加载器,手动操作类加载器去加载)
*/
public class ClassLoaderDemo {
    
    
    public static void main(String[] args) {
    
    
        ClassLoader loader1 = ClassLoaderDemo.class.getClassLoader();        
        System.out.println(loader1); //sun.misc.Launcher$AppClassLoader@18b4aac2 //应用类加载器
        
//1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
        Class clazz = DNSNameService.class; // Ext扩展包下       
        ClassLoader loader2 = clazz.getClassLoader();
        System.out.println(loader2);  //sun.misc.Launcher$ExtClassLoader@45ee12a7 //扩展类加载器
        
//11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
        ClassLoader loader3 = String.class.getClassLoader();
        System.out.println(loader3); // null ,因为不是用java写的。 //引导类加载器
    }

//11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 
    @Test
    public void method01(){
    
      //三种类加载器的关系:继承关系,组合关系,因为跨语言了
        ClassLoader loader1 = ClassLoaderDemo.class.getClassLoader();
        System.out.println(loader1);//sun.misc.Launcher$AppClassLoader@18b4aac2 //应用 
              
        ClassLoader loader2 = loader1.getParent(); //获取父加载器
        System.out.println(loader2);//sun.misc.Launcher$ExtClassLoader@452b3a41 //扩展
              
        ClassLoader loader3 = loader2.getParent();  
        System.out.println(loader3);//null //引导
    }
}

2. Agency rental case

package com.itheima02.proxy;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
/*
*   代理: 一个代理对象(中介) 拥有 被代理对象(房东) 的大部分权限, 控制外界(租客)对被代理对象的访问
*        1. 被代理类对象(房东)
*        2. 代理类对象(中介) 。代理对象 控制 外界对被代理类对象的 '直接'访问,找中介间接访问房东
* 
*      模型:
*           1. 中介 拥有 房东 的 大部分权限:代理类对象 拥有 被代理类对象的大部分方法
*            	解决: 不用继承,用接口
*           2. 中介要控制外界对房东的访问:中介要拥有房东的联系方式, 要有随时找得到房东
*               解决: 将房东作为中介的属性存在
* 
*       问题1:
*           我们没有限制对房东的直接访问, 用代理模式意义何在? 
* 			意义在于, 我们可以在不修改房东类的情况下, 限制访问条件
*           1. 假设 有个类 属于 某个jar包, 这个类改不了的
*           2. 我们又想改这个类的某些方法的访问条件 -> 代理模式
* 
*       问题2:
*           继承同样也是实现上述需求, 为什么要用代理模式? 代理模式的扩展性更强
*           继承: 只扩展了对FangDong类的访问限制
*           代理: 扩展了对Fd接口 所有实现类对象的 访问限制(如还代理其他房东)
*       BufferedReader : 代理模式(又称装饰设计模式)
*  new BufferedReader(new FileReader(a.txt)); //里面new FileReader(a.txt)是房东 //BufferedReader是中介,也可以代理除了 FileReader还有Reader其他实现类对象 (多态)
*/
public class ProxyDemo {
    
    
    public static void main(String[] args) throws FileNotFoundException {
    
            
        FangDong fangDong = new FangDong(); //使用的时候: 先创建被代理类对象        
        ZhongJie zh = new ZhongJie(fangDong); //接着: 创建代理类对象        
        zh.zufang(999); //钱不够, 房子不租给你 //访问中介,实际上通过中介访问房东
    }
}

Insert picture description here

package com.itheima02.proxy;

class FdSon extends FangDong{
    
     //继承:也没改FangDong类,但只扩展了对FangDong类的访问限制,只能单继承
    @Override
    public void zufang(int money) {
    
    
        if(money > 1000){
    
    
            super.zufang(money);
        }
    }
}

//11111111111111111111111111111111111111111111111111111111111111111111111111111
public class FangDong implements Fd{
    
    
    public void zufang(int money){
    
    
        System.out.println("房东出租房子: " + money);
    }
    public void maifang(int money){
    
    
        System.out.println("房东卖掉房子:" + money);
    }
    public void buyfang(int money){
    
     //这权限不给中介
        System.out.println("房东买入房子作为投资 :" + money);
    }
}
interface Fd{
    
     //房东类中的方法抽取
    void zufang(int money);
    void maifang(int money);
}

//11111111111111111111111111111111111111111111111111111111111111111111111111111
class ZhongJie implements Fd{
    
     //fd接口也可以其他类实现,所以ZhongJie类构造里传入其他类相当于多继承
    Fd fd; //房东对象   定义并没有实例化向string num
    public ZhongJie(Fd fd){
    
     //fd实例化,当我们创建中介对象的时候,必须要指定他所代理的房东对象
        this.fd = fd;
    }    
    @Override
    public void zufang(int money) {
    
    
        if(money > 1000){
    
     //租客找中介租房, 当钱>1000, 中介就会帮租客联系房东签合同
            fd.zufang(money);
        }else{
    
    
            System.out.println(" 钱不够, 房子不租给你");
        }
    }
    @Override
    public void maifang(int money) {
    
    
    }
}

//111111111111111111111111111111111111111111111111111111111111111111111111111111
class MyProxy implements Fd{
    
     //JVM底层代理类,为了解释proxy.zufang(1001)调用h.invoke 。这行Fd接口已通过Proxy.newProxyInstance方法中第二个参数传入了。
    InvocationHandler h;
    public MyProxy(InvocationHandler h){
    
     //h是实现类对象,不是接口
        this.h = h;
    }
    @Override
    public void zufang(int money) {
    
     //动态变化内容, JVM不能写, 交给程序员如下        
//    Method method = this.getClass().getMethod("zufang",int.class); //方法的反射,区分zufang还是maifang
//    Object[] param = {money}; 

//      h.invoke(this,method,param);
    }
    @Override
    public void maifang(int money) {
    
    
//        h.invoke();
    }
}

3. Dynamic proxy

newProxyInstance (three parameters).zufang() calls invoke (three parameters)

package com.itheima03.dynamic;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
/*
*  动态代理(比上面多一个反射而已): 动态: 代理类不是静态定义,是动态生成的
*       1. 静态定义: 把这个类写死了即上面class ZhongJie
*       2. 动态生成: 这个类(中介类)压根就没写,在运行时由JVM动态生成(肯定涉及反射)。前提: 必须有接口 
*/
public class DynamicDemo {
    
     //Dynamic反义词static
    public static void main(String[] args) {
    
    
        FangDong fd = new FangDong(); //先看上房东房子
        
        /*
        //没有中介类 //Proxy.newProxyInstance这个方法最终目的: 创建一个代理类对象!!!
                    1. 动态生成一个类就是代理类
                    2. 并且创建这个类的对象
        * static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
              1. loader : 类加载器(加载中介类的),这个中介类不存在,需要JVM动态生成
                    一般是应用类加载器(第三方类)。 直接 被代理类 的类加载器
                    
              2. interfaces : 接口的Class对象(让中介类实现的)
                    就是 被代理类(房东类) 所实现的接口

  class对象在生成期间需要一个类加载器(将字节码文件加载生成class对象),这个中介类需要实现接口从而拥有被代  理类方法,所以上面1,2。接下来需要重写接口里方法,所以下面3。
  
              3.InvocationHandler  h  : 调用处理器 (InvocationHandler是一个接口)
                   invoke方法可以看成 代理/中介类 对接口所有方法 的重写
        */
        ClassLoader loader = fd.getClass().getClassLoader();  //对应上面1      
        Class<?>[] interfaces = fd.getClass().getInterfaces(); //获取此类实现的所有接口 //对应上面2
        
//   Class<?>[] interfaces = {Fd.class}; //Class<?>[] interfaces = {com.itheima03.dynamic.Fd.class}; //效果同上行 //实现接口后要重写方法,重写不了,因为中介类运行时才会存在,所以需要上面的3。

// System.out.println(loader); //sun..$APPClassLoader.. //应用类加载器
// System.out.println(Arrays.toString(interfaces)); //[interface.com.itheima03.dynamic.Fd] //就是FD接口


        InvocationHandler h = new InvocationHandler() {
    
      //h是InvocationHandler接口实现类对象,只有这样的h才能传入Proxy.newProxyInstance这个方法
            /*
            *   参数:
            *       1. proxy : 当前代理类对象(几乎没用,因为下面有Fd proxy)
            *       2. method: 代理类对象当前调用的方法
            *       3. args: 代理类对象当前调用方法传入的参数列表
            */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
    
                System.out.println(method.getName());
                System.out.println(Arrays.toString(args));
                return null;
            }
        };
        
//1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111      
//Object obj = Proxy.newProxyInstance(loader, interfaces, h); //如上是Proxy.newProxyInstance的三个参数获取
         Fd proxy = (Fd) Proxy.newProxyInstance(loader, interfaces, h); //这行=号后面相当于new MyProxy 
//平时MyProxy proxy = new MyProxy(.) ,因为MyProxy是JVM随机命名,所以用Fd(这个Fd是FangDong类下面定义的)
     
		proxy.zufang(1001); //调用h.invoke即本文件上面 ,父类引用h,子类new InvocationHandler()
        //proxy.maifang(2001); //接口回调(callback)(接口形式的多态):相当于直接调用本文件上面invoke方法
    }
}

Insert picture description here

package com.itheima03.dynamic;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/*
*   动态代理 比之 普通代理:  好处在于 不用事先定义类, 代理类在运行时动态生成 (反射)
*       运用场景: 被代理对象(房东) 有 10000万个方法, 你只想修改其中一个
*/
public class Demo02 {
    
    
    public static void main(String[] args) {
    
    
        FangDong fd = new FangDong(); //先看上房东房子
        ClassLoader loader = fd.getClass().getClassLoader();
        Class<?>[] interfaces = fd.getClass().getInterfaces();
        InvocationHandler h = new InvocationHandler() {
    
    
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
    
                String name = method.getName();
                if("zufang".equals(name)){
    
    
                    int money = (int) args[0]; // Object[] param = {money};
                    if(money > 1000){
    
    
                        fd.zufang(money);
                    }else{
    
    
                        System.out.println("钱不够...");
                    }
                    
                }/*else if("maifang".equals(name)){
                }*/else{
    
     
                    method.invoke(fd,args); //中介类.其他方法,走这行,即其他9000个方法依然交给房东fd自己处理
                }
                return null;
            }
        };

//1111111111111111111111111111111111111111111111111111111111111111111111111111111
        Fd proxy = (Fd) Proxy.newProxyInstance(loader,interfaces,h);
        proxy.zufang(888);
//        proxy.maifang(3000);
    }
}

Station B/Zhihu/WeChat Official Account: Code Farm Programming Record
Insert picture description here

Guess you like

Origin blog.csdn.net/weixin_43435675/article/details/108461158