JVM学习-ClassLoader的父类委托机制

1,什么是类加载器?
        加载类的工具.
 
2,类加载器有什么作用?
        当程序需要的某个类,那么需要通过类加载器把类的二进制加载到内存中.
类加载器也是Java类
 
3,类加载器之间的父子关系和管辖范围.



        ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
        while (classLoader != null) {
            System.out.println(classLoader.getClass().getName());
            classLoader = classLoader.getParent();
        }
        System.out.println(classLoader);




4,类加载器的委托机制:
        1>当Java虚拟机要加载一个类时,到底派出哪个类加载器去加载呢?
                    ①首先当前线程的类加载器去加载线程中的第一个类.
                    ②如果类A中引用了类B,Java虚拟机将使用加载类A的类加载器加载类B
                    ③还可以直接调用ClassLoader.loadClass()方法来指定某个类加载器去加载某个类.
         2>每个类加载器加载类时,又先委托给其上级类加载器.
                    ①当所有祖宗类加载器没有加载到类
,回到发起者类加载器,如果还加载不了,则抛出ClassNotFoundException异常,它不会 去找发起者类加载器的儿子,因为没有getChild()方法,即使有,有那么多的儿子交给那一个呢?所以干错就不叫给儿子处理了.               
        委托机制有什么好处?集中管理,如果我们写了几个类加载器,都去加载某个类,那么内存中就有多份这个类的字节码
        能不能自己写一个类叫java.lang.System?
为了不让我们写System类,类加载采用委托机制,这样可以保证爸爸优先,也就是使用的永远是爸爸的(系统的)
System类,而不是我们写的System类.
 
5,编写自己的类加载器
public static void 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();
    }
 
    /**
     * 加密方法,同时也是解密方法
     * @param ips
     * @param ops
     * @throws Exception
     */
    private static void cypher(InputStream ips ,OutputStream ops) throws Exception{
        int b = -1;
        while((b=ips.read())!=-1){
            ops.write(b ^ 0xff);//如果是1就变成0,如果是0就变成1
        }
    }
然后在新建一个类,通过上面的方法将新建的类的字节码进行加密:
public class ClassLoaderAttachment extends Date { //为什么要继承Date待会再说?
    public String toString(){
        return "hello,itcast";
    } 
}
并在工程里新建一个文件夹,用来保存加密后的class文件.


那么这就需要使用我们自己的类加载器来进行解密了.
public class MyClassLoader extends ClassLoader{
 
    public static void 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();
    }
 
    /**
     * 加密方法,同时也是解密方法
     * @param ips
     * @param ops
     * @throws Exception
     */
    private static void cypher(InputStream ips ,OutputStream ops) throws Exception{
        int b = -1;
        while((b=ips.read())!=-1){
            ops.write(b ^ 0xff);//如果是1就变成0,如果是0就变成1
        }
    }
 
    private String classDir;
 
    @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();
            System.out.println("aaa");
            byte[] bytes = bos.toByteArray();
            return defineClass(bytes, 0, bytes.length);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
 
    public MyClassLoader(){
    }
 
    public MyClassLoader(String classDir){
        this.classDir = classDir;
    }
}
测试运行代码:
 
        Class clazz = new MyClassLoader("myClass").loadClass("ClassLoaderAttachment");
        //此处不能在使用ClassLoaderAttachment因为一旦用了之后,
        //系统的类加载器就会去加载,导致失败,所以该类就继承了Date类了.
        Date date = (Date)clazz.newInstance();
        System.out.println(date);
运行结果:



6,一个类加载器的高级问题:
        我们知道tomcat服务器,是一个大大的java程序,那么它就必须在JVM上运行.
这个大大的java程序内部也写了很多类加载器,它用这些类加载器去加载一些特定的类.注入servlet类.
下面我们新建一个javaweb工程,新建一个servlet程序.
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        response.setContentType("text/html");
        PrintWriter out = response.getWriter();
        ClassLoader classload = this.getClass().getClassLoader();
        while (classload != null) {
            out.println(classload.getClass().getName()+"<br>");
            classload = classload.getParent();
        }
        out.println();
        out.close();
    }
然后配置服务器,部署应用程序,启动tomcat服务器.在页面访问我们这个servlet,在页面打印的
结果如下图所示:
 

这是从小到大排序的.
现在呢?我想把该servlet打成jar包,放在ExtClassLoad类加载器加载的路径.
通过Eclipse即可完成

### 父类委托机制的作用 Java中的父类委托机制(也称为双亲委派模型)的主要作用是确保类的唯一性和安全性。该机制通过将类加载请求优先委托父类加载器来完成,从而保证一个类在整个JVM中只被加载一次。这种设计避免了类的重复加载和潜在的安全隐患,例如恶意代码试图通过自定义类加载器加载已存在的类以替换其行为[^2]。 ### 父类委托机制的工作原理 父类委托机制的工作过程如下:当一个类加载器(ClassLoader)收到类加载的请求时,它首先不会自己尝试加载这个类,而是将请求委托给其父类加载器。这一过程逐层向上,直到顶层的启动类加载器(Bootstrap ClassLoader)。如果父类加载器无法完成加载请求(即在它的搜索路径中没有找到所需的类),子类加载器才会尝试自己去加载这个类[^2]。 #### 举例说明 例如,当需要加载`java.lang.String`类时,系统类加载器(Application ClassLoader)会将请求委托给扩展类加载器(Extension ClassLoader),而扩展类加载器又会将请求委托给启动类加载器(Bootstrap ClassLoader)。启动类加载器负责加载核心Java类库(如`rt.jar`中的类),因此它会加载`String`类。如果启动类加载器找不到所需的类,扩展类加载器会尝试加载,如果扩展类加载器也无法加载,则由系统类加载器尝试加载。 #### 代码示例 以下是一个简单的类加载器实现,展示了如何使用`ClassLoader`来加载类,并遵循双亲委派模型: ```java public class CustomClassLoader extends ClassLoader { private String classPath; public CustomClassLoader(String classPath) { this.classPath = classPath; } @Override protected Class<?> findClass(String name) throws ClassNotFoundException { byte[] classData = loadClassData(name); if (classData == null) { throw new ClassNotFoundException(); } return defineClass(name, classData, 0, classData.length); } private byte[] loadClassData(String className) { String fileName = classPath + File.separatorChar + className.replace('.', File.separatorChar) + ".class"; try (InputStream inputStream = new FileInputStream(fileName); ByteArrayOutputStream byteStream = new ByteArrayOutputStream()) { int data; while ((data = inputStream.read()) != -1) { byteStream.write(data); } return byteStream.toByteArray(); } catch (IOException e) { e.printStackTrace(); return null; } } } ``` 在这个例子中,`CustomClassLoader`继承自`ClassLoader`,并重写了`findClass`方法。当调用`findClass`方法时,它会尝试从指定路径加载类的字节码。如果加载失败,则抛出`ClassNotFoundException`。这个实现遵循了双亲委派模型,因为`findClass`方法只有在父类加载器无法加载类时才会被调用。 ### 总结 父类委托机制通过将类加载请求优先委托父类加载器来完成,确保了类的唯一性和安全性。这种机制避免了类的重复加载,并防止了潜在的安全威胁。通过理解这一机制,开发者可以更好地掌握Java类加载的工作原理,并在需要时自定义类加载器来满足特定需求[^2]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值