GWT动态加载使用GWT.create()和String文字而不是类文字

时间:2009-01-16 19:36:19

标签: gwt

GWT.create()是GWT中的反射等价物, 但它只需要类文字,而不是Class类的完全限定String。 如何使用GWT.create()动态创建带有字符串的类?

根据许多GWT论坛帖子不可能,但是如何在Rocket-GWT(http://code.google.com/p/rocket-gwt/wiki/Ioc)和Gwittir(http://code.google.com/p/gwittir/wiki/Introspection)等框架中完成

9 个答案:

答案 0 :(得分:13)

尽管很棘手,但这是可能的。以下是血腥的细节:

如果你只把GWT视为JS的直接Java,它就行不通。但是,如果您在编译期间考虑使用GWT编译器Compiles and Executes的Generators - Special类,则可以。因此,您可以在编译时生成java源代码。

我今天有这样的需求 - 我们的系统处理服务的动态资源,结束为字符串和类的需要。这是我提出的解决方案 - 顺便说一句,它在托管,IE和Firefox下工作。

  • 创建一个GWT模块,声明:
    • 源路径
    • 生成器(应该 OUTSIDE GWT模块源路径的包)
    • 接口替换(它将注入Generated类而不是接口)
  • 在该软件包中,创建一个Marker接口(我称之为Constructable)。 Generator将查找该Marker
  • 创建一个基本抽象类来保存该工厂。我这样做是为了简化生成的源代码
  • 声明该模块继承在Application.gwt.xml

一些注意事项:

  • 理解的关键在于生成器的概念;
  • 为了方便起见,Abstract基类派上用场了。
  • 另外,要了解生成的.js源代码名称,甚至是生成的Java源代码
  • 记住Generator输出java文件
  • GWT.create需要对.class文件的一些引用。您的生成器输出可能会这样做,只要它从您的应用程序以某种方式引用(请检查Application.gwt.xml继承您的模块,该模块也替换了您的Application.gwt.xml声明的生成器的接口)
  • 将GWT.create调用包含在工厂方法/单例内,也包含在GWT.isClient()下
  • 在GWT.runAsync周围包装代码类加载调用是一个非常好的主意,因为它可能需要触发模块加载。这非常很重要。

我希望尽快发布源代码。交叉你的手指。 :)

答案 1 :(得分:5)

布赖恩,

问题是GWT.create不知道如何为你的抽象类选择正确的实现

我遇到了与新的GWT MVP编码风格类似的问题 (见GWT MVP documentation

当我打电话时:

ClientFactory clientFactory = GWT.create(ClientFactory.class);

我得到了同样的错误:

延迟绑定结果类型'com.test.mywebapp.client.ClientFactory'不应该是抽象的

我所要做的就是将以下行添加到MyWebapp.gwt.xml文件中:

<!-- Use ClientFactoryImpl by default -->
    <replace-with class="com.test.mywebapp.client.ClientFactoryImpl">
    <when-type-is class="com.test.mywebapp.client.ClientFactory"/>
    </replace-with>

然后它就像一个魅力

答案 2 :(得分:5)

我今天遇到了这个并找到了解决方案。提问者基本上想要编写一个方法,例如:

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

这里MyInterface只是一个标记接口,用于定义我希望能够动态生成的类的范围。如果您尝试编写上述代码,则会出现错误。诀窍是定义一个“实例化器”,例如:

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

现在定义一个返回上述实例的GWT延迟绑定生成器。在生成器中,查询TypeOracle以获取所有类型的MyInterface并为它们生成实现,就像对任何其他类型一样:

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.
}

MyIntefaceGenerator类就像任何其他延迟绑定生成器一样。除非您直接在上述生成器中调用它,而不是通过GWT.create。一旦完成MyInterface的所有已知子类型的生成(在生成器中生成MyInterface的子类型时,请确保使类名具有唯一的模式,例如MyInterface.class.getName()+“_ MySpecialImpl”) ,只需通过迭代MyInterface的所有已知子类型并创建一堆

来创建Instantiator
if (clz.getName().equals(MySpecialDerivativeOfMyInterface)) { return (T) new MySpecialDerivativeOfMyInterface_MySpecialImpl();}

代码风格。最后抛出异常,以便在所有情况下都可以返回一个值。

现在,您要拨打GWT.create(clz);,而不是执行以下操作:

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

另请注意,在GWT模块xml中,您只需为Instantiator定义一个生成器,而不是为MyInterface生成器定义:

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

宾果!

答案 3 :(得分:4)

究竟是什么问题 - 我猜你希望除了类文字之外还要将参数传递给生成器。

您可能已经知道传递给GWT.create()的类文字主要是一个选择器,以便GWT可以选择并执行一个最终吐出类的生成器。将参数传递给生成器的简单方法是在接口中使用注释并将interface.class传递给GWT.create()。注意,接口/类必须扩展传递给GWT.create()的类文字。

class Selector{
}

@Annotation("string parameter...")
class WithParameter extends Selector{}

Selector instance = GWT.create( WithParameter.class )

答案 4 :(得分:2)

一切皆有可能......尽管可能很困难甚至无用。正如Jan提到的那样,你应该使用发电机来做到这一点。基本上你可以创建你的接口生成器代码,它接受该接口并在创建时编译并返回实例。一个例子可能是:

//A marker interface
public interface Instantiable {
}
//What you will put in GWT.create
public interface ReflectionService {
 public Instantiable newInstance(String className);
}
//gwt.xml, basically when GWT.create finds reflectionservice, use reflection generator
<generate-with class="...ReflectionGenerator" >
<when-type-assignable class="...ReflectionService" />
</generate-with>  
//In not a client package
public class ReflectionGenerator extends Generator{
...
}
//A class you may instantiate
public class foo implements Instantiable{
}
//And in this way
ReflectionService service = GWT.create(ReflectionService.class);
service.newInstance("foo");

您需要知道的是如何做发电机。我可能会告诉你,最后你在生成器中做的是以这种方式创建Java代码:

if ("clase1".equals(className)) return new clase1();
else if ("clase2".equals(className)) return new clase2();
...

在决赛中我想,常见的是我可以在InstanceFactory中手工完成这个... 最诚挚的问候

答案 5 :(得分:2)

我能够做我认为你正在尝试做的事情,它正在加载一个类并动态地将它绑定到一个事件;我使用Generator来动态地将类链接到事件。我不推荐它,但这是一个例子,如果它有帮助:

http://francisshanahan.com/index.php/2010/a-simple-gwt-generator-example/

答案 6 :(得分:1)

没看过rocket / gwittir的代码(如果你想知道他们是怎么做的,你应该怎么做,毕竟它是开源的),我只能猜测他们采用延迟绑定这样的方式在编译期间,它们计算出所有对反射的调用,并静态生成实现这些调用所需的所有代码。所以在运行期间,你不能做不同的。

答案 7 :(得分:1)

在GWT中你无法做到的事情。

虽然GWT在编译时很好地模拟了Java,但运行时当然是完全不同的。大多数反射都不受支持,并且无法在运行时生成或动态加载类。

我简要介绍了Gwittir的代码,我认为他们在编译时正在做“反思”。这里:http://code.google.com/p/gwittir/source/browse/trunk/gwittir-core/src/main/java/com/totsp/gwittir/rebind/beans/IntrospectorGenerator.java

答案 8 :(得分:1)

您可以通过在服务器端执行此操作来避免整个问题。说服务 witch接受String并返回某种可序列化的超类型。 在服务器端,你可以做

return (MySerializableType)Class.forName("className").newInstance();

根据您的具体情况,它可能不是一个很大的性能瓶颈。