Redis实现自动预热缓存

84 阅读3分钟

说明: 我们有可能遇到,当我们项目启动的时候,我们就想预热一部分的缓存的场景,所以我们要创建在项目启动时就加载缓存的模块!

image.png

image.png

1. 定义缓存的抽象类AbstractCache

定义数据预热的规则

package com.ssm.redis.init;

import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;

//@Component把类注入到spring容器中使用Component(Service Repository/Mapper Controller)
//@Configuration用于定义配置类,可替换xml配置文件,被注解的类内部包含有一个或多个被@Bean注解的方法
@Component
public abstract class AbstractCache { //只定义预热缓存用到的方法
    public abstract void initCache(); //初始化缓存

    public abstract <T> T getCache();//获取cache (返回值任意类型,定义泛型T,要先声明<T>)


    public abstract void clearCache();//清除缓存

    public void reloadCache() { //重载缓存
        clearCache();
        initCache();
    }

}

2. 实现需要进行缓存的类

说明: 此处实现缓存的类在业务模块中创建!

image.png

package com.ssm.user.cache;

import com.ssm.redis.init.AbstractCache;
import com.ssm.redis.util.RedisUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class UserCache extends AbstractCache {

    private static final String USER_CACHE_KEY = "USER";

    @Autowired
    private RedisUtil redisUtil;

    @Override
    public void initCache() {
        //可以跟数据库联动
        redisUtil.set(USER_CACHE_KEY, "18");
    }

    @Override
    public <T> T getCache() {
        if(!redisUtil.exist(USER_CACHE_KEY)) { //不存在重新加载
            reloadCache();
        }
        return (T) redisUtil.get(USER_CACHE_KEY); //存在,直接返回
    }

    @Override
    public void clearCache() {
        redisUtil.del(USER_CACHE_KEY);
    }
}

3. 定义SpringContextUtil类来获取ApplicationContext

说明: 当一个类实现了ApplicationContextAware接口之后,这个类就可以方便的获得ApplicationContext对象(Spring上下文),Spring发现某个Bean实现了ApplicationContextAware接口,Spring容器会在创建该Bean之后,自动调用该Bean的setApplicationContext(参数)方法,调用该方法时,会将容器本身ApplicationContext对象作为参数传递给该方法。

package com.ssm.tool;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.Map;

@Component
public class SpringContextUtil implements ApplicationContextAware {

    private static ApplicationContext applicationCtxt;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        //实现接口后,会执行该set方法。我们在这里把applicationContext对象赋值到自己创建的applicationCtxt对象上
        applicationCtxt = applicationContext;
    }

    public static ApplicationContext getApplicationCtxt() {
        //applicationCtxt为private,外部想调用要二次封装get方法
        return applicationCtxt;
    }

    public static Object getBean(Class type) throws BeansException {
        //getBean 获取指定类的上下文(属性、方法等)
        return applicationCtxt.getBean(type);
    }

4. 启动并初始化缓存InitCache

说明: 在使用SpringBoot构建项目时,我们通常有一些预先数据的加载。那么SpringBoot提供了CommandLineRunner方式来实现,CommandLineRunner是一个接口,我们需要时,只需实现该接口就行(如果存在多个加载的数据,我们也可以使用@Order注解来排序)

package com.ssm.redis.init;

import com.ssm.tool.SpringContextUtil;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import java.util.Map;
import java.util.Map.Entry;

@Component
@ConditionalOnProperty(name = {"init.cache.enable"}, havingValue = "true") //符合规则的模块才预热数据
public class initCache implements CommandLineRunner {

    @Override
    public void run(String... args) throws Exception { //项目启动后会立即执行该方法(数据初始化、发送通知或者执行一些定时任务)

        //预热数据,抽象类AbstractCache有实现类,利用SpringContextUtil获取AbstractCache类的所有实现类,遍历即可

        ApplicationContext applicationContext = SpringContextUtil.getApplicationCtxt();

        //getBeansOfType获取一个接口下所有实现类
        Map<String, AbstractCache> beansMap = applicationContext.getBeansOfType(AbstractCache.class);

        if(!beansMap.isEmpty()) {
            for(Entry<String, AbstractCache> bean : beansMap.entrySet()) {
                // 获取AbstractCache的子类(实现缓存方法的类),并调用其initCache()方法
                AbstractCache abstractCache = (AbstractCache) SpringContextUtil.getBean(bean.getValue().getClass());
                abstractCache.initCache();
            };
        }

        System.out.println("缓存成功....");
    }
}