将 spring-boot 与 RESTEasy 集成
Integrating spring-boot with RESTEasy
我正在尝试制作 Spring 启动应用程序的原型。我来自 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
class.
冲突
我正在使用 spring-boot 1.3.3 和 spring-framework 4.3.0.rc1
这是完整的示例。
首先,一个示例 JAX-RS 端点:
@Path("/api")
public class SampleResource {
@GET
@Path("/sample")
@Produces(MediaType.APPLICATION_JSON)
public String getSample() {
return "Some JSON";
}
}
接下来,加载所有端点的 JAX-RS 配置 class。
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;
}
}
最后,在您的 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;
}
您的端点应该已启动并且 运行。如果您在 class 路径上缺少 FilterDispatcher class,请将 resteasy-jaxrs 库添加到您的构建描述符中。
另一个答案不会将您的资源作为 spring beans,此自动配置将正确集成它们:
配置class:
@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;
}
}
}
Jackson 供应商:
@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 启动器。以下是您的操作方法:
添加 POM 依赖项
将下面的 Maven 依赖项添加到您的 Spring 启动应用程序 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 应用程序classes
只需将您的 JAX-RS 应用程序 class(应用程序的子class)定义为 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 beans,它们就会自动注册。请注意,JAX-RS 资源可以是单例或请求范围的,而 JAX-RS 提供者必须是单例。
我正在尝试制作 Spring 启动应用程序的原型。我来自 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
class.
我正在使用 spring-boot 1.3.3 和 spring-framework 4.3.0.rc1
这是完整的示例。
首先,一个示例 JAX-RS 端点:
@Path("/api") public class SampleResource { @GET @Path("/sample") @Produces(MediaType.APPLICATION_JSON) public String getSample() { return "Some JSON"; } }
接下来,加载所有端点的 JAX-RS 配置 class。
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; } }
最后,在您的 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; }
您的端点应该已启动并且 运行。如果您在 class 路径上缺少 FilterDispatcher class,请将 resteasy-jaxrs 库添加到您的构建描述符中。
另一个答案不会将您的资源作为 spring beans,此自动配置将正确集成它们:
配置class:
@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;
}
}
}
Jackson 供应商:
@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 启动器。以下是您的操作方法:
添加 POM 依赖项
将下面的 Maven 依赖项添加到您的 Spring 启动应用程序 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 应用程序classes
只需将您的 JAX-RS 应用程序 class(应用程序的子class)定义为 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 beans,它们就会自动注册。请注意,JAX-RS 资源可以是单例或请求范围的,而 JAX-RS 提供者必须是单例。