Flex Structure 2
如何在业务层管理你的Cache
上次初步研究了一下前台与后台的关系,但还遗留了一个Server端的Cache问题。
关键字:EHCache, Spring Interceptor, Spring Advice, Java Annotation
前言
在看过很多的Cache的文章和讨论后,我是这样使用Cache的
1. 在Session的生命周期内使用Hibernate的First Level Cache来缓存对象(数据访问层,细粒度缓存)
2. 使用EHCache对Value Object在业务层做缓存(粗粒度缓存,写代码实现)
为什么我不想使用Hibernate的二级缓存呢?主要有以下几点思考
为了提高它的性能,我们把Cache和持久层关连起来,值得吗?
有必要所有的地方都做Cache吗?这些性能的提升是客户想要的吗?
哪些地方需要做Cache不是只有业务层才知道吗?
关于Hibernate二级缓存详细的介绍,大家还是看看下面几篇文章吧,讲得很好
分析Hibernate的缓存机制
http://www.enet.com.cn/article/2008/0115/A20080115110243.shtml
hibernate二级缓存攻略
http://www.iteye.com/topic/18904
Speed Up Your Hibernate Applications with Second-Level Caching
http://www.devx.com/dbzone/Article/29685/1954?pf=true
在现实生活中,在业务层做Cache又会有一些问题
在业务层需要做Cache的方法里要加上添加Cache或清除Cache的代码,这样不但做起来很麻烦,而且把Cache代码和业务逻辑混杂在一起。
在执行一个方法时,哪些关连的Cache需要被清除。如执行了UserBiz.update(userVO)后,需要清除findAll产生的所有Cache,同时也应该把id相同的findById产生的Cache清除。
下面的文章和代码也就是着重解决上面提到的问题
如附图所示,Spring会为所有的Biz方法加上MethodCacheInterceptor.java和MethodCacheAfterAdvice.java,当方法执行之前,Interceptor会对照Annotation的配置去看此方法的结果需不需要和有没有被Cache,然后决定是否直接从Cache中获得结果(如findAll方法)。而After Advice是在方法执行后决定是否要做一些Cache的清理工作(如update方法)。
具体的Annotation配置方法请参照后面的UserBiz.java
废话和理论还是少说点,上代码才是硬道理
ApplicationContext.xml
<!-- cache -->
<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<property name="configLocation">
<value>classpath:ehcache.xml</value>
</property>
</bean>
<bean id="methodCache" class="org.springframework.cache.ehcache.EhCacheFactoryBean">
<property name="cacheManager">
<ref local="cacheManager"/>
</property>
<property name="cacheName">
<value>com.novem.common.cache.ehcache.METHOD_CACHE</value>
</property>
</bean>
<bean id="methodCacheInterceptor" class="com.novem.common.cache.ehcache.MethodCacheInterceptor">
<property name="cache">
<ref local="methodCache" />
</property>
</bean>
<bean id="methodCacheAfterAdvice" class="com.novem.common.cache.ehcache.MethodCacheAfterAdvice">
<property name="cache">
<ref local="methodCache" />
</property>
</bean>
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames">
<value>*Biz</value>
</property>
<property name="interceptorNames">
<list>
<value>methodCacheInterceptor</value>
<value>methodCacheAfterAdvice</value>
</list>
</property>
</bean>
MethodCacheInterceptor.java
package com.novem.common.cache.ehcache;
import java.io.Serializable;
import net.sf.ehcache.Cache;
import net.sf.ehcache.Element;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
import com.novem.common.cache.annotation.MethodCache;
public class MethodCacheInterceptor implements MethodInterceptor,
InitializingBean
{
private Cache cache;
/**
* sets cache name to be used
*/
public void setCache(Cache cache)
{
this.cache = cache;
}
/**
* Checks if required attributes are provided.
*/
public void afterPropertiesSet() throws Exception
{
Assert.notNull(cache,
"A cache is required. Use setCache(Cache) to provide one.");
}
/**
* main method caches method result if method is configured for caching
* method results must be serializable
*/
public Object invoke(MethodInvocation invocation) throws Throwable
{
// do not need to cache
if(!invocation.getMethod().isAnnotationPresent(MethodCache.class)
|| MethodCache.FALSE.equals(invocation.getMethod().getAnnotation(MethodCache.class).isToCache()))
{
return invocation.proceed();
}
String targetName = invocation.getThis().getClass().getName();
String methodName = invocation.getMethod().getName();
Object[] arguments = invocation.getArguments();
Object result;
String cacheKey = getCacheKey(targetName, methodName, arguments);
Element element = cache.get(cacheKey);
if (element == null)
{
// call target/sub-interceptor
result = invocation.proceed();
// cache method result
element = new Element(cacheKey, (Serializable) result);
cache.put(element);
}
return element.getValue();
}
/**
* creates cache key: targetName.methodName.argument0.argument1...
*/
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++)
{
sb.append(".").append(arguments[i]);
}
}
return sb.toString();
}
}
MethodCacheAfterAdvice.java
package com.novem.common.cache.ehcache;
import java.lang.reflect.Method;
import java.util.List;
import net.sf.ehcache.Cache;
import org.springframework.aop.AfterReturningAdvice;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
import com.novem.common.cache.annotation.CacheCleanMethod;
import com.novem.common.cache.annotation.MethodCache;
public class MethodCacheAfterAdvice implements AfterReturningAdvice,
InitializingBean
{
private Cache cache;
public void setCache(Cache cache)
{
this.cache = cache;
}
public MethodCacheAfterAdvice()
{
super();
}
public void afterReturning(Object returnValue, Method method,
Object[] args, Object target) throws Throwable
{
// do not need to remove cache
if (!method.isAnnotationPresent(MethodCache.class)
|| method.getAnnotation(MethodCache.class).cacheCleanMethods().length == 0)
{
return;
}
else
{
String targetName = target.getClass().getName();
CacheCleanMethod[] cleanMethods = method.getAnnotation(
MethodCache.class).cacheCleanMethods();
List list = cache.getKeys();
for (int i = 0; i < list.size(); i++)
{
for (int j = 0; j < cleanMethods.length; j++)
{
String cacheKey = String.valueOf(list.get(i));
StringBuffer tempKey = new StringBuffer();
tempKey.append(targetName);
tempKey.append(".");
tempKey.append(cleanMethods[j].methodName());
if (CacheCleanMethod.CLEAN_BY_ID.equals(cleanMethods[j].cleanType()))
{
tempKey.append(".");
tempKey.append(getIdValue(target, method, args[0]));
}
if (cacheKey.startsWith(tempKey.toString()))
{
cache.remove(cacheKey);
}
}
}
}
}
private String getIdValue(Object target, Method method, Object idContainer)
{
String targetName = target.getClass().getName();
// get id value
String idValue = null;
if (MethodCache.TRUE.equals(method.getAnnotation(MethodCache.class)
.firstArgIsIdContainer()))
{
if (idContainer == null)
{
throw new RuntimeException(
"Id container cannot be null for method "
+ method.getName() + " of " + targetName);
}
Object id = null;
try
{
Method getIdMethod = idContainer.getClass().getMethod("getId");
id = getIdMethod.invoke(idContainer);
}
catch (Exception e)
{
throw new RuntimeException("There is no getId method for "
+ idContainer.getClass().getName());
}
if (id == null)
{
throw new RuntimeException("Id cannot be null for method "
+ method.getName() + " of " + targetName);
}
idValue = id.toString();
}
else if (MethodCache.TRUE.equals(method
.getAnnotation(MethodCache.class).firstArgIsId()))
{
if (idContainer == null)
{
throw new RuntimeException("Id cannot be null for method "
+ method.getName() + " of " + targetName);
}
idValue = idContainer.toString();
}
return idValue;
}
public void afterPropertiesSet() throws Exception
{
Assert.notNull(cache,
"Need a cache. Please use setCache(Cache) create it.");
}
}
MethodCache.java
package com.novem.common.cache.annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface MethodCache
{
String TO_CACHE = "TO_CACHE";
String NOT_TO_CACHE = "NOT_TO_CACHE";
String TRUE = "TRUE";
String FALSE = "FALSE";
public String isToCache() default TO_CACHE;
public String firstArgIsId() default FALSE;
public String firstArgIsIdContainer() default FALSE;
public CacheCleanMethod[] cacheCleanMethods() default {};
}
CacheCleanMethod.java
package com.novem.common.cache.annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface CacheCleanMethod
{
String CLEAN_ALL = "CLEAN_ALL";
String CLEAN_BY_ID = "CLEAN_BY_ID";
public String methodName();
public String cleanType() default CLEAN_ALL;
}
UserBiz.java
package com.novem.farc.biz;
import java.util.List;
import com.novem.common.cache.annotation.CacheCleanMethod;
import com.novem.common.cache.annotation.MethodCache;
import com.novem.farc.vo.UserVO;
public interface UserBiz
{
@MethodCache()
public UserVO findById(Long id);
@MethodCache()
public List findAll(int firstResult, int maxResults);
@MethodCache(
isToCache = MethodCache.FALSE,
firstArgIsIdContainer = MethodCache.TRUE,
cacheCleanMethods = {@CacheCleanMethod(methodName="findById", cleanType = CacheCleanMethod.CLEAN_BY_ID),
@CacheCleanMethod(methodName="findAll")}
)
public void update(UserVO vo);
@MethodCache(
isToCache = MethodCache.FALSE,
firstArgIsIdContainer = MethodCache.TRUE,
cacheCleanMethods = {@CacheCleanMethod(methodName="findAll")}
)
public Long insert(UserVO vo);
@MethodCache(
isToCache = MethodCache.FALSE,
firstArgIsId = MethodCache.TRUE,
cacheCleanMethods = {@CacheCleanMethod(methodName="findById", cleanType = CacheCleanMethod.CLEAN_BY_ID),
@CacheCleanMethod(methodName="findAll")}
)
public void remove(Long id);
}
注意:如果@CacheCleanMethod的cleanType = CacheCleanMethod.CLEAN_BY_ID,则此方法的第一个参数一定要是对象的ID(userId)或ID container(UserVO, 并且此对象中要有getId方法)。之所以要有这样的限制,我是觉得在企业开发中,大家follow这样的规则就好,没必要为了能灵活地取出ID再多搞出一些配置项出来。
为什么我不使用XML来配置Cache呢?
下面是我最早的时候写的一个配置XML,但后来发现,使用这种方法,就得为每一个Biz配置一个XML,就和早期的xxx.hbm.xml一样,管理起来比较麻烦,不如Annotation简洁
UserBiz.cache.xml
<ehcache>
<bean name="com.novem.farc.biz.UserBiz">
<method name="findById"/>
<method name="findAll"/>
<method name="update">
<cleanList>
<method name="findAll"/>
<method name="findById" type="exactly"/>
</cleanList>
</method>
<method name="insert">
<cleanList>
<method name="findAll"/>
</cleanList>
</method>
<method name="delete">
<cleanList>
<method name="findAll"/>
<method name="findById" type="exactly"/>
</cleanList>
</method>
</bean>
</ehcache>
- 大小: 15.5 KB
分享到:
相关推荐
在"flex+java+mysql登录"这个场景中,我们主要探讨的是如何使用Flex客户端与Java后台进行通信,以及通过Java处理数据库交互来实现用户登录功能。 1. Flex数据库操作:Flex中的数据访问通常通过 BlazeDS 或 LCDS ...
以上就是构建“新建Flex+Java的WEB项目”所需的主要知识点。通过熟练掌握这些技术和流程,开发者能够创建出功能强大、交互性丰富的Web应用程序。在实际项目中,还需要关注代码规范、错误处理、性能优化等多个方面,...
项目的一部分源码flex+java,项目的一部分源码flex+java,项目的一部分源码flex+java,
【Flex+Java+LCDSS 源码解析】 在IT领域,Flex是一种基于ActionScript的开源框架,主要用于构建富互联网应用程序(RIA)。它允许开发者创建交互性强、视觉效果丰富的用户界面,而Java则是一种广泛使用的后端编程...
Flex+Java、PHP 批量上传实例文档Flex+Java、PHP 批量上传实例文档Flex+Java、PHP 批量上传实例文档Flex+Java、PHP 批量上传实例文档Flex+Java、PHP 批量上传实例文档Flex+Java、PHP 批量上传实例文档Flex+Java、...
通过以上步骤,你可以创建一个完整的Flex+Java Servlet文件上传系统。这个系统允许用户在Flex前端选择文件,然后通过Java Servlet在后端处理文件上传,提供了一种灵活且可扩展的解决方案。在实际应用中,可以根据...
在入门Flex+Java开发的过程中,你需要掌握以下知识点: 1. **Flex基础**:学习Flex SDK,理解MXML和ActionScript 3.0,这是构建Flex应用的主要工具。熟悉基本组件如Button、Label、TextInput等,并学习如何布局和...
标题中的"book_flex_flex+java的留言板_Flex+Java_"表明这是一个关于使用Flex与Java技术实现的留言板系统的教程或者参考资料。Flex是Adobe开发的一种用于构建富互联网应用程序(RIA)的开源框架,主要用来创建交互式...
综上所述,"flex+java 项目创建 和 例子"这个主题涵盖的内容广泛,包括技术栈的配置、项目结构的设计、前端与后端的通信、实际应用场景的示例,以及开发过程中的注意事项。对于想要学习或提升Flex与Java集成开发技能...
综上所述,"flex+java文件上传"涉及到前端的Flex界面设计与交互、Flash Player运行环境、后端的Java处理逻辑以及文件上传的安全性和性能优化等多个知识点。在实际应用中,开发者需要结合这些技术来实现稳定、安全且...
Flex是Adobe公司开发的一种富互联网应用程序(RIA)框架,它...通过这个Flex+Java登录实例,开发者可以学习到如何结合两种技术创建功能完善的Web应用,理解前后端交互的过程,以及在实际项目中应用这些技术的最佳实践。
而"hx"可能是项目中的某个文件或文件夹,可能包含Flex的MXML和ActionScript代码、BlazeDS配置文件、Java服务器端代码等。通过对这些源码的深入研究,我们可以了解到具体的设计模式、数据处理方式以及服务调用逻辑,...
《Flex+Java档案管理系统XYZP详解》 在信息化建设中,档案管理系统的存在至关重要,它为企业数据的安全存储、高效检索提供了便利。本文将详细介绍一款基于Flex和Java技术开发的档案管理系统——XYZP,该系统充分...
通过以上步骤,你就能在Flash Builder 4.5和MyEclipse 10的集成环境中,成功地创建和运行一个Flex+Java项目,并将其部署到Tomcat服务器上。这个集成环境使得开发者能够同时处理前端的Flex界面和后端的Java逻辑,提高...
本例实现由Flex一端客户端发送消息, 然后由Java端在发布到所有订阅的其它Flex端. 里面有说明与源码, 还有一个直接放到Tomcat里面的直接发布的项目 小编使用工具 eclipse3.5 flex sdk4.6 jdk1.6 blzaeds
在IT领域,Flex、Java和MySQL的组合是一个...同时,它也展示了如何在实际项目中运用Flex的图形界面设计、Java的业务逻辑处理以及MySQL的数据存储。对于想要深入理解Web应用开发的开发者来说,这是一个值得研究的实例。
flex项目 java项目 flex项目源码 源码 留言版项目 flex项目 java项目 flex项目源码 源码 留言版项目 flex项目 java项目 flex项目源码 源码 留言版项目 flex项目 java项目 flex项目源码 源码 留言版项目 flex项目 ...
9. 验证配置无误后,可以开始在 Flex 客户端中编写代码,通过 BlazeDS 调用服务器端的 Java 服务。 BlazeDS 的优点在于它可以在不牺牲性能的情况下,提供简单易用的接口来处理复杂的分布式应用需求。它可以与各种 ...
Flex+Java完美整合框架是一种高效的应用开发解决方案,它结合了Adobe Flex的富互联网应用程序(RIA)开发能力与Java的强大后端服务支持。Flex是用于构建交互式、图形丰富的Web应用的开源框架,而Java则提供了稳定的...
在IT行业中,Flex、Java和MySQL是三种非常重要的技术,它们常常被联合使用来构建高效、用户友好的Web应用程序。本实例将详细讲解如何利用这些技术进行开发。 Flex是一种基于Adobe AIR的应用程序开发框架,主要用于...