Java类加载器(二)——自定义类加载器

  用户定制自己的ClassLoader可以实现以下的一些应用:

  1. 自定义路径下查找自定义的class类文件,也许我们需要的class文件并不总是在已经设置好的Classpath下面,那么我们必须想办法来找到这个类,在这种清理下我们需要自己实现一个ClassLoader。
  2. 确保安全性:Java字节码很容易被反编译,对我们自己的要加载的类做特殊处理,如保证通过网络传输的类的安全性,可以将类经过加密后再传输,在加密到JVM之前需要对类的字节码在解密,这个过程就可以在自定义的ClassLoader中实现。
  3. 实现类的热部署:可以定义类的实现机制,如果我们可以检查已经加载的class文件是否被修改,如果修改了,可以重新加载这个类。

  findClass()的功能是找到class文件并把字节码加载到内存中。自定义的ClassLoader一般覆盖改方法,以便使用不同的加载路径,然后调用defineClass()解析字节码。
  defineClass()方法用来将byte字节流解析成JVM能够识别的Class对象。有了这个方法意味着我们不仅仅可以通过class文件实例化对象,还可以通过其他方式实例化对象,如我们通过网络接收到一个类的字节码,拿这个字节码流直接创建类的Class对象形式实例化对象。
  自定义的加载器可以覆盖方法loadClass()以便定义不同的加载机制。
  如果自定义的加载器仅覆盖了findClass(),而未覆盖loadClass(即加载规则一样,但加载路径不同);则调用getClass().getClassLoader()返回的仍然是AppClassLoader!因为真正的load类,还是AppClassLoader.


加载自定义路径下的class文件

  下面演示一个方法加载指定路径下(”D:/workspace_jee/JavaTest/src/”)的类文件。

package classloader;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

public class PathClassLoader extends ClassLoader
{
    private String classPath;

    public PathClassLoader(String classPath)
    {
        this.classPath = classPath;
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException
    {
        byte[] classData = getData(name);
        if (classData == null)
        {
            throw new ClassNotFoundException();
        }
        else
        {
            return defineClass(name, classData, 0, classData.length);
        }
    }

    private byte[] getData(String className)
    {
        String path = classPath + File.separatorChar+className.replace('.', File.separatorChar)+".class";
        try
        {
            InputStream is = new FileInputStream(path);
            ByteArrayOutputStream stream = new ByteArrayOutputStream();
            byte[] buffer = new byte[2048];
            int num = 0;
            while((num = is.read(buffer))!=-1)
            {
                stream.write(buffer,0,num);
            }
            return stream.toByteArray();
        }
        catch(IOException e)
        {
            e.printStackTrace();
        }

        return null;
    }

    public static void main(String args[]) throws ClassNotFoundException, InstantiationException, IllegalAccessException
    {
        ClassLoader pcl = new PathClassLoader("D:/workspace_jee/JavaTest/src/");
        Class c = pcl.loadClass("classloader.SingleClass");
        System.out.println(c.newInstance());
    }
}

  输出结果:classloader.SingleClass@22a7fdef


加载自定义格式的class文件

  如果我们从网路上下载一个class文件的字节码,但是为了安全性在传输之前对这个字节码进行了简单的加密处理,然后再通过网络传输。当客户端接收到这个类的字节码后需要经过解密才能还原成原始的类格式,然后再通过ClassLoader的defineClass()方法创建这个类的实例,最后完成类的加载工作。
  比如上面的代码中,在获取到字节码(byte[] classData = getData(name);)之后再通过一个类似以下的代码:

    private byte[] deCode(byte[] src){
        byte[] decode = null;
        //do something niubility! 精密解码过程
        return decode;
    }

  将字节码解码成所需要的字节码即可。


实现类的热部署

  JVM默认不能热部署类,因为加载类时会去调用findLoadedClass(),如果类已被加载,就不会再次加载。
  JVM判断类是否被加载有两个条件:完整类名是否一样,ClasssLoader是否是同一个
  所以要实现热部署的话,只需要使用ClassLoader的不同实例来加载。
  如果用同一个ClassLoader实例来加载同一个类,则会抛出LinkageError.
  Jsp就是一个热部署的例子。
  如下所示:

package classloader;

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

public class ClassReloader extends ClassLoader
{
    private String classPath;
    String classname = "classloader.SingleClass";

    public ClassReloader(String classpath)
    {
        this.classPath = classpath;
    }

    protected Class<?> findClass(String name) throws ClassNotFoundException{
        byte [] classData = getData(name);
        if(classData == null)
        {
            throw new ClassNotFoundException();
        }
        else
        {
            return defineClass(classname,classData,0,classData.length);
        }
    }

    private byte[] getData(String className)
    {
        String path = classPath+className;
        try
        {
            InputStream is = new FileInputStream(path);
            ByteArrayOutputStream stream = new ByteArrayOutputStream();
            byte[] buffer = new byte[2048];
            int num = 0;
            while((num = is.read(buffer))!=-1)
            {
                stream.write(buffer,0,num);
            }
            return stream.toByteArray();
        }
        catch(IOException e)
        {
            e.printStackTrace();
        }
        return null;
    }

    public static void main(String[] args)
    {
        try
        {
            String path = "D:/workspace_jee/JavaTest/src/classloader/";
            ClassReloader reloader = new ClassReloader(path);
            Class r = reloader.findClass("SingleClass.class");
            System.out.println(r.newInstance());
//            ClassReloader reloader2 = new ClassReloader(path);
            Class r2 = reloader.findClass("SingleClass.class");
            System.out.println(r2.newInstance());
        }
        catch (ClassNotFoundException | InstantiationException | IllegalAccessException e)
        {
            e.printStackTrace();
        }
    }
}

  这段代码的运行结果为:

 java.lang.LinkageError: loader (instance of  classloader/ClassReloader): attempted  duplicate class definition for name: "classloader/SingleClass"
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClass(Unknown Source)
    at java.lang.ClassLoader.defineClass(Unknown Source)
    at classloader.ClassReloader.findClass(ClassReloader.java:26)
    at classloader.ClassReloader.main(ClassReloader.java:62)

  比较两个类是否“相等”,只有在这两个类是由同一个类加载器加载的前提下才有意义,否则,即使这两个类来源于同一个Class文件,被同一个虚拟机加载,只要加载他们的类加载器不同,那这两个类就必定不相等。

时间: 2024-11-10 00:21:49

Java类加载器(二)——自定义类加载器的相关文章

java jvm 类加载器-java中为什么要自定义类加载器

问题描述 java中为什么要自定义类加载器 java为什么要自定义类加载器 面试题 希望大神尽快解答 急用 谢谢了! 解决方案 http://www.iteye.com/topic/240013http://blog.sina.com.cn/s/blog_a1ebbd3501019equ.html 解决方案二: 一.类加载器类别? ?* ?1.java虚拟机自带的加载器? ?* ??根类加载器(Bootstrap,c++实现)? ?* ? ?扩展类加载器(Extension,java实现)? ?

WebServices中使用cxf开发日志拦截器以及自定义拦截器

首先下载一个cxf实例,里面包含cxf的jar包.我下的是apache-cxf-2.5.9 1.为什么要设置拦截器? 为了在webservice请求过程中,能动态操作请求和响应数据, CXF设计了拦截器. 2.拦截器分类 1. 按所处的位置分:服务器端拦截器,客户端拦截器 2. 按消息的方向分:入拦截器,出拦截器 3. 按定义者分:系统拦截器,自定义拦截器 3.拦截器API Interceptor(拦截器接口) AbstractPhaseInterceptor(自定义拦截器从此继承) Loggi

JAVA加密解密:自定义类加载器应用

最近在研究JAVA CLASS LOADING技术,已实现了一个自定义的加载器.对目前自定义加载器的应用,还在探讨中.下面是自定义的CLASSLOADER在JAVA加密解密方面的一些研究. JAVA安全 JAVA是解释执行的语言,对于不同的操作平台都有相应的JVM对字节码文件进行解释执行.而这个字节码文件,也就是我们平时所看到的每一个.class文件. 这是我们大家都知道的常识,也就是由.java文件,经过编译器编译,变成JVM所能解释的.class文件. 而这个过程,在现在公开的网络技术中,利

Java Reflection(十二):动态类加载与重载

原文地址作者: Jakob Jenkov 译者:叶文海(yewenhai@gmail.com) 内容索引类加载器类加载体系类加载动态类加载动态类重载自定义类重载类加载/重载示例 Java允许你在运行期动态加载和重载类,但是这个功能并没有像人们希望的那么简单直接.这篇文章将阐述在Java中如何加载以及重载类. 你可能会质疑为什么Java动态类加载特性是Java反射机制的一部分而不是Java核心平台的一部分.不管怎样,这篇文章被放到了Java反射系列里面而且也没有更好的系列来包含它了. 类加载器 所

struts2自定义拦截器怎么进不去

问题描述 一个破拦截器搞了半天没发现哪里出问题了就是进不去自定义的拦截器里,下面贴代码:struts.xml<?xml version="1.0" encoding="utf-8" ?><!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dt

关于Struts2文件上传与自定义拦截器_java

一.访问或添加request/session/application属性 public String scope() throws Exception{   ActionContext ctx = ActionContext.getContext();   ctx.getApplication().put("app", "应用范围");//往ServletContext里放入app   ctx.getSession().put("ses", &q

SSH-Struts2简单的自定义拦截器MethodFilterInterceptor

SSH-Struts2简单的自定义拦截器MethodFilterInterceptor      最近业余时间工作之余也在学习SSH相关的知识,今天刚刚尝试写了一个基础的Struts2拦截器通过继承MethodFilterInterceptor方法.    一.什么是拦截器 拦截器是动态拦截Action调用的对象.它提供了一种机制可以使开发者可以定义在一个action执行的前后执行的代码,也可以在一个action执行前阻止其执行,同时也提供了一种可以提取action中可重用部分的方式. 二.本文

Java类加载器学习2——自定义类加载器和父类委托机制带来的问题

  一.自定义类加载器的一般步骤   Java的类加载器自从JDK1.2开始便引入了一条机制叫做父类委托机制.一个类需要被加载的时候,JVM先会调用他的父类加载器进行加载,父类调用父类的父类,一直到顶级类加载器.如果父类加载器加载不了,依次再使用其子类进行加载.当然这类所说的父类加载器,不一定他们之间是继承的关系,有可能仅仅是包装的关系.   Java之所以出现这条机制,因为是处于安全性考虑.害怕用户自己定义class文件然后自己写一个类加载器来加载原本应该是JVM自己加载的类.这样会是JVM虚

java自定义类加载器

v前言 java反射,最常用的Class.forName()方法.做毕设的时候,接收到代码字符串,通过 JavaCompiler将代码字符串生成A.class文件(存放在classpath下,也就是eclipse项目中的bin目录里),然后通过java反射机制,获取main方法并执行..class文件名称固定.当 A.class文件更新的时候,问题出现了,main方法的执行结果总和第一次的执行结果相同. v程序流程 代码提交->接收代码->编译成A.class文件->java反射->