GWT Dynamic loading using GWT.create() with String literals instead of Class literals

后端 未结 9 1211
一个人的身影
一个人的身影 2020-12-14 10:11

GWT.create() is the reflection equivalent in GWT, But it take only class literals, not fully qualified String for the Class name. How do i dynamically create classes with

相关标签:
9条回答
  • 2020-12-14 10:45

    What exactly is the question - i am guessing you wish to pass parameters in addition to the class literal to a generator.

    As you probably already know the class literal passed to GWT.create() is mostly a selector so that GWT can pick and execute a generator which in the end spits out a class. The easist way to pass a parameter to the generator is to use annotations in an interface and pass the interface.class to GWT.create(). Note of course the interface/class must extend the class literal passed into GWT.create().

    class Selector{
    }
    
    @Annotation("string parameter...")
    class WithParameter extends Selector{}
    
    Selector instance = GWT.create( WithParameter.class )
    
    0 讨论(0)
  • 2020-12-14 10:51

    I ran into this today and figured out a solution. The questioner is essentially wanting to write a method such as:

    public <T extends MyInterface> T create(Class<T> clz) {
        return (T)GWT.create(clz);
    }
    

    Here MyInterface is simply a marker interface to define the range of classes I want to be able to dynamically generate. If you try to code the above, you will get an error. The trick is to define an "instantiator" such as:

    public interface Instantiator {
        public <T extends MyInterface> T create(Class<T> clz);
    }
    

    Now define a GWT deferred binding generator that returns an instance of the above. In the generator, query the TypeOracle to get all types of MyInterface and generate implementations for them just as you would for any other type:

    e.g:

    public class InstantiatorGenerator extends Generator {
    
    public String generate(...) {
       TypeOracle typeOracle = context.getTypeOracle();
       JClassType myTYpe= typeOracle.findType(MyInterface.class.getName());
    
        JClassType[] types = typeOracle.getTypes();
        List<JClassType> myInterfaceTypes = Collections.createArrayList();
    
        // Collect all my interface types.
        for (JClassType type : types) {
            if (type.isInterface() != null && type.isAssignableTo(myType)
                    && type.equals(myType) == false) {
                myInterfaceTypes.add(type);
            }
            for (JClassType nestedType : type.getNestedTypes()) {
                if (nestedType.isInterface() != null && nestedType.isAssignableTo(myType)
                        && nestedType.equals(myTYpe) == false) {
                    myInterfaceTypes.add(nestedType);
                }
            }
        }
    
        for (JClassType jClassType : myInterfaceTypes) {
            MyInterfaceGenerator generator = new MyInterfaceGenerator();
            generator.generate(logger, context, jClassType.getQualifiedSourceName());
        }
     }
    
     // Other instantiator generation code for if () else if () .. constructs as 
     // explained below.
    }
    

    The MyIntefaceGenerator class is just like any other deferred binding generator. Except you call it directly within the above generator instead of via GWT.create. Once the generation of all known sub-types of MyInterface is done (when generating sub-types of MyInterface in the generator, make sure to make the classname have a unique pattern, such as MyInterface.class.getName() + "_MySpecialImpl"), simply create the Instantiator by again iterating through all known subtypes of MyInterface and creating a bunch of

    if (clz.getName().equals(MySpecialDerivativeOfMyInterface)) { return (T) new MySpecialDerivativeOfMyInterface_MySpecialImpl();}
    

    style of code. Lastly throw an exception so you can return a value in all cases.

    Now where you'd call GWT.create(clz); instead do the following:

    private static final Instantiator instantiator = GWT.create(Instantiator.class);
    ...
    return instantiator.create(clz);
    

    Also note that in your GWT module xml, you'll only define a generator for Instantiator, not for MyInterface generators:

    <generate-with class="package.rebind.InstantiatorGenerator">
        <when-type-assignable   class="package.impl.Instantiator" />
    </generate-with>
    

    Bingo!

    0 讨论(0)
  • 2020-12-14 10:52

    It is possible, albeit tricky. Here are the gory details:

    If you only think as GWT as a straight Java to JS, it would not work. However, if you consider Generators - Special classes with your GWT compiler Compiles and Executes during compilation, it is possible. Thus, you can generate java source while even compiling.

    I had this need today - Our system deals with Dynamic resources off a Service, ending into a String and a need for a class. Here is the solutuion I've came up with - btw, it works under hosted, IE and Firefox.

    • Create a GWT Module declaring:
      • A source path
      • A Generator (which should be kept OUTSIDE the package of the GWT Module source path)
      • An interface replacement (it will inject the Generated class instead of the interface)
    • Inside that package, create a Marker interface (i call that Constructable). The Generator will lookup for that Marker
    • Create a base abstract class to hold that factory. I do this in order to ease on the generated source code
    • Declare that module inheriting on your Application.gwt.xml

    Some notes:

    • Key to understanding is around the concept of generators;
    • In order to ease, the Abstract base class came in handy.
    • Also, understand that there is name mandling into the generated .js source and even the generated Java source
    • Remember the Generator outputs java files
    • GWT.create needs some reference to the .class file. Your generator output might do that, as long as it is referenced somehow from your application (check Application.gwt.xml inherits your module, which also replaces an interface with the generator your Application.gwt.xml declares)
    • Wrap the GWT.create call inside a factory method/singleton, and also under GWT.isClient()
    • It is a very good idea to also wrap your code-class-loading-calls around a GWT.runAsync, as it might need to trigger a module load. This is VERY important.

    I hope to post the source code soon. Cross your fingers. :)

    0 讨论(0)
提交回复
热议问题