剛從.net轉java三個月,這裏記錄一下在java項目中使用緩存的學習。java
由於項目以前的開發人員離職了,在以前的項目上進行維護,對應歷來沒有接觸過java的小白,只能本身多看多理解多動手了。node
這個項目原來是沒有用java真正意義上的緩存,而是用的靜態的HashMap,可是在性能測試的過程當中出現了死鎖的過程,由於hashmap是不安全的線程,建議使用ConcurrentHashMap這個和.net的Dictonary很像。由於性能測試不經過,因此後來加了redis,其實java的一級緩存ehcache也是很好的,由於項目到了後期階段,項目時間緊就沒改ehcahe(這個.net的asp.net cache同樣)。web
言歸正傳:redis
hashmap的使用方法:spring
首先定義一個InitCacheData類,裏面填充各類屬性和方法。sql
建議用這種數據庫
public class InitCacheData { private static InitCacheData instance; private InitCacheData() { } /** * description 單例模式 * @param @return * @return InitCacheData */ public static InitCacheData getInstance() { synchronized (InitCacheData.class) { if (instance == null) { instance = new InitCacheData(); } return instance; } } /** * 初始化數據的緩存,key:數據類型,value:數據(key:實體類主鍵的值,value對應的實體類) */ private Map<CacheType, Map<Integer, Object>> cacheData = new HashMap<CacheType, Map<Integer, Object>>(); private Map<CacheType, Map<Integer, List<Object>>> cacheTreeData = new HashMap<CacheType, Map<Integer, List<Object>>>(); private Map<CacheType, Map<String, Object>> cacheItemMappingData = new HashMap<CacheType, Map<String, Object>>(); private Map<String,List<SingleTrainStudentAnswer>> cacheAnswerData = new ConcurrentHashMap<String,List<SingleTrainStudentAnswer>>(); private Map<CacheType,List<ProjectItem>> projectItemData = new HashMap<CacheType,List<ProjectItem>>(); /** * @description 初始化數據的類型 */ public enum CacheType { CLASS, MAJOR, COLLEGE, COURSE,USER_INFO,KNOW_POINT,STUDENT,TEACHER,ITEMMAPPING,PARAMETER_TYPE,TRDCO,ANSWER,PROJECTITEM,BASEITEMS }; public Map<Integer, Object> getCacheData(CacheType dataType){ return this.cacheData.get(dataType); } public Map<CacheType, List<ProjectItem>> getBasicItemsData() { return basicItemsData; } /** * description 初始化數據執行的方法 * @param * @return void */ public void initData() { initCourseData(); initBasicItemsData(); } //獲取學生練習列表:對應登陸後的練習菜單 private void initBasicItemsData() { IProjectItemService projectItemService = (IProjectItemService) SpringContextUtil.getBean("projectItemService"); List<ProjectItem> list = projectItemService.getBasicItems(); if(basicItemsData.get(CacheType.BASEITEMS)!=null) { basicItemsData.get(CacheType.BASEITEMS).clear(); } basicItemsData.put(CacheType.BASEITEMS,list); } private void initCourseData() { ICourseService courseService = (ICourseService)SpringContextUtil.getBean("courseService"); List<Course> courseList = courseService.findAll(); Map<Integer, Object> courseMap = new HashMap<Integer, Object>(courseList.size()); Iterator<Course> courseIt = courseList.iterator(); Course course = null; while(courseIt.hasNext()){ course = courseIt.next(); courseMap.put(course.getId(), course); } if(cacheData.get(CacheType.COURSE) != null){ cacheData.get(CacheType.COURSE).clear(); } cacheData.put(CacheType.COURSE, courseMap); } /** * @description 經過遞歸得到全部選中節點下面的全部節點id * @param * @return void */ @SuppressWarnings({ "rawtypes", "unchecked" }) public void getChildNodes(List<Integer> idsList,int nodeId){ Map<Integer,List<Object>> knowPointMap = cacheTreeData.get(CacheType.KNOW_POINT); if(knowPointMap.containsKey(nodeId)){ List<KnowPoint> knowPointList = (List)knowPointMap.get(nodeId); for(KnowPoint know : knowPointList){ idsList.add(know.getId()); getChildNodes(idsList,know.getId()); } } }
redis的使用方法:express
1.在maven項目中添加引用依賴緩存
1 <dependency> 2 <groupId>org.springframework.data</groupId> 3 <artifactId>spring-data-redis</artifactId> 4 <version>1.5.0.RELEASE</version> 5 </dependency> 6 <dependency> 7 <groupId>redis.clients</groupId> 8 <artifactId>jedis</artifactId> 9 <version>2.9.0</version> 10 </dependency> 11
2.要建立spring-redis.xml安全
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd "> <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig"> <property name="maxIdle" value="${redis.maxIdle}" /> <property name="minIdle" value="${redis.minIdle}" /> <property name="maxTotal" value="${redis.maxTotal}" /> <property name="maxWaitMillis" value="${redis.max-wait-millis}" /> </bean> <bean id="connectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" p:host-name="${redis.host}" p:port="${redis.port}" p:password="${redis.password}" p:database="0" p:timeout="${redis.timeout}" p:pool-config-ref="poolConfig"/> <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate"> <property name="connectionFactory" ref="connectionFactory" /> <property name="defaultSerializer" ref="jdkSerializationRedisSerializer" /> </bean> <bean id="stringRedisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate"> <property name="connectionFactory" ref="connectionFactory" /> </bean> <bean id="jdkSerializationRedisSerializer" class=" org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"></bean> </beans>
3.添加redis.properties配置文件
#redis setting redis.host=127.0.0.1 redis.port=6379 redis.password=123456 redis.maxIdle=100 redis.maxActive=300 redis.maxWait=1000 redis.testOnBorrow=true redis.timeout=100000 fep.local.cache.capacity =10000
4.在spring-context.xml文件中導入redis配置 <import resource="spring-redis.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:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:task="http://www.springframework.org/schema/task" xmlns:jms="http://www.springframework.org/schema/jms" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd http://www.springframework.org/schema/jms http://www.springframework.org/schema/jms/spring-jms-3.2.xsd"> <!-- 啓用annotation方式 --> <task:annotation-driven scheduler="myScheduler" /> <!-- 配置任務線程池 --> <task:scheduler id="myScheduler" pool-size="5" /> <context:annotation-config/> <!-- 設置屬於Spring管理的類 --> <context:component-scan base-package="com.gta"> <!-- 排除com.gta.demo.controller包下的類,由SpringMVC來管理 --> <context:exclude-filter type="regex" expression="com.gta.kjzh.controller.*"/> </context:component-scan> <!-- 設置項目中能夠獲取到的properties配置文件 --> <context:property-placeholder location="classpath*:/config/*.properties"/> <!-- 設置Druid數據源 --> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close"> <!-- 數據源URL --> <property name="url" value="${url}"/> <!-- 數據庫用戶名 --> <property name="username" value="${db.username}"/> <!-- 數據庫密碼 --> <property name="password" value="${db.password}"/> <!-- 數據庫驅動 --> <property name="driverClassName" value="${driverClassName}"/> <!-- 屬性類型是字符串,經過別名的方式配置擴展插件,經常使用的插件有: 監控統計用的filter:stat 日誌用的filter:log4j 防護sql注入的filter:wall --> <property name="filters" value="${druid.filters}"/> <!-- 最大鏈接池數量 --> <property name="maxActive" value="${maxActive}"/> <!-- 初始化時創建物理鏈接的個數 --> <property name="initialSize" value="${initialSize}"/> <!-- 獲取鏈接時最大等待時間,單位毫秒 --> <property name="maxWait" value="${maxWait}"/> <!-- 啓用非公平鎖 --> <property name="useUnfairLock" value="true"/> <!-- 最小鏈接池數量 --> <property name="minIdle" value="${minIdle}"/> <!-- 有兩個含義: 1) Destroy線程會檢測鏈接的間隔時間 2) testWhileIdle的判斷依據,詳細看testWhileIdle屬性的說明 --> <property name="timeBetweenEvictionRunsMillis" value="${timeBetweenEvictionRunsMillis}"/> <property name="minEvictableIdleTimeMillis" value="${minEvictableIdleTimeMillis}"/> <!-- 用來檢測鏈接是否有效的sql,要求是一個查詢語句,若是validationQuery爲null,testOnBorrow、testOnReturn、 testWhileIdle都不會其做用 --> <property name="validationQuery" value="${validationQuery}"/> <!-- 建議配置爲true,不影響性能,而且保證安全性。 申請鏈接的時候檢測,若是空閒時間大於 timeBetweenEvictionRunsMillis,執行validationQuery檢測鏈接是否有效。--> <property name="testWhileIdle" value="${testWhileIdle}"/> <!-- 申請鏈接時執行validationQuery檢測鏈接是否有效,作了這個配置會下降性能。 --> <property name="testOnBorrow" value="${testOnBorrow}"/> <!-- 歸還鏈接時執行validationQuery檢測鏈接是否有效,作了這個配置會下降性能 --> <property name="testOnReturn" value="${testOnReturn}"/> <!-- 要啓用PSCache,必須配置大於0,當大於0時,poolPreparedStatements自動觸發修改成true。在Druid中,不會存在Oracle下PSCache佔用內存過多的問題,能夠把這個數值配置大一些,好比說100 --> <!--<property name="maxOpenPreparedStatements" value="${maxOpenPreparedStatements}"/>--> <!-- 打開removeAbandoned功能 --> <property name="removeAbandoned" value="${removeAbandoned}"/> <!-- 1800秒,也就是30分鐘 --> <property name="removeAbandonedTimeout" value="${removeAbandonedTimeout}"/> <!-- 關閉abanded鏈接時輸出錯誤日誌 --> <property name="logAbandoned" value="${logAbandoned}"/> </bean> <!-- spring上傳文件配置,這裏申明的id必須爲multipartResolver --> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <property name="maxUploadSize" value="314572800" /> </bean> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"></property> </bean> <bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="hibernateProperties"> <props> <!-- 設置數據庫方言 --> <prop key="hibernate.dialect">${hibernate.dialect}</prop> <!--<prop key="hibernate.current_session_context_class">${hibernate.current_session}</prop>--> <!-- 設置是否在控制檯輸出sql語句 --> <prop key="hibernate.show_sql">${hibernate.show_sql}</prop> <!-- 輸出格式化的sql語句 --> <prop key="hibernate.format_sql">${hibernate.format_sql}</prop> <!-- 設置項目啓動時,hibernate檢查數據庫和實體類是否匹配 --> <prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</prop> <!-- hibernate ehcache 配置 EhCacheRegionFactory--> <prop key="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory </prop> <prop key="hibernate.cache.use_query_cache">true</prop> <prop key="hibernate.cache.use_second_level_cache">true</prop> <!-- 二級緩存配置文件路徑 /resources/hibernate--> <prop key="net.sf.ehcache.configurationResourceName">/config/ehcache.xml</prop> </props> </property> <!-- 掃描受Hibernate管理的實體類所在的包--> <property name="packagesToScan" value="com.gta.kjzh.*"/> </bean> <bean id="genericDao" class="com.gta.kjzh.sdk.base.dao.HibernateGenericDAO"></bean> <!-- 設置spring使用Hibernate的事務處理方式 --> <bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory"/> </bean> <!-- 設置事務註解使用spring事務管理的方式 --> <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/> <!-- 通用springMVC攔截器的設置 <bean id="springMVCInterceptor" class="com.gta.filter.SpringMVCInterceptor"></bean> <bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"> <property name="interceptors"> <list> <ref bean="springMVCInterceptor"/> </list> </property> </bean> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="get*" read-only="true" propagation="REQUIRED"/> <tx:method name="find*" read-only="true" propagation="REQUIRED"/> <tx:method name="save*" propagation="REQUIRED"/> <tx:method name="update*" propagation="REQUIRED"/> <tx:method name="remove*" propagation="REQUIRED"/> <tx:method name="add*" propagation="REQUIRED"/> <tx:method name="*"/> </tx:attributes> </tx:advice> <aop:config proxy-target-class="true"> <aop:advisor advice-ref="txAdvice" pointcut="execution(* com.gta.kjzh.*.service..*.*(..))"/> </aop:config>--> <!-- <import resource="activemq.xml"/> --> <import resource="spring-redis.xml"/> <!-- <import resource="spring-quartz-answer.xml"/> <import resource="spring-quartz.xml"/> --> </beans>
5.編輯RedisCache類,裏面放有redis的增刪改查操做。
package com.kjzh.redis.cache; import java.util.List; import java.util.Set; import java.util.concurrent.TimeUnit; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.ListOperations; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.SetOperations; import org.springframework.data.redis.core.ZSetOperations; import org.springframework.stereotype.Component; import com.gta.kjzh.util.SerializeUtils; import net.sf.ehcache.CacheException; @Component public class RedisCache<K, V> { private static String redisCode = "utf-8"; private String prefix; @Autowired private RedisTemplate<byte[], V> redisTemplate; @Autowired private RedisTemplate<String, String> stringRedisTemplate; public Set<String> getKeys(String keyPattern) { return stringRedisTemplate.keys(keyPattern); } public String getString(String key) { return stringRedisTemplate.opsForValue().get(key); } public void setString(String key, String value) { stringRedisTemplate.opsForValue().set(key, value); } public V get(K key) { byte[] bkey = getByteKey(key); return redisTemplate.opsForValue().get(bkey); } public void set(K key, V value) { byte[] bkey = getByteKey(key); redisTemplate.opsForValue().set(bkey, value); } public void remove(K key) { byte[] bkey = getByteKey(key); redisTemplate.delete(bkey); } public void set(K key, V value, long timeout) { byte[] bkey = getByteKey(key); redisTemplate.opsForValue().set(bkey, value, timeout, TimeUnit.SECONDS); } String get(K key, long start, long end){ byte[] bkey = getByteKey(key); return redisTemplate.opsForValue().get(bkey,start,end); } Long size(K key){ byte[] bkey = getByteKey(key); return redisTemplate.opsForValue().size(bkey); } List<V> range(K key, long start, long end){ byte[] bkey = getByteKey(key); return redisTemplate.opsForList().range(bkey,start,end); } public void clear() throws CacheException { redisTemplate.getConnectionFactory().getConnection().flushDb(); } public List<V> getListValue(K key) { byte[] bkey = getByteKey(key); ListOperations<byte[], V> list = redisTemplate.opsForList(); return redisTemplate.opsForList().range(bkey, 0,list.size(bkey)); } private byte[] getByteKey(K key){ if(key instanceof String){ String preKey = this.prefix + key; return preKey.getBytes(); }else{ return SerializeUtils.serialize(key); } } public String getPrefix() { return prefix; } /** * 批量刪除對應的value * * @param keys */ public void remove(final String... keys) { for (String key : keys) { remove(key); } } /** * 批量刪除key * * @param pattern */ public void removePattern(final String pattern) { Set<Serializable> keys = redisTemplate.keys(pattern); if (keys.size() > 0) redisTemplate.delete(keys); } /** * 判斷緩存中是否有對應的value * * @param key * @return */ public boolean exists(final String key) { return redisTemplate.hasKey(key); } }
6.具體使用
if(redisCache.get(answerkey)!=null) { redisCache.remove(answerkey); } redisCache.set(answerkey,reCombinsList);