Поведение загрузчика классов на Tomcat с несколькими приложениями - PullRequest
12 голосов
/ 10 марта 2010

На сервере Tomcat 5.5 я помещаю класс в системный путь к классам (и изменяю catalina.bat, чтобы выбрать его), или если я помещаю класс в общий каталог lib. Теперь, если у меня есть два разных приложения, использующих один и тот же класс, которые не имеют этого класса в своих каталогах lib / classes WEB-INF, они используют один и тот же экземпляр класса. Я понимаю концепцию, согласно которой загрузчик классов делегирует своему родительскому загрузчику классов для поиска класса, если он не может его найти, поэтому в этом случае, поскольку класс отсутствует в WEB-INF / classes или WEB-INF / lib, Загрузчик классов WebAppX попытается использовать совместно используемый, общий и системный загрузчик классов соответственно.

Однако мне почему-то кажется странным, что два разных приложения могут совместно использовать контекст, используя этот метод. Может ли кто-нибудь помочь мне понять, почему это так. например в приведенном ниже коде каждый из двух сервлетов развернут в отдельных войнах, когда CommonCounter используется совместно, и они могут считывать значения счетчиков, увеличенные другими.

Редактировать

Мне кажется нелогичным, что два отдельных приложения могут совместно использовать контекст таким образом. На самом деле, если у них один и тот же экземпляр класса, они могут даже реализовать многопоточность / синхронизацию в двух разных приложениях, что кажется крайне нелогичным.

package com.test;
public class CommonCounter {

    public static int servlet1;
    public static int servlet2;
}




public class Servlet1 extends javax.servlet.http.HttpServlet implements javax.servlet.Servlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        CommonCounter.servlet1++;
        System.out.println("Other one had "+CommonCounter.servlet2+" hits");
    }   
}



public class Servlet2 extends javax.servlet.http.HttpServlet implements javax.servlet.Servlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        CommonCounter.servlet2++;
        System.out.println("Other one had "+CommonCounter.servlet1+" hits");
    }   
}

1 Ответ

11 голосов
/ 10 марта 2010

Как говорится в комментариях, вы правильно объяснили, почему вы наблюдали наблюдаемое вами поведение.

Ключ в том, как структурированы ClassLoaders. Для двух ClassLoaders внутри одной JVM вполне возможно загрузить каждый класс и, следовательно, он содержит отдельные, независимые копии статических полей. «static» делает что-то «глобальным» для ClassLoader, а не для JVM. Я полагаю, что Tomcat не может содержать ClassLoader уровня контейнера с общими библиотеками и каким-то образом заставлять каждое приложение ClassLoader загружать совместно используемые библиотеки отдельно.

Но это было бы немного расточительно для других распространенных классов, таких как J2EE API и реализации. И в принципе классы не должны зависеть от этой структуры ClassLoader в любом случае.

Вот почему вы не должны помещать зависимости приложений в папки общей библиотеки Tomcat. Это и есть «решение». Он связывает ваше приложение с конкретной настройкой и развертыванием контейнера, что противоречит принципу веб-приложений J2EE. Просто поместите копии зависимостей в WEB-INF / lib для каждого приложения.

Наблюдаемое вами поведение является еще одной причиной, по которой вы этого не делаете: приложения становятся менее изолированными друг от друга. Это не кажется мне нелогичным, но я полагаю, это потому, что я привык к тому, как Tomcat работает и думает об этих вещах.

...