public Class getTargetClass() {
try {
return this.getTarget().getClass();
} catch (Exception e) {
return null;
}
}
}
现在,我们运行测试用例。测试状态条中出现绿条,测试通过。注意,我们在testcache.xml定义的更新数据的间隔是10秒;测试代码每隔2秒读取一次数据;因此我们预计前5次读取的数据是相同的。让我们观察Console窗口中DEBUG信息():
......
BizManager started.
......
27,781 DEBUG unittest.MyBusinessObject - create new MyBusnissObject
......
get id from BizObjec=9
get id from BizObjec=9
get id from BizObjec=9
get id from BizObjec=9
get id from BizObjec=9
37,796 DEBUG unittest.MyBusinessObject - create new MyBusnissObject
get id from BizObjec=59
果然如此,前5次变量id的值都是9,读取第6次数据时,时间已经过12秒,此时变量id的值是59。现在我们将refreshPeriod的值改为5秒,DEBUG信息如下:
BizManager started.
......
get id from BizObjec=9
get id from BizObjec=9
get id from BizObjec=9
03,921 DEBUG unittest.MyBusinessObject - create new MyBusnissObject
get id from BizObjec=39
get id from BizObjec=39
get id from BizObjec=39
重构OSCacheTargetSource
首先定义Cachable接口如下:
public interface Cachable {
public void refresh();
public void close();
}
然后,让MyBusinessObject实现Cachable接口,refresh方法如下:
public void refresh() {
logger.debug("refresh MyBusnissObject");
id = BizManager.getId();
}
接下来,修改OSCacheTargetSource类的getCached方法如下,其中屏蔽的是先前的实现代码:
private Object getCached() {
try {
//CacheEntry.INDEFINITE_EXPIRY = -1
target = (Object) admin.getFromCache(MY_KEY, refreshPeriod);
} catch (NeedsRefreshException nre) {
try {
//target = target.getClass().newInstance();
((Cachable)target).refresh();
admin.putInCache(MY_KEY, target);
} catch (Exception ex) {
target = nre.getCacheContent();
admin.cancelUpdate(MY_KEY);
}
}
return target;
}
最后,运行测试用例,出现了绿条,测试通过。下面是DEBUG信息输出:
BizManager started.
......
47,828 DEBUG base.Cache - No cache entry exists for key='mykey', creating
47,828 DEBUG unittest.MyBusinessObject - refresh MyBusnissObject
......
get id from BizObjec=9
get id from BizObjec=9
get id from BizObjec=9
53,843 DEBUG unittest.MyBusinessObject - refresh MyBusnissObject
get id from BizObjec=39
get id from BizObjec=39
get id from BizObjec=39
我们的方案看来起作用了,但仍然让我们感觉不好,因为需要缓存服务的对象必须要实现Cachable接口,而且OSCacheTargetSource假设需要缓存的对象必须拥有缺省的构造器,这种解决方案太具有侵入性,违反了Spring Framework的设计思想。为了重构代码,OSCacheTargetSource对象必须获得需要缓存的Bean的信息。因此,OSCacheTargetSource对象必须实现BeanFactoryAware或ApplicationContextAware接口。为了证实我们的思路,参考CommonsPoolTargetSource的源代码,发现它确实是实现了BeanFactoryAware接口。
基于以上的分析,我们把OSCacheTargetSource重构为:
public class OSCacheTargetSource implements TargetSource, BeanFactoryAware {
…
public void setTargetBeanName(String beanName) {
this.targetBeanName = beanName;
}
private Object getCached() {
try {
target = (Object) admin.getFromCache(MY_KEY, refreshPeriod);
} catch (NeedsRefreshException nre) {
try {
target = beanFactory.getBean(this.targetBeanName);
admin.putInCache(MY_KEY, target);
} catch (Exception ex) {
target = nre.getCacheContent();
admin.cancelUpdate(MY_KEY);
}
}
return target;
}
}
将MyBusinessObject重构为:
public class MyBusinessObject implements Business {
final Log logger = LogFactory.getLog(this.getClass());
private int id = 0;
public MyBusinessObject() {
logger.debug("create new MyBusnissObject");
id = BizManager.getId();
}
public int getId() {
return id;
}
public void close() {}
}
并修改testcache.xml配置文件如下:
<beans>
<bean id="businessObjectTarget" class="unittest.MyBusinessObject" singleton="false" destroy-method="close"/>
<bean id="cacheTargetSource" class="unittest.OSCacheTargetSource">
<property name="targetBeanName"><value>businessObjectTarget</value></property>
<property name="refreshPeriod"><value>10</value></property>
</bean>
<bean id="businessObject" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces"><value>unittest.Business</value></property>
<property name="targetSource"><ref local="cacheTargetSource"/></property>
</bean>
</beans>
现在,我们运行测试用例。测试状态条中出现绿条,测试通过。但注意到OSCacheTargetSource类中缓存的对象的key值仍然是写死的,我们需要一个这样的key值,对于BeanFactory或ApplicationContext必须是唯一的。因为每个Bean的id值正好符合这个条件,我们可以使用targetBeanName作为key值。因此OSCacheTargetSource类修改为:
private Object getCached() {
try {
target = (Object) admin.getFromCache(
this.targetBeanName, refreshPeriod);
} catch (NeedsRefreshException nre) {
try {
target = beanFactory.getBean(this.targetBeanName);
admin.putInCache(this.targetBeanName, target);
} catch (Exception ex) {
target = nre.getCacheContent();
admin.cancelUpdate(this.targetBeanName);
}
}
return target;
}