模拟一个redis缓存组件启动器

starter是springboot的一个启动器,引入这个启动器就可以使用想用的功能。

类似spring-boot-starter-web等功能,现在模拟一个redis缓存启动器,实现功能:

其他项目引入这个组件依赖,在配置文件中加上redis连接配置后

1.可以直接使用redis工具类操作redis存数据,不用在另外的项目中再弄一个工具类

2.可以在自定义缓存注解在指定方法上存放方法返回结果,不用代码中嵌入缓存存储逻辑

3.spring项目和spring-boot项目都可以使用,方式略有不同

缺点:

组件比较简单,只有redis一层缓存,没有设计redis,mongdb,encache的多级缓存

关于redis集群下组件使用待完善

 

spring项目使用:注意开启了动态代理,注意方法上有自定义缓存注解,注意扫描了依赖项目的包

public class App2 {

	public static void main(String[] args) {
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
		System.out.println(context.getBean(RedisProperties.class));
		System.out.println(context.getBean(RedisCacheAspect.class));
		System.out.println(context.getBean(RedisClient.class));
		
		Environment environment = context.getBean(Environment.class);
		System.out.println("-----"+environment.getProperty("my.redis.host"));
		
		System.out.println("---" + context.getBean(RedisProperties.class).getPort());
		
		RedisToolUtils.set("qq", "qq");
		
		RedisToolUtils.set("day00ForProperties.TestServiceImpl2.testServiceMethod.[]", "hahaha");
		
		TestServiceImpl2 testServiceImpl = context.getBean(TestServiceImpl2.class);
		testServiceImpl.testServiceMethod();
		testServiceImpl.testServiceMethod();
		testServiceImpl.testServiceMethod();
		context.close();
		System.out.println("----end-----");
	}
}
@Configuration
@ComponentScan(value={"com.my.springboot.spring_boot_starter_redis","day00ForRedisCacheStarter"})
@EnableAspectJAutoProxy
public class MyConfig {

}

 

@Service
public class TestServiceImpl2 {

	@RedisCache(expireTime=3600)
	public String testServiceMethod() {
		System.out.println("我即将查询数据库了");
		return "hahaha";
	}
}

 

spring-boot项目使用:同样扫描依赖包,有redis连接配置,有自定义注解

@SpringBootApplication

/**
 * 加载其他项目下的bean
 * @author yp-tc-m-7129
 */
@ComponentScan(value={"com.my.springboot.spring_boot_starter_redis.bean","com.my.springboot.starter"})
public class App {

	public static void main(String[] args) {
		ConfigurableApplicationContext context = SpringApplication.run(App.class, args);
		System.out.println(context.getBean(RedisCacheAspect.class));
		System.out.println(context.getBean(RedisClient.class));
		System.out.println(context.getBean(RedisProperties.class));
		
		
		
//		context.getBean(Jedis.class).set("hh", "567890");
//		System.out.println(context.getBean(Jedis.class).get("hh"));
		TestServiceImpl2 testServiceImpl = context.getBean(TestServiceImpl2.class);
    	testServiceImpl.testServiceMethod();
    	testServiceImpl.testServiceMethod();
    	testServiceImpl.testServiceMethod();
    	
    	RedisToolUtils.set("mm", "mm");
    	context.close();
	}
}

 

@Service
public class TestServiceImpl2 {

	@RedisCache(expireTime=3600)
	public String testServiceMethod() {
		System.out.println("我即将查询数据库了");
		return "hahaha";
	}
}
#zidingyi redis huancun peizhi
my.redis.host=127.0.0.1
my.redis.port=7011
#my.redis.database=0
#my.redis.timeout=2000
#my.redis.maxActive=8
#my.redis.maxWait=-1
#my.redis.maxIdle=5
#my.redis.minIdle=0

 

组件主要代码:

一个切面,拦截有这个自定义注解的方法,redis存储形式是key,json字符串形式

@Component
@Aspect
public class RedisCacheAspect {

	@Autowired
	private Environment env;
	
	/**
	 * 标记RedisCache注解当做切点
	 */
	@Pointcut("@annotation(com.my.springboot.spring_boot_starter_redis.bean.RedisCache)")
	public void pointcut() {
		
	}
	
	/**
	 * 获取缓存的key key 定义在注解上,支持SPEL表达式
	 * 
	 * @param pjp
	 * @return
	 */
	private String getSelDefKey(ProceedingJoinPoint pjp) {
		// 获取类名-全路径
		String classType = pjp.getTarget().getClass().getName();
		//获取方法名
		String methodName = pjp.getSignature().getName();
		//获取字符串数组的参数
		Object[]  paramterArray = (Object[]) pjp.getArgs();
		String paramterStr = Arrays.toString(paramterArray);
		//组装唯一key
		String key = classType + "." + methodName + "." + paramterStr;
		return key;
	}
	
	@Around("pointcut()")
	public Object redisCacheMethod(ProceedingJoinPoint pjp) throws Throwable {
		//全局统一配置,用于redis集群挂机后,标记为不再使用缓存
		String openFlag = env.getProperty("my.redis.cache.isOpen") == null ? "true" : env.getProperty("my.redis.cache.isOpen");
		Boolean cacheEnable = Boolean.valueOf(openFlag);
		// 判断是否需要拦截
		if (!cacheEnable) {
			try {
				pjp.proceed();
			} catch (Throwable e) {
				e.printStackTrace();
			}
		}
		/**
		 * 把方法所在全路径+方法名+参数作为一个关键key,不会重复
		 */
		String key = getSelDefKey(pjp);
		
		//获取注解参数
		Method method = getMethod(pjp);
		RedisCache redisCache = method.getAnnotation(RedisCache.class);
		
		// 获取方法的返回类型,让缓存可以返回正确的类型
		Class returnType = ((MethodSignature) pjp.getSignature()).getReturnType();
		Object result = RedisToolUtils.get(key);
		if (result == null) {
			result = pjp.proceed();
			if(result !=null){
				//只有数据库返回对象才会存储缓存,避免无用的null字符串对象遭受攻击造成redis服务器的内存移除
				String temp = com.alibaba.fastjson.JSONObject.toJSONString(result);
				//存储类型采用json字符串
				RedisToolUtils.setex(key, redisCache.expireTime(), temp);
			}
		} else {
			//找到直接返回缓存的结果,转为可用的类型
			result = JSONObject.parseObject((String) result, returnType);
		}
		return result;
	}
	
	/**
	 * 获取被拦截方法对象
	 * MethodSignature.getMethod() 获取的是顶层接口或者父类的方法对象 而缓存的注解在实现类的方法上
	 * 所以应该使用反射获取当前对象的方法对象
	 */
	public Method getMethod(ProceedingJoinPoint pjp) {
		MethodSignature signature = (MethodSignature) pjp.getSignature();
		Method method = signature.getMethod();
		return method;
	}
}

 

具体代码参见git地址:欢迎大家使用和提bug,有疑问qq联系

https://github.com/zhangmin1992/redis-cache-starter

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值