背景
工程是用的spring boot框架,但存在某些业务需要使用jersey框架提供的能力处理更方便的时候,需要集成jersey。
示例
spring boot本身提供了插拔的配置来支持集成jersey,存在多种实现写法,但是殊途同归。如下是一种写法:
1. 引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jersey</artifactId>
</dependency>
版本依赖于spring-boot-starter-parent
2. jersey配置,需要被spring 扫描到
@Configuration
// 上下文路径,也可以配置在application.properties,未配置的话就是所有/*的请求,当前为/jersey/*
@ApplicationPath("/jersey")
public class JerseyConfig extends ResourceConfig {
public JerseyConfig() {
// 在这里注册相关Controller或者需要的特性支持
register(JerseyController.class);
}
}
3. Controller实现
@Path("/api")
public class JerseyController {
@GET
@Path("/hello")
public String hello() {
return "hello";
}
}
如上写法,请求url:/jersey/api/hello。
如果希望这个类注册到spring容器,可以考虑使用spring 的绑定注解自动扫描等相关方式注册到spring容器,如果不需要不处理。jersey有自己的一套容器系统(使用了HK2)提供了IOC、AOP等能力。
原理
上面的示例只是一个简单实现,下面说下原理
spring mvc本身的实现是注册一个DispatcherServlet,拦截所有请求,进行分发处理,而spring boot集成jersey,其实也是注册了一个jersey的servlet到servlet容器对于映射的指定请求交给它这个servlet来处理。
在spring-boot-autoconfigure包下有个类JerseyAutoConfiguration,这个类上有这个注解:
@ConditionalOnBean(type = "org.glassfish.jersey.server.ResourceConfig")
就是需要存在ResourceConfig这个类型的bean,所以上面注册JerseyConfig这个bean的时候需要继承ResourceConfig,然后上面示例中JerseryConfig这个bean需要被spring 容器找到,无论采用哪种方式,比如:使用@Configuration或者@Component等注解自动扫描,或者使用@Bean注解主动注册到java config的类中等等都可以。
下面这段代码是JerseyAutoConfiguration注册jersey 的servlet的一部分代码:
@Bean
@ConditionalOnMissingBean(name = "jerseyServletRegistration")
@ConditionalOnProperty(prefix = "spring.jersey", name = "type", havingValue = "servlet", matchIfMissing = true)
public ServletRegistrationBean jerseyServletRegistration() {
ServletRegistrationBean registration = new ServletRegistrationBean(
new ServletContainer(this.config), this.path);
addInitParameters(registration);
registration.setName(getServletRegistrationName());
registration.setLoadOnStartup(this.jersey.getServlet().getLoadOnStartup());
return registration;
}
ServletRegistrationBean是spring mvc提供的一种很友好的注册servlet的实现形式,如果需要自定义动态注册servlet的时候可以考虑使用这种方法。关于内部实现,不是本文重点,不多说明了。
上面的this.config的类型是ResourceConfig,在这里的运行态的实际类型便是示例中的JerseyConfig(实际是spring创建的一个代理)。
本质就是spring boot的自动装配在满足条件的时候注册了一个jersey的servlet。
集成失败的可能原因及解决办法
有的时候可能工程环境比较复杂,影响到了spring 的bean定义的解析加载顺序。会出现写法很正确,但是集成jersey失败,比如是期望jersey处理的请求报404。
如果要确认jersey是否集成成功,查看启动日志即可,上面的原理也解释了,是注册一个jersey的servlet,所以如下面红色方框标记的日志,说明集成成功了:
当然,不同的工程配置/环境可能这个servlet注册日志出现的顺序可能不一样,只要出现有类似这个日志即可。
如果发现集成代码写的很正确,但这个jersey的servlet没有注册(没有这个日志)。那就可虑是自动配置初始化的时候出问题了,如原理解释中的在加载JerseyAutoConfiguration这个bean的时候,有一个先决条件是存在ResourceConfig类型的bean,所以如果我们示例中定义的JerseyConfig的bean加载的顺序晚于JerseyAutoConfiguration的加载的时候,就会出现这种情况。这个时候就需要让示例中的JerseyConfig的bean被提前解析到加载,这个可以根据工程的配置环境来进行调整。
如下给一种简单而又粗暴的解决办法,就是把这个bean注册到启动类中:
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Bean
public ResourceConfig resourceConfig() {
return new JerseyConfig();
}
}
还有不少其它好的解决方案,这里不多说明了。