您的位置:首页 > 数据库 > Redis

spring与redis集成之aop整合方案

2015-01-26 17:45 429 查看
java使用redis缓存可以使用jedis框架,jedis操作简单,没有什么复杂的东西需要学习,网上资料很多,随便看看就会了.

将spring与redis缓存集成,其实也是使用jedis框架,只不过spring对它进行了一层封装,并将这层封装库命名为spring-data-redis.

下面将要使用spring-data-redis与jedis的jar包,并通过spring的aop功能,将redis缓存无缝无侵入的整合进来.

1.先下载好依赖包

[html] view plaincopy





<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-core</artifactId>

<version>4.1.1.RELEASE</version>

</dependency>

<!-- 还有spring的其它包,这里不一一贴出-->

[html] view plaincopy





<dependency>

<groupId>org.springframework.data</groupId>

<artifactId>spring-data-redis</artifactId>

<version>1.4.1.RELEASE</version>

</dependency>

<dependency>

<groupId>redis.clients</groupId>

<artifactId>jedis</artifactId>

<version>2.6.0</version>

</dependency>

2.再配置spring文件

[html] view plaincopy





<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">

<property name="minIdle" value="${redis.minIdle}" />

<property name="maxIdle" value="${redis.maxIdle}" />

<property name="maxTotal" value="${redis.maxActive}" />

<property name="maxWaitMillis" value="${redis.maxWait}" />

<property name="testOnBorrow" value="${redis.testOnBorrow}" />

</bean>

<bean id="jedisConnFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">

<property name="hostName" value="${redis.host}" />

<property name="port" value="${redis.port}" />

<property name="password" value="${redis.password}" />

<property name="usePool" value="true" />

<property name="poolConfig" ref="poolConfig" />

</bean>

<!-- redis template definition -->

<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">

<property name="connectionFactory" ref="jedisConnFactory" />

<property name="keySerializer">

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

</property>

<property name="valueSerializer">

<bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer" />

</property>

<property name="hashKeySerializer">

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

</property>

<property name="hashValueSerializer">

<bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"/>

</property>

</bean>

3.开始编写aop代码

3.1 声明两个注解类,用于定义哪些方法将使用缓存

[java] view plaincopy





@Retention(RetentionPolicy.RUNTIME)

@Target({ElementType.METHOD})

public @interface Cacheable {

public enum KeyMode{

DEFAULT, //只有加了@CacheKey的参数,才加入key后缀中

BASIC, //只有基本类型参数,才加入key后缀中,如:String,Integer,Long,Short,Boolean

ALL; //所有参数都加入key后缀

}

public String key() default ""; //缓存key

public KeyMode keyMode() default KeyMode.DEFAULT; //key的后缀模式

public int expire() default 0; //缓存多少秒,默认无限期

}

[java] view plaincopy





@Retention(RetentionPolicy.RUNTIME)

@Target({ElementType.PARAMETER})

public @interface CacheKey {}

3.2 创建一个Aop拦截器的处理类,用于拦截加了@Cacheable的方法

[java] view plaincopy





@Aspect

@Component

public class CacheableAop {

@Autowired private RedisTemplate redisTemplate;

@Around("@annotation(cache)")

public Object cached(final ProceedingJoinPoint pjp,Cacheable cache) throws Throwable {

String key=getCacheKey(pjp, cache);

ValueOperations<String, Object> valueOper=redisTemplate.opsForValue();

Object value=valueOper.get(key); //从缓存获取数据

if(value!=null) return value; //如果有数据,则直接返回

value = pjp.proceed(); //跳过缓存,到后端查询数据

if(cache.expire()<=0) { //如果没有设置过期时间,则无限期缓存

valueOper.set(key, value);

} else { //否则设置缓存时间

valueOper.set(key, value,cache.expire(),TimeUnit.SECONDS);

}

return value;

}

/**

* 获取缓存的key值

* @param pjp

* @param cache

* @return

*/

private String getCacheKey(ProceedingJoinPoint pjp,Cacheable cache) {

StringBuilder buf=new StringBuilder();

buf.append(pjp.getSignature().getDeclaringTypeName()).append(".").append(pjp.getSignature().getName());

if(cache.key().length()>0) {

buf.append(".").append(cache.key());

}

Object[] args=pjp.getArgs();

if(cache.keyMode()==KeyMode.DEFAULT) {

Annotation[][] pas=((MethodSignature)pjp.getSignature()).getMethod().getParameterAnnotations();

for(int i=0;i<pas.length;i++) {

for(Annotation an:pas[i]) {

if(an instanceof CacheKey) {

buf.append(".").append(args[i].toString());

break;

}

}

}

} else if(cache.keyMode()==KeyMode.BASIC) {

for(Object arg:args) {

if(arg instanceof String) {

buf.append(".").append(arg);

} else if(arg instanceof Integer || arg instanceof Long || arg instanceof Short) {

buf.append(".").append(arg.toString());

} else if(arg instanceof Boolean) {

buf.append(".").append(arg.toString());

}

}

} else if(cache.keyMode()==KeyMode.ALL) {

for(Object arg:args) {

buf.append(".").append(arg.toString());

}

}

return buf.toString();

}

}

4.使用缓存示例

[java] view plaincopy





@Service

@Transactional

public class DemoServiceImpl implements DemoService {

@Autowired private DemoDao demoDao;

public List<Demo> findAll() {

return demoDao.findAll();

}

/*

对get()方法配置使用缓存,缓存有效期为3600秒,缓存的key格式为:{package_name}.DemoServiceImpl.get

同时为参数配置了@CacheKey后,表示此参数的值将做为key的后缀,此例的key,最终是:{package_name}.DemoServiceImpl.get.{id}

可以为多个参数配置@CacheKey,拦截器会调用参数的toString()做为key的后缀

若配置多个@CacheKey参数,那么最终的key格式为:{package_name}.{class_name}.{method}.{arg1}.{arg2}.{...}

*/

@Cacheable(expire=3600)

public Demo get(@CacheKey String id) {

return demoDao.get(id);

}

public Demo getByName(String name) {

return demoDao.getByName(name);

}

}

若为名称相同的方法配置缓存,可以在@Cacheable中加入key属性,追加额外的key后缀

@Cacheable还有一个KeyMode属性,用于配置哪些参数可以追加到key后缀中,
默认取值 DEFAULT:表示只有加了@CacheKey的参数才能追加到key后缀
BASIC:自动将基本类型追加到key后缀,而无需再配置@CacheKey
ALL:自动将所有参数追加到lkey后缀,而无需再配置@CacheKey

这只是一个初步整合方案,测试可行,还未在生产中使用,实际效果待验正.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: