spring自定义缓存标签的实现

自从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{

}

然后定义处理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自定义的标签,以及默认情况下产生key值的规则。最后生成的key值大概是这个样子:caching.aspectj.Calculator.sum(Integer=1;Integer=2;)

下边这段代码在方法上添加了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;
	}
}

在方法上加了MyCacheable标签,当key值相同的情况下会直接在缓存中获取数据,如果没有相同的key值,则会重新计算,因为这里只是一个加和操作,耗时非常的短暂。我们在这里让其睡眠3秒钟。

我们在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!

从结果来看第一次直接计算结果,第二次从缓存中获取。

你可能感兴趣的:(spring,缓存,自定义,切面,Cacheable)