SSM+Ehcache+Redis实现一二级缓存,并且实现模糊删除缓存

版权声明:本文为博主原创文章,转载请附上原文链接,谢谢! https://blog.csdn.net/qq_21454973/article/details/80372179

最近在做一个项目,要加上缓存。于是上网百度实现了一个二级缓存的例子:

首先是pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>WE</groupId>
	<artifactId>WE</artifactId>
	<version>1.0.0-SNAPSHOT</version>
	<packaging>war</packaging>
	<name>WE</name>
	<!--统一配置jar包版本 -->
	<properties>
		<springmvc-version>4.3.0.RELEASE</springmvc-version>
	</properties>
	<!--项目所需jar包 -->
	<dependencies>
		<!--单元测试依赖包 -->
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.12</version>
		</dependency>
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>jstl</artifactId>
			<version>1.2</version>
		</dependency>
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>javax.servlet-api</artifactId>
			<version>3.0.1</version>
			<scope>provided</scope>
		</dependency>
		<!--spring mvc depend jars begin -->
		<dependency>
			<groupId>aopalliance</groupId>
			<artifactId>aopalliance</artifactId>
			<version>1.0</version>
		</dependency>
		<dependency>
			<groupId>commons-logging</groupId>
			<artifactId>commons-logging</artifactId>
			<version>1.2</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-aop</artifactId>
			<version>${springmvc-version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-aspects</artifactId>
			<version>${springmvc-version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-beans</artifactId>
			<version>${springmvc-version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>${springmvc-version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context-support</artifactId>
			<version>${springmvc-version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-core</artifactId>
			<version>${springmvc-version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-expression</artifactId>
			<version>${springmvc-version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-instrument</artifactId>
			<version>${springmvc-version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-instrument-tomcat</artifactId>
			<version>${springmvc-version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-jdbc</artifactId>
			<version>${springmvc-version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-jms</artifactId>
			<version>${springmvc-version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-messaging</artifactId>
			<version>${springmvc-version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-orm</artifactId>
			<version>${springmvc-version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-oxm</artifactId>
			<version>${springmvc-version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-test</artifactId>
			<version>${springmvc-version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-tx</artifactId>
			<version>${springmvc-version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-web</artifactId>
			<version>${springmvc-version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-websocket</artifactId>
			<version>${springmvc-version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
			<version>${springmvc-version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc-portlet</artifactId>
			<version>${springmvc-version}</version>
		</dependency>
		<!--spring mvc depend jars end -->

		<!-- ssm or ssh need spring jar -->

		<!-- 添加mybatis的核心包 -->
		<dependency>
			<groupId>org.mybatis</groupId>
			<artifactId>mybatis</artifactId>
			<version>3.3.0</version>
		</dependency>
		<!-- 添加mybatis与Spring整合的核心包 -->
		<dependency>
			<groupId>org.mybatis</groupId>
			<artifactId>mybatis-spring</artifactId>
			<version>1.2.2</version>
		</dependency>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>5.1.8</version>
		</dependency>
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>druid</artifactId>
			<version>1.0.7</version>
		</dependency>
		<dependency>
			<groupId>redis.clients</groupId>
			<artifactId>jedis</artifactId>
			<version>2.7.3</version>
			<type>jar</type>
			<scope>compile</scope>
		</dependency>
		<!-- spring-redis -->
		<dependency>
			<groupId>org.springframework.data</groupId>
			<artifactId>spring-data-redis</artifactId>
			<version>1.6.0.RELEASE</version>
		</dependency>
		<!---sitemesh pom -->
		<dependency>
			<groupId>org.sitemesh</groupId>
			<artifactId>sitemesh</artifactId>
			<version>3.0.0</version>
		</dependency>
		<!-- pagehelper -->
		<dependency>
			<groupId>com.github.pagehelper</groupId>
			<artifactId>pagehelper</artifactId>
			<version>5.0.0</version>
		</dependency>
		<!-- log4j -->
		<dependency>
			<groupId>log4j</groupId>
			<artifactId>log4j</artifactId>
			<version>1.2.17</version>
		</dependency>
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-simple</artifactId>
			<version>1.7.25</version>
		</dependency>
		<!-- commons-lang -->
		<dependency>
			<groupId>commons-lang</groupId>
			<artifactId>commons-lang</artifactId>
			<version>2.6</version>
		</dependency>
		<!-- com.alibaba.fastjson -->
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>fastjson</artifactId>
			<version>1.2.6</version>
		</dependency>
		<!-- org.json -->
		<dependency>
			<groupId>org.json</groupId>
			<artifactId>json</artifactId>
			<version>20180130</version>
		</dependency>
		<!-- org.apache.shiro/shiro-all -->
		<dependency>
			<groupId>org.apache.shiro</groupId>
			<artifactId>shiro-all</artifactId>
			<version>1.3.2</version>
		</dependency>
		<!-- org.mybatis.generator/mybatis-generator-core -->
		<dependency>
			<groupId>org.mybatis.generator</groupId>
			<artifactId>mybatis-generator-core</artifactId>
			<version>1.3.6</version>
		</dependency>
		<!-- net.sf.ehcache -->
		<dependency>
			<groupId>net.sf.ehcache</groupId>
			<artifactId>ehcache</artifactId>
			<version>2.10.4</version>
		</dependency>
		<!-- netty 5.0 -->
		<dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>5.0.0.Alpha2</version>
        </dependency>
        <!-- System Log -->
        <dependency>
			<groupId>org.apache.commons</groupId>
			<artifactId>commons-lang3</artifactId>
			<version>3.3.2</version>
		</dependency>
		<!-- pagehelper 分页插件 -->
		<dependency>
    		<groupId>com.github.pagehelper</groupId>
    		<artifactId>pagehelper</artifactId>
    		<version>5.1.2</version>
		</dependency>

	</dependencies>
	<!--项目构建 -->
	<build>
		<!--项目打包之后的名称 -->
		<finalName>WE</finalName>
		<!--构建需要的插件 -->
		<plugins>
			<!--编译插件 -->
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>3.1</version>
				<configuration>
					<source>
						1.7</source>
					<target>1.7</target>
				</configuration>
			</plugin>

			<!-- 本地tomcat7插件 -->
			<plugin>
				<groupId>org.apache.tomcat.maven</groupId>
				<artifactId>tomcat7-maven-plugin</artifactId>
				<version>2.1</version>
				<configuration>
					<path>/</path>
					<uriEncoding>UTF-8</uriEncoding>
				</configuration>
			</plugin>
		</plugins>
	</build>
</project>

如果只是二级缓存的话里面东西会冗余,什么单元测试,druid连接池,sitemesh,pagehelper插件,fastjson,shiro,generator工具,netty等都是可以删掉的。

好,导入了jar包,后面搭ssm框架的过程我就省略了,网上一堆教程。

搭建好之后,大概的框架如下:


包名任意取,然后分以下几种的包,

cache——放入自定义缓存类

dao——数据持久层

model——实体类

service and impl——业务逻辑

utils——工具包

web——放入controller控制

至于resource中,mapper、mybatis、properties和shiro文件跟缓存关系不大。spring中新建applicationContext-cache.xml,填写缓存相关配置。

至于其他的配置都是SSM的配置了。

先实现缓存,再实现模糊删除。这里吐槽下,之前百度模糊删除资料很少,反而是自定义一个注解,弄了半天没弄出来,后来发现根本没必要。。。然后感觉自己傻傻的。。。

好了,上代码。

这是redis的缓存类,

package com.xx.cache;//包名自定义

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.Callable;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cache.Cache;
import org.springframework.cache.support.SimpleValueWrapper;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;


/**
 * Created by [email protected] on 2018-05-07 09:27:15.
 */
public class RedisCache implements Cache {
	static final Logger logger = LoggerFactory.getLogger(RedisCache.class); //日志
	
	private RedisTemplate<String, Object> redisTemplate;
	private String name;

	public RedisTemplate<String, Object> getRedisTemplate() {
		return redisTemplate;
	}

	public void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) {
		this.redisTemplate = redisTemplate;
	}

	public void setName(String name) {
		this.name = name;
	}

	@Override
	public String getName() {
		// TODO Auto-generated method stub
		return this.name;
	}

	@Override
	public Object getNativeCache() {
		// TODO Auto-generated method stub
		return this.redisTemplate;
	}

	@Override
	public ValueWrapper get(Object key) {
		// TODO Auto-generated method stub
		System.out.println("get key:"+ key.toString());
		final String keyf = key.toString();
		Object object = null;
		object = redisTemplate.execute(new RedisCallback<Object>() {
			public Object doInRedis(RedisConnection connection) throws DataAccessException {
				byte[] key = keyf.getBytes();
				byte[] value = connection.get(key);
				if (value == null) {
					return null;
				}
				return toObject(value);
			}
		});
		return (object != null ? new SimpleValueWrapper(object) : null);
	}

	@Override
	public void put(Object key, Object value) {
		// TODO Auto-generated method stub
		final String keyf = key.toString();
		final Object valuef = value;
		final long liveTime = 86400;
		redisTemplate.execute(new RedisCallback<Long>() {
			public Long doInRedis(RedisConnection connection) throws DataAccessException {
				byte[] keyb = keyf.getBytes();
				byte[] valueb = toByteArray(valuef);
				connection.set(keyb, valueb);
				if (liveTime > 0) {
					connection.expire(keyb, liveTime);
				}
				return 1L;
			}
		});
	}

	private byte[] toByteArray(Object obj) {
		byte[] bytes = null;
		ByteArrayOutputStream bos = new ByteArrayOutputStream();
		try {
			ObjectOutputStream oos = new ObjectOutputStream(bos);
			oos.writeObject(obj);
			oos.flush();
			bytes = bos.toByteArray();
			oos.close();
			bos.close();
		} catch (IOException ex) {
			ex.printStackTrace();
		}
		return bytes;
	}

	private Object toObject(byte[] bytes) {
		Object obj = null;
		try {
			ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
			ObjectInputStream ois = new ObjectInputStream(bis);
			obj = ois.readObject();
			ois.close();
			bis.close();
		} catch (IOException ex) {
			ex.printStackTrace();
		} catch (ClassNotFoundException ex) {
			ex.printStackTrace();
		}
		return obj;
	}

	@Override
	public void evict(Object key) {
		// TODO Auto-generated method stub
		System.out.println("del key:"+key.toString());//注意这个就会发现我为啥觉得自己傻傻的
		
		final String keyf = key.toString();
		redisTemplate.execute(new RedisCallback<Long>() {
			public Long doInRedis(RedisConnection connection) throws DataAccessException {
				return connection.del(keyf.getBytes());
			}
		});
	}

	@Override
	public void clear() {
		// TODO Auto-generated method stub
		System.out.println("clear key");
		redisTemplate.execute(new RedisCallback<String>() {
			public String doInRedis(RedisConnection connection) throws DataAccessException {
				connection.flushDb();
				return "ok";
			}
		});
	}

	@Override
	public <T> T get(Object key, Class<T> type) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public ValueWrapper putIfAbsent(Object key, Object value) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public <T> T get(Object o, Callable<T> callable) {
		return null;
	}

}

这个没啥说的,就是代码,然后是二级(Ehcache+Redis)的缓存类:

package com.xx.cache;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.regex.Pattern;

import org.apache.log4j.Logger;
import org.springframework.cache.Cache;
import org.springframework.cache.support.SimpleValueWrapper;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;

import net.sf.ehcache.Element;

/**
 * 两级缓存,一级:ehcache,二级为redisCache
 *
 */
public class EhRedisCache implements Cache{


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

    private String name;

    private net.sf.ehcache.Cache ehCache;

    private RedisTemplate<String, Object> redisTemplate;

     private long liveTime = 1*60*60; //默认1h=1*60*60

    @Override
    public String getName() {
        return this.name;
    }

    @Override
    public Object getNativeCache() {
        return this;
    }

    @Override
    public ValueWrapper get(Object key) {
         Element value = ehCache.get(key);
         logger.info("Cache L1 (ehcache) :{"+key+"}={"+value+"}");
         if (value!=null) {
             return (value != null ? new SimpleValueWrapper(value.getObjectValue()) : null);
         }
         final String keyStr = key.toString();  
         Object objectValue = redisTemplate.execute(new RedisCallback<Object>() {  
            public Object doInRedis(RedisConnection connection)  
                    throws DataAccessException {  
                byte[] key = keyStr.getBytes();  
                byte[] value = connection.get(key);  
                if (value == null) {  
                    return null;  
                }  
                //每次获得,重置缓存过期时间
                if (liveTime > 0) {  
                    connection.expire(key, liveTime);  
                }  
                return toObject(value);  
            }  
        },true);  
         ehCache.put(new Element(key, objectValue));//取出来之后缓存到本地
         logger.info("Cache L2 (redis) :{"+key+"}={"+objectValue+"}");
         return  (objectValue != null ? new SimpleValueWrapper(objectValue) : null);

    }

    @Override
    public void put(Object key, Object value) {
        ehCache.put(new Element(key, value));
        final String keyStr =  key.toString(); 
        final Object valueStr = value;  
        redisTemplate.execute(new RedisCallback<Long>() {  
            public Long doInRedis(RedisConnection connection)  
                    throws DataAccessException {  
                byte[] keyb = keyStr.getBytes();  
                byte[] valueb = toByteArray(valueStr);  
                connection.set(keyb, valueb);  
                if (liveTime > 0) {  
                    connection.expire(keyb, liveTime);  
                }  
                return 1L;  
            }  
        },true);  

    }

    @Override
    public void evict(Object key) {
    	System.out.println("del levels key:"+key.toString());
        ehCache.remove(key);
        final String keyStr =  key.toString();  
        redisTemplate.execute(new RedisCallback<Long>() {  
            public Long doInRedis(RedisConnection connection)  
                    throws DataAccessException {  
                return connection.del(keyStr.getBytes());  
            }  
        },true); 
    }

    @Override
    public void clear() {
        ehCache.removeAll();
        redisTemplate.execute(new RedisCallback<String>() {  
            public String doInRedis(RedisConnection connection)  
                    throws DataAccessException {  
                connection.flushDb();  
                return "clear done.";  
            }  
        },true);
    }

    public net.sf.ehcache.Cache getEhCache() {
        return ehCache;
    }

    public void setEhCache(net.sf.ehcache.Cache ehCache) {
        this.ehCache = ehCache;
    }

    public RedisTemplate<String, Object> getRedisTemplate() {
        return redisTemplate;
    }

    public void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    public long getLiveTime() {
        return liveTime;
    }

    public void setLiveTime(long liveTime) {
        this.liveTime = liveTime;
    }

    public void setName(String name) {
        this.name = name;
    }
    /** 
     * 描述 : Object转byte[]. <br> 
     * @param obj 
     * @return 
     */  
    private byte[] toByteArray(Object obj) {  
        byte[] bytes = null;  
        ByteArrayOutputStream bos = new ByteArrayOutputStream();  
        try {  
            ObjectOutputStream oos = new ObjectOutputStream(bos);  
            oos.writeObject(obj);  
            oos.flush();  
            bytes = bos.toByteArray();  
            oos.close();  
            bos.close();  
        } catch (IOException ex) {  
            ex.printStackTrace();  
        }  
        return bytes;  
    }  

    /** 
     * 描述 :  byte[]转Object . <br> 
     * @param bytes 
     * @return 
     */  
    private Object toObject(byte[] bytes) {  
        Object obj = null;  
        try {  
            ByteArrayInputStream bis = new ByteArrayInputStream(bytes);  
            ObjectInputStream ois = new ObjectInputStream(bis);  
            obj = ois.readObject();  
            ois.close();  
            bis.close();  
        } catch (IOException ex) {  
            ex.printStackTrace();  
        } catch (ClassNotFoundException ex) {  
            ex.printStackTrace();  
        }  
        return obj;  
    }

	@Override
	public <T> T get(Object arg0, Class<T> arg1) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public <T> T get(Object arg0, Callable<T> arg1) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public ValueWrapper putIfAbsent(Object arg0, Object arg1) {
		// TODO Auto-generated method stub
		return null;
	}  
}

缓存类搞定了,然后就是配置了。

这是applicationContext-cache.xml的全部配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
	xmlns:cache="http://www.springframework.org/schema/cache"
	xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/cache
        http://www.springframework.org/schema/cache/spring-cache.xsd
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context.xsd">
	<context:property-placeholder
		ignore-resource-not-found="true" location="classpath:properties/config.properties" />
	<!-- cache-manager指向缓存管理器的bean id名 -->
	<!-- springCacheManager/ehRedisCacheManager proxy-target-class="true"   -->
	<cache:annotation-driven cache-manager="springCacheManager" proxy-target-class="true" />
	<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
		<!-- 最大空闲数 -->
        <property name="maxIdle" value="${redis.maxIdle}" />
        <!-- 最大空连接数 -->
        <property name="maxTotal" value="${redis.maxTotal}" />
        <!-- 最大等待时间 -->
        <property name="maxWaitMillis" value="${redis.maxWaitMillis}" />
        <!-- 连接超时时是否阻塞,false时报异常,ture阻塞直到超时, 默认true -->
         <property name="blockWhenExhausted" value="${redis.blockWhenExhausted}" /> 
        <!-- 返回连接时,检测连接是否成功 -->
        <property name="testOnBorrow" value="${redis.testOnBorrow}" />
	</bean>
	<!-- Spring-redis连接池管理工厂 -->
	<bean id="jedisConnectionFactory"
		class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
		<!-- IP地址 -->
		<property name="hostName" value="${redis.host}" />
		<!-- password -->
		<property name="password" value="${redis.password}" />
		<!-- 端口号 -->
		<property name="port" value="${redis.port}" />
		<!-- 超时时间 默认2000 -->
		<property name="timeout" value="${redis.timeout}" />
		<!-- 连接池配置引用 -->
		<property name="poolConfig" ref="poolConfig" />
		<!-- usePool:是否使用连接池 -->
		<property name="usePool" value="${redis.usePool}" />
	</bean>
	<bean id="redisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate">
		<property name="connectionFactory" ref="jedisConnectionFactory" />
	</bean>

	<!-- spring自己的缓存管理器,这里定义了缓存位置名称 ,即注解中的value -->
	<bean id="springCacheManager" class="org.springframework.cache.support.SimpleCacheManager">
		<property name="caches">
			<set>
				<!-- 这里可以配置多个redis -->
				<bean class="com.xx.cache.RedisCache">
					<property name="redisTemplate" ref="redisTemplate" />
					<property name="name" value="layerCache" />
					<!-- myCache名称要在类或方法的注解中使用 -->
				</bean>
				<!-- 自定义ehcache+redis-->
				<bean  id="ehRedisCache" class="com.xx.cache.EhRedisCache">  
                     <property name="redisTemplate" ref="redisTemplate" />  
                     <property name="ehCache" ref="ehCache"/> 
                     <property name="name" value="layersCache"/>
                     <!-- redis缓存保留时间,单位秒  -->
                     <property name="liveTime" value="1000"/> 
                </bean>
			</set>
		</property>
	</bean>
	
	<!-- ehCache 操作对象 -->
	<!-- ehCacheManager 位于spring-shiro.xml -->
    <bean id="ehCache" class="org.springframework.cache.ehcache.EhCacheFactoryBean">
       <property name="cacheManager" ref="ehCacheManager"/>
    </bean>
	
</beans>

其实也就几个配置:连接池管理、连接池配置、缓存管理器。

其中ehCacheManager:

<bean id="ehCacheManager" class ="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">  
        <property name="configLocation" value="classpath:shiro/ehcache-shiro.xml" />  
        <property name="shared" value="true"></property>  
    </bean>

然后ehcache-shiro.xml:

<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   
    xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"  
    updateCheck="false" dynamicConfig="false" monitoring="autodetect">    
    <diskStore path="java.io.tmpdir" />  
    <!--  
        diskStore path:用来配置磁盘缓存使用的物理路径  
        name:   缓存名称,cache的唯一标识(ehcache会把这个cache放到HashMap里)  
        eternal="false"   元素是否永恒,如果是就永不过期(必须设置)  
        maxElementsOnDisk====磁盘缓存中最多可以存放的元素数量,0表示无穷大   
        maxElementsInMemory="1000" 内存缓存中最多可以存放的元素数量(必须设置)  
        timeToIdleSeconds="0"   导致元素过期的访问间隔(秒为单位). 0表示可以永远空闲,默认为0  
        timeToLiveSeconds="600" 元素在缓存里存在的时间(秒为单位). 0 表示永远存在不过期  
        overflowToDisk="false"  当缓存达到maxElementsInMemory值是,是否允许溢出到磁盘(必须设置)  
        diskPersistent="false"  磁盘缓存在VM重新启动时是否保持(默认为false)  
        diskExpiryThreadIntervalSeconds="100" 磁盘失效线程运行时间间隔,默认是120秒  
        memoryStoreEvictionPolicy="LFU" 内存存储与释放策略.当达到maxElementsInMemory时  
               共有三种策略,分别为LRU(最近最少使用)、LFU(最常用的)、FIFO(先进先出)默认使用"最近使用"策略  
    -->  
    <defaultCache    
        eternal="false"  
        maxElementsInMemory="10000" 
        timeToIdleSeconds="3600"    
        timeToLiveSeconds="0"    
        overflowToDisk="true"    
        diskPersistent="false"    
        diskExpiryThreadIntervalSeconds="120"    
        memoryStoreEvictionPolicy="LRU"/>  
        
          
</ehcache>

然后看springCacheManager中的set,我们可用的value就有两个了,一个是layerCache,直接存redis,一级的,还有一个是layersCache,二级的。

写一个测试:

public interface CacheService {
	
	@Cacheable(value = "layerCache",key = "#key")
	String addCache(String key);
	@CacheEvict(value = "layerCache",key = "#key")
	String delCache(String key);
	
	@Cacheable(value = "layersCache",key = "#key")
	String addCache2(String key);
	@CacheEvict(value = "layersCache",key = "#key")
	String delCache2(String key);

}

实现:

public class CacheServiceImpl implements CacheService{

	@Override
	public String addCache(String key) {
		System.out.println("第一次调用会会打印此语句---一级缓存----");
		return "cache test success !!!";
	}

	@Override
	public String delCache(String key) {
		System.out.println("移除缓存的值---一级缓存----");
		return "cache remove success !!!";
	}

	@Override
	public String addCache2(String key) {
		System.out.println("第一次调用会会打印此语句---二级缓存----");
		return "cache test success !!!";
	}

	@Override
	public String delCache2(String key) {
		System.out.println("移除缓存的值---二级缓存----");
		return "cache remove success !!!";
	}


	
	

}

controller:

/**
	 * 测试cache配置是否成功
	 * 
	 * @return
	 */
	@RequestMapping("/cache")
	public String testCache() {
		String value = cacheService.addCache("cacheTest");
		return "/cacheTest.jsp";
	}
	@RequestMapping("/delcache")
	public String delcache() {
		String value = cacheService.delCache("cacheTest");
		return "/cacheTest.jsp";
	}
	@RequestMapping("/cache2")
	public String testCache2() {
		String value = cacheService.addCache2("cacheTest2");
		return "/cacheTest.jsp";
	}
	@RequestMapping("/delcache2")
	public String delcache2() {
		String value = cacheService.delCache2("cacheTest2");
		return "/cacheTest.jsp";
	}

运行:


分别点击测试一级,测试二级一次,看控制台输出:


,然后再分别点击一次:


这时可以看到已经进入缓存了,没有再进入impl的方法内了。然后我们移除缓存再试一次:


发现移除缓存后再测试又没有进入缓存了,好了至此缓存已成功!

至于模糊删除缓存,我一开始没啥头绪,上网搜点资料,然后看见有人用spring 的AOP来实现,我因此尝试了一波,我并未成功,忙活了半天。。。。

好了,看代码中有一行,

System.out.println("del key:"+key.toString());

这不是已经取到key 了啊?傻傻的每次都能从控制台看到,一直没反应过来。

这不就是重写@CacheEvict方法吗?那还不就简单了,利用正则循环key,模糊匹配的就删掉。

EhRedisCache的改如下:

 @Override
    public void evict(Object key) {
    	
    	System.out.println("del levels key:"+key.toString());
		if (key.toString().startsWith("regex:")) {
			String pattern = key.toString().replace("regex:", "");
			//String pattern = res.replace("*", ".*");
			System.out.println("pattern:"+pattern);
			Set<String> keys_redis = redisTemplate.keys(pattern);
			for (Iterator<String> iterator = keys_redis.iterator(); iterator.hasNext();) {
				String string = (String) iterator.next();
				System.out.println("remove redis keys:"+string);
			}
			redisTemplate.delete(keys_redis);
			List<?> cacheKeys_encache = ehCache.getKeys();
			pattern = pattern.replace("*", ".*");
			for (Object cacheKey: cacheKeys_encache) {
				if(Pattern.matches(pattern, cacheKey.toString())){
					System.out.println("remove encache keys:"+cacheKey.toString());
					ehCache.remove(cacheKey.toString());
				}
            }
			return;
		}
    	
        ehCache.remove(key);
        final String keyStr =  key.toString();  
        redisTemplate.execute(new RedisCallback<Long>() {  
            public Long doInRedis(RedisConnection connection)  
                    throws DataAccessException {  
                return connection.del(keyStr.getBytes());  
            }  
        },true); 
    }

RedisCache的改如下:

@Override
	public void evict(Object key) {
		// TODO Auto-generated method stub
		System.out.println("del key:"+key.toString());
		if (key.toString().startsWith("regex:")) {
			String pattern = key.toString().replace("regex:", "");
			//String pattern = res.replace("*", ".*");
			System.out.println("pattern:"+pattern);
			Set<String> keys_redis = redisTemplate.keys(pattern);
			for (Iterator iterator = keys_redis.iterator(); iterator.hasNext();) {
				String string = (String) iterator.next();
				System.out.println("remove redis keys:"+string);
			}
			redisTemplate.delete(keys_redis);
			return;
		}
		final String keyf = key.toString();
		redisTemplate.execute(new RedisCallback<Long>() {
			public Long doInRedis(RedisConnection connection) throws DataAccessException {
				return connection.del(keyf.getBytes());
			}
		});
	}

这种场景,是在增删改查的时候,我查询的时候从缓存,然后增加、删除、修改的时候移除相关缓存。

大概是这样:

@Cacheable(value = "layersCache",key = "#root.targetClass + '_' + 'count'")
	long countByExample(CampusExample example);

	@CacheEvict(value = "layersCache",key = "'regex:'+#root.targetClass + '_*' ")
    int deleteByExample(CampusExample example);

	@CacheEvict(value = "layersCache",key = "'regex:'+#root.targetClass + '_*' ")
    int deleteByPrimaryKey(Integer id);

	@CacheEvict(value = "layersCache",key = "'regex:'+#root.targetClass + '_*' ")
    int insert(Campus record);

	@CacheEvict(value = "layersCache",key = "'regex:'+#root.targetClass + '_*' ")
    int insertSelective(Campus record);

    @Cacheable(value = "layersCache",key = "#root.targetClass + '_demo_' + #example")
    List<Campus> selectByExample(CampusExample example);

    @Cacheable(value = "layersCache",key = "#root.targetClass + '_demo_' + #id")
    Campus selectByPrimaryKey(Integer id);

    @CacheEvict(value = "layersCache",key = "'regex:'+#root.targetClass + '_*' ")
    int updateByExampleSelective(@Param("record") Campus record, @Param("example") CampusExample example);

    @CacheEvict(value = "layersCache",key = "'regex:'+#root.targetClass + '_*' ")
    int updateByExample(@Param("record") Campus record, @Param("example") CampusExample example);

    @CacheEvict(value = "layersCache",key = "'regex:'+#root.targetClass + '_*' ")
    int updateByPrimaryKeySelective(Campus record);

    @CacheEvict(value = "layersCache",key = "'regex:'+#root.targetClass + '_*' ")
    int updateByPrimaryKey(Campus record);

这是测试:

可以看到pattern:CampusServiceImpl_*,然后就移除一堆了。至此模糊删除也已成功。

猜你喜欢

转载自blog.csdn.net/qq_21454973/article/details/80372179