创建一个基础的springboot项目,这个我就不多说了,不明白的去搜教程。
pom文件
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.6</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>net.sf.json-lib</groupId>
<artifactId>json-lib</artifactId>
<version>2.4</version>
<classifier>jdk15</classifier>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.58</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>18.0</version>
</dependency>
</dependencies>
application.properties
spring.profiles.active=test
application-test.yml
server:
port: 8899
mybatis:
mapper-locations: classpath*:mapper/*Mapper.xml
#打印sql,不打印结果集(结果集多会造成性能影响)
logging:
level:
com.example.demo.mapper: debug
spring:
redis:
# Redis数据库索引(默认为0)
database: 0
# Redis服务器地址
# 正式服务
host: 127.0.0.1
# password: 123456
password: 123456
# 腾讯云
# host: 62.234.77.34
# Redis服务器连接端口
port: 6379
# Redis超时时间
timeout: 5000
jedis:
pool:
#最大连接数据库连接数,设 0 为没有限制
max-active: 8
#最大等待连接中的数量,设 0 为没有限制
max-idle: 8
#最大建立连接等待时间。如果超过此时间将接到异常。设为-1表示无限制。
max-wait: -1ms
#最小等待连接中的数量,设 0 为没有限制
min-idle: 0
lettuce:
pool:
max-active: 8
max-idle: 8
max-wait: -1ms
min-idle: 0
shutdown-timeout: 100ms
datasource:
driverClassName: com.mysql.jdbc.Driver
username: root
password: 123456
# spring2.0此处为jdbc-url
url: jdbc:mysql://localhost:3306/yfDemo?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true
type: com.alibaba.druid.pool.DruidDataSource
配置Druid的监控,可以在地址:http://127.0.0.1:8899/druid/login.html,那么这里我配置的端口是8899,所有地址后面是项目的端口不是8080
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
@Configuration
public class DruidConfiguration {
@ConfigurationProperties(prefix = "spring.datasource")
@Bean
public DataSource druid(){
return new DruidDataSource();
}
//配置Druid的监控
//1、配置一个管理后台的Servlet
@Bean
public ServletRegistrationBean statViewServlet(){
ServletRegistrationBean bean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");
Map<String,String> initParams = new HashMap<>();
initParams.put("loginUsername","admin");
initParams.put("loginPassword","123456");
initParams.put("allow","");//默认就是允许所有访问
//initParams.put("deny","127.0.0.1");
bean.setInitParameters(initParams);
return bean;
}
//2、配置一个web监控的filter
@Bean
public FilterRegistrationBean webStatFilter(){
FilterRegistrationBean bean = new FilterRegistrationBean();
bean.setFilter(new WebStatFilter());
Map<String,String> initParams = new HashMap<>();
initParams.put("exclusions","*.js,*.css,/druid/*");
bean.setInitParameters(initParams);
bean.setUrlPatterns(Arrays.asList("/*"));
return bean;
}
}
redis配置,这里是通过集合的key判断这个数据存储时长,可实现动态设置失效时间。
import com.alibaba.fastjson.support.spring.FastJsonRedisSerializer;
import com.google.common.collect.ImmutableMap;
import org.springframework.cache.CacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.time.Duration;
import java.util.Map;
@Configuration
public class RedisCacheManage {
@Bean
public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate();
template.setConnectionFactory(factory);
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
//配置使用FastJson进行序列化和反序列化操作
FastJsonRedisSerializer<Object> fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class);
template.setKeySerializer(stringRedisSerializer);
template.setHashKeySerializer(stringRedisSerializer);
template.setValueSerializer(fastJsonRedisSerializer);
template.setHashValueSerializer(fastJsonRedisSerializer);
template.afterPropertiesSet();
//开启Redis事务
/**
* 为何要开启事务呢?redis本身是单线程执行的one by one
* 虽然都是原子性操作,那需要多个原子性的操作才算一个完整的事件呢
* 譬如 redis get set可能就是一对操作才能保证操作的完整性
* 譬如记录访问数 value 从0开始,在并发情况下多线程会同时查询到 value 为0 的情况,
* 此时两线程都得到value 0 为此都 +1操作 执行结果 value=1,而我们期望的结果至少是2才对
* 所以需要用事务来解决这一堆操作(get set)
*/
template.setEnableTransactionSupport(true);
return template;
}
@Bean
public CacheManager cacheManager(RedisTemplate<String, Object> template) {
//配置多个缓存名称 这里我们用个能用的模板 这里有坑点往下看
RedisCacheConfiguration templateRedisCacheCfg = RedisCacheConfiguration
.defaultCacheConfig()
// 设置key为String
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(template.getStringSerializer()))
// 设置value 为自动转Json的Object
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(template.getValueSerializer()))
// 不缓存null
.disableCachingNullValues();
//这里对RedisCacheConfiguration不熟悉的可能会有疑问,我们这样批量templateRedisCacheCfg.entryTtl(Duration.ofMinutes(15))最后不都是同一个引用吗,过期时间会生效吗?
Map<String, RedisCacheConfiguration> expires = ImmutableMap.<String, RedisCacheConfiguration>builder()
.put("1m", templateRedisCacheCfg.entryTtl(Duration.ofMinutes(1)))
.put("30m", templateRedisCacheCfg.entryTtl(Duration.ofMinutes(30)))
.put("60m", templateRedisCacheCfg.entryTtl(Duration.ofMinutes(60)))
.put("1d", templateRedisCacheCfg.entryTtl(Duration.ofDays(1)))
.put("30d", templateRedisCacheCfg.entryTtl(Duration.ofDays(30)))
.put("common-30d", templateRedisCacheCfg.entryTtl(Duration.ofDays(30)))
.build();
RedisCacheManager redisCacheManager =
RedisCacheManager.RedisCacheManagerBuilder
// Redis 连接工厂
.fromConnectionFactory(template.getConnectionFactory())
// 默认缓存配置
.cacheDefaults(templateRedisCacheCfg.entryTtl(Duration.ofHours(1)))
// 配置同步修改或删除 put/evict
.transactionAware()
.initialCacheNames(expires.keySet())
.withInitialCacheConfigurations(expires)
.build();
return redisCacheManager;
}
}
这个时候有一个关键的注解,必须要加,不然redis不起作用,MapperScan注解是扫描mapper接口的。
@MapperScan("com.example.demo.mapper")
@EnableCaching
然后我们写一个接口查询的接口,mybatis我就不细讲了,
@RequestMapping("/redis")
@RestController
/**
* redis类型
* String List Hash Set Zset
*/
public class RedisController {
@Autowired
private RedisService redisService;
@GetMapping("/getCompany")
public List<CompanyModel> getCompany(){
return redisService.getCompany();
}
}
@Service
public interface RedisService {
List<CompanyModel> getCompany();
}
@Service
public class RedisServiceImpl implements RedisService {
@Autowired
private CompanyMapper companyMapper;
@Override
@Cacheable(value = "1m",key = "12")
public List<CompanyModel> getCompany() {
return companyMapper.selectCompanyAll();
}
}
@Repository
public interface CompanyMapper {
List<CompanyModel> selectCompanyAll();
}
<select id="selectCompanyAll" resultType="com.example.demo.model.CompanyModel">
SELECT ID id,CORP_NAME name FROM TC_COMPANY_INFO
</select>
redis Template 方式
其他重要的代码就省略了,看service
@Autowired
public RedisTemplate redisTemplate;
public void test1(){
Coupon coupon = new Coupon();
coupon.setGetTime("2022/1/21");
coupon.setMaoney(20);
coupon.setName("优惠卷");
coupon.setStart(0);
coupon.setTimeOut(1);
coupon.setId(UUID.randomUUID().toString());
couponMapper.inserCoupon(coupon);
String json = JSONObject.toJSONString(coupon);
redisTemplate.opsForValue().set(coupon.getId(),json,40,TimeUnit.SECONDS);
}
在这里我在数据库里面插入了一条数据,然后又将这个数据又存到redis中
可以看到这种格式是有问题的,查询出来会乱码
重写RedisTemplate,防止乱码
import org.springframework.cache.annotation.EnableCaching;
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.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
@EnableCaching
public class redisconfig {
@Bean(name = "redisTemplate")
public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, String> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
template.setHashKeySerializer(new GenericJackson2JsonRedisSerializer());
template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
template.afterPropertiesSet();
return template;
}
}
重写RedisTemplate这个我们再添加一次,就不会存在乱码,我们再看看
redis 延迟队列
就比如我们说的抢票之后不付款,30分钟之后自动把票放出来。比如外卖优惠卷是不是有一个使用时间,如果这个时间过了,我们是不是使用不了,那么也可以用延迟队列来实现。
配置监听
import org.springframework.cache.annotation.EnableCaching;
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.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
@EnableCaching
public class redisconfig {
/**
* TODO 监听 Redis键过期事件
* @author zhuzhen
* @date 14:38 2018/12/21
* @param connectionFactory
* @return org.springframework.data.redis.listener.RedisMessageListenerContainer
*/
@Bean
RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
return container;
}
}
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.listener.KeyExpirationEventMessageListener;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.stereotype.Component;
@Component
public class RedisExpiredListener extends KeyExpirationEventMessageListener {
public RedisExpiredListener(RedisMessageListenerContainer listenerContainer) {
super(listenerContainer);
}
@Override
public void onMessage(Message message, byte[] pattern) {
// 用户做自己的业务处理即可,注意message.toString()可以获取失效的key
System.out.println("message>>> " + message);
System.out.println("pattern>>> " + new String(pattern));
String expiredKey = message.toString();
System.out.println("Redis的键:" + expiredKey);
//具体的逻辑代码
}
}
很简单,只要有失效的,就会被监听到,onMessage方法就会执行,那么我们是不是可以在这里做文章,只对key中存在什么才怎么怎么样。
链接:https://pan.baidu.com/s/1dsAVgAhp6GWSPchplmH6OA
提取码:yyds
这个是源码