spring boot 整合 redis

来源:互联网 发布:淘宝一比一高仿奢侈品 编辑:程序博客网 时间:2024/06/06 09:22

1 引入依赖

<dependency>   <groupId>org.springframework.boot</groupId>   <artifactId>spring-boot-starter-data-redis</artifactId>   <version>1.5.6.RELEASE</version></dependency>

2 添加配置

配置默认是写在application.properties或application.yml中,也可以自己新建一个资源文件,但是在RedisCacheConfig用需要注解出配置具体的文件
###########################################################REDIS (RedisProperties) redis基本配置######################################################### database namespring.redis.database=0# Redis服务器IPspring.redis.host=10.176.65.137# Redis密码spring.redis.password=redis# Redis端口号spring.redis.port=6379# 连接超时时间 单位 ms(毫秒)spring.redis.timeout=3000###########################################################REDIS (RedisProperties) redis线程池设置######################################################### 控制一个pool最多有多少个状态为idle(空闲的)的jedis实例,默认值也是8。spring.redis.pool.max-idle=20# 控制一个pool最少有多少个状态为idle(空闲的)的jedis实例,默认值也是0。spring.redis.pool.min-idle=10# 如果赋值为-1,则表示不限制;如果pool已经分配了maxActive个jedis实例,则此时pool的状态为exhausted(耗尽)。spring.redis.pool.max-active=60# 等待可用连接的最大时间,单位毫秒,默认值为-1,表示永不超时。如果超过等待时间,则直接抛出JedisConnectionExceptionspring.redis.pool.max-wait=3000#######################################################################################REDIS (RedisProperties) redis哨兵设置,和上面的host和port不要同时配置##################################################################################### Redis服务器master的名字#spring.redis.sentinel.master=master8026# redis-sentinel的配置地址和端口(注意:不是redis的地址和端口)#spring.redis.sentinel.nodes=10.189.80.25:26379,10.189.80.26:26379,10.189.80.26:26378###########################################################REDIS (RedisProperties) redis 自定义参数#########################################################默认生命周期30天spring.redis.defaultExpiration = 2592000#服务器上下文路径spring.redis.contextPath = contextPath

3 redis配置代码实现

import com.fasterxml.jackson.annotation.JsonAutoDetect;import com.fasterxml.jackson.annotation.PropertyAccessor;import com.fasterxml.jackson.databind.ObjectMapper;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.boot.context.properties.ConfigurationProperties;import org.springframework.cache.Cache;import org.springframework.cache.annotation.*;import org.springframework.cache.interceptor.CacheErrorHandler;import org.springframework.cache.interceptor.KeyGenerator;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.PropertySource;import org.springframework.data.redis.cache.RedisCacheManager;import org.springframework.data.redis.connection.RedisConnectionFactory;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 java.lang.reflect.Field;import java.lang.reflect.Method;import java.util.*;/** * @author qianjianfeng * @version 1.0.0 * @since 1.0.0 */@Configuration@ConfigurationProperties(prefix = "spring.redis")@PropertySource(value = "classpath:/META-INF/redis.properties")@EnableCachingpublic class RedisCacheConfig  extends CachingConfigurerSupport {    private Logger logger = LoggerFactory.getLogger(this.getClass());    private Long defaultExpiration;    private String contextPath;    public Long getDefaultExpiration() {        return defaultExpiration;    }    public void setDefaultExpiration(Long defaultExpiration) {        this.defaultExpiration = defaultExpiration;    }    public String getContextPath() {        return contextPath;    }    public void setContextPath(String contextPath) {        this.contextPath = contextPath;    }    /**     * 自定义key.     * key --> 项目名 + 缓存空间值 + 所有参数的值     * 即使@Cacheable中的value属性一样,key也会不一样。     */    @Bean    @Override    public KeyGenerator keyGenerator() {        return new KeyGenerator(){            @Override            public String generate(Object o, Method method, Object... objects) {                // This will generate a unique key of the class name, the method name                //and all method parameters appended.                StringBuilder sb = new StringBuilder();                sb.append(contextPath).append("/:");                Cacheable cacheable = method.getAnnotation(Cacheable.class);                CachePut cachePut = method.getAnnotation(CachePut.class);                CacheEvict cacheEvict = method.getAnnotation(CacheEvict.class);                if (cacheable != null) {                    sb.append(Arrays.toString(cacheable.value())).append(":");                }else if (cachePut != null) {                    sb.append(Arrays.toString(cachePut.value())).append(":");                }else if (cacheEvict != null) {                    sb.append(Arrays.toString(cacheEvict.value())).append(":");                }                Map valueMap = new HashMap();                for (Object obj : objects) {                    try {                        getStringValueMap(obj,valueMap);                    } catch (IllegalAccessException e) {                        logger.info("生成key的时候,[{}]转换map异常,生成的key丢弃了该值。",obj.getClass(),e);                    }                }                sb.append(valueMap.toString());                System.err.println(sb.toString());                return sb.toString();            }        };    }    /**     * redis模板操作类,类似于jdbcTemplate的一个类;     * 虽然CacheManager也能获取到Cache对象,但是操作起来没有那么灵活;     * 这里在扩展下:RedisTemplate这个类不见得很好操作,我们可以在进行扩展一个我们     * 自己的缓存类,比如:RedisStorage类;     * @param factory : 通过Spring进行注入,参数在application.properties进行配置;     * @return     */    @Bean    public RedisTemplate<String,String> redisTemplate(RedisConnectionFactory factory) {        RedisTemplate<String,String> redisTemplate = new RedisTemplate<>();        redisTemplate.setConnectionFactory(factory);        //key序列化方式;(不然会出现乱码;),但是如果方法上有Long等非String类型的话,会报类型转换错误;        //所以在没有自己定义key生成策略的时候,以下这个代码建议不要这么写,可以不配置或者自己实现ObjectRedisSerializer        //或者JdkSerializationRedisSerializer序列化方式;        RedisSerializer<String> redisSerializer = new StringRedisSerializer();//Long类型不可以会出现异常信息;        redisTemplate.setKeySerializer(redisSerializer);        redisTemplate.setHashKeySerializer(redisSerializer);        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);        ObjectMapper om = new ObjectMapper();        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);        jackson2JsonRedisSerializer.setObjectMapper(om);        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);        redisTemplate.afterPropertiesSet();        return redisTemplate;    }    /**     * 缓存管理器     * @param redisTemplate     * @return     */    @Bean    public RedisCacheManager cacheManager(RedisTemplate<?,?> redisTemplate) {        RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate);        cacheManager.setDefaultExpiration(defaultExpiration);//默认生命周期30天        return cacheManager;    }    /**     * redis数据操作异常处理     * 这里的处理:在日志中打印出错误信息,但是放行     * 保证redis服务器出现连接等问题的时候不影响程序的正常运行,使得能够出问题时不用缓存     * @return     */    @Bean    @Override    public CacheErrorHandler errorHandler() {        CacheErrorHandler cacheErrorHandler = new CacheErrorHandler() {            @Override            public void handleCacheGetError(RuntimeException e, Cache cache, Object key) {                logger.error("redis异常:key=[{}]",key,e);            }            @Override            public void handleCachePutError(RuntimeException e, Cache cache, Object key, Object value) {                logger.error("redis异常:key=[{}]",key,e);            }            @Override            public void handleCacheEvictError(RuntimeException e, Cache cache, Object key) {                logger.error("redis异常:key=[{}]",key,e);            }            @Override            public void handleCacheClearError(RuntimeException e, Cache cache) {                logger.error("redis异常:",e);            }        };        return cacheErrorHandler;    }    /**     * 取出对象及其父类定义的字段名和值存入map中     * @param obj     * @param valueMap     * @throws IllegalAccessException     */    private void getStringValueMap(Object obj, Map valueMap) throws IllegalAccessException {        List<Field> fields = scanfields(obj.getClass());        for (Field field : fields){            Boolean accessFlag = field.isAccessible();            field.setAccessible(true);            Object o = field.get(obj);            if (o != null){                if (o instanceof Integer || o instanceof Long || o instanceof Float                        || o instanceof Double || o instanceof String || o instanceof Collections                        || o instanceof Map || o instanceof Byte){                    valueMap.put(field.getName(), o.toString());                }else {                    getStringValueMap(o,valueMap);                }            }            field.setAccessible(accessFlag);        }    }    /**     * 对一个类扫描取出它和它父类定义的字段     * @param clazz     * @return     */    private List<Field> scanfields(Class clazz){        List<Field> fields = new ArrayList<>();        if (clazz == Object.class) {            return fields;        }        Field[] fieldArray = clazz.getDeclaredFields();        String fieldName;        for (int i = 0; i < fieldArray.length; i++){            fieldName = fieldArray[i].getName();            if (!("$staticClassInfo".equals(fieldName) || "__$stMC".equals(fieldName)                    || "metaClass".equals(fieldName) || "$staticClassInfo$".equals(fieldName)                    || "$callSiteArray".equals(fieldName))){                fields.add(fieldArray[i]);            }        }        fields.addAll(scanfields(clazz.getSuperclass()));        return fields;    }}

4 测试

目前为止,所有的配置都已经写完了,接下来就是测试了。既然我们整合的是springboot,那接下来再依赖一个启动器,当然他的parent依赖也要加上,具体看springboot文档
<dependency>   <groupId>org.springframework.boot</groupId>   <artifactId>spring-boot-starter-web</artifactId>   <version>1.5.6.RELEASE</version></dependency>

测试用的service,需要缓存操作的加上Cacheable、CacheEvict或CachePut,必须要赋予value值
import org.springframework.cache.annotation.CacheEvict;import org.springframework.cache.annotation.Cacheable;import org.springframework.stereotype.Service;@Servicepublic class TestService {    @Cacheable(value = "hello")    public Person hello(Person person){        System.err.println("2");        return person;    }    @Cacheable(value = "hello2")    public Person hello2(Person person){        System.err.println("2");        return person;    }    @CacheEvict(value = "hello")    public Person bye(Person person){        System.err.println("2");        return person;    }    @CacheEvict(value = "hello2")    public Person bye2(Person person){        System.err.println("2");        return person;    }    @Cacheable(value = "door")    public Door door(Door door){        System.err.println("2");        return door;    }}

测试用的controller
import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.PathVariable;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;@RestControllerpublic class TestController {    @Autowired    private TestService testService;    @RequestMapping("/hello/{age}")    public Person hello(@PathVariable int age){        Person person = new Person("link",age,new Head(2,1));        return testService.hello(person);    }    @RequestMapping("/hello2/{age}")    public Person hello2(@PathVariable int age){        Person person = new Person("link",age,new Head(2,1));        return testService.hello2(person);    }    @RequestMapping("/bye/{age}")    public Person bye(@PathVariable int age){        Person person = new Person("link",age,new Head(2,1));        return testService.bye(person);    }    @RequestMapping("/bye2/{age}")    public Person bye2(@PathVariable int age){        Person person = new Person("link",age,new Head(2,1));        return testService.bye2(person);    }    @RequestMapping("/door/{id}")    public Door door(@PathVariable int id){        Door door = new Door(id);        return testService.door(door);    }}

spring boot的启动文件
import org.springframework.boot.SpringApplicationimport org.springframework.boot.autoconfigure.SpringBootApplication@SpringBootApplicationpublic class Application {    static void main(String[] args) {        SpringApplication.run(Application.class,args)    }}

原本这边放了目录结构和测试的图片,结果发上来一看图片飞走了,还是大家自己动手试试吧~~