Skip to content

redis

1,整合redis

xml
// pom.xml
<!--        redis-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

<!-- redis连接池-->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
        </dependency>

2,配置redis

yaml
spring:
  redis:
    database: 0 # Redis 数据库索引(默认为 0)
    host: 192.168.161.3 # Redis 服务器地址
    port: 6379 # Redis 服务器连接端口
    password: 123456 # Redis 服务器连接密码(默认为空)
    timeout:  5000  # 连接超时,单位ms
    lettuce:
      pool:
        max-active: 8 # 连接池最大连接数(使用负值表示没有限制) 默认 8
        max-wait: -1 # 连接池最大阻塞等待时间(使用负值表示没有限制) 默认 -1
        max-idle: 8 # 连接池中的最大空闲连接 默认 8
        min-idle: 0 # 连接池中的最小空闲连接 默认 0

3,redis配置类

使用fastjson序列化 解决乱码

java
@Configuration
public class MyRedisConfig {

    @Bean
    public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory redisConnectionFactory){
        RedisTemplate<String,Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
// key 序列化
        redisTemplate.setKeySerializer(new GenericFastJsonRedisSerializer());
//        值序列化
        GenericFastJsonRedisSerializer genericFastJsonRedisSerializer = new GenericFastJsonRedisSerializer();
        redisTemplate.setValueSerializer(genericFastJsonRedisSerializer);
//        hash key 序列化
        redisTemplate.setHashKeySerializer(new GenericFastJsonRedisSerializer());
//        hash value 序列化
        redisTemplate.setHashValueSerializer(genericFastJsonRedisSerializer);
        return redisTemplate;
    }
}

4,测试

java

@SpringBootTest
class ProductApplicationTests {

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    // 存
    @Test
    void contextLoads() {
        User yangliwei = new User("杨利伟", 18);
        redisTemplate.opsForValue().set("user:ylw", yangliwei,20,
                TimeUnit.MINUTES);
    }

    // 取

    @Test
    void redisGetTest(){
        User o = (User)redisTemplate.opsForValue().get("user:ylw");
        System.err.println(o);
    }

}

解决redis redistemplate KEY为字符串是多双引号的问题

java

@Configuration
public class MyRedisConfig {

    @Bean
    public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory redisConnectionFactory){
       ...
// 修改序列化方式
//        redisTemplate.setKeySerializer(new GenericFastJsonRedisSerializer());
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        ...
    }
}

redis 定时任务实现

TIP

  • 使用了redis的发布订阅模式。将redis中key过期作为一个事件发布出来,监听程序收到这key过期的消息。

1,redis增加消息监听器配置

java

@Configuration
public class MyRedisConfig {
        ...
         /**
     * redis 消息监听器
     * @return RedisMessageListenerContainer
     */
    @Bean
    public RedisMessageListenerContainer redisMessageListenerContainer(RedisConnectionFactory redisConnectionFactory){
        RedisMessageListenerContainer redisMessageListenerContainer = new RedisMessageListenerContainer();
        redisMessageListenerContainer.setConnectionFactory(redisConnectionFactory);
        return redisMessageListenerContainer;
    }
}

2,创建redis超时消息监听器

java

// RedisExpirationListener.java

/**
 * redis 监听器
 * 监听redis key 失效事件
 * @author ylw
 */
@Slf4j
@Component
public class RedisExpirationListener extends KeyExpirationEventMessageListener {
    public RedisExpirationListener(RedisMessageListenerContainer listenerContainer) {
        super(listenerContainer);
    }

    /**
     * 针对 redis 数据失效事件,进行数据处理
     * @param message 消息
     * @param pattern 模式
     */
    @Override
    public void onMessage(@NotNull Message message, byte[] pattern) {
        log.info("失效key:{}", message);
        // 判断message是否包含REDIS_TASK_KEY前缀
        if (message.toString().startsWith(Constant.REDIS_TASK_KEY)) {
            // 获取key
            String key = message.toString().replaceAll(Constant.REDIS_TASK_KEY,"");
            try {
                // 反射调用
                JobInvokeUtil.invokeMethod(key);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }else{
            log.info("不包含");
        }
    }
}

3,创建任务调度工具类

java

// JobInvokeUtil.java

/**
 * 定时任务工具类
 * 通过反射调用定时任务方法
 */
public class JobInvokeUtil
{

    /**
     * 执行方法
     *
     */
    public static <T> T invokeMethod(String invokeTarget) throws Exception
    {
        String beanName = getBeanName(invokeTarget);
        if (StringUtils.isEmpty(beanName)) {
            return null;
        }
        String methodName = getMethodName(invokeTarget);
        List<Object[]> methodParams = getMethodParams(invokeTarget);
        if(methodParams==null){
            return null;
        }
        if (!isValidClassName(beanName))
        {
            Object bean = SpringUtils.getBean(beanName);
            return invokeMethod(bean, methodName, methodParams);
        }
        else
        {
            Object bean = Class.forName(beanName).newInstance();
            return invokeMethod(bean, methodName, methodParams);
        }
    }

    /**
     * 调用任务方法
     *
     * @param bean 目标对象
     * @param methodName 方法名称
     * @param methodParams 方法参数
     */
    private static <T> T invokeMethod(Object bean, String methodName, List<Object[]> methodParams)
            throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException,
            InvocationTargetException
    {
        if (StringUtils.isNotNull(methodParams) && methodParams.size() > 0)
        {
            Method method = bean.getClass().getDeclaredMethod(methodName, getMethodParamsType(methodParams));
            return (T) method.invoke(bean, getMethodParamsValue(methodParams));
        }
        else
        {
            Method method = bean.getClass().getDeclaredMethod(methodName);
            return (T) method.invoke(bean);
        }
    }

    /**
     * 校验是否为为class包名
     *
     * @return true是 false否
     */
    public static boolean isValidClassName(String invokeTarget)
    {
        return StringUtils.countMatches(invokeTarget, ".") > 1;
    }

    /**
     * 获取bean名称
     *
     * @param invokeTarget 目标字符串
     * @return bean名称
     */
    public static String getBeanName(String invokeTarget)
    {
        String beanName = StringUtils.substringBefore(invokeTarget, "(");
        return StringUtils.substringBeforeLast(beanName, ".");
    }

    /**
     * 获取bean方法
     *
     * @param invokeTarget 目标字符串
     * @return method方法
     */
    public static String getMethodName(String invokeTarget)
    {
        String methodName = StringUtils.substringBefore(invokeTarget, "(");
        return StringUtils.substringAfterLast(methodName, ".");
    }

    /**
     * 获取method方法参数相关列表
     *
     * @param invokeTarget 目标字符串
     * @return method方法相关参数列表
     */
    public static List<Object[]> getMethodParams(String invokeTarget)
    {
        String methodStr = StringUtils.substringBetween(invokeTarget, "(", ")");
        if (StringUtils.isEmpty(methodStr))
        {
            return null;
        }
        String[] methodParams = methodStr.split("#");
        List<Object[]> classs = new LinkedList<>();
        for (int i = 0; i < methodParams.length; i++)
        {
            String str = StringUtils.trimToEmpty(methodParams[i]);
            // String字符串类型,包含'
            if (StringUtils.contains(str, "'"))
            {
                classs.add(new Object[] { StringUtils.replace(str, "'", ""), String.class });
            }
            // boolean布尔类型,等于true或者false
            else if (StringUtils.equals(str, "true") || StringUtils.equalsIgnoreCase(str, "false"))
            {
                classs.add(new Object[] { Boolean.valueOf(str), Boolean.class });
            }
            // long长整形,包含L
            else if (StringUtils.containsIgnoreCase(str, "L"))
            {
                classs.add(new Object[] { Long.valueOf(StringUtils.replaceIgnoreCase(str, "L", "")), Long.class });
            }
            // double浮点类型,包含D
            else if (StringUtils.containsIgnoreCase(str, "D"))
            {
                classs.add(new Object[] { Double.valueOf(StringUtils.replaceIgnoreCase(str, "D", "")), Double.class });
            }
            // 其他类型归类为整形
            else
            {
                classs.add(new Object[] { Integer.valueOf(str), Integer.class });
            }
        }
        return classs;
    }

    /**
     * 获取参数类型
     *
     * @param methodParams 参数相关列表
     * @return 参数类型列表
     */
    public static Class<?>[] getMethodParamsType(List<Object[]> methodParams)
    {
        Class<?>[] classs = new Class<?>[methodParams.size()];
        int index = 0;
        for (Object[] os : methodParams)
        {
            classs[index] = (Class<?>) os[1];
            index++;
        }
        return classs;
    }

    /**
     * 获取参数值
     *
     * @param methodParams 参数相关列表
     * @return 参数值列表
     */
    public static Object[] getMethodParamsValue(List<Object[]> methodParams)
    {
        Object[] classs = new Object[methodParams.size()];
        int index = 0;
        for (Object[] os : methodParams)
        {
            classs[index] = (Object) os[0];
            index++;
        }
        return classs;
    }
}

4,创建测试类

java

@Test
    public void testRedisTimer(){
        String action = Constant.REDIS_TASK_KEY + "sysRoleService.test({}#{})";
        redisTemplate.opsForValue().set(StrUtil.format(action, 2,1), 1, 10, TimeUnit.SECONDS);
        Object o = redisTemplate.opsForValue().get(StrUtil.format(action, 2,1));
        System.err.println(o);
    }

5,创建实际调用类

java

@Service("sysRoleService")
public class SysRoleServiceImpl implements SysRoleService {
    @Resource
    private SysRoleDao sysRoleDao;

    public void test(Integer arg1,Integer arg2){
        log.info("arg1:{},arg2:{}",arg1,arg2);
    }
}

6,测试结果

java
2023-11-14T10:59:25.837+08:00  INFO 19336 --- [enerContainer-1] c.y.p.common.RedisExpirationListener     : 失效key:task:sysRoleService.test(2#1)
2023-11-14T10:59:25.839+08:00  INFO 19336 --- [enerContainer-1] c.y.p.service.impl.SysRoleServiceImpl    : arg1:2,arg2:1

杨利伟的博客