Spring Cache 系列 & 0x01 开篇

Spring Cache 系列 & 0x01 开篇
Spring Cache 系列 & 0x02 组件
Spring Cache 系列 & 0x03 注解

Spring 缓存对现存的缓存框架做了进一步封装,使得接入和使用第三方缓存框架更加简便,同时可以实现自定义缓存。

接下来会参考 Spring Framework(4.3.16.RELEASE) 源码来介绍 Spring Cache。

Spring Cache Mode

  • Proxy
  • AspectJ

如果不理解介绍的这两种模式,可以去了解一下 Spring Aop。

这里为什么会提到 AOP 呢?不是在讲 Cache 吗?

进入正题......

Spring 如何实现缓存的?

传统实现方式
public class ExampleService {
    @Autowire
    private Cache cache;
    @Autowire
    private ExampleDao exampleDao;

    public List getResult() {
        List result = cache.get("cache-key");
        if (result == null) {
            result = exampleDao.getResult();
            cache.put("cache-key", result);
        }
        return result;
    }
}

相信上面的伪代码大家都能理解吧;
ExampleService#getResult方法先去缓存中取数据,如果缓存中没有,查询数据库,并且把查询结果放到缓存中。

如果业务中使用缓存的地方很多的话,每次都会getCacheputCache是不是会让你觉得不是很舒服。不但这些代码完全和业务没有关系,而且还破坏面向对象 - 单一原则。

我们知道 java 有三大特性:封装继承多态。是不是可以考虑把getCacheputCache的部分封装起来,提供公共服务,避免业务中充斥着无关的代码。

Spring 实现方式

Spring Cache 要做的就是,把和业务无关的getCacheputCache代码封装成更简便的服务。

在看一下上面提到的伪代码ExampleService#getResult(),如果要封装,该如何封装?

一点一点分解代码.....

  • 方式一

我们上面讲过了,侵入代码的方式

  • 方式二

组合;也可以看作静态代理

业务代码

import java.util.List;

public interface ExampleService {
     List getResult();
}

import org.springframework.stereotype.Component;

import java.util.List;

@Component("ExampleServiceImpl")
public class ExampleServiceImpl implements ExampleService {

    @Override
    public  List getResult() {
        return null;
    }
}
  1. 创建一个接口 ExampleService,只有一个方法
  2. 创建ExampleServiceImpl 去实现 ExampleService
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.cache.Cache;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;

import java.util.List;

@Primary
@Component
public class ExampleProxyService implements ExampleService {

    @Autowired
    @Qualifier("ExampleServiceImpl")
    private ExampleService exampleService;

    @Autowired
    private Cache cache;

    @Override
    public  List getResult() {
        Cache.ValueWrapper key = cache.get("key");
        if (key.get() == null) {
            List result = exampleService.getResult();
            cache.put("key", result);
        }
        return (List) key.get();
    }
}
 
 
  1. 重新建一个实现类
  2. 原来侵入代码的部分,放到这了代理类里;这种方式可以自由的注入ExampleService
  3. 如果一个类里要使用缓存,都需要建立这样一个代理类,不但增加了开发成本,还带来维护难;这种方式也不是最佳方案。

但是通过这种方式,我们是不是可以联想到使用动态代理(dynamic proxy)的方式来做。

  • 方式三

代理

** 创建代理入口 **

import com.keng.spring.cache.proxy.aop.advisor.CacheAdvisor;
import org.springframework.aop.framework.ProxyFactoryBean;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

public class CacheBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        Class clazz = bean.getClass();
        Class[] interfaces = clazz.getInterfaces();
        ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
        proxyFactoryBean.setInterfaces(interfaces);
        proxyFactoryBean.setTarget(bean);
        proxyFactoryBean.addAdvisor(new CacheAdvisor());
        return proxyFactoryBean.getObject();
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return null;
    }
}

** 增强 **

public class CacheAdvisor implements Advisor {
    @Override
    public Advice getAdvice() {
        return new CacheInterptor();
    }

    @Override
    public boolean isPerInstance() {
        return false;
    }
}

** 通知 **

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.cache.Cache;

import java.lang.reflect.Method;

public class CacheInterptor implements MethodInterceptor {

    private Cache cache;

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        Method method = invocation.getMethod();

        // key 如何获取
        Cache.ValueWrapper valueWrapper = cache.get("key");
        if (valueWrapper == null || valueWrapper.get() == null) {
            return invocation.proceed();
        }
        return valueWrapper.get();
    }

    public Cache getCache() {
        return cache;
    }
}

以上是使用 Spring Aop 实现的一个简易版缓存机制。

  • BeanPostProcessor

该接口会在 Spring 容器初始化完 Bean 调用,我们拿来对初始化后的 Bean 创建代理

  • Advisor

Aop 接口

  • MethodInterceptor

Aop 接口


但是上面有个问题,在类CacheInterptor中注释的地方;我们如何知道当前方法返回值要缓存的 key 呢?

注解,一个很简单的实现方式。

改造最终版......

import java.lang.annotation.*;

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface CacheKey {

    String value();

}
import java.util.List;

public interface ExampleService {
     List getResult();
}
import com.keng.spring.cache.annotation.CacheKey;
import org.springframework.stereotype.Component;

import java.util.List;

@Component("ExampleServiceImpl")
public class ExampleServiceImpl implements ExampleService {

    @Override
    @CacheKey(value = "getResult")
    public  List getResult() {
        return null;
    }
}
import com.keng.spring.cache.annotation.CacheKey;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.cache.Cache;

import java.lang.reflect.Method;

public class CacheInterptor implements MethodInterceptor {

    private Cache cache;

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        Method method = invocation.getMethod();
        CacheKey cacheKey = method.getAnnotation(CacheKey.class);
        if (cacheKey == null) {
            return invocation.proceed();
        }

        Cache.ValueWrapper valueWrapper = cache.get(cacheKey.value());
        if (valueWrapper == null || valueWrapper.get() == null) {
            return invocation.proceed();
        }
        return valueWrapper.get();
    }

    public Cache getCache() {
        return cache;
    }
}
import com.keng.spring.cache.proxy.aop.interceptor.CacheInterptor;
import org.aopalliance.aop.Advice;
import org.springframework.aop.Advisor;

public class CacheAdvisor implements Advisor {
    @Override
    public Advice getAdvice() {
        return new CacheInterptor();
    }

    @Override
    public boolean isPerInstance() {
        return false;
    }
}
import com.keng.spring.cache.proxy.aop.advisor.CacheAdvisor;
import org.springframework.aop.framework.ProxyFactoryBean;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

public class CacheBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        Class clazz = bean.getClass();
        Class[] interfaces = clazz.getInterfaces();
        ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
        proxyFactoryBean.setInterfaces(interfaces);
        proxyFactoryBean.setTarget(bean);
        proxyFactoryBean.addAdvisor(new CacheAdvisor());
        return proxyFactoryBean.getObject();
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return null;
    }
}

以上就是 Spring Cache 大致实现方法,上面写这么多还没进入介绍 Spring Cache 就是为了让你对 Spring Cache 有一个基本认识,方便来理解 Spring Cache 的实现方式。

你可能感兴趣的:(Spring Cache 系列 & 0x01 开篇)