RESTEasy 不会使用自定义 Spring ContextLoader 映射我的 Spring bean

发布于 2024-10-27 18:39:36 字数 3349 浏览 4 评论 0原文

  • RESTEasy 2.0.1GA
  • Java 1.6
  • Spring 3.0.3

我已经尝试了一切,但无法弄清楚发生了什么。我有一个 Spring MVC 应用程序,但是我希望在 Spring MVC 应用程序外部有一些可用的 RESTEasy 端点,但在同一个容器中,最终能够连接到相同的 bean 中。

第一步,我只是尝试在容器内建立 RESTEasy,为来自 Spring 配置的 bean 的请求提供服务。我已经尝试了说明中的样板,也尝试了手动设置,但无济于事。

Bean

@Resource
@Path("/")
public class NeighborComparison {

    private String foo;

    @GET @Path(value="customer") @Produces("text/plain")
    public String getNeighborComparison() {
        return "foo";
    }
}

web.xml

<context-param>
    <param-name>resteasy.servlet.mapping.prefix</param-name>
    <param-value>/api</param-value>
</context-param>

<listener>
    <listener-class>org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap</listener-class>
</listener>

<!-- NOT configuring SpringContextLoaderListener because I declare my own, so if I do, everything
     blows up, plus  all it actually does is sanity check configuration -->
<listener>
    <listener-class>com.example.MyCustomContextLoaderListener</listener-class>
</listener>

<servlet>
    <servlet-name>Resteasy</servlet-name>
    <servlet-class>org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher</servlet-class>
</servlet>

<servlet-mapping>
    <servlet-name>Resteasy</servlet-name>
    <url-pattern>/api/*</url-pattern>
</servlet-mapping>

applicationContext.xml

<bean id="resteasy.providerFactory" class="org.jboss.resteasy.spi.ResteasyProviderFactory"
      factory-method="getInstance">
</bean>

<bean id="resteasy.dispatcher" class="org.jboss.resteasy.core.SynchronousDispatcher">
    <constructor-arg ref="resteasy.providerFactory"/>
</bean>

<bean id="resteasy.spring.bean.processor" class="org.jboss.resteasy.plugins.spring.SpringBeanProcessor">
    <description>
        Add Resources and @Providers to the appropriate places
        in Resteasy's infrastructure
    </description>
    <constructor-arg ref="resteasy.dispatcher"/>
</bean>

<bean id="neighborComparison" class="opower.api.customer.neighbor_comparison.NeighborComparison">
</bean>

根据文档,我所要做的就是“通过分配 org.jboss.resteasy.plugins 的实例来手动注册 RESTeasy BeanFactoryPostProcessor” .spring.SpringBeanProcessor”。我相信这个弹簧配置可以做到这一点。

Jetty 启动并且应用程序上下文旋转起来没有任何问题。应用程序正常工作,但是当我

> curl -H"Accept: text/plain" localhost:8080/ei/api/customer

(“ei”是应用程序上下文)时。日志显示(仅此而已):

2011-03-29 16:44:24,153 DEBUG [qtp-575315405-0] [EI] [] [asy.core.SynchronousDispatcher] PathInfo: /customer
2011-03-29 16:44:24,156 DEBUG [qtp-575315405-0] [EI] [] [asy.core.SynchronousDispatcher] Failed executing GET /customer
org.jboss.resteasy.spi.NotFoundException: Could not find resource for relative : /customer of full path: http://localhost:8080/ei/api/customer

即使我可以说服 RESTEasy 向我显示映射,它似乎也没有发现我的 bean。

如果我通过resteasy.resources上下文参数显式映射它,它就可以工作,尽管显然无法访问自动连接的Spring bean。

我还能尝试什么吗?我有整个 RESTEasy 代码库的调试日志,但没有收到任何消息。我还确认 Spring 实际上正在创建我的 bean,所以只是 RESTEasy 没有找到它。

  • RESTEasy 2.0.1GA
  • Java 1.6
  • Spring 3.0.3

I have tried everything I can, and cannot make head or tail of what's going on. I have a Spring MVC application, however I'd like to have some RESTEasy endpoints available outside the Spring MVC app, but in the same container, ultimately being able to wire in the same beans.

As a first step, I'm simply trying to stand-up RESTEasy inside the container, serving requests from a Spring-configured bean. I have tried the boilerplate from the instructions and have also tried manual setup, to no avail.

Bean

@Resource
@Path("/")
public class NeighborComparison {

    private String foo;

    @GET @Path(value="customer") @Produces("text/plain")
    public String getNeighborComparison() {
        return "foo";
    }
}

web.xml

<context-param>
    <param-name>resteasy.servlet.mapping.prefix</param-name>
    <param-value>/api</param-value>
</context-param>

<listener>
    <listener-class>org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap</listener-class>
</listener>

<!-- NOT configuring SpringContextLoaderListener because I declare my own, so if I do, everything
     blows up, plus  all it actually does is sanity check configuration -->
<listener>
    <listener-class>com.example.MyCustomContextLoaderListener</listener-class>
</listener>

<servlet>
    <servlet-name>Resteasy</servlet-name>
    <servlet-class>org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher</servlet-class>
</servlet>

<servlet-mapping>
    <servlet-name>Resteasy</servlet-name>
    <url-pattern>/api/*</url-pattern>
</servlet-mapping>

applicationContext.xml

<bean id="resteasy.providerFactory" class="org.jboss.resteasy.spi.ResteasyProviderFactory"
      factory-method="getInstance">
</bean>

<bean id="resteasy.dispatcher" class="org.jboss.resteasy.core.SynchronousDispatcher">
    <constructor-arg ref="resteasy.providerFactory"/>
</bean>

<bean id="resteasy.spring.bean.processor" class="org.jboss.resteasy.plugins.spring.SpringBeanProcessor">
    <description>
        Add Resources and @Providers to the appropriate places
        in Resteasy's infrastructure
    </description>
    <constructor-arg ref="resteasy.dispatcher"/>
</bean>

<bean id="neighborComparison" class="opower.api.customer.neighbor_comparison.NeighborComparison">
</bean>

According to the documentation, all I have to do is “manually register the RESTeasy BeanFactoryPostProcessor by allocating an instance of org.jboss.resteasy.plugins.spring.SpringBeanProcessor”. I believe this spring configuration does that.

Jetty starts and the app context spins up with no issues. Application works normally, however when I

> curl -H"Accept: text/plain" localhost:8080/ei/api/customer

("ei" is the application context). The log shows (this and only this):

2011-03-29 16:44:24,153 DEBUG [qtp-575315405-0] [EI] [] [asy.core.SynchronousDispatcher] PathInfo: /customer
2011-03-29 16:44:24,156 DEBUG [qtp-575315405-0] [EI] [] [asy.core.SynchronousDispatcher] Failed executing GET /customer
org.jboss.resteasy.spi.NotFoundException: Could not find resource for relative : /customer of full path: http://localhost:8080/ei/api/customer

Even if I could convince RESTEasy to show me the mappings, it seems that it's just not discovering my bean.

If I map it explicitly via the resteasy.resources context param, it works, though obviously doesn't have access to auto-wired Spring beans.

Anything else I can try? I have debug log on the entire RESTEasy codebase and I don't get any messages. I've also confirmed that Spring is, in fact, creating my bean, so it's just that RESTEasy isn't finding it.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

书信已泛黄 2024-11-03 18:39:36

您的资源类需要使用 @Path 注释进行注释,以便 RESTeasy 在引导期间获取它:

@Path("/customer")
@Resource
public class NeighborComparison {

    @GET @Path("/{customerId}") @Produces("text/plain")
    public String getNeighborComparison(@PathParam("customerId") long customerId) {
        return "foo";
    }
}

请注意 @Path("/{customerId}} 注释,如果没有该注释,您的资源类将无法使用该注释。 该服务由 RESTeasy 获取。

@PathParam 参数将无法正确映射,从而导致非常详细的异常(以及客户端上附带的 500 响应)当然,假设 不要使用 RESTeasy 的 SpringContextLoader,您必须确保您的 SpringBeanProcessor 实例已通过注册 ApplicationContext 委托给它。 SpringContextLoader 中的 code>ApplicationListener:

  ApplicationListener listener = new ApplicationListener() {
     public void onApplicationEvent(ApplicationEvent event) {
        if (event instanceof ContextRefreshedEvent) {
           ContextRefreshedEvent cre = (ContextRefreshedEvent) event;
           ConfigurableListableBeanFactory autowireCapableBeanFactory = (ConfigurableListableBeanFactory) cre
                 .getApplicationContext().getAutowireCapableBeanFactory();
           new SpringBeanProcessor(dispatcher, registry, providerFactory)
                 .postProcessBeanFactory(autowireCapableBeanFactory);
        }
     }
  };
  configurableWebApplicationContext.addApplicationListener(listener);

如果使用自定义上下文加载器而不是 RESTEasy 提供的加载器,则此代码必须出现在上下文加载器中的某个位置,以便一切都有点复杂,是的,SpringBeanProcessor 遍历所有 Spring bean,并向 RESTeasy 注册那些在其层次结构中具有 @Path 注释的 bean。类型及其相应的接口)。

Your resource class needs to be annotated with @Path annotation for RESTeasy to pick up on it during bootstrap:

@Path("/customer")
@Resource
public class NeighborComparison {

    @GET @Path("/{customerId}") @Produces("text/plain")
    public String getNeighborComparison(@PathParam("customerId") long customerId) {
        return "foo";
    }
}

Note the @Path("/{customerId}} annotation without which your @PathParam parameter would not have been mapped correctly, resulting in a pretty detailed exception (and an accompanying 500 response on the client side). Assuming the service is picked up by RESTeasy of course.

In addition if you don't use RESTeasy's SpringContextLoader, you have to make sure your SpringBeanProcessor instance is registered with the ApplicationContext. RESTeasy delegates to it by registering an ApplicationListener in SpringContextLoader:

  ApplicationListener listener = new ApplicationListener() {
     public void onApplicationEvent(ApplicationEvent event) {
        if (event instanceof ContextRefreshedEvent) {
           ContextRefreshedEvent cre = (ContextRefreshedEvent) event;
           ConfigurableListableBeanFactory autowireCapableBeanFactory = (ConfigurableListableBeanFactory) cre
                 .getApplicationContext().getAutowireCapableBeanFactory();
           new SpringBeanProcessor(dispatcher, registry, providerFactory)
                 .postProcessBeanFactory(autowireCapableBeanFactory);
        }
     }
  };
  configurableWebApplicationContext.addApplicationListener(listener);

If using a custom context loader and not the RESTEasy-provided one, this code has to appear somewhere in your context loader so that everything gets wired up. A bit convoluted, yeah. It is SpringBeanProcessor that goes through all Spring beans and registers with RESTeasy those that have a @Path annotation somewhere in their hierarchy (type and their corresponding interfaces).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文