代码之家  ›  专栏  ›  技术社区  ›  Chetan Shirke

在tomcat中重新加载类文件

  •  1
  • Chetan Shirke  · 技术社区  · 13 年前

    我正在运行时创建一个类文件。

    我想用类加载器中更新的类文件替换现有的类文件。

    它类似于热交换(例如JRebel),避免了服务器重启和重新部署。

    我为tomcat找到了context.xml方法来重新加载上下文。 但在生产环境的情况下,它并不是很有用。

    我们可以在运行时向ClassLoader注册类吗? 请建议是否有任何替代方案可以在运行时重新加载类。


    我正在使用以下代码来检索当前的classLoader。

    ClassLoader classLoader = LoggingAspect.class.getClassLoader();
    

    下面是load类方法的实现。

    public class AspectClassLoader extends ClassLoader{
    
    @Override
    public synchronized Class<?> loadClass(String name) throws ClassNotFoundException
    {
        String customLoadClass = "com.log.LoggingAspect";
        try
        {
            if(!customLoadClass.equals(name))
            {
                return super.loadClass(name);
            }
            else
            {
                URL classLoadUrl = new URL(this.reloadClassUrl);
                URLConnection connection = classLoadUrl.openConnection();
                InputStream input = connection.getInputStream();
                ByteArrayOutputStream buffer = new ByteArrayOutputStream();
                int data = input.read();
    
                while(data != -1){
                    buffer.write(data);
                    data = input.read();
                }
    
                input.close();
    
                byte[] classData = buffer.toByteArray();
                return defineClass(name, classData, 0, classData.length);
            }
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
        return null; 
    }
    

    为了重新加载类,我使用以下代码。

    AspectClassLoader urlClsLoader = new AspectClassLoader(classLoader);
    Class aspect = urlClsLoader.reloadClass("com.log.LoggingAspect", true, new String("file:"+className));
    

    我的类加载器的重载方法是

    public synchronized Class<?> reloadClass(String name, boolean resolve, String reloadClassUrl) throws ClassNotFoundException
    {
        this.reloadClassUrl = reloadClassUrl;
        return this.loadClass(name, resolve);
    }
    

    在重新加载之后,如果我创建了LoggingAspect的新实例,它仍然会给我旧的实例。请提出建议。

    Class aspect = urlClsLoader.reloadClass("com.log.LoggingAspect", true, new String("file:"+className));
            com.log.aspect.Aspect aspectObj = (com.log.aspect.Aspect)aspect.newInstance();
    

    尽管如此,我还是得到了老例子。

    请说明类加载器为什么不加载修改后的类?

    1 回复  |  直到 12 年前
        1
  •  2
  •   Aaron Digulla    13 年前

    这是可能的,但很复杂。你需要做的是创建一个新的 URLClassLoader 用电流 ClassLoader 作为父母。将文件夹的URL与您的类(不包括包文件夹)一起添加到新的 URL类加载器

    接下来,您必须进行修补 loadClass() 在打电话之前返回您的新课程 parent.loadClass() (否则,将使用现有类,并忽略更新后的类)。

    当你想的时候,它会变得复杂 使用 新班级。为此,您必须使用新的类加载器创建它,并确保每个人都使用该类型的此实例。

    如果WAR中不存在该类,那么它可能更安全(也更容易调试)。不使用类型,而是使用 URL类加载器 并创建它的一个实例。因此,使用此类的代码如下所示:

    IService service = createService();
    

    哪里 IService 是一个接口,用于定义生成的类支持的方法。这样,您就可以编写使用这些方法的代码,而无需实际实现。