关于JAVA的redis客户端的实现

来源:互联网 发布:淘宝首页服务 编辑:程序博客网 时间:2024/05/23 22:43

最近在研究java的redis使用,尤其是在spring中的使用,总结一下。

首先java的redis客户端比较好用的,也是比较常用的是Jedis。
关于Jedis需要注意的有2点:
1. Jedis不是线程安全的。也就是说当多个线程同时使用Jedis的同一个实例的时候会出现并发问题。所以Jedis提供了一个线程池JedisPool,这个我们在后面的代码中也会使用到。
2. JedisPool需要你去手动释放其中的Jedis对象,通过你去调用returnResource方法或者returnBrokenResource方法

另外在我们的实例中,因为我们不想去每次调用jedis时都写一遍释放jedis对象的方法,所以我们利用了cglib做了一个AOP,去截获所有的jedis方法,并给它加上一个释放对象的后缀。

前提基本上说完,下面我们直接来看代码。
代码主要有两个类,EnhanceRedis和RedisCacheSupport。
其中,EnhanceRedis负责Jedis的初始化,以及AOP部分
RedisCacheSupport负责具体业务的实现,比如setValue,getkey之类

//Component标签,表示这个类会被自动扫描//可以看到这个类实现了很多接口,其中FactoryBean<EnhancerRedis>是为了实现生产bean,以便于可以自动装配(@autowired)EnhancerRedis的实例//MethodInterceptor是cglib的一个接口,用来实现动态代理,从而截获jedis方法,达到AOP//JedisCommands,MultiKeyCommands, AdvancedJedisCommands等等xxxxCommands都是Jedis的接口,用来实现不同的redis命令行@Componentpublic class EnhancerRedis implements FactoryBean<EnhancerRedis>,  MethodInterceptor, JedisCommands,    MultiKeyCommands, AdvancedJedisCommands, ScriptingCommands,    BasicCommands, ClusterCommands{    //这一部分我们通过value标签,在properties文件里面定义了redis的一些参数,比如host,最大等待时间    @Value("${redis.maxTotal}")    private int maxTotal;    @Value("${redis.maxWaitMillis}")    private int maxWaitMillis;    @Value("${redis.maxIdle}")    private int maxIdle;    @Value("${redis.minIdle}")    private int minIdle;    @Value("${redis.timeBetweenEvictionRunsMillis}")    private int timeBetweenEvictionRunsMillis;    @Value("${redis.hostName}")    private String host;    @Value("${redis.port}")    private int port;    @Value("${redis.password}")    private String password;    //Jedis的线程池    private JedisPool pool = null;    //PostConstruct标签表示,被@PostConstruct修饰的方法会在服务器加载Servle的时候运行,并且只会被服务器执行一次。PostConstruct在构造函数之后执行,servlet的init()方法之前执行。    @PostConstruct    public void init() {        //对JedisPool的一些参数设置        JedisPoolConfig config = new JedisPoolConfig();        config.setMaxTotal(maxTotal);        config.setMaxWaitMillis(maxWaitMillis);        config.setMaxIdle(maxIdle);        config.setMinIdle(minIdle);        config.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);        if(password == null || password.trim().equals("")) {            pool = new JedisPool(config, host, port);        } else {            pool = new JedisPool(config, host, port, Protocol.DEFAULT_TIMEOUT, password);        }    }    //这个方法继承自FactoryBean接口,返回的是这个类通过beanfactory所获得的实例    @Override    public EnhancerRedis getObject() throws Exception {        //Enhancer类来自cglib,用来创建一个代理(create方法),同时绑定委托类(setSuperclass),以及指定代理类(setCallback)。这里委托类和代理类都是EnhancerRedis本身。        Enhancer enhancer = new  Enhancer();        enhancer.setSuperclass(EnhancerRedis.class);        enhancer.setCallback(this);        return (EnhancerRedis) enhancer.create();    }   //这个方法继承自FactoryBean接口,表明这个类返回的bean的类型为EnhancerRedis    @Override    public Class<EnhancerRedis> getObjectType() {        return EnhancerRedis.class;    }    //这个方法继承自FactoryBean接口,表示创建的bean是否是单例    @Override    public boolean isSingleton() {        return true;    }    //这个方法继承自MethodInterceptor,用来拦截所有EnhancerRedis类的方法    //因为上面的getObject()方法创建的代理,委托类和代理类都是EnhancerRedis类自身。所以这里相当于EnhancerRedis类自身拦截自身的方法,并替换成intercept里面的内容    //这个方法主要做了两件事:1.把EnhancerRedis的实例执行的方法,改成由JedisPool里面的一个实例来执行,因为EnhancerRedis继承了一堆Jedis的接口,所以EnhancerRedis实例的方法跟JedisPool里面jedis实例的方法一致。这样做的好处是外部是不知道有JedisPool这个线程池存在的,也不需要关心它是否存在    //2. 在方法执行后通过returnRedis方法释放了JedisPool里面的jedis实例,而不需要外部调用者去手动释放,同样外部调用者也感知不到释放实例,也不需要关心它是否存在    //总之通过这个类,外部调用者可以用Jedis的线程池来调用jedis的方法,但是又无需关心Jedis线程池的存在    @Override    public Object intercept(Object obj, Method method, Object[] args,            MethodProxy proxy) throws Throwable {        Jedis redis = getRedis();        Object result = null;        try {            //获取到截获的方法(根据方法名和参数类型)            Method methodRedis = redis.getClass().getMethod(method.getName(), method.getParameterTypes());            //实际执行时,从JedisPool里面获取一个实例来执行方法(Jedis redis = getRedis())            result = methodRedis.invoke(redis, args);            //执行完毕后,释放JedisPool的实例            returnRedis(redis);        } catch (Exception e) {            //如果出现错误,强制释放JedisPool的实例            returnBrokenRedis(redis);        }        return result;    }    //从Jedis的线程池中去一个实例    public Jedis getRedis() {        return pool.getResource();    }    //释放线程池中指定的实例    public void returnRedis(Jedis jed){        pool.returnResource(jed);    }    //释放线程池中指定的实例    public void returnBrokenRedis(Jedis jed){        pool.returnBrokenResource(jed);    }    //redis的操作接口,并不需要实现, 最终将用jedis的实例来操作    //下面有大量的方法,都继承自XXXCommands接口,都是redis的具体命令    //而这些方法我们都没有具体去实现,因为这些方法实际上都不会走到它自身的方法体,而是通过intercept被截获,最终走的JedisPool里面jedis实例的方法,而jedis实例的方法则不是空的,也不会return null,所以我们无需去实现这里的这些方法    @Override    public String clusterNodes() {        // TODO Auto-generated method stub        return null;    }    @Override    public String clusterMeet(String ip, int port) {        // TODO Auto-generated method stub        return null;    }

EnhancerRedis类讲完,这个类主要是对jedis的初始化和封装。
下面说一下redis的实际操作类RedisCacheSupport :

public class RedisCacheSupport {    final static Logger LOGGER = LoggerFactory.getLogger(RedisCacheSupport.class);    protected static final String DEFAULT_CHARSET = "utf-8";    //因为EnhancerRedis类已经实现了接口FactoryBean,所以我们可以使用Autowired进行自动装配。而且这样装配出来的bean的实例,实际上走的方法是EnhancerRedis的getObject()方法,也就是说实际上通过Autowired自动装配,这里EnhancerRedis的redis已经绑定了委托类和指定了代理类,这里的redis实例实际上已经是一个proxy    @Autowired    protected EnhancerRedis redis;    //这里的两个常量,用作redis存储的key的前缀,来区分不同的业务    /* 通用缓存KEY前缀 */    private static final String KEY_COMM = "comm_";    /* 团队数据缓存前缀 */    private static final String KEY_TEAM = "team_";    //下面几个方法是redis最简单的使用(String类型存储)    //需要注意的是传进去的key要加前缀来区分业务,比如key=KEY_COMM + "001"    /**     * 根据key判断是否存在,存在返回true,反之返回false     * @param key     * @return     */    public boolean exists(String key) {        return redis.exists(key);    }    /**     * 根据Key获取缓存中的值     * @param key     * @return     */    public String get(String key) {        return redis.get(key);    }    /**     * 设置值到缓存中     * @param key   缓存的键     * @param value 缓存的值     */    public void put(String key, String value) {        redis.set(key, value);    }    /**

EnhancerRedis类的代码可以看这里:http://download.csdn.net/download/l00149133/10158682
RedisCacheSupport类非常简单,不再给出代码。

原创粉丝点击