Spring请求级备忘录

介绍

记忆化是一种方法级别的缓存技术,用于加快连续调用的速度。

这篇文章将演示如何仅使用Spring AOP实现任何数据源的请求级可重复读取。

Spring缓存

Spring提供了非常有用的缓存抽象 ,允许您将应用程序逻辑与缓存实现细节分离。

Spring Caching使用应用程序级范围,因此对于仅请求的备忘录,我们需要采用DIY方法。

请求级缓存

请求级缓存条目生命周期始终绑定到当前请求范围。 这种缓存与提供会话级可重复读取的 Hibernate Persistence Context非常相似。

为了防止更新丢失 ,甚至对于NoSQL解决方案,必须进行可重复的读取

分步实施

首先,我们将定义一个“记忆标记”注释:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Memoize {
}

该注释将显式标记所有需要记住的方法。

为了区分不同的方法调用,我们将方法调用信息封装为以下对象类型:

public class InvocationContext {

    public static final String TEMPLATE = "%s.%s(%s)";

    private final Class targetClass;
    private final String targetMethod;
    private final Object[] args;

    public InvocationContext(Class targetClass, String targetMethod, Object[] args) {
        this.targetClass = targetClass;
        this.targetMethod = targetMethod;
        this.args = args;
    }

    public Class getTargetClass() {
        return targetClass;
    }

    public String getTargetMethod() {
        return targetMethod;
    }

    public Object[] getArgs() {
        return args;
    }

    @Override
    public boolean equals(Object that) {
        return EqualsBuilder.reflectionEquals(this, that);
    }

    @Override
    public int hashCode() {
        return HashCodeBuilder.reflectionHashCode(this);
    }

    @Override
    public String toString() {
        return String.format(TEMPLATE, targetClass.getName(), targetMethod, Arrays.toString(args));
    }
}

很少有人知道Spring Request / Session bean的作用域。

因为我们需要一个请求级的备忘录作用域,所以我们可以使用Spring请求作用域来简化我们的设计,该作用域隐藏了实际的HttpSession解析逻辑:

@Component
@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS, value = "request")
public class RequestScopeCache {

    public static final Object NONE = new Object();

    private final Map<InvocationContext, Object> cache = new HashMap<InvocationContext, Object>();

    public Object get(InvocationContext invocationContext) {
        return cache.containsKey(invocationContext) ? cache.get(invocationContext) : NONE;
    }

    public void put(InvocationContext methodInvocation, Object result) {
        cache.put(methodInvocation, result);
    }
}

由于没有运行时处理引擎,仅注释就没有任何意义,因此,我们必须定义一个实现实际备注逻辑的Spring Aspect:

@Aspect
public class MemoizerAspect {

    @Autowired
    private RequestScopeCache requestScopeCache;

    @Around("@annotation(com.vladmihalcea.cache.Memoize)")
    public Object memoize(ProceedingJoinPoint pjp) throws Throwable {
        InvocationContext invocationContext = new InvocationContext(
                pjp.getSignature().getDeclaringType(),
                pjp.getSignature().getName(),
                pjp.getArgs()
        );
        Object result = requestScopeCache.get(invocationContext);
        if (RequestScopeCache.NONE == result) {
            result = pjp.proceed();
            LOGGER.info("Memoizing result {}, for method invocation: {}", result, invocationContext);
            requestScopeCache.put(invocationContext, result);
        } else {
            LOGGER.info("Using memoized result: {}, for method invocation: {}", result, invocationContext);
        }
        return result;
    }
}

测试时间

让我们对所有这些进行测试。 为了简单起见,我们将使用Fibonacci数字计算器模拟请求级范围的备忘需求:

@Component
public class FibonacciServiceImpl implements FibonacciService {

    @Autowired
    private ApplicationContext applicationContext;

    private FibonacciService fibonacciService;

    @PostConstruct
    private void init() {
        fibonacciService = applicationContext.getBean(FibonacciService.class);
    }

    @Memoize
    public int compute(int i) {
        LOGGER.info("Calculate fibonacci for number {}", i);
        if (i == 0 || i == 1)
            return i;
        return fibonacciService.compute(i - 2) + fibonacciService.compute(i - 1);
    }
}

如果我们要计算第十个斐波那契数,我们将得到以下结果:

Calculate fibonacci for number 10
Calculate fibonacci for number 8
Calculate fibonacci for number 6
Calculate fibonacci for number 4
Calculate fibonacci for number 2
Calculate fibonacci for number 0
Memoizing result 0, for method invocation: com.vladmihalcea.cache.FibonacciService.compute([0])
Calculate fibonacci for number 1
Memoizing result 1, for method invocation: com.vladmihalcea.cache.FibonacciService.compute([1])
Memoizing result 1, for method invocation: com.vladmihalcea.cache.FibonacciService.compute([2])
Calculate fibonacci for number 3
Using memoized result: 1, for method invocation: com.vladmihalcea.cache.FibonacciService.compute([1])
Using memoized result: 1, for method invocation: com.vladmihalcea.cache.FibonacciService.compute([2])
Memoizing result 2, for method invocation: com.vladmihalcea.cache.FibonacciService.compute([3])
Memoizing result 3, for method invocation: com.vladmihalcea.cache.FibonacciService.compute([4])
Calculate fibonacci for number 5
Using memoized result: 2, for method invocation: com.vladmihalcea.cache.FibonacciService.compute([3])
Using memoized result: 3, for method invocation: com.vladmihalcea.cache.FibonacciService.compute([4])
Memoizing result 5, for method invocation: com.vladmihalcea.cache.FibonacciService.compute([5])
Memoizing result 8, for method invocation: com.vladmihalcea.cache.FibonacciService.compute([6])
Calculate fibonacci for number 7
Using memoized result: 5, for method invocation: com.vladmihalcea.cache.FibonacciService.compute([5])
Using memoized result: 8, for method invocation: com.vladmihalcea.cache.FibonacciService.compute([6])
Memoizing result 13, for method invocation: com.vladmihalcea.cache.FibonacciService.compute([7])
Memoizing result 21, for method invocation: com.vladmihalcea.cache.FibonacciService.compute([8])
Calculate fibonacci for number 9
Using memoized result: 13, for method invocation: com.vladmihalcea.cache.FibonacciService.compute([7])
Using memoized result: 21, for method invocation: com.vladmihalcea.cache.FibonacciService.compute([8])
Memoizing result 34, for method invocation: com.vladmihalcea.cache.FibonacciService.compute([9])
Memoizing result 55, for method invocation: com.vladmihalcea.cache.FibonacciService.compute([10])

结论

备注是一个贯穿各领域的问题,Spring AOP允许您将缓存详细信息与实际的应用程序逻辑代码分离。

翻译自: https://www.javacodegeeks.com/2014/12/spring-request-level-memoization.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值