SpringBoot_缓存_Redis

本文详细介绍SpringBoot中集成Redis缓存的两种方式,包括添加依赖、配置文件、代码使用等,同时提供自定义注解缓存实现及工具类示例。

方式一

添加依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-redis</artifactId>
</dependency>

添加配置文件

spring.redis.host=127.0.0.1
spring.redis.port=6379
spring.redis.pool.max-active=5   #最大连接池

代码中使用

@Autowired
private StringRedisTemplate redisTemplate;

方式二

引用依赖

	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-data-redis</artifactId>
	</dependency>
	<!-- 高版本redis的lettuce需要commons-pool2 -->
	<dependency>
		<groupId>org.apache.commons</groupId>
		<artifactId>commons-pool2</artifactId>
	</dependency>
	<dependency>
		<groupId>com.alibaba</groupId>
		<artifactId>fastjson</artifactId>
	</dependency>
	<dependency>
		<groupId>com.google.code.gson</groupId>
		<artifactId>gson</artifactId>
	</dependency>

配置类

//# 自定义缓存喷子
//pingruan:
//	  base:
//	    cache-redis-ttl: -1
//
//spring:
//  application:
//    name: vander-springboot-demo
//  redis:
//    timeout: 6000ms
//    password: 
//    port: 6379
//    database: 0
//    host: 192.168.164.100
//    lettuce: 
//      pool: 
//        max-active: 1000  # 连接池最大连接数(使用负值表示没有限制)
//        max-idle: 10 # 连接池中的最大空闲连接
//        min-idle: 5 # 连接池中的最小空闲连接
//        max-wait: -1 # 连接池最大阻塞等待时间(使用负值表示没有限制)

//缓存业务数据步骤:
//1、缓存
//@Cacheable(value = "project_user",key = "'id_' + #id")
//2、删除
//@CacheEvict(value="project_user",key="'id_' + #id")
//3、修改
//@CachePut(value="project_user",key="'id_' + #id")
//备注:
//#user.id  获取方法user对象的id值

//指定方法唯一数据缓存,方式一:
//@Cacheable(value = "project_cache",keyGenerator="keyGenerator")

//指定方法唯一数据缓存(可以指定过期时间s),方式二:
//@VCacheable(expireTime=60)

@Configuration
@EnableCaching // 开启缓存支持
@AutoConfigureAfter(RedisAutoConfiguration.class)
@SuppressWarnings("all")
public class RedisConfig extends CachingConfigurerSupport {
	
	@Autowired
	private BProperties bProperties;
	
	@Bean
	public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory factory) {
		RedisTemplate<String, Object> template = new RedisTemplate<>();
		RedisSerializer<String> redisSerializer = new StringRedisSerializer();
		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);

		template.setConnectionFactory(factory);
		// key序列化方式
		template.setKeySerializer(redisSerializer);
		// value序列化
		template.setValueSerializer(jackson2JsonRedisSerializer);
		// value hashmap序列化
		template.setHashValueSerializer(jackson2JsonRedisSerializer);
		return template;
	}
	
	@Bean
	public ValueOperations<String, Object> valueOperations(RedisTemplate<String, Object> redisTemplate){
		ValueOperations<String, Object> opsForValue =redisTemplate.opsForValue();
		return opsForValue;
	}
	
	//缓存管理器
	@Bean
	public CacheManager cacheManager(LettuceConnectionFactory factory) {
		RedisSerializer<String> redisSerializer = new StringRedisSerializer();
		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);

		// 配置序列化(解决乱码的问题),过期时间30秒
		RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofSeconds(bProperties.getCacheRedisTtl()))
				.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
				.serializeValuesWith(
						RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
				.disableCachingNullValues();
		RedisCacheManager cacheManager = RedisCacheManager.builder(factory).cacheDefaults(config).build();
		return cacheManager;
	}

	/**
	 * 
	 * 1、针对调用方法唯一缓存,使用该方式
	 * 2、该方法只是声明了key的生成策略,还未被使用,需在@Cacheable注解中指定keyGenerator
	 * 3、如: @Cacheable(value = "users", keyGenerator = "keyGenerator")
	 */
	@Bean
	public KeyGenerator keyGenerator() {
		return new KeyGenerator() {
			@Override
			public Object generate(Object target, Method method, Object... params) {
				StringBuffer sb = new StringBuffer();
				sb.append(target.getClass().getName());
				sb.append(method.getName());
				for (Object obj : params) {
					sb.append(JSON.toJSONString(obj).hashCode());
				}
				return sb.toString();
			}
		};
	}
}

工具类

/**
 * Redis工具类
 * 
 * 
 * @author vander
 * @date 2018年11月28日
 */
@Component
public class RedisService {
	
    @Autowired(required=false)
    private RedisTemplate<String, Object> redisTemplate;

    @Autowired(required=false)
    private ValueOperations<String, Object> valueOperations;
    
    
    /**  默认过期时长,单位:秒 */
    public final static long DEFAULT_EXPIRE = 60 * 60 * 24;//1天
    /**  不设置过期时长 */
    public final static long NOT_EXPIRE = -1;
    private final static Gson gson = new Gson();
    
    public void set(String key, Object value, long expire){
        try{
        	valueOperations.set(key, toJson(value));
            if(expire != NOT_EXPIRE){
                redisTemplate.expire(key, expire, TimeUnit.SECONDS);
            }
        }catch (Exception e){
            throw new BException("Redis服务异常",500);
        }
    }

    public void set(String key, Object value){
        try{
            set(key, value, DEFAULT_EXPIRE);
        }catch (Exception e){
            throw new BException("Redis服务异常",500);
        }

    }
    public <T> T get(String key, Class<T> clazz, long expire) {
        String value = null;
        try{
            value = valueOperations.get(key).toString();
            if(expire != NOT_EXPIRE){
                redisTemplate.expire(key, expire, TimeUnit.SECONDS);
            }
        }catch (Exception e){
            throw new BException("Redis服务异常",500);
        }
        return value == null ? null : fromJson(value, clazz);
    }
    
    public <T> T get(String key, Type clazz, long expire) {
        String value = null;
        try{
            value = valueOperations.get(key).toString();
            if(expire != NOT_EXPIRE){
                redisTemplate.expire(key, expire, TimeUnit.SECONDS);
            }
        }catch (Exception e){
            throw new BException("Redis服务异常",500);
        }
        return value == null ? null : fromJson(value, clazz);
    }

    public <T> T get(String key, Class<T> clazz) {
        T t;
        try{
            t = get(key, clazz, NOT_EXPIRE);
        }catch (Exception e){
            throw new BException("Redis服务异常",500);
        }
        return t;
    }
    
    public <T> T get(String key, Type clazz) {
        T t;
        try{
            t = get(key, clazz, NOT_EXPIRE);
        }catch (Exception e){
            throw new BException("Redis服务异常",500);
        }
        return t;
    }

    public String get(String key, long expire) {
        String value = null;
        try{
            value = valueOperations.get(key).toString();
            if(expire != NOT_EXPIRE){
                redisTemplate.expire(key, expire, TimeUnit.SECONDS);
            }
        }catch (Exception e){
            throw new BException("Redis服务异常",500);
        }
        return value;
    }

    public String get(String key) {
        String str = null;
        try{
            str = get(key, NOT_EXPIRE);
        }catch (Exception e){
            throw new BException("Redis服务异常",500);
        }
        return str;
    }

    public void delete(String key) {
        try{
            redisTemplate.delete(key);
        }catch (Exception e){
            throw new BException("Redis服务异常",500);
        }
    }

    /**
     * Object转成JSON数据
     */
    private String toJson(Object object){
        if(object instanceof Integer || object instanceof Long || object instanceof Float ||
                object instanceof Double || object instanceof Boolean || object instanceof String){
            return String.valueOf(object);
        }
        return gson.toJson(object);
    }

    /**
     * JSON数据,转成Object
     */
    private <T> T fromJson(String json, Class<T> clazz){
        return gson.fromJson(json, clazz);
    }
    
    /**
     * JSON数据,转成Object
     */
    private <T> T fromJson(String json,Type clazz){
        return gson.fromJson(json, clazz);
    }

	public boolean hasKey(String key) {
		return redisTemplate.hasKey(key);
	}
}

自定义注解缓存

/**
 * 自定义缓存aop
 * 
 * @author vander
 *
 */
@Component
@Aspect
public class CacheableAspect {

	@Autowired
	RedisService redisService;

	@Pointcut("@annotation(org.pingruan.framework.component.starter.data.redis.custom.VCacheable)")
	public void annotationPointcut() {
	}

	@Around("annotationPointcut()")
	public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
		// 获得当前访问的class
		Class<?> className = joinPoint.getTarget().getClass();
		// 获得访问的方法名
		String methodName = joinPoint.getSignature().getName();
		// 得到方法的参数的类型
		Class<?>[] argClass = ((MethodSignature) joinPoint.getSignature()).getParameterTypes();
		Object[] args = joinPoint.getArgs();
		String key = "";
		long expireTime = 1800; //30分钟
		// 得到访问的方法对象
		Method method = className.getMethod(methodName, argClass);
		Type type = method.getAnnotatedReturnType().getType();
		method.setAccessible(true);
		// 判断是否存在@VCacheable注解
		if (method.isAnnotationPresent(VCacheable.class)) {
			VCacheable annotation = method.getAnnotation(VCacheable.class);
			String keys = annotation.key();
			if (StringUtils.isNotBlank(keys)) {
				key = keys;
			} else {
				key = getKey(className.getName(), methodName, args);
			}
			expireTime = annotation.expireTime();
		}
		// 获取缓存是否存在
		boolean hasKey = redisService.hasKey(key);
		if (hasKey) {
			return redisService.get(key,type);
		} else {
			Object res = joinPoint.proceed();
			redisService.set(key, res, expireTime);
			return res;
		}
	}

	/**
	 * 获取唯一key
	 * 
	 * @param name
	 * @param methodName
	 * @param args
	 * @return
	 */
	private String getKey(String name, String methodName, Object[] args) {
		StringBuffer sb = new StringBuffer();
		sb.append(name).append(".").append(methodName);
		for (Object object : args) {
			sb.append(JSON.toJSONString(object).hashCode());
		}
		return sb.toString();
	}

}

@Target({ ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface VCacheable {
	String key() default ""; 
	long expireTime() default 1800;// 30分钟
}
### Spring Boot 使用 Redis 实现缓存的最佳实践和配置方法 #### 1. 环境准备 确保开发环境中已正确配置并能够访问到 Redis 服务器[^3]。 #### 2. 添加依赖 为了使 Spring Boot 应用程序支持 Redis 缓存,需在 `pom.xml` 文件中加入 Spring Data Redis 依赖启动器。这一步骤允许应用程序与 Redis 数据库交互,从而实现高效的缓存机制[^4]。 ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> ``` #### 3. 配置 Redis 连接 完成依赖添加后,在项目的全局配置文件 `application.properties` 或者 `application.yml` 中指定 Redis 服务的相关参数,比如主机地址、端口号以及密码等信息。这些设置对于建立稳定的数据库连接至关重要。 ```properties # application.properties example spring.redis.host=localhost spring.redis.port=6379 spring.redis.password= ``` 或者采用 YAML 格式的配置: ```yaml # application.yml example spring: redis: host: localhost port: 6379 password: ``` #### 4. 自定义 RedisTemplate 配置 创建一个新的 Java 类来定制化 RedisTemplate 的行为,例如调整键值对的序列化策略。这样做不仅提高了读写的效率,还增强了系统的可维护性和扩展性[^5]。 ```java import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; @Configuration public class RedisConfig { @Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) { RedisTemplate<String, Object> template = new RedisTemplate<>(); template.setConnectionFactory(factory); // 设置key的序列化方式为String类型 template.setKeySerializer(new StringRedisSerializer()); // 设置value的序列化方式为JSON格式 template.setValueSerializer(new GenericJackson2JsonRedisSerializer()); return template; } } ``` #### 5. 使用注解简化缓存逻辑 借助于 Spring 提供的一系列缓存相关注解(如 `@Cacheable`, `@CachePut`, `@CacheEvict`),可以直接标注在业务方法上,以此达到自动化的缓存管理和优化目的。 ```java @Service public class UserService { private final Logger logger = LoggerFactory.getLogger(UserService.class); @Autowired private UserRepository userRepository; /** * 当查询用户不存在时会抛出异常,并不会被缓存; * 如果存在,则会被保存至名为"user"的缓存区域里。 */ @Cacheable(value = "users", unless = "#result == null") public User getUserById(Long id){ logger.info("Finding user by ID {}", id); return userRepository.findById(id).orElseThrow(() -> new RuntimeException("User not found")); } /** 更新或新增一条记录的同时刷新对应的缓存项 */ @CachePut(value="users", key="#user.id") public User saveOrUpdateUser(User user){ logger.info("Saving or updating user with details :{}", user.toString()); return userRepository.save(user); } /** 删除特定用户的缓存条目 */ @CacheEvict(value="users", key="#id") public void deleteUserById(Long id){ logger.info("Deleting user by ID {} from cache.", id); userRepository.deleteById(id); } } ``` 通过以上步骤,能够在 Spring Boot 项目中有效地集成 Redis 并发挥其强大的缓存能力,进而提高整个应用的服务质量和响应速度。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值