java高分局之类热加载

java高分局之类热加载

有时候我们会遇到热加载的需求,即在修改了类的代码后,不重启的情况下动态的加载这个类。

先介绍一个java的类加载器 之前写过一篇关于类加载器的,请访问此地址:

http://blog.csdn.net/maosijunzi/article/details/42555617

看到这个问题,我想到有两种方式来实现:(能力有限,不知道还有没有其他的方案)

1:把原来的类信息卸载掉,然后重新加载此类。

2:新建一个类加载器(new),重新加载此类,不管原来的类信息,等待垃圾回收它。

第一种方案是行不通的,因为java并没有给我们提供手动卸载类信息的功能,也就是运行时方法区内的类信息是不能卸载的,

除非这个类已经不再使用,这时GC会自动把它回收掉。所以我们只能通过第二种方案来实现。

几个问题

在使用这种方案实现之前我们考虑几个问题,注意:下面的问题是在这种方案下提出的,也在这种方案下回答,不适用与其他方案。

1:是不是所有的类都能进行热加载呢?

我们程序的入口类都是系统类加载器加载的,也就是AppClassLoader加载的。当你重新使用系统类加载器加载这个类的时候

是不会被重新加载的。因为虚拟机会检测这个类是否被加载过,如果已经被加载过,那么就不会重新加载。所以由系统类加载器加载的

类,是不能进行热加载的。只有使用我们自定义的类加载器加载的类才能热加载。

2:自定义类加载器的父加载器应该是哪个加载器?

我们自定义类加载器的父加载器有两种选择,一个是系统类加载器(AppClassLoader),另一种是扩展类加载器(ExtClassLoader)。

首先我们要知道,扩展类加载器是系统类加载器的父加载器。

我们先考虑第一种,如果父加载器是系统类加载器(当然如果你不指定父加载器,默认就是系统类加载器),那么会出现一个现象,这个动态

加载的类不能出现在系统类加载器的classpath下。因为如果在系统类加载器的classpath下的话,当你用自定义类加载器去加载的时候,会先

使用父类加载器去加载这个类,如果父类加载器可以加载这个类就直接加载了,达不到热加载的目的。所以我们必须把要热加载的类从classpath

下删除。

在考虑第二种,如果父加载器是扩展类加载器,这时候热加载的类就可以出现在classpath下,但又会出现另一种现象,这个类中不能引用由系统

类加载器加载的类。因为这时候,自定义类加载器和系统类加载器是兄弟关系,他们两个加载器加载的类是互不可见的。这个问题貌似是致命的。

除非热加载的类中只引用了扩展类加载器加载的类(大部分javax开头的类)。

所以我们自定义的类加载器的父加载器最好是系统类加载器。

3:热加载的类要不要实现接口?

要不要实现接口,要根据由系统类加载器加载的类A中是否有热加载类B的引用。如果有B的引用,那么加载A的时候,系统类加载器就会去加载B,这时候B

不在classpath下,就会加载报错。这时候我们就需要为B定义一个接口,A类中只有接口的引用。这样我们使用系统类加载器加载接口,使用自定义

类加载器加载B类,这样我们就可以热加载B类。

如果A中没有B的引用的话,就灵活多了,可以实现接口,也可以不实现接口,都可以进行热加载。

解决了这三个问题后,实现代码就已经在我们心中了。下面直接看代码:
包结构如下:

这里写图片描述
这里写图片描述

源码:

package com.load.custom;

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

public class MyClassLoader extends ClassLoader {
    private String classpath;

    public MyClassLoader(String classpath){
        super(ClassLoader.getSystemClassLoader());
        this.classpath = classpath;
    }

    @Override
    public Class<?> findClass(String name) {
        System.out.println("加载类==="+name);
        byte[] data = loadClassData(name);
        return this.defineClass(name, data, 0, data.length);
    }

    public byte[] loadClassData(String name) {
        try {
            name = name.replace(".", "//");
            FileInputStream is = new FileInputStream(new File(classpath + name + ".class"));
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            int b = 0;
            while ((b = is.read()) != -1) {
                baos.write(b);
            }
            is.close();
            return baos.toByteArray();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}
package com.load.manager;

/**
 * @author chuer
 * @Description: 此类的子类需要动态更新
 * @date 2015年5月13日 上午11:09:24 
 * @version V1.0
 */
public interface BaseManager {
    public void logic();
}
package com.load;

import com.load.custom.MyClassLoader;
import com.load.manager.BaseManager;

public class LoadInfo {

    private MyClassLoader myLoader;
    private long loadTime;
    private BaseManager manager;

    public LoadInfo(MyClassLoader myLoader,long loadTime){
        this.myLoader = myLoader;
        this.loadTime = loadTime;
    }
    public MyClassLoader getMyLoader() {
        return myLoader;
    }
    public void setMyLoader(MyClassLoader myLoader) {
        this.myLoader = myLoader;
    }
    public long getLoadTime() {
        return loadTime;
    }
    public void setLoadTime(long loadTime) {
        this.loadTime = loadTime;
    }
    public BaseManager getManager() {
        return manager;
    }
    public void setManager(BaseManager manager) {
        this.manager = manager;
    }



}
package com.load;

public class Logic {
    public void logic(){
        System.out.println("logic");
    }

}
package com.load;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;

import com.load.custom.MyClassLoader;
import com.load.manager.BaseManager;

public class ManagerFactory {

    /**
     *记录热加载类的加载信息 
     */
    private static final Map<String,LoadInfo> loadTimeMap = new HashMap<>();

    public static final String CLASS_PATH = "D:/";

    public static final String MY_MANAGER = "com.load.manager.MyManager";


    public static BaseManager getManager(String className){
        File loadFile = new File(CLASS_PATH+className.replaceAll("\\.","/")+".class");
        long lastModified = loadFile.lastModified();

        //查看是否被家再过 ,如果没有被家再过则加载
        if(loadTimeMap.get(className) == null){
            load(className,lastModified);
        }else if(loadTimeMap.get(className).getLoadTime() != lastModified){//如果被加载过,查看加载时间,如果该类已经被修改,则重新加载
                load(className,lastModified);
        }

        return loadTimeMap.get(className).getManager();
    }


    private static void load(String className,long lastModified){

        MyClassLoader myLoader = new MyClassLoader(CLASS_PATH);
        Class<?> loadClass = null;
        try {
            loadClass = myLoader.loadClass(className);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        BaseManager manager = newInstance(loadClass);
        LoadInfo loadInfo2 = new LoadInfo(myLoader,lastModified);
        loadInfo2.setManager(manager);
        loadTimeMap.put(className, loadInfo2);
    }


    private static BaseManager newInstance(Class<?> cls){
        try {
            return (BaseManager)cls.getConstructor(new Class[]{}).newInstance(new Object[]{});
        } catch (InstantiationException | IllegalAccessException
                | IllegalArgumentException | InvocationTargetException
                | NoSuchMethodException | SecurityException e) {
            e.printStackTrace();
        }
        return null;
    }
}
package com.load;

import com.load.manager.BaseManager;

public class MsgHandler implements Runnable{



    @Override
    public void run() {

        while(true){
            BaseManager manager = ManagerFactory.getManager(ManagerFactory.MY_MANAGER);
            manager.logic();

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        new Thread(new MsgHandler()).start();

//      File loadFile = new File("D:/"+ManagerFactory.MY_MANAGER.replaceAll(".","/")+".class");
//      long lastModified = loadFile.lastModified();
//      System.out.println("com.load.manager.MyManager".replaceAll("\\.","/"));
    }

}

运行后,修改MyManager类后输出如下:

加载类===com.load.manager.MyManager
bbb
logic
bbb
logic
bbb
logic
bbb
logic
bbb
logic
加载类===com.load.manager.MyManager
aaaaaaaaaaaaaaaaaaaa
logic
aaaaaaaaaaaaaaaaaaaa
logic
aaaaaaaaaaaaaaaaaaaa
logic
aaaaaaaaaaaaaaaaaaaa
logic

可见,修改后重新加载了MyManager。到此热加载成功。

还有另一种方案可以进行类的热加载,
http://blog.csdn.net/maosijunzi/article/details/45918477

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值