如何查看spring ehcache是不是生效

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何查看spring ehcache是不是生效相关的知识,希望对你有一定的参考价值。

参考技术A Service 代码:
@Service
public class GameareaServiceImpl extends BaseServiceImpl<Gamearea> implements IGameareaService

@Resource
private GameareaMapper gameareaMapper;
@Override
@Cacheable(value="myCache")
public Gamearea find(int id)
return gameareaMapper.find(id);


@Override
@Cacheable(value="myCache")
public HandlerResult list(Map map)
HandlerResult rs = new HandlerResult();
rs.setResultObj(gameareaMapper.list(map));
return rs;


@Override
@Cacheable(value="myCache")
public Gamearea findByParameter(Gamearea gamearea)
return gameareaMapper.findByParameter(gamearea);




web.xml;
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>

applicationContext.xml:
<context:component-scan base-package="com.jadyer"/>
<!-- 缓存配置 -->
<!-- 启用缓存注解功能(请将其配置在Spring主配置文件中) -->
<cache:annotation-driven cache-manager="cacheManager"/>
<!-- Spring自己的基于java.util.concurrent.ConcurrentHashMap实现的缓存管理器(该功能是从Spring3.1开始提供的) -->
<!--
<bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
<property name="caches">
<set>
<bean name="myCache" class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean"/>
</set>
</property>
</bean>
-->
<!-- 若只想使用Spring自身提供的缓存器,则注释掉下面的两个关于Ehcache配置的bean,并启用上面的SimpleCacheManager即可 -->
<!-- Spring提供的基于的Ehcache实现的缓存管理器 -->
<bean id="cacheManagerFactory" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<property name="configLocation" value="classpath:ehcache.xml"/>
</bean>
<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager">
<property name="cacheManager" ref="cacheManagerFactory"/>
</bean>

</beans>

ehcache.xml

<!-- Ehcache2.x的变化(取自https://github.com/springside/springside4/wiki/Ehcache) -->
<!-- 1)最好在ehcache.xml中声明不进行updateCheck -->
<!-- 2)为了配合BigMemory和Size Limit,原来的属性最好改名 -->
<!-- maxElementsInMemory->maxEntriesLocalHeap -->
<!-- maxElementsOnDisk->maxEntriesLocalDisk -->
<ehcache mlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd" >
<diskStore path="java.io.tmpdir"/>
<defaultCache
maxElementsInMemory="1000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="false"/>
<cache name="myCache"
maxElementsOnDisk="20000"
maxElementsInMemory="2000"
eternal="true"
overflowToDisk="true"
diskPersistent="true"/>
</ehcache>
<!--
<diskStore>==========当内存缓存中对象数量超过maxElementsInMemory时,将缓存对象写到磁盘缓存中(需对象实现序列化接口)
<diskStore path="">==用来配置磁盘缓存使用的物理路径,Ehcache磁盘缓存使用的文件后缀名是*.data和*.index
name=================缓存名称,cache的唯一标识(ehcache会把这个cache放到HashMap里)
maxElementsOnDisk====磁盘缓存中最多可以存放的元素数量,0表示无穷大
maxElementsInMemory==内存缓存中最多可以存放的元素数量,若放入Cache中的元素超过这个数值,则有以下两种情况
1)若overflowToDisk=true,则会将Cache中多出的元素放入磁盘文件中
2)若overflowToDisk=false,则根据memoryStoreEvictionPolicy策略替换Cache中原有的元素
eternal==============缓存中对象是否永久有效,即是否永驻内存,true时将忽略timeToIdleSeconds和timeToLiveSeconds
timeToIdleSeconds====缓存数据在失效前的允许闲置时间(单位:秒),仅当eternal=false时使用,默认值是0表示可闲置时间无穷大,此为可选属性
即访问这个cache中元素的最大间隔时间,若超过这个时间没有访问此Cache中的某个元素,那么此元素将被从Cache中清除
timeToLiveSeconds====缓存数据在失效前的允许存活时间(单位:秒),仅当eternal=false时使用,默认值是0表示可存活时间无穷大
即Cache中的某元素从创建到清楚的生存时间,也就是说从创建开始计时,当超过这个时间时,此元素将从Cache中清除
overflowToDisk=======内存不足时,是否启用磁盘缓存(即内存中对象数量达到maxElementsInMemory时,Ehcache会将对象写到磁盘中)
会根据标签中path值查找对应的属性值,写入磁盘的文件会放在path文件夹下,文件的名称是cache的名称,后缀名是data
diskPersistent=======是否持久化磁盘缓存,当这个属性的值为true时,系统在初始化时会在磁盘中查找文件名为cache名称,后缀名为index的文件
这个文件中存放了已经持久化在磁盘中的cache的index,找到后会把cache加载到内存
要想把cache真正持久化到磁盘,写程序时注意执行net.sf.ehcache.Cache.put(Element element)后要调用flush()方法
diskExpiryThreadIntervalSeconds==磁盘缓存的清理线程运行间隔,默认是120秒
diskSpoolBufferSizeMB============设置DiskStore(磁盘缓存)的缓存区大小,默认是30MB
memoryStoreEvictionPolicy========内存存储与释放策略,即达到maxElementsInMemory限制时,Ehcache会根据指定策略清理内存
共有三种策略,分别为LRU(最近最少使用)、LFU(最常用的)、FIFO(先进先出)
-->本回答被提问者采纳

ehcache使用及缓存不生效处理方法

ehcache使用及缓存不生效处理方法

ehcache是什么

ehcache是一个纯java的进程内的缓存框架, 具有快速,精干等特点。java中最应用广泛的一款缓存框架(java’s most widely-used cache)
ehcache是一个基于标准、开源的高性能缓存,扩展简单。因为它健壮、经过验证、功能齐全、方便的和第三方框架与库集成。Ehcache 从进程内缓存一直扩展到具有 TB 大小缓存的进程内/进程外混合部署。

(ehcache is an open source, standards-based cache that boosts performance, offloads your databases, and simplifies scalability, it’s the most
widely-used java-based cache because it’s robust, proven, full-featured, and integrates with other popular libraries and frameworks, ehcache
from in-prrocess caching, all the way to mixed in process/out-of process deployments with terabyte-sized caches.)

优缺点

优点:

 1.纯java开发,便于学习,集成进主流的spring boot
 2.不依赖中间件
 3.快速,简单
 4.缓存数据由两级: 内存和磁盘,无需担心容量问题

缺点:

 1.多节点不能同步,缓存共享麻烦
 2.一般在架构中应用于二级缓存(一级缓存redis ---->二级缓存ehcache  -> database)

怎么用

这里我们使用ehcache3

引入依赖包

        <!-- ehcache 缓存 -->
		<dependency>
			<groupId>org.ehcache</groupId>
			<artifactId>ehcache</artifactId>
		</dependency>
		<dependency>
			<groupId>javax.cache</groupId>
			<artifactId>cache-api</artifactId>
		</dependency>

ehcache.xml配置

<config
		xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
		xmlns='http://www.ehcache.org/v3'
		xmlns:jsr107='http://www.ehcache.org/v3/jsr107'>

	<cache-template name="heap-cache">
			<listeners>
			</listeners>
			<resources>
				<heap unit="entries">2000</heap>
				<offheap unit="MB">100</offheap>
			</resources>
	</cache-template>

	<cache-template name="defaultCache">
		<heap unit="entries">100</heap>
	</cache-template>

	<cache alias="allViewScenesCount" uses-template="defaultCache">
		<expiry>
			<ttl unit="hours">4</ttl>
		</expiry>
	</cache>

</config>

集成到springboot

ehcache集成到springboot。 springboot启动类增加注解@EnableCaching

@Configuration
public class CacheConfig 

    @Bean
    public CacheManager cacheManager() throws FileNotFoundException, URISyntaxException 
        return new JCacheCacheManager(jCacheManager());
    

    @Bean
    public javax.cache.CacheManager jCacheManager() throws FileNotFoundException, URISyntaxException 
        EhcacheCachingProvider ehcacheCachingProvider = new EhcacheCachingProvider();
        URI uri = ResourceUtils.getURL("classpath:ehcache.xml").toURI();
        javax.cache.CacheManager cacheManager  = ehcacheCachingProvider.getCacheManager(uri, this.getClass().getClassLoader());
        return cacheManager;
    

缓存结果

@Slf4j
@RestController
@RequestMapping("/api/statistics")
public class StatisticsController 

	
    @RequestMapping("/allViewScenesCount")
    public RestResult<Object> allViewScenesCount(@RequestBody StatisticsDTO statisticsDTO) 
        StatisticsViewAllVO viewAllVO = statisticsService.allViewScenesCount(statisticsDTO);
        return RestResult.ok(viewAllVO);
    


@Service
@Slf4j
public class StatisticsService 
	// 这里会使用allViewScenesCount作为key, statisticsDTO的所有属性作为field, StatisticsViewAllVO的所有属性作为value
	// 这里注意StatisticsViewAllVO 需要实现 Serializable 接口,否则无法序列化到磁盘
    @Cacheable(value = "allViewScenesCount")
    public StatisticsViewAllVO allViewScenesCount(StatisticsDTO statisticsDTO)
        //do something
    


原理

请求流程

用户发起请求---> StatisticsController.allViewScenesCount() ---> CglibAopProxy 动态代理 ---> cacheInterceptor 缓存查询与存储 ---> 

StatisticsService.allViewScenesCount() -->  cacheInterceptor 缓存存储  -->  CglibAopProxy 代理返回结果

CglibAopProxy(spring)
-------cacheInterceptor(ehcache)
-----------StatisticsService(我们的代码)
-------cacheInterceptor(ehcache)
CglibAopProxy(spring)		

ehcache数据结构

ehcache的数据结构:  key field  value  (类似redis的hash)

key: 为注解 @Cacheable(value = "diffMoneyTrend")
filed: 为@Cacheable(value = "diffMoneyTrend", key = '')的key, 如果没有则是方法的参数

spring cglib代理

CglibAopProxy
	retVal = (new CglibAopProxy.CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy)).proceed();
proxy:   CglibAopProxy
target: statistiocsService
chain:  cacheInterceptor
 @Nullable
        public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable 
            Object oldProxy = null;
            boolean setProxyContext = false;
            Object target = null;
            TargetSource targetSource = this.advised.getTargetSource();

            Object var16;
            try 
                if (this.advised.exposeProxy) 
                    oldProxy = AopContext.setCurrentProxy(proxy);
                    setProxyContext = true;
                
				// 代理目标bean,我们的statistiocsService
                target = targetSource.getTarget();
                Class<?> targetClass = target != null ? target.getClass() : null;
				// 缓存切面
                List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
                Object retVal;
				// 判断是否有其他调用链,没有,直接代理我们bean
                if (chain.isEmpty() && CglibAopProxy.CglibMethodInvocation.isMethodProxyCompatible(method)) 
                    Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
                    retVal = CglibAopProxy.invokeMethod(target, method, argsToUse, methodProxy);
                 else 
					//有其他调用链 
                    retVal = (new CglibAopProxy.CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy)).proceed();
                
				// 返回结果
                retVal = CglibAopProxy.processReturnType(proxy, target, method, retVal);
                var16 = retVal;
             finally 
                if (target != null && !targetSource.isStatic()) 
                    targetSource.releaseTarget(target);
                

                if (setProxyContext) 
                    AopContext.setCurrentProxy(oldProxy);
                

            

            return var16;
        

CacheAspectSupport缓存切面

	@Nullable
	private Object execute(final CacheOperationInvoker invoker, Method method, CacheOperationContexts contexts) 
		// Special handling of synchronized invocation
		if (contexts.isSynchronized()) 
			CacheOperationContext context = contexts.get(CacheableOperation.class).iterator().next();
			if (isConditionPassing(context, CacheOperationExpressionEvaluator.NO_RESULT)) 
				Object key = generateKey(context, CacheOperationExpressionEvaluator.NO_RESULT);
				Cache cache = context.getCaches().iterator().next();
				try 
					return wrapCacheValue(method, handleSynchronizedGet(invoker, key, cache));
				
				catch (Cache.ValueRetrievalException ex) 
					// Directly propagate ThrowableWrapper from the invoker,
					// or potentially also an IllegalArgumentException etc.
					ReflectionUtils.rethrowRuntimeException(ex.getCause());
				
			
			else 
				// No caching required, only call the underlying method
				return invokeOperation(invoker);
			
		


		// Process any early evictions
		processCacheEvicts(contexts.get(CacheEvictOperation.class), true,
				CacheOperationExpressionEvaluator.NO_RESULT);

		// Check if we have a cached item matching the conditions
		Cache.ValueWrapper cacheHit = findCachedItem(contexts.get(CacheableOperation.class));

		// Collect puts from any @Cacheable miss, if no cached item is found
		List<CachePutRequest> cachePutRequests = new ArrayList<>();
		if (cacheHit == null) 
			collectPutRequests(contexts.get(CacheableOperation.class),
					CacheOperationExpressionEvaluator.NO_RESULT, cachePutRequests);
		

		Object cacheValue;
		Object returnValue;

		if (cacheHit != null && !hasCachePut(contexts)) 
			// If there are no put requests, just use the cache hit
			// 查到了缓存,使用缓存
			cacheValue = cacheHit.get();
			returnValue = wrapCacheValue(method, cacheValue);
		
		else 
			// Invoke the method if we don't have a cache hit
			// 没有查到缓存,调用我们的代码,执行实际逻辑
			returnValue = invokeOperation(invoker);
			cacheValue = unwrapReturnValue(returnValue);
		

		// Collect any explicit @CachePuts
		collectPutRequests(contexts.get(CachePutOperation.class), cacheValue, cachePutRequests);

		// Process any collected put requests, either from @CachePut or a @Cacheable miss
		for (CachePutRequest cachePutRequest : cachePutRequests) 
			cachePutRequest.apply(cacheValue);
		

		// Process any late evictions
		processCacheEvicts(contexts.get(CacheEvictOperation.class), false, cacheValue);

		return returnValue;
	


//CacheAspectSupport 
private Object execute()
	if (cacheHit != null && !hasCachePut(contexts)) 
			// If there are no put requests, just use the cache hit
			// 查到了缓存,使用缓存
			cacheValue = cacheHit.get();
			returnValue = wrapCacheValue(method, cacheValue);
		
		else 
			// Invoke the method if we don't have a cache hit
			// 没有查到缓存,调用我们的代码,执行实际逻辑
			returnValue = invokeOperation(invoker);
			cacheValue = unwrapReturnValue(returnValue);
		

@cacheable不生效,处理

注意:因为控制器调用service通过了cglib代理,只对调用生成代理方法: 因此ehcahce只对一级方法生效:如seviceA,有A,B方法, A内部调用B方法,B加@cacheable缓存不生效。

例如:

B方法注解不生效

@Slf4j
@RestController
@RequestMapping("/api/statistics")
public class StatisticsController 
	
    @RequestMapping("/allViewScenesCount")
    public RestResult<Object> allViewScenesCount(@RequestBody StatisticsDTO statisticsDTO) 
        StatisticsViewAllVO viewAllVO = statisticsService.A(statisticsDTO);
        return RestResult.ok(viewAllVO);
    


@Service
@Slf4j
public class StatisticsService 

    public StatisticsViewAllVO A(StatisticsDTO statisticsDTO)
		// 这里调用不会有cglib动态代理,因此也就没有缓存切面了
		return b();
    


	 @Cacheable(value = "B")
	  public StatisticsViewAllVO b(StatisticsDTO statisticsDTO)
        //do something
    


修改使B方法缓存生效

@Slf4j
@RestController
@RequestMapping("/api/statistics")
public class StatisticsController 
	
    @RequestMapping("/allViewScenesCount")
    public RestResult<Object> allViewScenesCount(@RequestBody StatisticsDTO statisticsDTO) 
        StatisticsViewAllVO viewAllVO = statisticsService.A(statisticsDTO);
        return RestResult.ok(viewAllVO);
    


@Service
@Slf4j
public class StatisticsService 

    public StatisticsViewAllVO A(StatisticsDTO statisticsDTO)
        // 我们强制这里用动态代理, 这里执行就会走切面了
		StatisticsService statisticsService = SpringUtil.getBean(StatisticsService.class);
		return  statisticsService.b();
    


	 @Cacheable(value = "B")
	  public StatisticsViewAllVO b(StatisticsDTO statisticsDTO)
        //do something
    


参考

https://www.jianshu.com/p/154c82073b07

https://www.ehcache.org/

https://www.ehcache.org/documentation/3.10/xml.html

以上是关于如何查看spring ehcache是不是生效的主要内容,如果未能解决你的问题,请参考以下文章

如何查看员工在部门最晚生效日期是不是为部门经理?

spring怎么配置EHCACHE

ehcache使用及缓存不生效处理方法

ehcache使用及缓存不生效处理方法

ehcache 或 Spring MVC 的 Spring 缓存中的最佳缓存实践是啥?

Spring项目中使用JMX监控EhCache缓存