通过GWT RPC传递类对象的问题
我使用Eclipse和Google插件运行了Google Web Toolkit StockWatcher教程 ,我正在尝试对其进行一些基本的更改,以便我可以更好地理解RPC框架。
我已经在StockServiceImpl服务器端类上修改了“getStocks”方法,以便它返回Stock对象的数组而不是String对象。 应用程序编译完美,但Google Web Toolkit返回以下错误:
“没有源代码可用于com.google.gwt.sample.stockwatcher.server.Stock类型;您是否忘记inheritance所需的模块?”
Google Web Toolkit托管模式http://sofzh.miximages.com/java/a47r83.jpg
似乎客户端类无法找到Stock对象的实现,即使已导入该类。 作为参考,这是我的包层次结构的屏幕截图:
Eclipse包层次结构http://sofzh.miximages.com/java/14tr5gk.jpg
我怀疑我在web.xml中遗漏了一些内容,但我不知道它是什么。 谁能指出我正确的方向?
编辑:忘了提到Stock类是可持久的,所以它需要留在服务器端。
经过多次试验和错误,我设法找到了这样做的方法。 它可能不是最好的方式,但它有效。 希望这篇文章可以为其他人节省大量的时间和精力。
这些说明假定您已完成基本StockWatcher教程和Google App Engine StockWatcher修改 。
创建Stock类的客户端实现
GWT有几点需要注意:
- 服务器端类可以导入客户端类,但反之亦然(通常)。
- 客户端无法导入任何Google App Engine库(即com.google.appengine.api.users.User)
由于上述两个项目,客户端永远不能实现我们在com.google.gwt.sample.stockwatcher.server中创建的Stock类。 相反,我们将创建一个名为StockClient的新客户端Stock类。
StockClient.java:
package com.google.gwt.sample.stockwatcher.client; import java.io.Serializable; import java.util.Date; public class StockClient implements Serializable { private Long id; private String symbol; private Date createDate; public StockClient() { this.createDate = new Date(); } public StockClient(String symbol) { this.symbol = symbol; this.createDate = new Date(); } public StockClient(Long id, String symbol, Date createDate) { this(); this.id = id; this.symbol = symbol; this.createDate = createDate; } public Long getId() { return this.id; } public String getSymbol() { return this.symbol; } public Date getCreateDate() { return this.createDate; } public void setId(Long id) { this.id = id; } public void setSymbol(String symbol) { this.symbol = symbol; } }
修改客户端类以使用StockClient []而不是String []
现在我们对客户端类进行一些简单的修改,以便他们知道RPC调用返回StockClient []而不是String []。
StockService.java:
package com.google.gwt.sample.stockwatcher.client; import com.google.gwt.sample.stockwatcher.client.NotLoggedInException; import com.google.gwt.user.client.rpc.RemoteService; import com.google.gwt.user.client.rpc.RemoteServiceRelativePath; @RemoteServiceRelativePath("stock") public interface StockService extends RemoteService { public Long addStock(String symbol) throws NotLoggedInException; public void removeStock(String symbol) throws NotLoggedInException; public StockClient[] getStocks() throws NotLoggedInException; }
StockServiceAsync.java:
package com.google.gwt.sample.stockwatcher.client; import com.google.gwt.sample.stockwatcher.client.StockClient; import com.google.gwt.user.client.rpc.AsyncCallback; public interface StockServiceAsync { public void addStock(String symbol, AsyncCallback async); public void removeStock(String symbol, AsyncCallback async); public void getStocks(AsyncCallback async); }
StockWatcher.java:
添加一个导入:
import com.google.gwt.sample.stockwatcher.client.StockClient;
除了addStock,loadStocks和displayStocks之外,所有其他代码保持不变:
private void loadStocks() { stockService = GWT.create(StockService.class); stockService.getStocks(new AsyncCallback() { public void onFailure(Throwable error) { handleError(error); } public void onSuccess(String[] symbols) { displayStocks(symbols); } }); } private void displayStocks(String[] symbols) { for (String symbol : symbols) { displayStock(symbol); } } private void addStock() { final String symbol = newSymbolTextBox.getText().toUpperCase().trim(); newSymbolTextBox.setFocus(true); // Stock code must be between 1 and 10 chars that are numbers, letters, // or dots. if (!symbol.matches("^[0-9a-zA-Z\\.]{1,10}$")) { Window.alert("'" + symbol + "' is not a valid symbol."); newSymbolTextBox.selectAll(); return; } newSymbolTextBox.setText(""); // Don't add the stock if it's already in the table. if (stocks.contains(symbol)) return; addStock(new StockClient(symbol)); } private void addStock(final StockClient stock) { stockService.addStock(stock.getSymbol(), new AsyncCallback() { public void onFailure(Throwable error) { handleError(error); } public void onSuccess(Long id) { stock.setId(id); displayStock(stock.getSymbol()); } }); }
修改StockServiceImpl类以返回StockClient []
最后,我们修改StockServiceImpl类的getStocks方法,以便在返回数组之前将服务器端Stock类转换为客户端StockClient类。
StockServiceImpl.java
import com.google.gwt.sample.stockwatcher.client.StockClient;
我们需要稍微更改addStock方法,以便返回生成的ID:
public Long addStock(String symbol) throws NotLoggedInException { Stock stock = new Stock(getUser(), symbol); checkLoggedIn(); PersistenceManager pm = getPersistenceManager(); try { pm.makePersistent(stock); } finally { pm.close(); } return stock.getId(); }
除getStocks外,所有其他方法保持不变:
public StockClient[] getStocks() throws NotLoggedInException { checkLoggedIn(); PersistenceManager pm = getPersistenceManager(); List stockclients = new ArrayList (); try { Query q = pm.newQuery(Stock.class, "user == u"); q.declareParameters("com.google.appengine.api.users.User u"); q.setOrdering("createDate"); List stocks = (List ) q.execute(getUser()); for (Stock stock : stocks) { stockclients.add(new StockClient(stock.getId(), stock.getSymbol(), stock.getCreateDate())); } } finally { pm.close(); } return (StockClient[]) stockclients.toArray(new StockClient[0]); }
概要
上面的代码在部署到Google App Engine时非常适合我,但在Google Web Toolkit托管模式中触发错误:
SEVERE: [1244408678890000] javax.servlet.ServletContext log: Exception while dispatching incoming RPC call com.google.gwt.user.server.rpc.UnexpectedException: Service method 'public abstract com.google.gwt.sample.stockwatcher.client.StockClient[] com.google.gwt.sample.stockwatcher.client.StockService.getStocks() throws com.google.gwt.sample.stockwatcher.client.NotLoggedInException' threw an unexpected exception: java.lang.NullPointerException: Name is null
如果您遇到同样的问题,请告诉我。 它在Google App Engine中运行的事实似乎表明托管模式中存在错误。
除了.class文件之外,GWT还需要.java文件。 此外,Stock需要位于GWT模块的“客户”位置。
GWT编译器不知道Stock,因为它不在它所查找的位置。你可以将它移动到客户端文件夹,或者如果它更有意义就把它放在原处并创建一个ModuleName.gwt.xml,引用您想要的任何其他类,并从中inheritanceMain.gwt.xml文件。
例如:DomainGwt.gwt.xml
和:
这里有一个更好的答案: GWT简单RPC用例问题:包含代码
基本上,您可以向APPNAME.gwt.xml文件添加参数,以便编译器为编译器提供服务器端类的路径。
我得到了同样的问题,“mvn gwt:compile”输出不是很有用。 相反,当我尝试部署到tomcat(通过maven tomcat插件:mvn tomcat:deploy)时,我收到了有用的错误消息。
我必须解决一些问题:
- 使从客户端发送到服务器的对象实现Serializable
- 将empty-arg构造函数添加到同一个对象
是的,我们确定需要使用序列化来获取客户端的服务器对象。 这些莫代尔? 文件设置无法在客户端使用Stock类。
在您的情况下,您只有一个类库存,您可以在客户端创建StockClient。 这很容易。 但是,如果有人有更多的课程,那将是什么解决方案。 类似于这个类的属性也是一些其他类型的类。
示例: stock.getEOD(date).getHigh();
getEOD
将返回具有给定日期的另一个类,该类具有getHigh
方法。
在这么大的案件中该怎么办? 我不认为在客户端创建实现序列化的所有类都是有益的。 然后我们必须在服务器和客户端编写代码。 所有class级两次。
以上关于rustyshelf的回答……
在我的情况下,我需要编辑ModuleName.gwt.xml文件并添加以下内容:
我使用New-> Web Application Project向导创建了我的项目,但取消选中了Generate project sample code选项。 然后我创建了共享包。 如果我没有取消选中,那么将为我创建包,并根据上述修改xml文件。
有一个更加简单易用的解决方案。 如果要将自定义设计类的对象从服务器端发送到客户端,则应在共享包中定义此自定义类。
例如,对于您的情况,您只需携带Stock.java类(通过拖放)
com.google.gwt.sample.stockwatcher.shared
包。 但是,从您的包层次结构截图中,您似乎已删除此共享包。 只需重新创建此包并将Stock.java放入其中并让游戏开始。