控制单例Bean的有效期

问题描述

这是由之前的一个问题《Java中多层级一对多关系的处理》引发的,当时我为了让用户的http请求,可以复用数据库的数据,将一个包含数据的service作用域设置为了request。

后面这个service在后台跑的job中也用到了,此时会报错:

Scope ‘request’ is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton

想了几个方案,都有问题:

1、将这个bean类的作用域设置为多例@Scope(“prototype”)

经测试,发现因为这个类只会在Spring容器初始化的时候加载一次,因此每个用户过来调用的实例,都是同一个,这样后台数据更新后,用户这边就无法及时获取到变更内容。

2、不缓存数据,每次都查询数据库

该类提供了一个类似getXXXById(int id)的方法,这个方法被调用的频率非常高,如果每次都查询数据库,吃不消。

后来想到,要不给这个数据设置一个缓存时间吧,通过Spring容器默认单例的特性,这样既能复用数据,又能保证数据更新不会太延迟。经过测试,这个方案是可行的。

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
abstract public class MySQLCacheServiceImpl implements CacheService {

protected HashMap<Integer, Object> data = null;

protected long initTime = 0;

protected void reload()
{
long ts = System.currentTimeMillis();
// 一分钟缓存
if (0 == initTime || ts - initTime >= 60000 || null == data) {
try {
data = initCache();
} catch (Exception exp) {
exp.printStackTrace();
}
initTime = ts;
}
}

/**
* 给指标组添加属性
* @param ids
* @return
*/
@Override
public ArrayList addAttributesForIds(List<Integer> ids)
{
this.reload();

ArrayList dataList = new ArrayList();
for (int id : ids) {
if (null != data.get(id)) {
dataList.add(data.get(id));
}
}
return dataList;
}

/**
* 考虑到这里的数据,仅用于输出http接口,没必要封装为具体对象了,因此直接处理为HashMap和ArrayList即可
*
* 这里的处理逻辑应该优化下,不要直接从最顶级开始往下拼装;可以分关联关系进行拼装。
* 比如overlay_id, domain_id, index_industry_code, period_id这些属性只和index关联,那么可以先组装一个index和这些属性的对应HashMap
*
*
* @return
*/
abstract protected HashMap<Integer, Object> initCache();

}