0
点赞
收藏
分享

微信扫一扫

使用 Aop 实现缓存 Ehcache

janedaring 2021-09-21 阅读 62
日记本

shiro-ehcache maven 坐标

        <!-- Shiro使用EhCache缓存框架 -->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-ehcache</artifactId>
        </dependency>
        <!-- SpringBoot 拦截器 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

核心代码

EhCacheManager 配置 这里的 bt 是 ehcache-shiro.xml 文件 ehcache 节点 name

    /**
     * 缓存管理器 使用Ehcache实现
     */
    @Bean
    public EhCacheManager getEhCacheManager()
    {
        net.sf.ehcache.CacheManager cacheManager = net.sf.ehcache.CacheManager.getCacheManager("bt");
        EhCacheManager em = new EhCacheManager();
        if (StringUtils.isNull(cacheManager))
        {
            em.setCacheManager(new net.sf.ehcache.CacheManager(getCacheManagerConfigFileInputStream()));
            return em;
        }
        else
        {
            em.setCacheManager(cacheManager);
            return em;
        }
    }

    /**
     * 返回配置文件流 避免ehcache配置文件一直被占用,无法完全销毁项目重新部署
     */
    protected InputStream getCacheManagerConfigFileInputStream()
    {
        String configFile = "classpath:ehcache/ehcache-shiro.xml";
        InputStream inputStream = null;
        try
        {
            inputStream = ResourceUtils.getInputStreamForPath(configFile);
            byte[] b = IOUtils.toByteArray(inputStream);
            InputStream in = new ByteArrayInputStream(b);
            return in;
        }
        catch (IOException e)
        {
            throw new ConfigurationException(
                    "Unable to obtain input stream for cacheManagerConfigFile [" + configFile + "]", e);
        }
        finally
        {
            IOUtils.closeQuietly(inputStream);
        }
    }

ehcache-shiro.xml

<?xml version="1.0" encoding="UTF-8"?>
<ehcache name="bt" updateCheck="false">

    <!-- 磁盘缓存位置 -->
    <diskStore path="java.io.tmpdir"/>

    <!-- 默认缓存 -->
    <defaultCache
            maxEntriesLocalHeap="1000"
            eternal="false"
            timeToIdleSeconds="3600"
            timeToLiveSeconds="3600"
            overflowToDisk="false">
    </defaultCache>

    <!--  自定义缓存 cache -->
    <cache name="babanCache"
           maxElementsInMemory="100000"
           overflowToDisk="true"
           eternal="true"
           timeToIdleSeconds="0"
           timeToLiveSeconds="0"
           diskPersistent="true"
           maxEntriesLocalHeap="2000"
           diskExpiryThreadIntervalSeconds="120"
           memoryStoreEvictionPolicy="LRU">
    </cache>
<!--
    maxElementsInMemory="10000"     //Cache中最多允许保存的数据对象的数量
    external="false"                //缓存中对象是否为永久的,如果是,超时设置将被忽略,对象从不过期
    timeToLiveSeconds="3600"        //缓存的存活时间,从开始创建的时间算起
    timeToIdleSeconds="3600"        //多长时间不访问该缓存,那么ehcache 就会清除该缓存

    这两个参数很容易误解,看文档根本没用,我仔细分析了ehcache的代码。结论如下:
    1、timeToLiveSeconds的定义是:以创建时间为基准开始计算的超时时长;
    2、timeToIdleSeconds的定义是:在创建时间和最近访问时间中取出离现在最近的时间作为基准计算的超时时长;
    3、如果仅设置了timeToLiveSeconds,则该对象的超时时间=创建时间+timeToLiveSeconds,假设为A;
    4、如果没设置timeToLiveSeconds,则该对象的超时时间=min(创建时间,最近访问时间)+timeToIdleSeconds,假设为B;
    5、如果两者都设置了,则取出A、B最少的值,即min(A,B),表示只要有一个超时成立即算超时。

    overflowToDisk="true"           //内存不足时,是否启用磁盘缓存
    diskSpoolBufferSizeMB   //设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区
    maxElementsOnDisk       //硬盘最大缓存个数
    diskPersistent          //是否缓存虚拟机重启期数据The default value is false
    diskExpiryThreadIntervalSeconds //磁盘失效线程运行时间间隔,默认是120秒。
    memoryStoreEvictionPolicy="LRU" //当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。
    clearOnFlush    //内存数量最大时是否清除
    maxEntriesLocalHeap="0"  //堆内存中最大缓存对象数,0没有限制
    maxEntriesLocalDisk="1000" //硬盘最大缓存个数。
-->
</ehcache>

添加缓存

@Service
@Aspect
public class BabanAipCache {

    private static final Logger logger = LoggerFactory.getLogger(BabanAipCache.class);

    @Autowired
    private CacheManager cacheManager;

    private Cache<String,Object> cache;

    @PostConstruct
    public void init(){
        cache = cacheManager.getCache("babanCache");
    }

    private static HashMap<String, Class> map = new HashMap<String, Class>() {
        {
            put("java.lang.Integer", Integer.class);
            put("java.lang.Double", Double.class);
            put("java.lang.Float", Float.class);
            put("java.lang.Long", Long.class);
            put("java.lang.Short", Short.class);
            put("java.lang.Boolean", Boolean.class);
            put("java.lang.Char", Char.class);
            put("java.lang.String", String.class);
        }
    };

    @Around("execution(* com.bt.web.controller.api.*.*(..))")
    public Object getCache(ProceedingJoinPoint joinPoint){
        try {
            //获取目标方法所在类
            String target = joinPoint.getTarget().toString();
            String className = target.split("@")[0];
            //获取目标方法的方法名称
            String methodName = joinPoint.getSignature().getName();
            // 参数值
            Object[] args = joinPoint.getArgs();
            // 通过map封装参数和参数值
            HashMap<String, Object> paramMap = null;
            if(args.length > 0){
                Class<?>[] classes = new Class[args.length];
                for (int k = 0; k < args.length; k++) {
                    if (!args[k].getClass().isPrimitive()) {
                        // 获取的是封装类型而不是基础类型
                        /*String result = args[k].getClass().getName();
                        Class s = map.get(result);
                        classes[k] = s == null ? args[k].getClass() : s;*/
                        classes[k] = args[k].getClass();
                    }
                }

                ParameterNameDiscoverer pnd = new DefaultParameterNameDiscoverer();
                // 获取指定的方法,第二个参数可以不传,但是为了防止有重载的现象,还是需要传入参数的类型
                Method method = Class.forName(className).getMethod(methodName, classes);
                // 参数名
                String[] parameterNames = pnd.getParameterNames(method);
                // 通过map封装参数和参数值
                paramMap = new HashMap();
                for (int i = 0; i < parameterNames.length; i++) {
                    paramMap.put(parameterNames[i], args[i]);
                }
            }
            //cache key
            String cacheKey =  ":" + className + "." + methodName+"_"+ (paramMap != null ? JSON.marshal(paramMap) : "");
            Object cache = this.cache.get(cacheKey);
            if(cache != null){
                logger.info("缓存中获取,cacheKey={},cache={}",cacheKey,cache);
                return cache;
            }
            Object obj = joinPoint.proceed();
            this.cache.put(cacheKey,obj);
            logger.info("存入缓存,cacheKey={},cache={}",cacheKey,obj);
            return obj;
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return null;
    }

清除缓存

@Aspect
@Service
public class BabanClearCache {

    private static final Logger logger = LoggerFactory.getLogger(BabanAipCache.class);

    @Autowired
    private CacheManager cacheManager;

    private Cache<String,Object> cache;

    @PostConstruct
    public void init(){
        cache = cacheManager.getCache("babanCache");
    }

    @After("execution(* com.bt.web.controller.baban.*.addSave(..)) || " +
            "execution(* com.bt.web.controller.baban.*.editSave(..)) || " +
            "execution(* com.bt.web.controller.baban.*.remove(..))")
    public void clearCache(){
        // 清除指定缓存
        //this.cache.remove("babanCache");
        // 清除所有缓存
        this.cache.clear();
    }
}
举报

相关推荐

0 条评论