【问题标题】:GWT RequestFactory + ServiceLocator BugGWT RequestFactory + ServiceLocator 错误
【发布时间】:2023-04-30 14:26:01
【问题描述】:

我正在使用带有 RequestFactory 的 GWT 2.2。该应用程序有一个现有的服务层(服务器端),所以我使用 ServiceLocator 来提供这些实现。我的 Proxy 和 RequestContexts 指定要使用的正确服务和定位器(如 here 所示)。我能够对数据提出基本请求,但是当我尝试保存时,出现以下异常:

com.google.gwt.requestfactory.server.UnexpectedException: Could not instantiate Locator com.schedgy.core.service.OrganizationService. Is it default-instantiable?
at com.google.gwt.requestfactory.server.ServiceLayerDecorator.die(ServiceLayerDecorator.java:185)
at com.google.gwt.requestfactory.server.LocatorServiceLayer.newInstance(LocatorServiceLayer.java:222)
at com.google.gwt.requestfactory.server.LocatorServiceLayer.createLocator(LocatorServiceLayer.java:47)
at com.google.gwt.requestfactory.server.ServiceLayerDecorator.createLocator(ServiceLayerDecorator.java:54)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

OrganizationService 定义如下:

// Parent class provides the generic Locator<Organization, String> methods
public class OrganizationService extends CompanyEntityService<Organization> {

        protected OrganizationDao organizationDao;

        protected UserDao userDao;

        protected RoleDao roleDao;

        @Inject
        public OrganizationService(
                OrganizationDao organizationDao,
                UserDao userDao,
                RoleDao roleDao) {

            super(organizationDao, Organization.class);

            this.organizationDao = organizationDao;
            this.userDao = userDao;
            this.roleDao = roleDao;
        }

... additional methods
}

我的定位器类看起来像:

public class CompanyServiceLocator implements ServiceLocator {

    protected Injector injector;

    public CompanyServiceLocator() {        
        injector = GuiceFactory.getInjector();
    }

    @Override
    public Object getInstance(Class<?> clazz) {
        return injector.getInstance(clazz);
    }
}

OrganizationProxy 看起来像:

@ProxyFor(value=Organization.class, locator=OrganizationService.class)
public interface OrganizationProxy extends CompanyEntityProxy {
... setters/getters defined here
}

OrganizationRequest 看起来像:

@Service(value=OrganizationService.class, locator=CompanyServiceLocator.class)
public interface OrganizationRequest extends RequestContext {
...
}

客户端代码如下所示:

OrganizationRequest req = organizationRequestFactory.request();
req.paginate(0, 10).fire(paginationReceiver); // Works!

req = organizationRequestFactory.request();
OrganizationProxy org = req.create(OrganizationProxy.class);
org.setName("test");
req.save(org).fire(receiver); // This causes the server side exception

很明显,ServiceLayerDecorator 无法实例化 OrganizationService,因为它没有默认构造函数,但这就是我使用 Guice 并重写 ServiceLocator 以使用 Guice 创建服务实例的原因。但是为什么第一个调用正确地使用了我的 ServiceLocator 而第二个没有呢?

【问题讨论】:

  • 您使用的@Proxy 和@Service 注释是什么?错误消息看起来像您在其中一个注释中将非默认可实例化的组织服务用作定位器值的目标。
  • 不,我很确定他们是正确的。同样,当我获取数据时它工作正常。我只在保存实体时遇到问题。我更新了问题以包含代理接口和请求上下文,以便您可以看到。

标签: java gwt requestfactory


【解决方案1】:

定位器必须是默认可实例化的。

@ProxyFor(value=Organization.class, locator=OrganizationService.class)

是事情出轨的地方。如果OrganizationService 正在出售Organization 的实例以实现Locator 接口,则需要将其设为默认可实例化或注入实现createLocator()ServiceLayerDecorator

第一个代码示例有效而不是第二个有效的原因是第二个代码示例正在根据来自客户端的命令创建和改变Organization。在这种情况下,Locator.create() 必须由 RequestFactory 服务器代码调用。在不知道 paginate() 向客户端返回什么的情况下,我怀疑没有返回 Organization 的实例,因为有必要调用 Locator.getId()Locator.getVersion() 方法。

【讨论】:

  • 让 CompanyServiceLocator 提供一个 OrganizationService 实例的目的是什么?现在是否由两个不同的类(ServiceLayerDecorator 和 ServiceLocator 的实现)提供/实例化服务(定位器的实现)?
  • 服务由ServiceLocators提供;实体由定位器提供。 RequestFactory 使用它们的无参数构造函数创建一个(ServiceLocator 或 Locator),除非您提供 ServiceLayerDecorator 来更改该行为。您可能很难理解谁在做什么,因为您的服务和“操作实体定位器”在您的案例中是同一类(老实说,我不太明白为什么)
  • @Thomas,我有两个不同的类:CompanyServiceLocator 提供服务,OrganizationService 提供“操作实体的定位器”。我看不出它们是一样的吗?也许我在设置问题方面做得不够好,但我相信职责是正确分开的。如果没有,我想听听为什么,以便我可以纠正它们。
  • 我的意思是,OrganizationService 既是你的服务又是一个定位器。事实是您的 CompanyServiceLocator 将提供一个“作为服务”的实例(如果您有这样的 ServiceLocator,这可能意味着您的服务不能简单地使用其默认构造函数实例化)并且 RequestFactory 将使用默认构造函数来实例化类“作为定位器”。最好的 IMO 为服务设置一个类,另一个作为实体的定位器。
  • @Thomas,感谢您的澄清。我明白你现在在说什么了。
【解决方案2】:

我仍然对为什么两个类需要为定位器提供实现感到困惑,但这是我能够解决这个问题的方法。

扩展默认的 RequestFactoryServlet,以便您可以默认注入您的自定义 ServiceLayerDecorator。

public class CompanyRequestFactoryServlet extends RequestFactoryServlet {

    public CompanyRequestFactoryServlet() {
        this(new DefaultExceptionHandler(), new CompanyServiceLayerDecorator());
    }

    public SchedgyRequestFactoryServlet(ExceptionHandler exceptionHandler,
            ServiceLayerDecorator... serviceDecorators) {
        super(exceptionHandler, serviceDecorators);
    }
}

创建一个 ServiceLayerDecorator 来提供定位器的实例。我使用 Guice,所以这很容易。下面代码中的 GuiceFactory 只是一个提供 Guice Injector 实例的单例。

public class CompanyServiceLayerDecorator extends ServiceLayerDecorator {

    @Override
    public <T extends Locator<?, ?>> T createLocator(Class<T> clazz) {
        return GuiceFactory.getInjector().getInstance(clazz);
    }
}

最后,更新您的 web.xml 以使用您的自定义 servlet:

<servlet>
    <servlet-name>requestFactoryServlet</servlet-name>
    <servlet-class>com.company.core.requestfactory.CompanyRequestFactoryServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>requestFactoryServlet</servlet-name>
    <url-pattern>/gwtRequest</url-pattern>
</servlet-mapping>

【讨论】: