2013-01-14 9 views
12

Powiedz, że mam klasę o nazwie NameGenerator. Mogę użyć tego do wygenerowania nazw zgodnie z określoną logiką. Następnie piszę klasę TestNameGeneration metodą, która prosi o list od użytkownika i generuje nazwę zgodnie. Teraz chcę zmienić logikę w klasie NameGeneration i zastosować tę konkretną zmianę bez zatrzymywania aplikacji.Jak zastąpić klasy w działającej aplikacji w java?

Zrobiłem to, aby dowiedzieć się więcej na temat ładowarek klas i czy ktoś może wyjaśnić kluczowe pojęcia, które muszę nauczyć się robić, aby coś takiego lub witryny odniesienia?

Odpowiedz

18

Oto test działania. Co 5 sekund Test.main() przeładowuje test.Test1.klasa z systemu plików i wywołań Test1.hello()

package test; 

public class Test1 { 
    public void hello() { 
     System.out.println("Hello !"); 
    } 
} 

public class Test { 

    static class TestClassLoader extends ClassLoader { 
     @Override 
     public Class<?> loadClass(String name) throws ClassNotFoundException { 
      if (name.equals("test.Test1")) { 
       try { 
        InputStream is = Test.class.getClassLoader().getResourceAsStream("test/Test1.class"); 
        byte[] buf = new byte[10000]; 
        int len = is.read(buf); 
        return defineClass(name, buf, 0, len); 
       } catch (IOException e) { 
        throw new ClassNotFoundException("", e); 
       } 
      } 
      return getParent().loadClass(name); 
     } 
    } 

    public static void main(String[] args) throws Exception { 
     for (;;) { 
      Class cls = new TestClassLoader().loadClass("test.Test1"); 
      Object obj = cls.newInstance(); 
      cls.getMethod("hello").invoke(obj); 
      Thread.sleep(5000); 
     } 
    } 
} 

Uruchom. Następnie zmień i ponownie skompiluj Test1

System.out.println("Hello !!!"); 

podczas testu. widać wyjście Test1.hello zmienił

... 
Hello ! 
Hello ! 
Hello !!! 
Hello !!! 

ten sposób np Tomcat przeładowuje webapps. Ma osobną klasę ClassLoader dla każdej aplikacji internetowej i ładuje nową wersję w nowej klasie ClassLoader. Ten stary jest GCed, tak jak każdy obiekt Java, a także stare klasy.

Pamiętaj, że wczytaliśmy Test1 z TestClassLoader i wywołaliśmy jego pierwszą metodę z odbiciem. Ale wszystkie zależności Test1 będą domyślnie ładowane za pomocą programu ładującego klasy Test1, to znaczy, że wszystkie aplikacje Test1 będą ładowane przez JVM do TestClassLoader.

+0

Dzięki za pomoc. Posiadanie nowej instancji modułu ładującego klasy dla każdego nowego ładowania klas, jak to tutaj zrobiono, rozwiązało problem. –

0

Co z numerem strategy pattern? Może to być lepsze rozwiązanie twojego problemu zamiast używania programów ładujących klasy.

+1

Jak to może pomóc mu dynamicznie ładować zmienioną wersję zajęć? – Andremoniy

+0

Jak wiem, wzór startowy pomógłby wybrać między kilkoma implementacjami, jak wolimy w czasie wykonywania, przez kodowanie do interfejsu. Ale nie możemy wprowadzić nowej implementacji w locie. –

+0

@Prasad można wstrzykiwać w locie: zobacz mój przykład powyżej ze wzorem strategii i DI: setNameGeneration (Class.forName ("com.bean.ClasseA"). NewInstance()) –

1

Istnieją 2 sposoby:

  1. nadpisać ładującego klasy ty używa się przez pierwsze użycie istniejący program ładujący klasy do załadowania aplikacji, a konkretnie do klasy, która wymaga aktualizacji dynamicznej, należy użyć nadpisanego programu ładującego klasy.
  2. Aby użyć frameworka OSGi. W zależności od skali aplikacji, struktura OSGi może nie być dobrym wyborem, ponieważ wymaga przestrzegania konwencji kodowania.

Nadzieja pomaga

+0

+1 dla OSGi. Obsługuje dodawanie/usuwanie pakietów po uruchomieniu w trybie natychmiastowym. – Puce

+0

Dzięki za pierwszą sugestię. Ale nie chcę używać OSGI, ponieważ jedyną intencją tej małej aplikacji jest uczenie się podstawowych pojęć ładowania klas java. –

1

To naprawdę proste, można będzie korzystać z zaletą OOP i obsłudze interfejs, nie trzeba zarządzać classloader można wstrzykiwać realizację z seter:

Utwórz interfejs o nazwie NameGeneration tworzyć realizacji n NameGenerationImpl1, NameGenerationimpl2 na przykład W swojej klasie klienta zdefiniować zmienną:

NameGeneration nameGeneration ; 

a seter:

public void setNameGeneration(NameGeneration nameGeneration) { 
this.nameGeneration = nameGeneration ; 
} 

nameGeneration wygeneruje co chcesz.

Po zmianie algorytmu zmiany realizację wykonując na przykład:

setNameGeneration(new NameGenerationImpl1()) ; 

lub

setNameGeneration(new NameGenerationImpl2()) ; 
+0

Co to jest "POO"? Nie rozumiem tego. –

+0

@Mohammad: Myślę, że on ma na myśli OOP ... –

+0

Jest to w zasadzie wzorzec strategii. –

1

napisać własny classloader:

miałem sukces z this jednej jako baza Powinieneś również zapoznać się z samouczkiem this.

+0

Twoje linki są zepsute. Masz pomysł, jak znaleźć te artykuły? –

0

Możesz napisać własny niestandardowy program ładujący klasy. Jeśli kiedykolwiek pojawi się zmiana w pliku klasy lub zasobu/słoiku zawierającym plik klasy (sprawdź sygnaturę czasową), zniszcz poprzednią instancję klasy ładującej i utwórz nową instancję klasy ładującej, która z kolei załaduje plik nowej klasy.

Powiązane problemy