1. 程式人生 > >spring+redis 實現快取 解決序列化和反序列化的問題

spring+redis 實現快取 解決序列化和反序列化的問題

1.config.properties

# Redis settings
redis.host=127.0.0.1
redis.port=6379  
#redis.pass=password
redis.dbIndex=0  
redis.expiration=3000  
redis.maxIdle=300  
redis.maxActive=600  
redis.maxWait=1000  
redis.testOnBorrow=true
 

2.spring-context.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:p="http://www.springframework.org/schema/p"
    xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context"
    xmlns:util="http://www.springframework.org/schema/util"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="
            http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd  
            http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd  
            http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd
            http://www.springframework.org/schema/tx 
            http://www.springframework.org/schema/tx/spring-tx.xsd 
            http://www.springframework.org/schema/aop 
            http://www.springframework.org/schema/aop/spring-aop.xsd
           http://www.springframework.org/schema/cache 
           http://www.springframework.org/schema/cache/spring-cache.xsd"
            > 
            
            <!-- 自動掃描 -->  
    <context:component-scan base-package="com.bigname" /> 

<!-- 讀入配置屬性檔案 -->
<!--     <context:property-placeholder location="classpath:config.properties" />
<context:property-placeholder location="classpath:pgsql-config.properties" />
     -->
    <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">  
    <property name="locations">  
       <list>  
          <value>classpath:config.properties</value>  
          <value>classpath:pgsql-config.properties</value>  
        </list>  
    </property>  
</bean>  
    
    
    <!-- 引入spring-redis.xml -->
    <import resource="classpath:spring-redis.xml" />
    
<!-- 配置c3p0 -->
    <!-- 連線池 -->
   <!--  <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
        <property name="jdbcUrl" value="jdbc:mysql://localhost/qw?characterEncoding=utf8&amp;serverTimezone=UTC"></property>
        <property name="user" value="root"></property>
        <property name="password" value="123456"></property>
        <property name="minPoolSize" value="1"></property>
        <property name="maxPoolSize" value="5"></property>
        <property name="initialPoolSize" value="1"></property>
        <property name="acquireIncrement" value="1"></property>
    </bean> -->
    
    <!-- 定義資料庫連線池資料來源bean destroy-method="close"的作用是當資料庫連線不使用的時候,就把該連線重新放到資料池中,方便下次使用呼叫 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
        destroy-method="close">
        <!-- 設定JDBC驅動名稱 -->
        <property name="driverClass" value="${jdbc.driver}" />
        <!-- 設定JDBC連線URL -->
        <property name="jdbcUrl" value="${jdbc.url}" />
        <!-- 設定資料庫使用者名稱 -->
        <property name="user" value="${jdbc.username}" />
        <!-- 設定資料庫密碼 -->
        <property name="password" value="${jdbc.password}" />
        <!-- 設定連線池初始值 -->
        <property name="initialPoolSize" value="${c3p0.initialPoolSize}" />
        <!-- 設定連線池最大值 -->
        <property name="maxPoolSize" value="${c3p0.maxPoolSize}"></property>
        <!-- 設定連線池最小值 -->
        <property name="minPoolSize" value="${c3p0.minPoolSize}"></property>
        <property name="maxIdleTime" value="${c3p0.maxIdleTime}"></property>
        <property name="acquireIncrement" value="${c3p0.acquireIncrement}"></property>
        <property name="maxIdleTimeExcessConnections" value="${c3p0.maxIdleTimeExcessConnections}"></property>
    </bean>
    <!-- 配置事務管理器 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!-- 使用註解來控制事務 -->
    <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
    <!-- 配置mybatis, 繫結c3p0-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"></property>
        <property name="configLocation" value="classpath:mybatis-config.xml"></property>
        <property name="mapperLocations">
            <list>
                <value>classpath:mapper/*.xml</value>
            </list>
        </property>
    </bean>

    <!-- 掃描生成所有dao層 -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.bigname.demo.dao"></property>
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
    </bean>
    
    
    
</beans>  

3.spring-redis.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:redis="http://www.springframework.org/schema/redis" xmlns:cache="http://www.springframework.org/schema/cache"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
    http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.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.xsd
    http://www.springframework.org/schema/redis
    http://www.springframework.org/schema/redis/spring-redis.xsd
    http://www.springframework.org/schema/cache
    http://www.springframework.org/schema/cache/spring-cache.xsd
    ">
    <!-- Redis -->
    <!-- 連線池引數 -->
     <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
        <property name="maxIdle" value="${redis.maxIdle}" />
        <property name="maxTotal" value="${redis.maxActive}" />
        <property name="maxWaitMillis" value="${redis.maxWait}" />
        <property name="testOnBorrow" value="${redis.testOnBorrow}" />
    </bean>

    <!-- 配置JedisConnectionFactory -->
    <bean id="jedisConnectionFactory"
          class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
        <property name="hostName" value="${redis.host}" />
        <property name="port" value="${redis.port}" />
        <!-- <property name="password" value="${redis.pass}" /> -->
        <property name="database" value="${redis.dbIndex}" />
        <property name="poolConfig" ref="poolConfig" />
    </bean>
 
    <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
        <property name="connectionFactory" ref="jedisConnectionFactory" />
        
        <!-- 配置預設的序列化策略,非hash的value和hashValue都有效-->
       <property name="defaultSerializer" ref="genericJackson2JsonRedisSerializer">
        </property>
        <!--&lt;!&ndash; 配置redis的key的序列化Serializer方式,使5中型別,key都以String型別進行序列化 &ndash;&gt;-->
        <!--&lt;!&ndash; Hash型別,用Jedis儲存key為brandList,但是在redis-cli中檢視key卻是 "\xac\xed\x00\x05t\x00\tbrandList" &ndash;&gt;-->
        <property name="keySerializer" ref="stringRedisSerializer" />
        <property name="hashKeySerializer" ref="stringRedisSerializer" />
    </bean>
 
 
 
   <!-- 配置key的序列化方式,使用String型別進行序列化 -->
    <bean id="stringRedisSerializer"
          class="com.bigname.common.utils.FastJsonRedisSerializer" />
    <!-- 配置hashValue的序列化方式,使用Jackson serializer,將hashValue以json串的形式儲存到redis中 -->
    <bean id="genericJackson2JsonRedisSerializer"
          class="org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer">
    </bean>
    
    <!-- 配置RedisCacheConfig -->
    <bean id="redisCacheManager" class="org.springframework.data.redis.cache.RedisCacheManager">
        <constructor-arg name="redisOperations" ref="redisTemplate" />
        <property name="defaultExpiration" value="${redis.expiration}" />
    </bean>

    <!-- 配置RedisCacheConfig -->
    <bean id="redisCacheConfig" class="com.bigname.common.utils.RedisCacheConfig">
        <constructor-arg ref="jedisConnectionFactory" />
        <constructor-arg ref="redisTemplate" />
        <constructor-arg ref="redisCacheManager" />
    </bean>
</beans>
 

4.FastJsonRedisSerializer

package com.bigname.common.utils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.SerializationException;
import org.springframework.util.Assert;

import java.io.ByteArrayOutputStream;
import java.nio.charset.Charset;

public class FastJsonRedisSerializer implements RedisSerializer<Object>{

      private final Charset charset;

        private final String target = "\"";

        private final String replacement = "";

        public FastJsonRedisSerializer() {
            this(Charset.forName("UTF8"));
        }

        public FastJsonRedisSerializer(Charset charset) {
            Assert.notNull(charset, "Charset must not be null!");
            this.charset = charset;
        }

        @Override
        public String deserialize(byte[] bytes) {
            return (bytes == null ? null : new String(bytes, charset));
        }

        @Override
        public byte[] serialize(Object object) {
            String string = JSON.toJSONString(object);
            if (string == null) {
                return null;
            }
            string = string.replace(target, replacement);
            return string.getBytes(charset);
        }


}
5.RedisCacheConfig

package com.bigname.common.utils;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import com.fasterxml.jackson.databind.ObjectMapper;

@Configuration  
@EnableCaching
public class RedisCacheConfig extends CachingConfigurerSupport{

    private volatile JedisConnectionFactory jedisConnectionFactory;
    private volatile RedisTemplate<String, String> redisTemplate;
    private volatile RedisCacheManager redisCacheManager;

    public RedisCacheConfig() {
        super();
    }

    /**
     * 帶引數的構造方法 初始化所有的成員變數
     * 
     * @param jedisConnectionFactory
     * @param redisTemplate
     * @param redisCacheManager
     */
    public RedisCacheConfig(JedisConnectionFactory jedisConnectionFactory, RedisTemplate<String, String> redisTemplate,
            RedisCacheManager redisCacheManager) {
        this.jedisConnectionFactory = jedisConnectionFactory;
        this.redisTemplate = redisTemplate;
        this.redisCacheManager = redisCacheManager;
    }

    public JedisConnectionFactory getJedisConnecionFactory() {
        return jedisConnectionFactory;
    }

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

    public RedisCacheManager getRedisCacheManager() {
        return redisCacheManager;
    }

    @Bean
    public KeyGenerator customKeyGenerator() {
        return new KeyGenerator() {
            public Object generate(Object target, Method method, Object... objects) {
                StringBuilder sb = new StringBuilder();
                sb.append(target.getClass().getName());
                sb.append(method.getName());
                for (Object obj : objects) {
                    sb.append(obj.toString());
                }
                return sb.toString();
            }
        };
    }
    
    
}
 

6.實體類

package com.bigname.demo.entity;

import java.io.Serializable;

import com.bigname.common.BaseModel;

public class User extends BaseModel implements Serializable{

    
    /**
     * 
     */
    private static final long serialVersionUID = -9187083138474340478L;

    private String userName;
    
    private String password;

    private Integer age;
    

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

}
 

7.dao

package com.bigname.demo.dao;

import java.util.List;

import org.apache.ibatis.annotations.Param;

import com.bigname.demo.entity.User;

public interface UserDao {

    User selectUser(@Param("userName")String name,@Param("passWord")String password);
    
    int register(User user);
    
    String selectByUserName(String name);
    
    List queryAll();
    
    String selectUserqw();
}
 

8.UserServiceImpl

package com.bigname.demo.service.impl;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import com.bigname.demo.dao.UserDao;
import com.bigname.demo.entity.User;
import com.bigname.demo.service.UserService;
@Service
public class UserServiceImpl implements UserService {

     @Autowired
     private UserDao userdao;

     
    public User selectUser(String userName,String password) {
        // TODO Auto-generated method stub
        return userdao.selectUser(userName,password);
    }

    //@Cacheable("getUserById")
    //標註該方法查詢的結果進入快取,再次訪問時直接讀取快取中的資料
    @CacheEvict(value="getUserById",allEntries=true) 
    public int insertUser(User user) {
        // TODO Auto-generated method stub
        return userdao.register(user);
    }

    public String selectByUserName(String name) {
        // TODO Auto-generated method stub
        return userdao.selectByUserName(name);
    }

//標註該方法查詢的結果進入快取,再次訪問時直接讀取快取中的資料   

 @Cacheable("queryUserAlls")
    public List<User> queryUserAll() {
        // TODO Auto-generated method stub
        return userdao.queryAll();
    }
    
    
    
    public String selectUserqw() {
        // TODO Auto-generated method stub
        return userdao.selectUserqw();
    }

}
 

9.controller

@RequestMapping(value = "/sys/ls")
        @ResponseBody
        public Object demo1(User users,HttpSession session,HttpServletRequest request){
            String list = userservice.selectUserqw();
            return list;
        }

心得:研究了兩天左右,網上又得說法都太離譜了。我這個目前解決了序列化和反序列化的問題,但是還有一個問題就是當一個service出現兩個@Cacheable 就可能會失效 也就是說 另外一個@Cacheable也會進入之前的快取 或者不起作用,這個還在研究,個人能力有限,還望大牛指導,歡迎評論