引入Maven依赖
下面以Jersey 2.0和Spring 3.0版本为例。
<!-- jersey基础依赖-->
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-server</artifactId>
<version>2.0</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet</artifactId>
<version>2.0</version>
</dependency>
<!--
1、核心内容:配置该组件后就可以在Jersey Resource类中通过注解注入Spring组件了。
2、该组件目前已支持Spring4和Spring5,只需将artifactId修改为jersey-spring4或者jersey-spring5,并调整相应版本号即可。
-->
<dependency>
<groupId>org.glassfish.jersey.ext</groupId>
<artifactId>jersey-spring3</artifactId>
<version>2.2</version>
</dependency>
代码示例
// 使用Spring的Service注解生成实现类
@Service
public class WorkServiceImpl implements WorkService {
...
}
@Path("/work")
public class WorkResource {
// 使用Spring的Autowired注解
@Autowired
private WorkService workService;
@Path("/")
@POST
@Produces(MediaType.TEXT_PLAIN)
public void accept(@BeanParam WorkVO work) {
workService.accept(work);
}
}
可能会遇到的问题
一般情况下,上边代码中的workService可以被正常调用。但是如果项目中WorkService接口有多个实现类,此时在Spring环境下就不可以按照接口类型(byType)进行注入了,而只能按照名称(byName)注入,所以需要在@Service中增加beanName,例如:
@Service("weixinWorkService")
public class WeixinWorkServiceImpl implements WorkService {
...
}
@Service("appWorkService")
public class AppWorkServiceImpl implements WorkService {
...
}
这时如果想在WorkService中注入WeixinWorkServiceImpl,我们可能首先想到的是使用javax.annotation.Resource注解,将代码修改成:
@Path("/work")
public class WorkResource {
// 使用Javax的Resource注解
@Resource(name = "weixinWorkService")
private WorkService workService;
@Path("/")
@POST
@Produces(MediaType.TEXT_PLAIN)
public void accept(@BeanParam WorkVO work) {
workService.accept(work);
}
}
但运行代码后发现控制台会报出空指针异常,然后定位到是workService为null导致的。
后来经过一系列尝试并追踪到相关源码才发现,原来Jersey-Spring3组件只支持Spring的@Autowired和@Qualifier,根本就不会解析@Resource。
Jersey-Spring3中负责解析注入组件的是org.glassfish.jersey.server.spring.AutowiredInjectResolver类。
public class AutowiredInjectResolver implements InjectionResolver<Autowired> {
@Override
public Object resolve(Injectee injectee, ServiceHandle<?> root) {
AnnotatedElement parent = injectee.getParent();
String beanName = null;
if(parent != null) {
// 划重点:解析@Qualifier注解,尝试获取beanName
Qualifier an = parent.getAnnotation(Qualifier.class);
if(an != null) {
beanName = an.value();
}
}
return getBeanFromSpringContext(beanName, injectee.getRequiredType());
}
private Object getBeanFromSpringContext(String beanName, Type beanType) {
Class<?> bt = getClassFromType(beanType);
// 划重点:如果beanName不为空,则通过名称获取Spring
if(beanName != null) { bean
return ctx.getBean(beanName, bt);
}
// 划重点:如果beanName为空,则按照类型获取
Map<String, ?> beans = ctx.getBeansOfType(bt);
// 划重点:如果未获取到bean或者获取到多个bean,则返回null。
// 如果一个接口有多个实现类但没有定义@Qualifier时,注入也会失败,运行代码后会报出NPE。
if(beans == null || beans.size() != 1) {
LOGGER.warning(LocalizationMessages.NO_BEANS_FOUND_FOR_TYPE(beanType));
return null;
}
return beans.values().iterator().next();
}
}
找到原因后,解决就很容易了。
@Path("/work")
public class WorkResource {
// 同时使用Autowired和Qualifier
@Autowired
@Qualifier(name = "weixinWorkService")
private WorkService workService;
......
}
小结
- Jersey2支持通过注解与Spring进行整合,但仅支持@Autowired和@Qualifier。
- 不同框架或者组件对于注解的支持范围可能是不同的。因为平时使用Spring比较多,所以很容易混淆。
- 尽量使用统一的技术栈。例如,在没有特殊情况下,Spring环境可以使用SpringMVC替换Jersey。