【问题标题】:Integrating spring-boot with RESTEasy将 spring-boot 与 RESTEasy 集成
【发布时间】:2016-04-19 21:58:43
【问题描述】:

我正在尝试对 Spring Boot 应用程序进行原型设计。我来自 Guice JAX-RS 应用程序,所以我更喜欢标准的 JAX-RS 注释而不是 Spring MVC。我已经启动并服务了 Jetty:

@Configuration
@Import({ResteasyBootstrap.class, SpringBeanProcessorServletAware.class, HttpServletDispatcher.class})
public class EmbeddedJetty {
    @Bean
    @Singleton
    public EmbeddedServletContainerFactory servletContainer() {
        JettyEmbeddedServletContainerFactory factory = new JettyEmbeddedServletContainerFactory();
        factory.setPort(9000);
        factory.setSessionTimeout(10, TimeUnit.MINUTES);
        return factory;
    }
}

但是,我就是不知道如何正确连接 RESTEasy。有了上面的SpringBeanProcessorServletAware,它就退出了,似乎ServletContext 在最终被使用之前没有通过ServletContextAware 注入:

java.lang.NullPointerException: null
    at org.jboss.resteasy.plugins.spring.SpringBeanProcessorServletAware.getRegistry(SpringBeanProcessorServletAware.java:30)
    at org.jboss.resteasy.plugins.spring.SpringBeanProcessor.postProcessBeanFactory(SpringBeanProcessor.java:247)
    at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:284)
    at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:174)
    at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:680)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:522)
    at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:118)
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:766)

我也尝试使用 SpringContextLoaderListener,但这似乎与 spring-boot AnnotationConfigEmbeddedWebApplicationContext 类冲突。

我正在使用 spring-boot 1.3.3 和 spring-framework 4.3.0.rc1

【问题讨论】:

标签: spring spring-boot jax-rs resteasy


【解决方案1】:

另一个答案不会将您的资源作为 spring bean,此自动配置将正确集成它们:

配置类:

@Configuration
@ConditionalOnWebApplication
public class RestEasyAutoConfigurer {


    private Environment environment;   

    @Bean(name = "resteasyDispatcher")
    public ServletRegistrationBean resteasyServletRegistration() {
        ServletRegistrationBean registrationBean = new ServletRegistrationBean(new HttpServletDispatcher(), getPrefix()
                + "/*");
        registrationBean.setInitParameters(ImmutableMap.of("resteasy.servlet.mapping.prefix", "/rs/")); // set prefix here
        registrationBean.setLoadOnStartup(1);
        return registrationBean;
    }

    @Bean(destroyMethod = "cleanup")
    public static RestEasySpringInitializer restEasySpringInitializer() {
        return new RestEasySpringInitializer();
    }    

    @Bean
    // use Spring Boot configured Jackson
    public CustomResteasyJackson2Provider jackson2Provider(ObjectMapper mapper) {
        return new CustomResteasyJackson2Provider(mapper); 
    }

    public static class RestEasySpringInitializer
            implements
                ServletContextInitializer,
                ApplicationContextAware,
                BeanFactoryPostProcessor {

        private ResteasyDeployment deployment;

        private ConfigurableApplicationContext applicationContext;

        private ConfigurableListableBeanFactory beanFactory;

        public void cleanup() {
            deployment.stop();
        }

        @Override
        public void onStartup(ServletContext servletContext) throws ServletException {
            ListenerBootstrap config = new ListenerBootstrap(servletContext);
            deployment = config.createDeployment();
            deployment.start();

            servletContext.setAttribute(ResteasyProviderFactory.class.getName(), deployment.getProviderFactory());
            servletContext.setAttribute(Dispatcher.class.getName(), deployment.getDispatcher());
            servletContext.setAttribute(Registry.class.getName(), deployment.getRegistry());

            SpringBeanProcessor processor = new SpringBeanProcessor(deployment.getDispatcher(),
                    deployment.getRegistry(), deployment.getProviderFactory());
            processor.postProcessBeanFactory(beanFactory);
            applicationContext.addApplicationListener(processor);
        }

        @Override
        public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
            this.beanFactory = beanFactory;
        }

        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            this.applicationContext = (ConfigurableApplicationContext) applicationContext;
        }
    }
}

还有杰克逊提供者:

@Provider
@Consumes({"application/*+json", "text/json"})
@Produces({"application/*+json", "text/json"})
public class CustomResteasyJackson2Provider extends ResteasyJackson2Provider {
    private ObjectMapper mapper;

    public CustomResteasyJackson2Provider(ObjectMapper mapper) {
        this.mapper = mapper;
    }

    @Override
    public ObjectMapper locateMapper(Class<?> type, MediaType mediaType) {
        return Optional.ofNullable(_mapperConfig.getConfiguredMapper()).orElse(mapper);
    }    
}

注意:这是 Spring Boot 1.3.3 / RESTEasy 3.0.16 的工作配置

【讨论】:

  • 只是为了记录,“另一个答案不会有你的资源作为春豆”,这是不正确的。我的回答中下面提到的 RESTEasy Spring Boot 状态器功能齐全,包括自动将每个资源和提供者注册为 Spring bean,此外还自动注册 JAX-RS 应用程序子类。
【解决方案2】:

您可以使用 RESTEasy Spring Boot 启动器。以下是你的做法:

添加 POM 依赖

将下面的 Maven 依赖项添加到您的 Spring Boot 应用程序 pom 文件中。

<dependency>
   <groupId>com.paypal.springboot</groupId>
   <artifactId>resteasy-spring-boot-starter</artifactId>
   <version>2.1.1-RELEASE</version>
   <scope>runtime</scope>
</dependency>

注册 JAX-RS 应用程序类

只需将您的 JAX-RS 应用程序类(Application 的子类)定义为 Spring bean,它就会自动注册。请参见下面的示例。有关详细信息,请参阅How to use RESTEasy Spring Boot Starter 中的 JAX-RS 应用程序注册方法部分。

package com.test;

import org.springframework.stereotype.Component;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;

@Component
@ApplicationPath("/sample-app/")
public class JaxrsApplication extends Application {
}

注册 JAX-RS 资源和提供者

只需将它们定义为 Spring bean,它们就会自动注册。请注意,JAX-RS 资源可以是单例的或请求范围的,而 JAX-RS 提供者必须是单例的。

Further information at the project GitHub page.

【讨论】:

    【解决方案3】:

    这是完整的工作示例。

    1. 首先,一个示例 JAX-RS 端点:

      @Path("/api")
      public class SampleResource {
          @GET
          @Path("/sample")
          @Produces(MediaType.APPLICATION_JSON)
          public String getSample() {
              return "Some JSON";
          }
      }
      
    2. 接下来,加载所有端点的 JAX-RS 配置类。

      import javax.ws.rs.core.Application;
      
      public class RestEasyConfig extends Application {
          @Override
          public Set<Class<?>> getClasses() {
              Set<Class<?>> classes = new HashSet<>();
              classes.add(SampleRest.class);
              return classes;
          }
      }
      
    3. 最后,在你的 Spring 配置中,初始化 RESTEast 过滤器并通知框架它的存在。

      import org.springframework.boot.context.embedded.FilterRegistrationBean;
      import org.jboss.resteasy.plugins.server.servlet.FilterDispatcher;
      ...
      
      @Bean
      public FilterRegistrationBean filterRegistrationBean() {
          Map<String, String> initParams = new HashMap<>();
          initParams.put("javax.ws.rs.Application", RestEasyConfig.class.getCanonicalName());
      
          FilterRegistrationBean registrationBean = new FilterRegistrationBean();
          registrationBean.setFilter(new FilterDispatcher());
          registrationBean.setInitParameters(initParams);
          return registrationBean;
      } 
      

      您的端点应该已启动并正在运行。如果您的类路径中缺少 FilterDispatcher 类,请将 resteasy-jaxrs 库添加到您的构建描述符中。

    【讨论】:

    • 谢谢!我试过这个,但正如 Jeff S 所说,这意味着 RESTEasy 资源不是 Spring 生命周期的一部分——例如不会触发 @PostConstruct 注释。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-02-23
    • 1970-01-01
    相关资源
    最近更新 更多