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)等框架中是如何实现的呢?
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)等框架中是如何实现的呢?
虽然有些复杂,但是确实可以实现。以下是详细内容:
如果你只把GWT看作是Java转JS的工具,那么它是无法胜任这个任务的。但是,如果你考虑到生成器——一种特殊的类,在编译期间由GWT编译器编译和执行,就可以实现这个功能。因此,即使在编译过程中,你也可以生成Java源代码。
我今天有这个需求——我们的系统处理服务端的动态资源,最终输出一个字符串和需要的类。下面是我想出的解决方案——顺便说一句,它可以在hosted、IE和Firefox上运行。
一些说明:
希望很快能够发布源代码。祝好运 :)
Brian,
问题在于GWT.create不知道如何为你的抽象类选择正确的实现。
我曾经遇到过与新的GWT MVP编码风格相似的问题(请参见GWT MVP文档)。
当我调用:
ClientFactory clientFactory = GWT.create(ClientFactory.class);
我也得到了同样的错误:
Deferred binding result type 'com.test.mywebapp.client.ClientFactory' should not be abstract
我所要做的就是在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>
我今天遇到了这个问题,并找到了解决方案。提问者基本上想编写一个方法,例如:
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);
}
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子类型并创建一堆 实例化程序即可
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);
<generate-with class="package.rebind.InstantiatorGenerator">
<when-type-assignable class="package.impl.Instantiator" />
</generate-with>
万岁!
您的问题是什么 - 我猜您希望在向生成器传递类字面量的同时传递参数。
正如您可能已经知道的那样,传递给 GWT.create() 的类字面量大多是一个选择器,以便 GWT 可以选择并执行生成器,最终输出一个类。向生成器传递参数的最简单方法是在接口中使用注释,并将该接口.class传递给 GWT.create()。当然要注意,接口/类必须扩展传递到 GWT.create() 中的类字面量。
class Selector{
}
@Annotation("string parameter...")
class WithParameter extends Selector{}
Selector instance = GWT.create( WithParameter.class )
一切皆有可能,尽管可能会很困难甚至毫无用处。正如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...
祝好!如果你想找出他们是如何做到的,那么你应该查看rocket/gwittir的代码(毕竟它是开源的)。我猜测他们采用延迟绑定的方式,在编译时计算所有反射调用,并静态生成实现这些调用所需的所有代码。因此在运行时,你不能进行不同的调用。
在 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
你也许可以通过在服务器端完成它来避免整个问题。例如使用一个接受字符串并返回某种可序列化超类型的服务。在服务器端,你可以这样做:
return (MySerializableType)Class.forName("className").newInstance();