java 通过 GWT RPC 传递类对象的问题
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/958879/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Problems passing class objects through GWT RPC
提问by Templar
I've run through the Google Web Toolkit StockWatcher Tutorialusing Eclipseand the Google Plugin, and I'm attempting to make some basic changes to it so I can better understand the RPC framework.
我已经使用Eclipse和Google Plugin浏览了 Google Web Toolkit StockWatcher 教程,我正在尝试对其进行一些基本更改,以便更好地理解 RPC 框架。
I've modified the "getStocks" method on the StockServiceImpl server-side class so that it returns an array of Stock objects instead of String objects. The application compiles perfectly, but the Google Web Toolkit is returning the following error:
我修改了 StockServiceImpl 服务器端类上的“getStocks”方法,以便它返回一个 Stock 对象数组而不是 String 对象。应用程序编译完美,但 Google Web Toolkit 返回以下错误:
"No source code is available for type com.google.gwt.sample.stockwatcher.server.Stock; did you forget to inherit a required module?"
“没有可用于 com.google.gwt.sample.stockwatcher.server.Stock 类型的源代码;您是否忘记继承所需的模块?”
It seems that the client-side classes can't find an implementation of the Stock object, even though the class has been imported. For reference, here is a screenshot of my package hierarchy:
客户端类似乎无法找到 Stock 对象的实现,即使该类已被导入。作为参考,这是我的包层次结构的屏幕截图:
I suspect that I'm missing something in web.xml, but I have no idea what it is. Can anyone point me in the right direction?
我怀疑我在 web.xml 中遗漏了一些东西,但我不知道它是什么。任何人都可以指出我正确的方向吗?
EDIT:Forgot to mention that the Stock class is persistable, so it needs to stay on the server-side.
编辑:忘了提到 Stock 类是持久的,所以它需要留在服务器端。
回答by Templar
After much trial and error, I managed to find a way to do this. It might not be the best way, but it works. Hopefully this post can save someone else a lot of time and effort.
经过多次反复试验,我设法找到了一种方法来做到这一点。这可能不是最好的方法,但它有效。希望这篇文章可以为其他人节省很多时间和精力。
These instructions assume that you have completed both the basic StockWatcher tutorialand the Google App Engine StockWatcher modifications.
这些说明假定您已完成基本 StockWatcher 教程和Google App Engine StockWatcher 修改。
Create a Client-Side Implementation of the Stock Class
创建 Stock 类的客户端实现
There are a couple of things to keep in mind about GWT:
关于 GWT,有几点需要牢记:
- Server-side classes can import client-side classes, but not vice-versa (usually).
- The client-side can't import any Google App Engine libraries (i.e. com.google.appengine.api.users.User)
- 服务器端类可以导入客户端类,但反之亦然(通常)。
- 客户端无法导入任何 Google App Engine 库(即 com.google.appengine.api.users.User)
Due to both items above, the client can never implement the Stock class that we created in com.google.gwt.sample.stockwatcher.server. Instead, we'll create a new client-side Stock class called StockClient.
由于上述两个原因,客户端永远无法实现我们在 com.google.gwt.sample.stockwatcher.server 中创建的 Stock 类。相反,我们将创建一个名为 StockClient 的新客户端 Stock 类。
StockClient.java:
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;
}
}
Modify Client Classes to Use StockClient[] instead of String[]
修改客户端类以使用 StockClient[] 而不是 String[]
Now we make some simple modifications to the client classes so that they know that the RPC call returns StockClient[] instead of String[].
现在我们对客户端类进行一些简单的修改,以便它们知道 RPC 调用返回 StockClient[] 而不是 String[]。
StockService.java:
股票服务.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:
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<Long> async);
public void removeStock(String symbol, AsyncCallback<Void> async);
public void getStocks(AsyncCallback<StockClient[]> async);
}
StockWatcher.java:
股票观察者.java:
Add one import:
添加一个导入:
import com.google.gwt.sample.stockwatcher.client.StockClient;
All other code stays the same, except addStock, loadStocks, and displayStocks:
所有其他代码保持不变,除了 addStock、loadStocks 和 displayStocks:
private void loadStocks() {
stockService = GWT.create(StockService.class);
stockService.getStocks(new AsyncCallback<String[]>() {
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<Long>() {
public void onFailure(Throwable error) {
handleError(error);
}
public void onSuccess(Long id) {
stock.setId(id);
displayStock(stock.getSymbol());
}
});
}
Modify the StockServiceImpl Class to Return StockClient[]
修改 StockServiceImpl 类以返回 StockClient[]
Finally, we modify the getStocks method of the StockServiceImpl class so that it translates the server-side Stock classes into client-side StockClient classes before returning the array.
最后,我们修改 StockServiceImpl 类的 getStocks 方法,使其在返回数组之前将服务器端 Stock 类转换为客户端 StockClient 类。
StockServiceImpl.java
StockServiceImpl.java
import com.google.gwt.sample.stockwatcher.client.StockClient;
We need to change the addStock method slightly so that the generated ID is returned:
我们需要稍微更改 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();
}
All other methods stay the same, except getStocks:
所有其他方法保持不变,除了 getStocks:
public StockClient[] getStocks() throws NotLoggedInException {
checkLoggedIn();
PersistenceManager pm = getPersistenceManager();
List<StockClient> stockclients = new ArrayList<StockClient>();
try {
Query q = pm.newQuery(Stock.class, "user == u");
q.declareParameters("com.google.appengine.api.users.User u");
q.setOrdering("createDate");
List<Stock> stocks = (List<Stock>) 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]);
}
Summary
概括
The code above works perfectly for me when deployed to Google App Engine, but triggers an error in Google Web Toolkit Hosted Mode:
上面的代码在部署到 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
Let me know if you encounter the same problem or not. The fact that it works in Google App Engine seems to indicate a bug in Hosted Mode.
如果您遇到同样的问题,请告诉我。它在 Google App Engine 中工作的事实似乎表明托管模式中存在错误。
回答by Steve Reed
GWT needs the .java file in addition to the .class file. Additionally, Stock needs to be in the "client" location of a GWT module.
除了 .class 文件之外,GWT 还需要 .java 文件。此外,Stock 需要位于 GWT 模块的“客户端”位置。
回答by rustyshelf
The GWT compiler doesn't know about Stock, because it's not in a location it looks in. You can either move it to the client folder, or if it makes more sense leave it where it is and create a ModuleName.gwt.xml that references any other classes you want, and get your Main.gwt.xml file to inherit from that.
GWT 编译器不知道 Stock,因为它不在它查找的位置。您可以将它移动到客户端文件夹,或者如果更有意义,将它保留在原处并创建一个 ModuleName.gwt.xml引用您想要的任何其他类,并让您的 Main.gwt.xml 文件从中继承。
eg: DomainGwt.gwt.xml
例如:DomainGwt.gwt.xml
<module>
<inherits name='com.google.gwt.user.User'/>
<source path="javapackagesabovethispackagegohere"/>
</module>
and:
和:
<module rename-to="gwt_ui">
<inherits name="com.google.gwt.user.User"/>
<inherits name="au.com.groundhog.groundpics.DomainGwt"/>
<entry-point class="au.com.groundhog.groundpics.gwt.client.GPicsUIEntryPoint"/>
</module>
回答by Tim
There's a better answer here: GWT Simple RPC use case problem : Code included
这里有一个更好的答案:GWT 简单 RPC 用例问题:包含的代码
Basically, you can add parameters to your APPNAME.gwt.xml file so the compiler to give the compiler a path to the server-side class.
基本上,您可以向 APPNAME.gwt.xml 文件中添加参数,以便编译器为编译器提供服务器端类的路径。
回答by jasop
I was getting the same issue and the "mvn gwt:compile" output was not very helpful. Instead, when I tried deploying to tomcat (via the maven tomcat plugin: mvn tomcat:deploy) I got helpful error messages.
我遇到了同样的问题,“mvn gwt:compile”输出不是很有帮助。相反,当我尝试部署到 tomcat(通过 maven tomcat 插件:mvn tomcat:deploy)时,我收到了有用的错误消息。
A few things I had to fix up:
我必须解决的一些问题:
- Make the object that is sent from the client to the server implement Serializable
- Add an empty-arg constructor to that same object
- 使客户端发送到服务端的对象实现Serializable
- 向同一个对象添加一个空参数构造函数
回答by Bob Dillon
Keying off of rustyshelf's answer above ...
关闭上面 rustyshelf 的答案......
In my case I needed to edit the ModuleName.gwt.xml file and add the following:
就我而言,我需要编辑 ModuleName.gwt.xml 文件并添加以下内容:
<source path='client'/>
<source path='shared'/>
I created my project with the New->Web Application Projectwizard but unchecked the Generate project sample codeoption. I then created the shared package. Had I not unchecked that, the package would have been created for me and the xml file modified per the above.
我使用New->Web Application Project向导创建了我的项目,但未选中Generate project sample code选项。然后我创建了共享包。如果我没有取消选中,就会为我创建包,并根据上述修改 xml 文件。
回答by ali kerim erkan
There is a far more simple and easy solution for that. If you want to send an object of your custom designed class from server side to client side you should define this custom class in sharedpackage.
对此有一个更简单易行的解决方案。如果要将自定义设计类的对象从服务器端发送到客户端,则应在共享包中定义此自定义类。
For example for your case the you just have to carry the Stock.java class (by drag and drop) into
例如,对于您的情况,您只需将 Stock.java 类(通过拖放)放入
com.google.gwt.sample.stockwatcher.shared
com.google.gwt.sample.stockwatcher.shared
package. However from your package hierarchy screenshot it seems that you had deleted this shared package. Just re-create this package and drop the Stock.java inside it and let the game begin.
包裹。但是,从您的包层次结构屏幕截图来看,您似乎已删除了此共享包。只需重新创建这个包并将 Stock.java 放入其中,让游戏开始。
回答by ali kerim erkan
Yes, it is sure that we need to use the Serialization for getting the server objects to the client. These modile?? file settings won't work to use the Stock class in the client side.
是的,我们肯定需要使用序列化来将服务器对象发送到客户端。这些模型??文件设置无法在客户端使用 Stock 类。
In your case you have only one class Stock and you can create a StockClient in client side. It is easy. But what will be the solution if anyone having more classes. Something like the properties of this class are also some other type of classes.
在您的情况下,您只有一个类 Stock 并且您可以在客户端创建一个 StockClient 。这很容易。但是,如果有人有更多的课程,那将是什么解决方案。类似这个类的属性的东西也是一些其他类型的类。
Example: stock.getEOD(date).getHigh();
例子: stock.getEOD(date).getHigh();
getEODwill return another class with the given date and that class has the getHighmethod.
getEOD将返回具有给定日期的另一个类,并且该类具有该getHigh方法。
What to do in such big cases? I don't think creating all classes implementing serialization in client side is good for that. Then we have to write code in both server and client. all classes two times.
这么大的案子怎么办?我不认为在客户端创建所有实现序列化的类对此有好处。然后我们必须在服务器和客户端编写代码。所有课程两次。


