annotations
Leonard Milcin Jr. - 06-07-2007 00:04
annotations
Witam!
Czy któryś z grupowiczów orientuje się w jaki sposób można uzyskać listę klas zawierających konkretną adnotację?
Np. mam sobie coś takiego:
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface Plugin {
String value();
}
oraz klasę:
@Plugin("testTask") public class TestTask extends BaseTask {
}
W jaki sposób mogę gdzieś indziej uzyskać (np. wylistować) wszystkie klasy z załączoną adnotacją @Plugin? Zakładam oczywiście, że pluginy w tym wypadku znajdują się w classpath.
Pozdrawiam, Leonard
Artur Zabronski - 06-07-2007 00:04
Leonard Milcin Jr. wrote: > > W jaki sposób mogę gdzieś indziej uzyskać (np. wylistować) wszystkie > klasy z załączoną adnotacją @Plugin? Zakładam oczywiście, że pluginy w > tym wypadku znajdują się w classpath. > IMHO nie ma takiej możliwości, najlepszym sposobem chyba jest pobranie listy plików .class np z JARa ładowanie ich poprzez Class.forName() i sprawdzanie czy zawiera taką adnotację.
-- Pozdrawiam, Artur
Leonard Milcin Jr. - 06-07-2007 00:04
Artur Zabronski wrote: > Leonard Milcin Jr. wrote: >> >> W jaki sposób mogę gdzieś indziej uzyskać (np. wylistować) wszystkie >> klasy z załączoną adnotacją @Plugin? Zakładam oczywiście, że pluginy w >> tym wypadku znajdują się w classpath. >> > IMHO nie ma takiej możliwości, najlepszym sposobem chyba jest pobranie > listy plików .class np z JARa ładowanie ich poprzez Class.forName() i > sprawdzanie czy zawiera taką adnotację. >
Hmm... tak mi się wydawało bo w sumie klasa nie powinna być ładowana zanim nie zostanie gdzieś użyta. Wskazywałby na to np. fakt, że bloki static{} są wykonywane gdy klasa jest pierwszy raz użyta gdzieś w kodzie.
Z drugiej jednak strony wiele frameworków robi właśnie dokładnie to. Np. JPA w jakiś sposób dostaję listę klas zawierającą adnotację @Entity, etc. Myślałem, że może istnieje jakiś prosty sposób...
Pozdrawiam, Leonard
Artur Zabronski - 06-07-2007 00:04
Leonard Milcin Jr. wrote: > > Hmm... tak mi się wydawało bo w sumie klasa nie powinna być ładowana > zanim nie zostanie gdzieś użyta. Wskazywałby na to np. fakt, że bloki > static{} są wykonywane gdy klasa jest pierwszy raz użyta gdzieś w kodzie. > Również można to wywnioskować po ClassNotFoundException dopiero w momencie użycia :-) > Z drugiej jednak strony wiele frameworków robi właśnie dokładnie to. Np. > JPA w jakiś sposób dostaję listę klas zawierającą adnotację @Entity, > etc. Myślałem, że może istnieje jakiś prosty sposób... > W JPA standalone musisz w persistence.xml wskazać klasy-encje poprzez znacznik class np <class>foo.bar.MyEntity</class>, w JPA z EJB po prostu serwer aplikacji sprawdza każdą klasę w archiwum EJB i rozpoznaje EJB i encje.
Sprawdziłem w źródłach GlassFish-a i jest właśnie tak, są szukane .class i ładowane przez URL-a a później sprawdzane jest czy klasa zawiera odpowiednie adnotacje.
-- Pozdrawiam, Artur
Jacek Laskowski - 06-07-2007 00:04
Artur Zabronski wrote:
> Sprawdziłem w źródłach GlassFish-a i jest właśnie tak, są szukane .class > i ładowane przez URL-a a później sprawdzane jest czy klasa zawiera > odpowiednie adnotacje.
Jest jeszcze inny sposób, ale nie mogę sobie teraz przypomnieć. Ładowanie klas powoduje odłożenie ich na PermGen, więc dla wielu klas a niewielu encji może to być kosztowne pamięciowo. W xbean-reflect wydaje mi się, że jest efektywniejszy sposób niż Class.forName, ale za późno, żebym sobie przypomniał.
Pewnie Piotr Kobzda - spec od asm/cglib - wiedziałby jakie mamy faktycznie efektywniejsze sposoby, ale temat annotations to pewnie go nie zainteresuje. Ale jak umieściłoby się "asm/cglib do bani", czy podobnie to, kto wie. ;-)
Jacek
-- Jacek Laskowski http://www.JacekLaskowski.pl
Artur Zabronski - 06-07-2007 00:06
Jacek Laskowski wrote: > > Jest jeszcze inny sposób, ale nie mogę sobie teraz przypomnieć. > Ładowanie klas powoduje odłożenie ich na PermGen, więc dla wielu klas a > niewielu encji może to być kosztowne pamięciowo. W xbean-reflect wydaje > mi się, że jest efektywniejszy sposób niż Class.forName, ale za późno, > żebym sobie przypomniał. > Nie mam czasu sprawdzać ale może jest to rozwiązane przez bezpośrednie odczytanie pliku klasy i podglądniecie jego struktury bez ładowania jej do JVM? To odkładanie klas w PermGen można doświadczyć w połączeniu Tomcat + Hibernate przy kilkunastu przeładowaniach aplikacji występuje OOM PermGen space i trzeba restartować Tomcata.
-- Pozdrawiam, Artur
Piotr Kobzda - 07-07-2007 00:03
Jacek Laskowski wrote:
> Ładowanie klas powoduje odłożenie ich na PermGen, więc dla wielu klas a > niewielu encji może to być kosztowne pamięciowo.
Czy to PermGen, czy gdzieś indziej, to nie ma tu znaczenia, klas których jeszcze nie wczytano nie znajdziesz w runtime.
> W xbean-reflect wydaje > mi się, że jest efektywniejszy sposób niż Class.forName, ale za późno, > żebym sobie przypomniał.
Jeśli tylko wśród załadowanych klas szukać, to czasem efektywniejsza może okazać się instrumentacja (Instrumentation.getAllLoadedClasses()), choć niekoniecznie musi tak też być -- np. gdy interesują nas klasy tylko pewnego class-loader'a. Poza tym Class.forName() pozwala także odszukać klasy jeszcze niezaładowane, więc efektywność nie ma tu w zasadzie większego znaczenia (można by tu co najwyżej zasugerować jeszcze użycie wariantu Class.forName() z initialize=false, co niepotrzebnych inicjalizacji klas pozwoliłoby uniknąć, choć to mało ważny drobiazg wydaje mi się już...).
> Pewnie Piotr Kobzda - spec od asm/cglib - wiedziałby jakie mamy > faktycznie efektywniejsze sposoby, ale temat annotations to pewnie go > nie zainteresuje.
Heh, interesuje, a jakże. :) Trudno jednak dodać coś do tematu, który właściwe został już wyczerpany... :)
Możliwości są tu generalnie dwie: 1) wywnioskować z CLASSPATH jakie klasy znajdują się w otoczeniu i wszystkie je przeanalizować; 2) dostarczyć gotowej listy klas, które mają oczekiwane własności (w postaci pliku, rozszerzenia manifestu, jakiejś specjalnej klasy ...).
Możliwość 1) została już dość szczegółowo opisana, dodać można jedynie, że właśnie ASM ładnie może wspomóc analizę class-file'i bez ich ładowania (o czym Artur wspomniał). Zawsze jednak będzie to metoda niedoskonała, gdyż mocno zależy od otoczenia, ograniczając jednocześnie możliwości dostarczania klas JVMie.
Osobiście bardziej skłaniałbym się zatem do 2), zwłaszcza, że listę taką można sobie wygodnie generować w czasie kompilacji prostym procesorem adnotacji (patrz: javax.annotation.processing).
piotr
Piotr Lipski - 07-07-2007 00:03
> Czy któryś z grupowiczów orientuje się w jaki sposób można uzyskać listę > klas zawierających konkretną adnotację? > > Np. mam sobie coś takiego: > > @Retention(RetentionPolicy.RUNTIME) > @Target(ElementType.TYPE) > public @interface Plugin { > > String value(); > > } > > oraz klasę: > > @Plugin("testTask") > public class TestTask extends BaseTask { > > } > > W jaki sposób mogę gdzieś indziej uzyskać (np. wylistować) wszystkie > klasy z załączoną adnotacją @Plugin? Zakładam oczywiście, że pluginy w > tym wypadku znajdują się w classpath.
można to zrobić tak jak poniżej. U mnie działa ;-)
PL
import java.lang.annotation.Annotation;
import javax.tools.*; import java.util.*; import java.io.*; import javax.annotation.processing.*; import javax.lang.model.element.*; import javax.lang.model.util.*; import javax.lang.model.type.*; import static javax.lang.model.SourceVersion.*; import static java.util.Arrays.*; import static java.util.Collections.*;
public class ListPlugins { public static void main(String[] args) throws Exception { JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); final StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
fileManager.setLocation(StandardLocation.CLASS_PAT H, asList(new File("plugins.jar")));//, new File("../../annotations_classes"))); final Iterable<JavaFileObject> jfos = fileManager.list(StandardLocation.CLASS_PATH, "", EnumSet.of(JavaFileObject.Kind.CLASS), true); List<String> classes = new ArrayList<String>(){{ for(JavaFileObject jfo : jfos) add(fileManager.inferBinaryName(StandardLocation.C LASS_PATH, jfo)); }};
List<String> options = asList(/*"-verbose",*/ "-classpath", "plugins.jar;../../annotations_classes"); JavaCompiler.CompilationTask task = compiler.getTask(null, null, null, options, classes, null);
@SupportedSourceVersion(RELEASE_6) @SupportedAnnotationTypes("*") class MyProcessor extends AbstractProcessor { public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnvironment) { if (!roundEnvironment.processingOver()) { final Elements elements = processingEnv.getElementUtils(); final Types types = processingEnv.getTypeUtils(); DeclaredType annotationTypeElement = types.getDeclaredType(processingEnv.getElementUtil s().getTypeElement("pl.Plugin"));
Set<Element> result = Collections.emptySet(); ElementScanner6<Set<Element>, DeclaredType> scanner = new ElementScanner6<Set<Element>, DeclaredType>(result){ Set<Element> annotatedElements = new LinkedHashSet<Element>();
@Override public Set<Element> scan(Element e, DeclaredType p) { java.util.List<? extends AnnotationMirror> annotationMirrors = processingEnv.getElementUtils().getAllAnnotationMi rrors(e); for (AnnotationMirror annotationMirror : annotationMirrors) { if (annotationMirror.getAnnotationType().equals(p)) annotatedElements.add(e); } e.accept(this, p); return annotatedElements; } };
for (Element element : roundEnvironment.getRootElements()) { result = scanner.scan(element, annotationTypeElement); }
System.out.println(result); } return false; } }
task.setProcessors(asList(new MyProcessor())); task.call();
fileManager.close(); } }
Piotr Kobzda - 07-07-2007 00:03
Piotr Lipski wrote:
> można to zrobić tak jak poniżej. U mnie działa ;-)
Jeśli jeszcze Filer'a dorzucić, to może i nie tylko u Ciebie będzie potem działać... ;-)
Tak nawiasem jeszcze drobna rada dla OP: procesor adnotacji można wpiąć wprost do javac'a (opcja -processor), lub do apt'a, wtedy pozostanie już tylko to w środku... :)
piotr
lipski@gmail.com - 07-07-2007 00:03
On Jul 6, 2:43 am, Piotr Kobzda <p...@gazeta.pl> wrote: > Piotr Lipski wrote: > > można to zrobić tak jak poniżej. U mnie działa ;-) > > Jeśli jeszcze Filer'a dorzucić, to może i nie tylko u Ciebie będzie > potem działać... ;-) > > Tak nawiasem jeszcze drobna rada dla OP: procesor adnotacji można wpiąć > wprost do javac'a (opcja -processor), lub do apt'a, wtedy pozostanie już > tylko to w środku... :) > > piotr
Filer tu do niczego nie jest potrzebny. Chyba, że nie zrozumiałem Twojej intencji. Jeśli miałeś na myśli, że wygląda to skomplikowanie - zgadzam się - może ktoś zaproponuje coś prostszego.
PL
Piotr Kobzda - 07-07-2007 00:03
lipski@gmail.com wrote:
> Filer tu do niczego nie jest potrzebny. Chyba, że nie zrozumiałem > Twojej intencji. Jeśli miałeś na myśli, że wygląda to skomplikowanie - > zgadzam się - może ktoś zaproponuje coś prostszego.
Chodziło mi realizację mojej sugestii w odrębnej gałęzi tego wątku. Czyli zapis znalezionych przez procesor klas do np. pliku .properties, po to by w przyszłości odczytać je za jednym zamachem (bez szukania w runtime).
piotr
Artur Zabronski - 07-07-2007 00:03
Piotr Kobzda wrote: > > Chodziło mi realizację mojej sugestii w odrębnej gałęzi tego wątku. > Czyli zapis znalezionych przez procesor klas do np. pliku .properties, > po to by w przyszłości odczytać je za jednym zamachem (bez szukania w > runtime). > Jeszcze na myśl przychodzi mi taki pomysł: utworzyć instancje class loadera, nim ładować te klasy i sprawdzać pod jakimś kątem a na końcu go zwolnić i wtedy klasy załadowane przez niego również chyba zostaną zwolnione?
-- Pozdrawiam, Artur
Piotr Kobzda - 07-07-2007 00:03
lipski@gmail.com wrote:
> Filer tu do niczego nie jest potrzebny. Chyba, że nie zrozumiałem > Twojej intencji. Jeśli miałeś na myśli, że wygląda to skomplikowanie - > zgadzam się - może ktoś zaproponuje coś prostszego.
Ha! Ja teraz dopiero widzę Twoją intencję ;) jest jednak nieco inna od mojej... :)
Nie przyjrzałem się dokładnie jak budujesz JavaCompiler, przyjmując, że przetwarzasz źródła (ja dotąd, tylko tak go używałem), a Ty pokazałeś jak nim przetwarzać adnotowane klasy, wybacz. :-)
W ramach powrotu do rozmowy o tym samym ;) poniżej propozycja nieco prostszego wydaje mi się procesora. U mnie działa, mam nadzieję, że u Ciebie też? :-)
piotr
@SupportedAnnotationTypes("pl.Plugin") @SupportedSourceVersion(RELEASE_6) class MyProcessor extends AbstractProcessor {
@Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { new ElementScanner6<Void, Void>() {
@Override public Void visitType(TypeElement e, Void p) { Plugin a = e.getAnnotation(Plugin.class); if (a != null) { System.out.println(a + " " + e.getQualifiedName()); } return super.visitType(e, p); }
}.scan(roundEnv.getRootElements(), null); return false; } }
zanotowane.pldoc.pisz.plpdf.pisz.pleffulla.pev.pl
|
[PGSQL] Strumienie np. dla ostrzeżeń w procedurach Pl Pg SQL
[mysql] grupowanie wierszy
=?iso-8859-2?q?Problem_z_redefinicjami_makr_i_wielko=B6ci_w_f unkcji_C_dla_Postgresa?=
[FoxPro] Kupię Microsoft FoxPro 2.6 for DOS
sql zapytanie o czas
sortowanie order by
[OT]Pomoc w kupnie zdjęć
shop cs2, przezroczyste warstwy
DB2 / AS400 narzędzia
mapa swiata w formacie wektorowym -potrzebne
zanotowane.pldoc.pisz.plpdf.pisz.plmarcelq.xlx.pl
Cytat
Decede mihi sole - nie zasłaniaj mi słonca. Gdy kogoś kochasz, jesteś jak stworzyciel świata - na cokolwiek spojrzysz, nabiera to kształtu, wypełnia się barwą, światłem. Powietrze przytula się do ciebie, choćby był mróz, a ty masz w sobie tyle radości, że musisz ją rozdawać wokoło, bo się w tobie nie mieści Hoc fac - tak czyń. A tergo - od tyłu; z tyłu. I czarne włosy posiwieją. Safona |
|