spring+jersey写api服务端,@autowired引起的并发请求问题和解决过程

问题描述

给一个系统写服务端api,采用的spring+jersey的代码架构如下图
spring+jersey写api服务端,@autowired引起的并发请求问题和解决过程_第1张图片

定义了一个InfoResource,其中使用@autowired 来注入对应的InfoQueryService,调用方式如下。测试时发现,如果单线程调用接口则一切正常,如果多线程并发调用这个接口,则部分请求返回内容是不完整的,然而程序运行并没有任何报错

// InfoResource.java
@Path("/info")
@Component
public class InfoResource {
     
 	@Autowired
	private InfoQueryService infoQueryService;
	.......
	@POST
	@Path("/json/upload")
	@Consumes({
      MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON, MediaType.APPLICATION_FORM_URLENCODED })
	@Produces(MediaType.APPLICATION_JSON)
	public Response uploadJson(@Context final HttpHeaders headers, final RequestBodyBean requestBodybean) {
     
		NormalResponseBean normalResponseBean = null;
		try {
     
			normalResponseBean = infoQueryService.doResponseService(headers, requestBodybean);
		} catch (Exception e) {
     
			e.printStackTrace();
		}
		......
	}
}

// InfoQueryService.java
@Component
public class InfoQueryService {
     
	@Autowired
	private ConfigProperty configProperty;
	@Autowired
	private OrgDAO orgDAO;
	@Autowired
	private InfoDAO infoDAO;
	/**
	 * 查询请求接口service 主函数
	 * 
	 * @param headers
	 * @param bodyBean
	 * @return
	 */
	public NormalResponseBean doResponseService(HttpHeaders inHeaders, RequestBodyBean bodyBean) {
     
	......
	}
}
	

问题分析

在service上添加synchronized,发现问题解决了。但是接口速度明显降低,公网调用从80ms降低到130ms,这显然不能接受。删除synchronized。
继续测试,程序不报错说明service注入是成功的,那么哪一层错了呢?
在service层打日志,查看每次接口拼装的返回结果都是对的。
在resource层打日志,发现部分返回结果是错误的。也就是service处理是正确的,返回给resource层就错误了。
判定是service注入到resource的过程有问题。

为什么呢?
spring对组建的注入(@autowired)默认用的单例模式,在上面代码中,spring使用了一个infoQueryService实例来处理多个接口请求。高并发情况下就可能导致service实例在内存层面出现冲突。

有问题的代码:

 	@Autowired
	private InfoQueryService infoQueryService;

举例子,service实例是一个服务员,服务员一次服务一个客户是没问题的,但是同时服务多个客户时,服务员思维就不能保持清晰了。
怎么解决?
多配置服务员!
把单例注入修改为多例注入,每次请求新建一个infoQueryService实例。

代码实现

在spring的单例中注入多例由多种实现方式,笔者偏好于使用@Lookup注解,实现如下。

// InfoResource.java
@Path("/info")
@Component
public class InfoResource {
     
 	// @Autowired // 不再使用autowired
	private InfoQueryService infoQueryService;
	.......
	// 使用Lookup实现多例注入
	@Lookup
	public InfoQueryService getPrototypeBean() {
     
		// 返回null即可,spring会自动重写这个方法,为你返回正确的InfoQueryService实例
		return null;
	}
	@POST
	@Path("/json/upload")
	@Consumes({
      MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON, MediaType.APPLICATION_FORM_URLENCODED })
	@Produces(MediaType.APPLICATION_JSON)
	public Response uploadJson(@Context final HttpHeaders headers, final RequestBodyBean requestBodybean) {
     
		NormalResponseBean normalResponseBean = null;
		try {
     
			infoQueryService = getPrototypeBean();  // 每次请求会新建一个实例
			normalResponseBean = infoQueryService.doResponseService(headers, requestBodybean);
		} catch (Exception e) {
     
			e.printStackTrace();
		}
		......
	}
}
	

最后,多例注入显然会比单例注入慢,测试结果看会慢2到5个毫秒。

更多多例注入的实现方法:
Java编程中的Spring多例

你可能感兴趣的:(java,spring,spring,多例)