Springboot-03:Panic Buying Project 整合Redis+缓存Key

集成Redis

Jedis :jedis就是集成了redis的一些命令操作,封装了redis的java客户端。提供了连接池管理。一般不直接使用jedis,而是在其上在封装一层,作为业务的使用。

  • 添加Jedis依赖
    <dependency>
    	<groupId>redis.clients</groupId>
    	<artifactId>jedis</artifactId>
    	<version>2.7.3</version>
    </dependency>
  • 添加Fastjson依赖
    <dependency>
    	<groupId>com.alibaba</groupId>
    	<artifactId>fastjson</artifactId>
    	<version>1.2.38</version>
    </dependency>
  • 添加redis的配置项
#redis  配置服务器等信息
redis.host=127.0.0.1
redis.port=6379
redis.timeout=10
//redis.password=123456
redis.poolMaxTotal=1000
redis.poolMaxldle=500
redis.poolMaxWait=500
  • 新建一个包redis,在里面新建一个RedisConfig类
@Component
	@ConfigurationProperties(prefix="redis")//将application.properties里面前缀redis都读取
    @lombok
	public class RedisConfig {
	private String host;
	private int port;
	private int timeout;
	private String password;
	private int poolMaxTotal;
	private int poolMaxldle;
	private int poolMaxWait;
}

@ConfigurationProperties(prefix=“redis”)指定配置文件里面前缀为"redis"的配置项,与配置项里面的属性对应起来。

  • 再在redis包中创建RedisPoolFactory类,RedisPoolFactory 通过配置文件,生成Jedis连接池(配置),方便在RedisService中调用。
@Service
	public class RedisPoolFactory {
	@Autowired
	RedisConfig redisConfig;
	//JedisPool的实例注入到spring容器里面
		@Bean
		public JedisPool JedisPoolFactory() {
			JedisPoolConfig poolConfig=new JedisPoolConfig();
			System.out.println("redisConfig.getPoolMaxldle():"+redisConfig.getPoolMaxldle());
			System.out.println("redisConfig.getPoolMaxTotal():"+redisConfig.getPoolMaxTotal());
			System.out.println("redisConfig.getPoolMaxWait():"+redisConfig.getPoolMaxWait());
			System.out.println("redisConfig.getPassword():"+redisConfig.getPassword());
			poolConfig.setMaxIdle(redisConfig.getPoolMaxldle());
			poolConfig.setMaxTotal(redisConfig.getPoolMaxTotal());
			poolConfig.setMaxWaitMillis(redisConfig.getPoolMaxWait()*1000);//s-->ms
			//因为我们使用的是s(秒)来配置的,而源码使用的是ms(毫秒),所以转换一下
			JedisPool jp=new JedisPool(poolConfig,redisConfig.getHost(),redisConfig.getPort(),
					redisConfig.getTimeout()*1000,redisConfig.getPassword(),0);
			return jp;
			
		}
}

注意:RedisPoolFactory 通过配置文件,生成Jedis连接池(配置),方便在RedisService中调用。

  • 再在redis包中创建RedisService类来提供所有关于redis的服务方法。

注意:该类中封装了常用的Redis 方法操作。

  1. public T get(KeyPrefix prefix,String key,Class data) 根据key取得缓存中值(根据传入的前缀)
  2. public boolean delete(KeyPrefix prefix,String key) 删除key
  3. public boolean set(KeyPrefix prefix,String key,T value) 根据key设置缓存中值
  4. public Long decr(KeyPrefix prefix,String key) 自减
  5. public Long incr(KeyPrefix prefix,String key) 自增
  6. public boolean exitsKey(KeyPrefix prefix,String key) 是否存在key
  7. public static T stringToBean(String s,Class clazz)
  8. public static String beanToString(T value)

但是其中,存入redis 的是String类型,这个时候我们的数据不一定是String类型,所以需要类型转换,将数据转换成String格式存入。
BeanToString()这个方法,就是来转化,先获取传入数据的Class类型,根据类型判断,int,long,String 类型,通过API转换直接转换成String即可,或是其他的自定义对象,则利用fastjson库将我们项目中定义的JavaBean 对象,转化为json字符串。StringToBean() 相当于上个是反方法。都需要传入一个类型,这样才知道将字符串转换为什么对象。

通用缓存Key的设计与封装

为什么要这个通用缓存Key?
当项目中的模块越来越多的时候,需要存的缓存也越来越多,比如商品Id,订单Id,用户id等,此时若是id出现重复,将给系统带来错误。那么使用KeyPrefix来更好的操作和管理缓存中对应的key。给不同模块的key带有一个前缀。

不加前缀的情况:

public <T> boolean set(String key,T value){
		Jedis jedis=null;
		//在JedisPool里面取得Jedis
		try {
			jedis=jedisPool.getResource();
			//将T类型转换为String类型
			String s=beanToString(value);
			if(s==null) {
				return false;
			}
			jedis.set(key, s);
			return true;
		}finally {
			returnToPool(jedis);
		}
	}

解决覆盖key的办法:将前缀+一个key一起作为一个redis里面真正的key,这样不同模块之间就不会重复。
好处:不同功能或者不同模块的前缀不同,即使有同名key出现,那么前缀不同,并不会引起key冲突被其他功能覆盖的情况。

使用一个模板模式来封装:接口<---抽象类<---实现类

接口:

/**
 *做缓存的前缀接口 
 */
	public interface KeyPrefix {
	//有效期
	public int expireSeconds();
	//前缀
	public String getPrefix();
}

BasePrefix 抽象类:简单的实现一下KeyPrefix(定义成抽象类原因:防止不小心被创建)我们不希望BasePrefix被实例化。我们只希望它被继承。不同模块的前缀类都继承他。

//定义成抽象类
	public abstract class BasePrefix implements KeyPrefix{
	private int expireSeconds;
	private String prefix;
	public BasePrefix() {
	}
	public BasePrefix(String prefix) {
		//this(0, prefix);//默认使用0,不会过期
		this.expireSeconds=0;
		this.prefix=prefix;		
	}
	public BasePrefix(int expireSeconds,String prefix) {//覆盖了默认的构造函数
		this.expireSeconds=expireSeconds;
		this.prefix=prefix;
	}
	//默认为0代表永不过期
	public int expireSeconds() {
		return expireSeconds;
	}
	//前缀为类名:+prefix
	public String getPrefix() {
		String className=getClass().getSimpleName();
		return className+":"+prefix;
	}
	
}

注意:该类2种不同构造方法:用于继承。一个只带前缀名,一个带前缀名和过期时间。当实现public BasePrefix(String prefix)的时候,我们将默认这个key不会失效,因为有一些场景,我们不希望key失效,但是有些场景我们需要设置key的合适的有效期。

具体实现类:用户UserKey只去继承了super(prefix),即public BasePrefix(String prefix),那么代表user的key的过期时间为不会过期。

public class UserKey extends BasePrefix{
	public UserKey(String prefix) {
		super(prefix);
	}
	public static UserKey getById=new UserKey("id");
	public static UserKey getByName=new UserKey("name");
}

秒杀用户的MiaoshaUserKey ,继承了super(expireSeconds,prefix),可以设置有效期时间为2天。

    public class MiaoshaUserKey extends BasePrefix{
	public static final int TOKEN_EXPIRE=3600*24*2;//3600S*24*2    =2天
	public MiaoshaUserKey(int expireSeconds,String prefix) {
		super(expireSeconds,prefix);
	}
	public static MiaoshaUserKey token=new MiaoshaUserKey(TOKEN_EXPIRE,"tk");
	//对象缓存一般没有有效期,永久有效
	public static MiaoshaUserKey getById=new MiaoshaUserKey(0,"id");
	}

具体实现类的具体使用场景:

/**
	 *避免key被不同类的数据覆盖 
	 *使用Prefix前缀-->不同类别的缓存,用户、部门、
	 */
	@RequestMapping("/redis/set")
	@ResponseBody
	public Result<Boolean> redisSet() {//0代表成功		
		User user=new User(1,"1111");
		boolean f=redisService.set(UserKey.getById,""+1,user);
		return Result.success(true);
	}
	@RequestMapping("/redis/getbyid")
	@ResponseBody
	public Result<User> redisGetById() {//0代表成功		
		User res=redisService.get(UserKey.getById,""+1,User.class);
		//redisService.get("key1",String.class);
		//System.out.println("res:"+userService.tx());
		return Result.success(res);
	}

完善后的get和set缓存的Key值的方法:

public <T> T get(KeyPrefix prefix,String key,Class<T> data){
		Jedis jedis=null;
		//在JedisPool里面取得Jedis
		try {
			jedis=jedisPool.getResource();
			//生成真正的key  className+":"+prefix;  BasePrefix:id1
			String realKey=prefix.getPrefix()+key;
			//System.out.println("jedis:"+jedis);
			String sval=jedis.get(realKey);
			//将String转换为Bean入后传出
			T t=stringToBean(sval,data);
			return t;
		}finally {
			returnToPool(jedis);
		}
	}
	/**
	 * 设置单个、多个对象
	 */						
	 //MiaoshaUserKey.token, token, user
	public <T> boolean set(KeyPrefix prefix,String key,T value){		
		Jedis jedis=null;
		try {//在JedisPool里面取得Jedis
			jedis=jedisPool.getResource();
			String realKey=prefix.getPrefix()+key;		
			String s=beanToString(value);		
			if(s==null||s.length()<=0) {
				return false;
			}
			int seconds=prefix.expireSeconds();
			if(seconds<=0) {//有效期:代表不过期,这样才去设置
				jedis.set(realKey, s);
			}else {//没有设置过期时间,即没有设置有效期,那么自己设置。
				jedis.setex(realKey, seconds,s);				
			}
			return true;
		}finally {
			returnToPool(jedis);			
		}
	}

getPrefix(通过反射再getsimpleclass得到前缀)然后再加在key的前面。当然前缀里也可以设置expire时间

关于token:

猜你喜欢

转载自blog.csdn.net/qq_43378019/article/details/113530465