Spring Cache Redis 源码解读

/**
 * Cache和map接口类似,可以get和put值。实现类有RedisCache。
 * cache中有个name,这个name就是注解@CacheConfig、@Cacheable、@CacheEvict中的cacheNames,注意这里的cacheNames可以是数组。
 * 一个cacheName会对应一个Cache实例。
 */
public interface Cache {

	String getName();

	 T get(Object key, @Nullable Class type);

	 T get(Object key, Callable valueLoader);

	void put(Object key, @Nullable Object value);

	void clear();

}

/**
 * Cache管理,可以通cache名获取Cahce。
 */
public interface CacheManager {

	Cache getCache(String name);
	
	Collection getCacheNames();
}


/**
 * 通过context(方法信息的封装)获取对应的cache集合,这里为集合因为@Cacheable、@CacheEvict等注解中的cacheNames可能为数组。
 * CacheResolver会调用CacheManager获取cache
 */
public interface CacheResolver {
	/**
	 * 获取Cache集合。在使用Spring Cache时在需要缓存的方法上加@Cacheable、@CacheEvict等注解,方法的信息封在context中。
	 * 直白一点就是通过方法获取对应的cache集合
	 */
	Collection resolveCaches(CacheOperationInvocationContext context);
}



/**
 * 不了解CacheInterceptor 的,学习地址: https://blog.csdn.net/hong10086/article/details/91554901
 * 后文会涉及到CacheInterceptor。
 */
public interface MethodInterceptor extends Interceptor {
	
	Object invoke(MethodInvocation invocation) throws Throwable;

}


首先说一下Spring cache的实现思路。Spring cahce是基于MethodInterceptor方法拦截器实现的,在调用目标方法时会被方法拦截器拦截。通过拦截器invoke方法中的MethodInvocation参数,可以获取目标方法信息,
方法信息会被封装在CacheOperationInvocationContext中,再调用CacheResolver中的resolveCaches()方法获取Cache。




//创建拦截器
public  class ProxyCachingConfiguration{

	//创建CacheInterceptor 缓存拦截器。
	@Bean
	public CacheInterceptor cacheInterceptor() {
		//创建拦截器 CacheInterceptor
		CacheInterceptor interceptor = new CacheInterceptor();
		
		//下面是为拦截器设置必要属性
	
		interceptor.setCacheOperationSources(cacheOperationSource());
		
		if (this.cacheResolver != null) {//存在自定义cacheResolver
			interceptor.setCacheResolver(this.cacheResolver);
		}
		else if (this.cacheManager != null) {//存在自定义cacheManager
			interceptor.setCacheManager(this.cacheManager);
		}
		if (this.keyGenerator != null) {//存在自定义keyGenerator
			interceptor.setKeyGenerator(this.keyGenerator);
		}
		if (this.errorHandler != null) {//存在自定义errorHandler
			interceptor.setErrorHandler(this.errorHandler);
		}
		return interceptor;
	}
}





public class CacheInterceptor extends CacheAspectSupport implements MethodInterceptor{

	//对MethodInterceptor接口的实现。
	@Override
	public Object invoke(final MethodInvocation invocation) throws Throwable {
		Method method = invocation.getMethod();

		CacheOperationInvoker aopAllianceInvoker = () -> {
			try {
				return invocation.proceed();
			}
			catch (Throwable ex) {
				throw new CacheOperationInvoker.ThrowableWrapper(ex);
			}
		};

		try {
			return execute(aopAllianceInvoker, invocation.getThis(), method, invocation.getArguments());
		}
		catch (CacheOperationInvoker.ThrowableWrapper th) {
			throw th.getOriginal();
		}
	}

}


class CacheAspectSupport implements SmartInitializingSingleton{
	private CacheResolver cacheResolver;
	//获取cacheResolver
	public CacheResolver getCacheResolver() {
		return this.cacheResolver;
	}
	
	//创建cacheResolver
	public void setCacheManager(CacheManager cacheManager) {
		this.cacheResolver = new SimpleCacheResolver(cacheManager);
	}

	//对SmartInitializingSingleton接口的实现,在bean创建后调用。
	@Override
	public void afterSingletonsInstantiated() {
		if (getCacheResolver() == null) {
			//从bean容器中获取CacheManager
			setCacheManager(this.beanFactory.getBean(CacheManager.class));
		}
	}
	
}





public class SimpleCacheResolver extends AbstractCacheResolver   {

	public SimpleCacheResolver(CacheManager cacheManager) {
		super(cacheManager);
	}
}

public abstract class AbstractCacheResolver implements CacheResolver{

	private CacheManager cacheManager;
	
	protected AbstractCacheResolver(CacheManager cacheManager) {
		this.cacheManager = cacheManager;
	}
	
	public CacheManager getCacheManager() {
		return this.cacheManager;
	}

   
   //对CacheResolver接口的实现。
	@Override
	public Collection resolveCaches(CacheOperationInvocationContext context) {
		Collection cacheNames = getCacheNames(context);
		if (cacheNames == null) {
			return Collections.emptyList();
		}
		Collection result = new ArrayList<>(cacheNames.size());
		for (String cacheName : cacheNames) {
			Cache cache = getCacheManager().getCache(cacheName);
			if (cache == null) {
				throw new IllegalArgumentException("Cannot find cache named '" +
						cacheName + "' for " + context.getOperation());
			}
			result.add(cache);
		}
		return result;
	}


}

 

 

 

 

运行流程

 

1、首先执行@CacheEvict(如果beforeInvocation=true且condition 通过),如果allEntries=true,则清空所有  
2、接着收集@Cacheable(如果condition 通过,且key对应的数据不在缓存),放入cachePutRequests(也就是说如果cachePutRequests为空,则数据在缓存中)  
3、如果cachePutRequests为空且没有@CachePut操作,那么将查找@Cacheable的缓存,否则result=缓存数据(也就是说只要当没有cache put请求时才会查找缓存)  
4、如果没有找到缓存,那么调用实际的API,把结果放入result  
5、如果有@CachePut操作(如果condition 通过),那么放入cachePutRequests  
6、执行cachePutRequests,将数据写入缓存(unless为空或者unless解析结果为false);  
7、执行@CacheEvict(如果beforeInvocation=false 且 condition 通过),如果allEntries=true,则清空所有  


Spring Cache Redis 源码解读_第1张图片

@Caching

有时候我们可能组合多个Cache注解使用;比如用户新增成功后,我们要添加id-->user;username--->user;email--->user的缓存;此时就需要@Caching组合多个注解标签了。

如用户新增成功后,添加id-->user;username--->user;email--->user到缓存;

 

@Caching(  
        put = {  
                @CachePut(value = "user", key = "#user.id"),  
                @CachePut(value = "user", key = "#user.username"),  
                @CachePut(value = "user", key = "#user.email")  
        }  
)  
public User save(User user) 

 

你可能感兴趣的:(spring)