15

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

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

4

9 回答 9

13

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

如果您只将 GWT 视为直接从 Java 到 JS,那它是行不通的。但是,如果您考虑使用 GWT 编译器 Compiles and Executes 在编译期间生成器 - 特殊类,这是可能的。因此,您甚至可以在编译时生成 java 源代码。

我今天有这个需求——我们的系统处理来自服务的动态资源,以字符串结尾并需要一个类。这是我想出的解决方案-顺便说一句,它可以在托管、IE 和 Firefox 下运行。

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

一些注意事项:

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

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

于 2010-02-24T21:42:15.590 回答
5

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!

于 2011-12-12T20:09:00.600 回答
5

布莱恩,

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

我在使用新的 GWT MVP 编码风格时遇到了类似的问题(请参阅GWT MVP 文档

当我打电话时:

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>

然后它就像一个魅力

于 2010-11-14T17:27:10.600 回答
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 )
于 2009-01-31T02:58:32.063 回答
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 中手动做到这一点......最好的问候

于 2009-09-08T19:52:26.620 回答
2

我能够做我认为您正在尝试做的事情,即加载一个类并将其动态绑定到一个事件;我使用生成器将类动态链接到事件。我不推荐它,但如果它有帮助,这里有一个例子:

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

于 2010-04-09T23:57:29.353 回答
1

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

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

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

于 2011-10-27T14:58:16.957 回答
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

于 2009-06-18T09:18:38.903 回答
1

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

于 2009-01-18T10:26:11.670 回答