我正在开发一个Web应用程序,其中一个要求是调用通过Jersey客户端完成的外部Web服务。现在我想在一个单独的线程中调用此Web服务api,以便不会阻止UI等待WS响应。我还希望能够只记录Web服务调用返回的响应,我不会在任何业务逻辑中使用响应。
为此,我创建了一个spring managed bean,它将保存对spring ThreadPoolTaskExecutor
的引用。配置如下:
<bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<property name="corePoolSize" value="5" />
<property name="maxPoolSize" value="10" />
<property name="queueCapacity" value="25" />
</bean>
<bean id="updateEmailSubscriptionClient" class="com.myorg.MyClass">
<constructor-arg ref="taskExecutor" />
</bean>
MyClass
有一个实现runnable的内部类,用于生成将执行实际webservice调用并记录响应的线程,所有这些都是异步完成的,因此不会阻止UI。 MyClass
看起来像这样:
public class MyClass {
private Client jClient; //The jersey client
private TaskExecutor taskExecutor;
public MyClass(TaskExecutor taskExecutor){
this.taskExecutor = taskExecutor;
ClientConfig cc = new DefaultClientConfig();
//Initialize jersey client config here ...
jClient = Client.create(cc);
}
public void makeWSCall(){
//Do some stuff
taskExecutor.execute(new WorkerClass());
}
private class WorkerClass implements Runnable{
void run(){
WebResource webResource = jClient.resource(myResource);
//Setup webresource here with headers, mime etc....
try{
String resp = webResource.post(String.class);
//Log the response here ...
}catch(UniformInterfaceException e){
//Do something ...
}
}
}
}
从上面的代码中可以看出,尝试是在WorkerClass
的run方法中将jersey客户端调用放到WS中,该方法实现Runnable
。公共方法makeWSCall
然后,taskExecutor
会在调用时WorkerClass
执行新的taskExecutor
对象。我的问题/疑虑如下:
run()
提供任何destroy方法。有必要吗?WorkerClass
的{{1}}方法会抛出异常,会发生什么?该线程是否可以再次从池中获得?请注意,我尝试使用Jersey的AsyncWebResource
,但我仍然需要从仍然会阻止UI的帖子上返回的Future对象获取响应。此外,我们使用的是旧版本的jersey客户端(1.0.2),并且可能无法使用其中一些较新的功能。升级到较新版本目前超出了该项目的范围。
修改
我在Spring中阅读了关于@Async注释的内容,我认为这比使用实现runnable的内部类更好。我认为解决这个问题的方法是将WorkerClass
中的代码转移到makeWSCall
,使用@Async注释makeWSCall
,在spring配置文件中进行必要的更改。但是,如果在应用程序上下文中定义了另一个执行程序会发生什么。 Spring如何知道在哪个执行器中运行我的@Async方法?
修改
我最终使用@Async注释工作正常。另一个问题是关闭连接。如何确保在返回响应后关闭连接。我可以在getEntity
对象上调用ClientResponse
来确保连接已关闭(根据球衣文档)。但是,如果getEntity
中存在异常,或者甚至在我的网络资源上执行实际发布请求时,连接是否仍会关闭,或者我是否必须明确执行此操作?在我的泽西版本中,close()
没有ClientResponse
方法。我应该如何在例外情况下关闭连接?