自定义注解实现redis与对象相互转换

简介

本文则通过自定义注解的方式,来完成一个hash与POJO之间的转换。目标是为了简化代码结构。
类似的功能,Spring Data Redis是有的。

定义POJO在redis中的数据结构

这里随便定了几个,本文只实现了hash。

public enum RedisStorageStructure {
    SET, // 基本元素
    HASH, // 哈希
    LIST, // 列表
}

定义注解

@Retention(RUNTIME)
@Target(TYPE)
@Inherited
public @interface RedisStorageInjector {
	RedisStorageStructure value() default RedisStorageStructure.HASH;// 默认为hash存储
}

定义存入redis的key值

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD,ElementType.METHOD})
public @interface RedisStorageKey {
}

定义存入redis的非key值

@Retention(RUNTIME)
@Target(ElementType.FIELD)
public @interface RedisStorageElement {
    String value();
}

存储抽象工具类

通过本类

public abstract class RedisStorageUtil {
	protected RedisTemplate<String, String> redisTemplate;

	protected RedisStorageUtil(RedisTemplate<String, String> redisTemplate) {
		this.redisTemplate = redisTemplate;
	}

	public static RedisStorageWrtieUtil writeOps(RedisTemplate<String, String> redisTemplate) {
		return new RedisStorageWrtieUtil(redisTemplate);
	}

	public static RedisStorageReadUtil readOps(RedisTemplate<String, String> redisTemplate) {
		return new RedisStorageReadUtil(redisTemplate);
	}
}

写工具类

public class RedisStorageWrtieUtil extends RedisStorageUtil {

    public RedisStorageWrtieUtil(RedisTemplate<String, String> redisTemplate) {
        super(redisTemplate);
    }

    public void write(Object object) throws IllegalArgumentException, IllegalAccessException, IllegalStateException, InvocationTargetException {

        // 判断o是加了那种类型的注解,hash有hash的处理方式。
        if (object.getClass().isAnnotationPresent(RedisStorageInjector.class)) {
            RedisStorageInjector gg = object.getClass().getAnnotation(RedisStorageInjector.class);
            RedisStorageStructure way = gg.value();
            // TODO 后续扩展其他
            switch (way) {
                case HASH:
                    handleHash(object);
                    break;
                default:
                    throw new IllegalStateException("该类不支持以" + way + "存储进redis");
            }
        } else {
            throw new IllegalStateException("该类不支持存储进redis,请添加 @RedisStorageInjector");
        }
    }

    private void handleHash(Object object) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {

        String key = null;
        Map<String, String> mapStorageStr = new HashMap<>();
        //
        for (Field field : object.getClass().getDeclaredFields()) {

            if (field.isAnnotationPresent(RedisStorageKey.class)) { // 拿key
                field.setAccessible(true);
                key = (String) field.get(object);
            } else if (field.isAnnotationPresent(RedisStorageElement.class)) {
                // 拿到加了注解的域
                RedisStorageElement g = field.getAnnotation(RedisStorageElement.class);
                String value = g.value();// 拿到存进去的域名
                if (value != null && value.length() > 0) {
                    // TODO 目前只实现String类型
                    Object nextObject = field.get(object);
                    if (nextObject == null) {// 默认值
                        nextObject = "";
                    }
                    if (nextObject instanceof String) {
                        mapStorageStr.put(value, nextObject.toString());
                    } else {
                        throw new IllegalStateException(field.getName() + "该值不支持非String存储进redis");
                    }
                } else {
                    throw new IllegalStateException(field.getName() + "值缺少映射关系,无法存储进redis");
                }
            }
        }
        if (key == null) {
            for (Method m : object.getClass().getDeclaredMethods()) {
                if (m.isAnnotationPresent(RedisStorageKey.class)) {
                    m.setAccessible(true);
                    key = m.invoke(object).toString();
                }
            }
        }
        if (key != null) {
            redisTemplate.opsForHash().putAll(key, mapStorageStr);
        } else {
            throw new IllegalStateException(object.getClass().getName() + "缺少key,无法存储进redis");
        }
    }
}

读工具类

public class RedisStorageReadUtil extends RedisStorageUtil {

    public RedisStorageReadUtil(RedisTemplate<String, String> redisTemplate) {
        super(redisTemplate);
    }

    public Object read(Object object) throws IllegalArgumentException, IllegalAccessException, IllegalStateException, InvocationTargetException {

        if (object.getClass().isAnnotationPresent(RedisStorageInjector.class)) {
            RedisStorageInjector gg = object.getClass().getAnnotation(RedisStorageInjector.class);
            RedisStorageStructure way = gg.value();
            // TODO 扩展处理方式
            switch (way) {
                case HASH:
                    handleHash(object);
                    break;
                default:
                    throw new IllegalStateException("该类不支持以" + way + "读取");
            }

        } else {
            throw new IllegalStateException("该类不支持读取redis");
        }
        return object;
    }


    private String getKey(Object object) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
        // 在field上找
        for (Field field : object.getClass().getDeclaredFields()) {// 可以设计为直接获取名为key的域
            if (field.isAnnotationPresent(RedisStorageKey.class)) { // 拿key
                field.setAccessible(true);
                return field.get(object).toString();
            }
        }
        // 在method上找
        for (Method m : object.getClass().getDeclaredMethods()) {
            if (m.isAnnotationPresent(RedisStorageKey.class)) {
                m.setAccessible(true);
                return m.invoke(object).toString();
            }
        }
        return null;
    }

    private void handleHash(Object object, String key) throws IllegalArgumentException, IllegalAccessException {

        if (object == null || key == null) {
            throw new IllegalStateException("无法存取");
        }
        // 读取redis
        Map<Object, Object> map =redisTemplate.opsForHash().entries(key);
        // map转类
        for (Field field:object.getClass().getDeclaredFields()) {
            if (field.isAnnotationPresent(RedisStorageElement.class)) {
                // 拿到加了注解的域
                RedisStorageElement g = field.getAnnotation(RedisStorageElement.class);
                String value = g.value();// 拿到存进去的域名
                if (value != null && value.length() > 0) {
                    String data = (String) map.get(value);
                    field.set(object, data);
                }
            } else if (field.isAnnotationPresent(RedisStorageKey.class)) {
                field.setAccessible(true);
                field.set(object, key);// 设置key
            }
        }
    }

    private void handleHash(Object object) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
        // 拿key,然后读取
        String key = getKey(object);
        handleHash(object, key);
    }
}

使用示例

redis.properties

redis.host=127.0.0.1
redis.port=9479
redis.pass=hello
redis.timeout=15000
redis.usePool=true
redis.maxIdle=6
redis.minEvictableIdleTimeMillis=300000
redis.numTestsPerEvictionRun=3
redis.timeBetweenEvictionRunsMillis=60000

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="  
            http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd  
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

	<context:property-placeholder location="classpath:redis.properties" />
	<context:component-scan base-package="gg.zsw.redis">
	</context:component-scan>
	<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
		<property name="maxIdle" value="100" />
		<!-- <property name="maxActive" value="600" /> -->
		<!-- <property name="maxWait" value="10000" /> -->
		<!-- 最大等待时间 -->
		<property name="maxWaitMillis" value="20000" />
		<property name="testOnBorrow" value="true" />
	</bean>
	<!--<bean id="jdkSerializer"-->
		<!--class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer" />	-->

	<bean id="jdkSerializer"
		class="org.springframework.data.redis.serializer.StringRedisSerializer" />

	<bean id="jedisConnectionFactory"
		class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
		<property name="hostName" value="${redis.host}"></property>
		<property name="port" value="${redis.port}"></property>
	 	<property name="password" value="${redis.pass}"></property>
		<property name="usePool" value="${redis.usePool}"></property>
		<property name="poolConfig" ref="jedisPoolConfig"></property>
	</bean>
	
	<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
		<property name="connectionFactory" ref="jedisConnectionFactory"></property>
		<property name="keySerializer">
			<bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />
		</property>
		<property name="valueSerializer">
			<bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />
		</property>
		<property name="hashKeySerializer">
			<bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />
		</property>
		<property name="hashValueSerializer">
			<bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />
		</property>
	</bean>
</beans>

User类

@RedisStorageInjector(RedisStorageStructure.HASH)
public class User {

//	@RedisStorageKey
	public String key;

	/**
	 * 基本结构,只有存储没有绑定
	 */
	@RedisStorageElement("name")
	public String name;

	@RedisStorageElement("age")
	public String age;

	public String haha;// 这属性没加注解,不存

	@RedisStorageKey
	private String k(){
		return key;
	}
}

测试类

@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@ContextConfiguration(locations = {"classpath*:applicationContext.xml"})
public class Main extends AbstractJUnit4SpringContextTests {

    @Autowired
    protected RedisTemplate<String, String> redisTemplate;

    @Test
    public void test001write() {

        RedisStorageWrtieUtil r = RedisStorageUtil.writeOps(redisTemplate);

        User user = new User();
        user.key = "person";
        user.name = "ada";
        user.age = "18";
        try {
            r.write(user);
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (IllegalStateException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Test
    public void test002read() {
        RedisStorageReadUtil r = RedisStorageUtil.readOps(redisTemplate);
        User user = new User();
        user.key = "person";
        try {
            r.read(user);
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (IllegalStateException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println(user.key + "||name:" + user.name + "|age:" + user.age);
    }
}

运行效果

person||name:ada|age:18

总结

前面输入很多代码,但测试中,仅仅是一个User类,以及RedisStorageWrtieUtil和RedisStorageReadUtil的操作,实际是大大方便了使用,只需要设定好User类中的关系即可。

注解的使用,可以提升可读性,使用也更加方便,可减少重复代码,相当好用。
本文旨在形成一套用注解来解决实际问题的思路。

发布了25 篇原创文章 · 获赞 1 · 访问量 3871

猜你喜欢

转载自blog.csdn.net/a215095167/article/details/105307516
今日推荐