自从spring3.1之后,spring引入了抽象缓存,可以通过在方法上添加@Cacheable等标签对方法返回的数据进行缓存。但是它到底是怎么实现的呢,我们通过一个例子来看一下。首先我们定义一个@MyCacheable
package caching.springaop; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.lang.annotation.ElementType; /** * 使用@MyCacheable注解方法 * @author txxs */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface MyCacheable{ }
package caching.springaop; import java.util.HashMap; import java.util.Map; import org.apache.log4j.Logger; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; /** * 处理MyCacheable方法的切面 * @author txxs */ @Aspect public class CacheAspect { private Logger logger = Logger.getLogger(CacheAspect.class); private Map<String, Object> cache; public CacheAspect() { cache = new HashMap<String, Object>(); } /** * 所有标注了@Cacheable标签的方法切入点 */ @Pointcut("execution(@MyCacheable * *.*(..))") @SuppressWarnings("unused") private void cache() { } @Around("cache()") public Object aroundCachedMethods(ProceedingJoinPoint thisJoinPoint) throws Throwable { logger.debug("Execution of Cacheable method catched"); //产生缓存数据的key值,像是这个样子caching.aspectj.Calculator.sum(Integer=1;Integer=2;) StringBuilder keyBuff = new StringBuilder(); //增加类的名字 keyBuff.append(thisJoinPoint.getTarget().getClass().getName()); //加上方法的名字 keyBuff.append(".").append(thisJoinPoint.getSignature().getName()); keyBuff.append("("); //循环出cacheable方法的参数 for (final Object arg : thisJoinPoint.getArgs()) { //增加参数的类型和值 keyBuff.append(arg.getClass().getSimpleName() + "=" + arg + ";"); } keyBuff.append(")"); String key = keyBuff.toString(); logger.debug("Key = " + key); Object result = cache.get(key); if (result == null) { logger.debug("Result not yet cached. Must be calculated..."); result = thisJoinPoint.proceed(); logger.info("Storing calculated value '" + result + "' to cache"); cache.put(key, result); } else { logger.debug("Result '" + result + "' was found in cache"); return result; } }
下边这段代码在方法上添加了MyCacheable标签
package caching.springaop; import org.apache.log4j.Logger; /** * 时间计算类 * @author Igor Urmincek * @author txxs */ public class Calculator { private Logger logger = Logger.getLogger(Calculator.class); @MyCacheable public int sum(int a, int b) { logger.info("Calculating " + a + " + " + b); try { //假设这是代价非常高的计算 Thread.sleep(3000); } catch (InterruptedException e) { logger.error("Something went wrong...", e); } return a + b; } }
我们在spring-config.xml配置如下:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"> <aop:aspectj-autoproxy /> <bean class="caching.springaop.CacheAspect" /> <bean id="calc" class="caching.springaop.Calculator" /> </beans>
package caching.springaop; import org.apache.log4j.Logger; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * 使用SpringAOP缓存的简单例子 * @author txxs */ public class App { private static Logger logger = Logger.getLogger(App.class); public static void main(String[] args) { logger.debug("Starting..."); ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-config.xml"); Calculator calc = (Calculator) ctx.getBean("calc"); //计算出来的结果将会被存储在cache logger.info("1 + 2 = " + calc.sum(1, 2)); //从缓存中获取结果 logger.info("1 + 2 = " + calc.sum(1, 2)); logger.debug("Finished!"); } }
2015-12-11 10:43:26,953 [main] DEBUG caching.springaop.CacheAspect.aroundCachedMethods(CacheAspect.java:37) - Execution of Cacheable method catched
2015-12-11 10:43:26,954 [main] DEBUG caching.springaop.CacheAspect.aroundCachedMethods(CacheAspect.java:52) - Key = caching.springaop.Calculator.sum(Integer=1;Integer=2;)
2015-12-11 10:43:26,955 [main] DEBUG caching.springaop.CacheAspect.aroundCachedMethods(CacheAspect.java:55) - Result not yet cached. Must be calculated...
2015-12-11 10:43:26,978 [main] INFO caching.springaop.Calculator.sum(Calculator.java:14) - Calculating 1 + 2
2015-12-11 10:43:29,979 [main] INFO caching.springaop.CacheAspect.aroundCachedMethods(CacheAspect.java:57) - Storing calculated value '3' to cache
2015-12-11 10:43:29,979 [main] INFO caching.springaop.App.main(App.java:20) - 1 + 2 = 3
2015-12-11 10:43:29,979 [main] DEBUG caching.springaop.CacheAspect.aroundCachedMethods(CacheAspect.java:37) - Execution of Cacheable method catched
2015-12-11 10:43:29,979 [main] DEBUG caching.springaop.CacheAspect.aroundCachedMethods(CacheAspect.java:52) - Key = caching.springaop.Calculator.sum(Integer=1;Integer=2;)
2015-12-11 10:43:29,979 [main] DEBUG caching.springaop.CacheAspect.aroundCachedMethods(CacheAspect.java:60) - Result '3' was found in cache
2015-12-11 10:43:29,980 [main] INFO caching.springaop.App.main(App.java:22) - 1 + 2 = 3
2015-12-11 10:43:29,980 [main] DEBUG caching.springaop.App.main(App.java:23) - Finished!
从结果来看第一次直接计算结果,第二次从缓存中获取。