2010-06-14 10 views
6

ich eine Klasse int folgenden Namen com.test.TestClassWie in gwt neue Instanz von Klassennamen erstellen

An einem Punkt in meinem Code haben habe ich Instanz dieser Klasse zu erhalten, indem nur die Klassennamen Zeichenfolge mit. Ich habe versucht mit GWT.create() Aber es funktioniert nur im Dev-Modus. Kann mir jemand sagen, wie man eine Instanz in gwt vom Klassennamen bekommt?

+0

sieht aus wie ein Duplikat von http://stackoverflow.com/questions/451658/gwt-dynamic-loading-using-gwt-create-with-string-literals-instead-of-class-lite –

+0

Thanks.I am erwarte auch dasselbe. – DonX

Antwort

11

Da eine Reflektion auf der Client-Seite nicht möglich ist, ist die einzige Lösung, die Sie nachahmen müssen, die verzögerte Bindung.

Verwenden Sie die verzögerte Bindung, um alle Klassen zu ermitteln, die Sie während der Kompilierung mit dem Klassennamen instanziieren möchten. Sie können eine Markierungsschnittstelle für alle diese Klassen verwenden, um TypeOracle dabei zu unterstützen, diese zu identifizieren. Sie generieren dynamisch eine Factory-Klasse, die den einfachen Namen der Klasse übernimmt und ein neu instanziiertes Objekt dieser Klasse zurückgibt. Der Ansatz ist sehr geradlinig und Sie finden eine gute Erklärung der verzögerten Bindung in Googles Tutorials zu booten.

Bearbeiten: - Einige Skelett-Code, um loszulegen. (abgespeckte Version meines Produktionscode, überprüfen Sie Compiler-Fehler in der generierten Datei! Und debuggen den Fluss)

Zuerst> den folgenden Klappentext in Ihren * .gwt.xml hinzufügen, um damit Der Compiler ruft unseren com.package.ReflectionGenerator auf, der eine einfache Factory-Klasse generiert, um die Reflektion auf der Client-Seite nachzuahmen.

<generate-with class="com.package.ReflectionGenerator"> 
     <when-type-assignable class="com.package.client.Reflection" /> 
    </generate-with> 

Weiter> eine Schnittstelle für unsere Fabrik Klasse definieren

public interface Reflection { 
    public <T, V extends T> T instantiate(Class<V> clazz); 
} 

Letzte> ReflectionGenerator Implement

import java.io.PrintWriter; 
import java.util.ArrayList; 
import java.util.List; 

import com.google.gwt.core.ext.BadPropertyValueException; 
import com.google.gwt.core.ext.Generator; 
import com.google.gwt.core.ext.GeneratorContext; 
import com.google.gwt.core.ext.PropertyOracle; 
import com.google.gwt.core.ext.TreeLogger; 
import com.google.gwt.core.ext.UnableToCompleteException; 
import com.google.gwt.core.ext.typeinfo.JClassType; 
import com.google.gwt.core.ext.typeinfo.TypeOracle; 
import com.google.gwt.user.rebind.ClassSourceFileComposerFactory; 
import com.google.gwt.user.rebind.SourceWriter; 

public class ReflectionGenerator extends Generator 
{  
    @Override 
    public String generate(TreeLogger logger, GeneratorContext context, String typeName) throws UnableToCompleteException 
    { 
     TypeOracle oracle = context.getTypeOracle(); 

     JClassType instantiableType = oracle.findType(MarkerInterface.class.getName()); 

     List<JClassType> clazzes = new ArrayList<JClassType>(); 

     PropertyOracle propertyOracle = context.getPropertyOracle(); 

     for (JClassType classType : oracle.getTypes()) 
     { 
      if (!classType.equals(instantiableType) && classType.isAssignableTo(instantiableType)) 
       clazzes.add(classType); 
     } 

     final String genPackageName = "com.package.client"; 
     final String genClassName = "ReflectionImpl"; 

     ClassSourceFileComposerFactory composer = new ClassSourceFileComposerFactory(genPackageName, genClassName); 
     composer.addImplementedInterface(Reflection.class.getCanonicalName()); 

     composer.addImport("com.package.client.*"); 

     PrintWriter printWriter = context.tryCreate(logger, genPackageName, genClassName); 

     if (printWriter != null) 
     { 
      SourceWriter sourceWriter = composer.createSourceWriter(context, printWriter); 
      sourceWriter.println("ReflectionImpl() {"); 
      sourceWriter.println("}"); 

      printFactoryMethod(clazzes, sourceWriter); 

      sourceWriter.commit(logger); 
     } 
     return composer.getCreatedClassName(); 
    } 

    private void printFactoryMethod(List<JClassType> clazzes, SourceWriter sourceWriter) 
    { 
     sourceWriter.println(); 

     sourceWriter.println("public <T, V extends T> T instantiate(Class<V> clazz) {"); 

     for (JClassType classType : clazzes) 
     { 
      if (classType.isAbstract()) 
       continue; 

      sourceWriter.println(); 
      sourceWriter.indent(); 
      sourceWriter.println("if (clazz.getName().endsWith(\"." + classType.getName() + "\")) {"); 
      sourceWriter.indent(); 
      sourceWriter.println("return (T) new " + classType.getQualifiedSourceName() + "();"); 
      sourceWriter.outdent(); 
      sourceWriter.println("}"); 
      sourceWriter.outdent(); 
      sourceWriter.println(); 
     } 
     sourceWriter.indent(); 
     sourceWriter.println("return (T) null;"); 
     sourceWriter.outdent(); 
     sourceWriter.println(); 
     sourceWriter.println("}"); 
     sourceWriter.outdent(); 
     sourceWriter.println(); 
    } 
} 

Dies sollte die Factory-Klasse ReflectionGenerator in Ihrem Arbeitsbereich erzeugen, Scheck die generierte Datei und optimieren Sie den Quelltext des Writers, um den Code zu generieren de du begehrst.

Nutzungs GWT.create(Reflection.class).instantiate(YourClass.class);

ich eine Markierungsschnittstelle 'MarkerInterface' im Generator verwendet haben, die Anzahl der Klassen durch die Fabrik, also als Ergebnis alle teilnehmenden Klassen implementieren müssen 'MarkerInterface'

+0

Vielen Dank. Wenn Sie den Beispielcode posten können, wird es für mich und andere Benutzer sehr nützlich sein. – DonX

+0

Ich habe meine Antwort bearbeitet, um einen Skelettcode zu veröffentlichen, der die Verwendung der Bindung demonstriert. Bitte gehen Sie durch, und verzeihen Sie mir, wenn Sie nach einem oder zwei Kompilierungsfehlern zufällig sind, habe ich den Code nicht über den Compiler ausgeführt. Bin zu faul dafür: P –

2

GWT unterstützt zu beschränken. create (Reflection.class) .instantiate (YourClass.class);

Warum benutzen Sie dann nicht einfach GWT.create(YourClass.class);?

Vielleicht ment Sie GWT.create(Reflection.class).instantiate("YourClass");

5

hier gut getestet, kommentiert und leicht überarbeitet Version von Ashwin Prabhus Code:

https://bitbucket.org/espinosa/z025-gwt-maven-alternative-setup/src/d35a3fb7e627b5598fb763f480e3f76932cf4232/src/main/java/my/code/z025/util/ClassFromStringFactoryGenerator.java?at=master

Anwendungsbeispiel:

String targetEntryPointClass = "my.code.client.Sample3"; 
ClassFromStringFactory classFromStringFactory = GWT.create(ClassFromStringFactory.class); 
Object targetEntryPointInstance = classFromStringFactory.instantiate(targetEntryPointClass); 
if (targetEntryPointInstance == null) { 
     // throw some exception 
} 
if (targetEntryPointInstance instanceof EntryPoint) { 
     ((EntryPoint) targetEntryPointInstance).onModuleLoad(); 
} else { 
     // throw some exception 
} 

Komplette Quelle Code: https://bitbucket.org/espinosa/z025-gwt-maven-alternative-setup/src/d35a3fb7e627b5598fb763f480e3f76932cf4232/src/main/java/my/code/z025/client/Dispatcher.java?at=master

In meinem Projekt verwende ich GWT eigenen EntryPoint als Marker-Schnittstelle. Dadurch kann ich einen beliebigen EntryPoint einfach über die URL http://localhost:8080/my.code.client.Sample3; Der Dispatcher EntryPoint instanziiert my.code.client.Sample3 über meine ClassFromStringFactory. Nur der Dispatcher-Einstiegspunkt wird im GWT-Moduldeskriptor und der verzögerten Bindung konfiguriert, alles andere ist dynamisch.

Für neugierig, hier ist das, was GWT (Code-Server in DevMode oder Compiler für Produktionsbetrieb) erzeugt, Inhalt meiner ClassFromStringFactoryImpl:

package my.code.client.reflection; 

public class ClassFromStringFactoryImpl implements ClassFromStringFactory { 
    public ClassFromStringFactoryImpl() {} 

    public Object instantiate(String className) { 
    if (className == null) { 
     return null 
    } 
    else if (className.equals("my.code.client.Sample1")) { 
     return new my.code.client.Sample1(); 
    } 
    else if (className.equals("my.code.client.Sample2")) { 
     return new my.code.client.Sample2(); 
    } 
    ..and so on, 3 same lines per every supported type 
    return null; 
    } 
} 

In temporäre Datei wie: C:\Users\espinosa\AppData\Local\Temp\my.code.client.reflection.ClassFromStringFactoryImpl4245548251877324156.java. Hinweis: Diese Datei wird nur im Falle des Scheiterns erzeugt wird, nicht auf erfolgreiche Compilation

Wie Sie sehen können, ist es keine wirkliche Selbstbeobachtung. Zurückgestellte Bindung macht keine besondere Magie. Ähnliche Java-Code kann durch eine Velocity-Vorlage als Teil von Maven Build oder spezielle Tools wie XText, APT-Jelly generiert werden. Mit GWT Generator ist nur eine Annehmlichkeit.

Es ist wichtig, die Anzahl der "unterstützten" Klassen zu begrenzen, ansonsten wäre das generierte ClassFromStringFactoryImpl zu groß, unpraktisch riesig oder würde sogar die Grenzen der Java-Klasse überschreiten. Eine Art von Filterung ist notwendig, Markierungsschnittstelle ist nur eine Option, andere markieren Annotation (siehe GWT's JClassType # getAnnotation (Class)) oder nur ausgewählte Pakete. Stellen Sie in jedem Fall sicher, dass die Anzahl der unterstützten Klassen durch diese "Reflexion" die Größenordnung von Hunderten nicht überschreitet.

Vielen Dank Ashwin Prabhu für mich in die richtige Richtung.

Verwandte Themen