下面以Jersey 2.0和Spring 3.0版本为例。
<dependency>
<groupId>org.glassfish.jersey.coregroupId>
<artifactId>jersey-serverartifactId>
<version>2.0version>
dependency>
<dependency>
<groupId>org.glassfish.jersey.containersgroupId>
<artifactId>jersey-container-servletartifactId>
<version>2.0version>
dependency>
<dependency>
<groupId>org.glassfish.jersey.extgroupId>
<artifactId>jersey-spring3artifactId>
<version>2.2version>
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;
......
}