最近在做一个J2EE的分布式项目,多个子系统之间需要通过Web Service进行通信,项目中使用EJB发布WebService。
为了提高系统的响应效率决定给所有的Web Service查询方法返回值添加缓存。要给所有的查询方法统一添加缓存,首先就会想到使用AOP,然后会想到Spring AOP。但是发布成Web Service的EJB是不受Spring管理的,于是想到了EJB中的AOP策略:Interceptor
实现思路如下:
给EJB的实现类添加拦截器
拦截器判断查询方法的返回值是否已在缓存中,如果在则直接返回,否则调用EJB的方法,并将返回值放到缓存中,然后将结果返回
项目中使用的缓存产品是Ehcache,使用时需要引用以下jar包
ehcache-core.jar
slf4j-api.jar
slf4j-log4j.jar
首先封装一个缓存处理类,功能如下
获取缓存对象
根据名称获取缓存对象
清空缓存对象
根据名称清空缓存对象
添加缓存对象
public class CacheHandler {
//缓存管理器变量
private CacheManager manager;
//缓存变量
private Cache cache;
//缓存名称
private final String cacheName="EJB_METHOD_CACHE";
/********************************单例模式(饿汉式) begin***********************************************/
private static CacheHandler cacheHandler=new CacheHandler();
/**
* 私有构造函数
*/
private CacheHandler(){
System.out.println("---------------创建单例 begin--------------");
initCache();
System.out.println("---------------创建单例 end--------------");
}
public static CacheHandler getInstance(){
return cacheHandler;
}
/********************************单例模式(饿汉式) end***********************************************/
/**
* @MethodName : initCache
* @Description : 初始化缓存
*/
private void initCache(){
//1.创建cachemanager
URL url=getClass().getResource("/ehcache.xml");
manager=CacheManager.create(url);
cache=manager.getCache(cacheName);
//如果cache是空,則手動創建
// 当启动hibernate二级缓存时,cache为空
if(cache==null){
cache=new Cache("EJB_METHOD_CACHE", 10000, true, false, 600000, 300000);
manager.addCache(cache);
}
}
/**
* @MethodName : getCache
* @Description : 外部通过get方法拿到cache后,可清空cache
* @return
*/
public Cache getCache() {
return cache;
}
/**
* @MethodName : getCacheByName
* @Description : 根据缓存名称获取缓存
* @param cacheName 缓存名称
* @return 缓存名称对应的缓存对象
*/
public Cache getCacheByName(String cacheName){
return manager.getCache(cacheName);
}
/**
* @MethodName : clearCache
* @Description : 清理缓存
*/
public void clearCache(){
if(cache!=null){
cache.removeAll();
}
}
/**
* @MethodName : clearCacheByName
* @Description : 根据缓存名称删除缓存
* @param cacheName 缓存名称
*/
public void clearCacheByName(String cacheName){
getCacheByName(cacheName).removeAll();
}
/**
* @MethodName : addCache
* @Description : 用户自定义缓存
* @param cacheName 自定义缓存的名称
* @return 自定义的缓存
*/
public Cache addCache(String cacheName){
Cache customCache = manager.getCache(cacheName);
if(customCache==null){
customCache =new Cache(cacheName, 10000, false, false, 600000, 300000);
manager.addCache(customCache);
}
return customCache;
}
}
为查询方法添加缓存
应用于查询方法的拦截器
public class CacheInterceptor {
@AroundInvoke
public Object processCache(InvocationContext context) throws Exception{
String targetName = context.getTarget().getClass().getName();
String methodName = context.getMethod().getName();
Object[] arguments =context.getParameters();
Object result;
//获取缓存对象
Cache cache = CacheHandler.getInstance().getCache();
//如果方法名以find、query、或get开头则执行缓存策略
if(methodName.startsWith("find") || methodName.startsWith("get") || methodName.startsWith("query")){
String cacheKey = getCacheKey(targetName, methodName, arguments);
Element element = cache.get(cacheKey);
if (element == null) {
result = context.proceed(); // 执行目标方法,并保存目标方法执行后的返回值
element = new Element(cacheKey, (Serializable) result);
cache.put(element);
System.out.println("createCache-->" + cacheKey);
} else {
System.out.println("hit Cache-->" + cacheKey);
}
return element.getObjectValue();
}
//否则直接执行目标方法
return context.proceed();
}
/**
* @MethodName : getCacheKey
* @Description : 获得cache key的方法,cache key是Cache中一个Element的唯一标识 cache key包括
* 包名+类名+方法名+各个参数的具体指,如com.co.cache.service.UserServiceImpl.getAllUser
* @param targetName 类名
* @param methodName 方法名
* @param arguments 方法实参数组
* @return cachekey
*/
private String getCacheKey(String targetName, String methodName,
Object[] arguments) {
StringBuffer sb = new StringBuffer();
sb.append(targetName).append(".").append(methodName);
if ((arguments != null) && (arguments.length != 0)) {
for (int i = 0; i < arguments.length; i++) {
if(arguments[i] instanceof String[]){
String[] strArray = (String[])arguments[i];
sb.append(".");
for(String str : strArray){
sb.append(str);
}
}else{
sb.append(".").append(arguments[i]);
}
}
}
return sb.toString();
}
}
将此拦截器应用到需要添加缓存的EJB实现类上即可
其实就是一行注解:@Interceptors(CacheInterceptor.class),如下所示
@Stateless
@Remote( IJcCommonBean.class)
@TransactionManagement(TransactionManagementType.CONTAINER)
@TransactionAttribute(TransactionAttributeType.REQUIRED)
@WebService(endpointInterface = "mgr.jc.webservice.common.IJcCommonBean",serviceName = "JcCommonService",targetNamespace = "http://common.webservice.jc.mgr/")
@Interceptors(CacheInterceptor.class)
public class JcCommonBeanImpl extends BaseMgr implements IJcCommonBean {
//此处是EJB业务逻辑方法
}
清除缓存
当EJB执行了增删改方法后,缓存中的数据就变成了脏数据,需要清空缓存
同样是用一个拦截器来解决,不同的是,这个拦截器是添加在EJB增删改方法上的
拦截器代码如下
public class CacheClearInterceptor {
@AroundInvoke
public Object clearCache(InvocationContext context) throws Exception{
//执行目标方法
Object returnObj =context.proceed();
/**************************清空本地缓存 begin**************************************/
System.out.println("清空前的缓存数:"+CacheHandler.getInstance().getCache().getSize());
//清空本地缓存
CacheHandler.getInstance().clearCache();
System.out.println("清空后的缓存数:"+CacheHandler.getInstance().getCache().getSize());
/**************************清空本地缓存 end**************************************/
return returnObj;
}
}
将此拦截器应用到增删改方法上
@Interceptors(CacheClearSyncInterceptor.class)
public void addAgency(Agency agency) {
commonEao.save(agency);
}
这样就实现了为WebService添加缓存
下一篇博客将为大家讲述分布式环境下的缓存同步问题