`
edwardpro
  • 浏览: 303698 次
  • 性别: Icon_minigender_1
社区版块
存档分类
最新评论

用SPRING AOP实现主动缓存

阅读更多

缓存的实现最简单的模式,我称作为get set invalid 法,这也是缓存中最常用的一种模式,以前实现缓存(特别是后加)一般都会写一大堆诸如:

 

Object target = cache.get(KEY)
if(target!=null){
//do something
}else{
//do set
}

 这个其实很不好看,代码很不优雅.当然你可以使用一些设计模式去解决比如说圆盘套圆盘法---函数模板...但实现起来并那么容易有时候会导致代码可读性下降,而且缓存的key可能一样不灵活.

 

现在好了,我们可以尝试用SPRING的AOP来做:

<?xml version="1.0" encoding="gbk"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	   xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
       http://www.springframework.org/schema/beans/spring-beans-2.0.xsd 
       http://www.springframework.org/schema/aop 
       http://www.springframework.org/schema/aop/spring-aop-2.0.xsd" default-autowire="byName">
	<bean id="AopManagerForCache" class="org.edwardpro.common.aop.CacheAOPManagerImpl">
        <property name="expTime">
            <value>900</value>            
        </property>
	</bean>
	<aop:config>
		<aop:aspect id="dataAOP" ref="AopManagerForCache">
            <aop:around pointcut="execution(* org.edwardpro.aop.biz.core.manager.*Manager.*(..))" method="checkJointPoint" />
		</aop:aspect>
	</aop:config>
</beans>

 

这样在指定包的地方都会被拦截,方法被拦截了干什么还是一个问题,这里我使用了注入,一共有两类注入,一种叫CheckCache一种叫FlushCache.Flush是用来delete缓存的 checkcache是用来检查缓存的,典型的get时候用check而update时候用flush.由于缓存key是来自于参数的,所以这个注解我定义了三个参数:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CacheCheck {
	/**
	 * 缓存的前缀,就是缓存那些参数前面的修饰符号: MSP_SHOP_SHOPINFO
	 * 
	 * 这个字段支持表达式了,表达式的含义是最后输出的前缀是什么并会影响整个键值生成系统
	 * 表达式的样例:
	 * expr:{0 equa}
	 * @return
	 */
	public String cachePrefix();

	/**
	 * 活动参数的表达式,这个需要用拦截器获得的,这里只是把参数列举出来,最终的形式: _{value}_
	 * 
	 * 如果是复杂对象,使用 0.aaa 1 2.ccc的形式来写
	 * 
	 * 
	 * @return
	 */
	public String[] paramList();

	/**
	 * New Add
	 * 
	 * 有些缓存刷新需要返回值,比如分页缓存的刷新 方便起见格式用json写:
	 * 
	 * {'isRange':'0','range-start':'1','range-end':'10','range-step':'1'}
	 * 
	 * @return
	 */
	public String range();
}

 

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface FlushCache {
	/**
	 * 缓存的前缀,就是缓存那些参数前面的修饰符号: MSP_SHOP_SHOPINFO
	 * 
	 * @return
	 */
	public String[] cachePrefix();

	/**
	 * 活动参数的表达式,这个需要用拦截器获得的,这里只是把参数列举出来,最终的形式: _{value}_
	 * 
	 * 如果是复杂对象,使用 0.aaa 1 2.ccc的形式来写
	 * 
	 * 
	 * @return
	 */
	public String[] paramList();
	
	/**
	 * New Add
	 * 
	 * 有些缓存刷新需要返回值,比如分页缓存的刷新 方便起见格式用json写:
	 * 
	 * {'isRange':'0','range-start':'1','range-end':'10','range-step':'1'}
	 * 
	 * @return
	 */
	public String[] range();
}

 

参数的表达是这样的 (0.xx) xx一定是要set get方法的属性值,因为我的实现依赖于beanutil. 0,1,2 是参数的位置,因为在aop后参数名没有了,虽然也有方法可以得到,但是那个很麻烦,省事期间我还是用0,1,2这种表示是哪个参数,下面看下解析参数用的类:

 

	/**
	 * aop的工具之一用来通过参数表解析式得到参数信息
	 * 
	 * @param paramStr
	 * @param obj
	 * @return paramStr 最简单形式是 0 0.aa
	 */
	public static List<Object> getValueByStr(String paramStr, Object[] obj) {
		List<Object> ret = new ArrayList<Object>();
		String[] args = paramStr.split("\\.");
		if (args != null && args.length >= 1) {
			// 原始对象
			Object valueOrg = obj[Integer.parseInt(args[0])];
			if (valueOrg instanceof Collection) {
				for (Object childObj : (Collection) valueOrg) {
					if (args.length == 1) {
						ret.add(childObj);
						continue;
					}
					for (int i = 1; i < args.length; i++) {
						PropertyDescriptor pd = BeanUtils
								.getPropertyDescriptor(childObj.getClass(),
										args[i]);
						try {
							// 增加容错代码
							if (pd != null) {
								childObj = pd.getReadMethod().invoke(childObj);
							}
							if (i == (args.length - 1)) {
								ret.add(childObj);
							}
						} catch (IllegalArgumentException e) {
							continue;
						} catch (IllegalAccessException e) {
							continue;
						} catch (InvocationTargetException e) {
							continue;
						}
					}
				}
			} else {
				if (args.length == 1) {
					ret.add(valueOrg);
				}
				for (int i = 1; i < args.length; i++) {
					PropertyDescriptor pd = BeanUtils.getPropertyDescriptor(
							valueOrg.getClass(), args[i]);
					try {
						// 增加容错代码
						if (pd != null) {
							valueOrg = pd.getReadMethod().invoke(valueOrg);
						}
						if (i == (args.length - 1)) {
							ret.add(valueOrg);
						}
					} catch (IllegalArgumentException e) {
						continue;
					} catch (IllegalAccessException e) {
						continue;
					} catch (InvocationTargetException e) {
						continue;
					}
				}
			}

			// return valueOrg;
		}
		return ret;

 

 

 代码比较粗俗随便看看就是了~_~

 

然后差不多把这些组合起来:

 

	/*
	 * (non-Javadoc)
	 * 
	 * @seecom.taobao.common.aop.CacheAOPManager#checkCache(org.aspectj.lang.
	 * ProceedingJoinPoint, com.taobao.common.annotation.cache.CacheCheck)
	 */
	@Override
	public Object checkCache(ProceedingJoinPoint jointPoint,
			CacheCheck cacheCheck) {
		String key = this.getCheckKey(cacheCheck, jointPoint.getArgs());
		Result<DataEntry> ret = this.getTairManager().get(this.getNameSpace(),
				key);
		logger.info(">>checkValue>>" + ret.getRc().getMessage());
		if (ret != null && ret.getRc().getCode() == 0) {
			Object obj = ret.getValue().getValue();
			return obj;
		} else {
			try {
				// 没有得到缓存所以会执行对象
				logger.info(">>checkValue>>No Cache");
				Object obj = jointPoint.proceed();
				ResultCode retCode = this.getTairManager().put(
						this.getNameSpace().intValue(), key,
						(Serializable) obj, 0, this.getExpTime());
				logger.info("set cache result>>" + retCode.getCode() + "|"
						+ retCode.getMessage());
				return obj;
			} catch (Throwable e) {
				logger.error(e);
			}
		}
		return null;
	}

 

 

	/*
	 * (non-Javadoc)
	 * 
	 * @seecom.taobao.common.aop.CacheAOPManager#flushCache(org.aspectj.lang.
	 * ProceedingJoinPoint, com.taobao.common.annotation.cache.FlushCache)
	 */
	@Override
	public Object flushCache(ProceedingJoinPoint jointPoint,
			FlushCache flushCache) {
		List<Object> keyList = this.getFlushKey(flushCache, jointPoint
				.getArgs());
		ResultCode ret = this.tairManager.mdelete(this.getNameSpace()
				.intValue(), keyList);
		logger.info(">>flush cache result>>" + keyList + ">>" + ret.getCode()
				+ ">>" + ret.getMessage());
		try {
			return jointPoint.proceed();
		} catch (Throwable e) {
			logger.error(e);
		}
		return null;
	}

 

	/**
	 * 通过参数得到前缀
	 * 
	 * @param flushCache
	 * @param args
	 * @return
	 */
	public List<Object> getFlushKey(FlushCache flushCache, Object[] args) {
		List<Object> ret = new ArrayList<Object>();
		if (flushCache != null) {
			for (int i = 0; i < flushCache.cachePrefix().length; i++) {
				String prefix = flushCache.cachePrefix()[i];
				List<String> paramList = this.genParamList(flushCache, i, args,
						prefix);
				List<String> rangeList = this.genRange(flushCache, i);
				for (String paramItem : paramList) {
					if (rangeList.size() > 0) {
						for (String rangeItem : rangeList) {
							StringBuilder sb = new StringBuilder();
							sb.append(paramItem);
							sb.append(rangeItem);
							ret.add(sb.toString());
						}
					} else {
						// StringBuilder sb = new StringBuilder();
						// sb.append(paramItem);
						ret.add(paramItem);
					}
				}
			}
			this.removeDuplicated(ret);
		}
		return ret;
	}

	private List<String> genParamList(FlushCache flushCache, int index,
			Object[] args, String prefix) {
		List<String> ret = new ArrayList<String>();
		List<String> paramList;
		if (index <= (flushCache.paramList().length - 1)) {
			// 对应的关系
			paramList = this.getPrefix(flushCache.paramList()[index]);
		} else {
			paramList = this.getPrefix(flushCache.paramList()[0]);
		}
		for (String argStr : paramList) {
			List<Object> argsValue = valueProcessManager.getValue(argStr, args);
			if (ret.size() > 0) {
				List<String> tempRet = new ArrayList<String>();
				for (String tempItem : ret) {
					for (Object tempObj : argsValue) {
						StringBuilder sbTemp = new StringBuilder();
						sbTemp.append(tempItem);
						sbTemp.append(KEY_SPILIT);
						sbTemp.append(tempObj);
						tempRet.add(sbTemp.toString());
					}
				}
				ret.clear();
				ret.addAll(tempRet);
			} else {
				for (Object item : argsValue) {
					ret.add(prefix + KEY_SPILIT + item.toString());
				}
			}
		}
		if (ret.size() == 0) {
			ret.add(prefix + KEY_SPILIT);
		}
		return ret;
	}

	private List<String> genRange(FlushCache flushCache, int index) {
		List<String> ret = new ArrayList<String>();
		if (null != flushCache.range()) {
			String range;
			if (index <= (flushCache.range().length - 1)) {
				// 对应的关系
				range = flushCache.range()[index];
			} else {
				range = flushCache.range()[0];
			}
			Map<String, Integer> map = JsonUtils.mapFromJson(range,
					Integer.class);
			KeyRanger keyRanger = KeyRanger.getInstance(map);
			if (keyRanger.getIsRange() > 0) {
				for (int j = keyRanger.getRangeStart(); j <= keyRanger
						.getRangeEnd(); j = j + keyRanger.getRangeStep()) {
					StringBuilder sb = new StringBuilder();
					sb.append(KEY_SPILIT);
					sb.append(j);
					ret.add(sb.toString());
				}
			}
		}
		return ret;
	}

	// 去除重复KEY
	private void removeDuplicated(List<Object> input) {
		List<Object> tempList = new ArrayList<Object>(input);
		HashSet<Object> tempSet = new HashSet<Object>(tempList);
		input.clear();
		for (Object obj : tempSet) {
			input.add(obj);
		}
	}

	public String getCheckKey(CacheCheck cacheCheck, Object[] args) {
		StringBuffer ret = new StringBuffer();
		ret.append(cacheCheck.cachePrefix());
		for (String argStr : cacheCheck.paramList()) {
			List<Object> argsValue = valueProcessManager.getValue(argStr, args);
			if (argsValue != null) {
				for (Object oChild : argsValue) {
					if (oChild != null) {
						ret.append(KEY_SPILIT);
						ret.append(oChild);
					}
				}

			}
		}
		logger.info(ret.toString());
		return ret.toString();
	}

	private List<String> getPrefix(String keys) {
		List<String> ret = new ArrayList<String>();
		if (null != keys) {
			String[] arr = keys.split(",");
			if (null != arr) {
				for (String s : arr) {
					if (!TBStringUtil.isBlank(s)) {
						ret.add(s);
					}
				}
			}
		}
		return ret;
	}

 

变成贴代码了....天太热不想多打字,呵呵,大家凑合看,基本上看下spring配置就知道大概的意图了,试用下来对于一般的前后台应用足够了,但是要注意,拦截的方法一定是要业务层的,另外前后台的get方法最好分开,否则缓存一旦配置有问题,就会导致后台更新不了的故障,这个要注意...

over,其实只是想提供一种思路至于实现就贻笑大方了...

0
0
分享到:
评论

相关推荐

    spring AOP实现查询缓存

    本代码通过使用spring aop+ehcache的技术,实现了方法级别的查询缓存,主要原理是 方法的完整路径+方法参数值,作为key,放入cache中,下次访问时先判断cache中是否有该key.

    Spring AOP实现Redis缓存数据库查询

    在本文中,我们将使用Spring AOP来实现Redis缓存数据库查询,解决了缓存内容的正确性、避免脏读、序列化查询结果等问题。 在解决问题中,我们首先需要解决的是如何避免脏读的问题。为了解决这个问题,我们可以在...

    Spring源码最难问题:当Spring AOP遇上循环依赖.docx

    在AbstractBeanFactory的doGetBean方法中,可以看到Spring是如何实现三级缓存的。 首先,Spring会尝试从一级缓存中获取bean,如果不成功 maka 会尝试从二级缓存中获取,如果仍然不成功 maka 会尝试从三级缓存中获取...

    spring aop 自定义缓存实现

    spring aop 自定义缓存实现的一个小实例,地址:http://blog.csdn.net/maoyeqiu/article/details/50260357

    Spring AOP如何整合redis(注解方式)实现缓存统一管理详解

    2. Spring AOP结合Redis:我们可以使用Spring AOP来实现缓存的统一管理,通过切面技术来拦截需要缓存的数据,并将其存储到Redis中。 3. SPEL解析注解参数:我们可以使用SPEL来解析注解参数,获取缓存key和过期时间...

    SpringAOP实例.docx

    在本例中,我们将使用 Spring AOP 来实现日志记录功能,记录商品的 service 层的日志操作。下面是实现的步骤和关键技术点: 1. 创建商品日志记录表 Product_Log 在日志记录功能中,首先需要创建一个用于存储日志...

    SpringAOP结合ehCache实现简单缓存实例

    NULL 博文链接:https://honda418.iteye.com/blog/354945

    浅谈SpringBoot集成Redis实现缓存处理(Spring AOP实现)

    本文主要介绍了如何使用SpringBoot框架集成Redis实现缓存处理,通过Spring AOP(面向切面编程)实现缓存处理。下面是相关知识点的总结: 一、需求分析 在开源项目中,需要引入Redis实现缓存处理,以提高系统性能。...

    第四章:Spring AOP API 设计模式1

    【Spring AOP设计模式】是Spring框架中面向切面编程的重要组成部分,它允许开发者通过分离关注点来解耦代码,实现灵活的模块化设计。在本章中,我们将探讨17种设计模式在Spring AOP中的应用和实现,以及它们如何帮助...

    Spring AOP+ehCache简单缓存系统解决方案

    需要使用Spring来实现一个Cache简单的解决方案,具体需求如下:使用任意一个现有开源Cache Framework,要求可以Cache系统中Service或则DAO层的get/find等方法返回结果,如果数据更新(使用Create/update/delete方法...

    Spring AOP 1.0示例

    Spring AOP 1.0是Spring框架早期的一个版本,它引入了面向切面编程(Aspect Oriented Programming,AOP)的概念,使得开发者可以方便地实现横切关注点,如日志记录、事务管理、性能监控等,从而提高代码的可读性和可...

    spring aop

    Spring AOP(Aspect Oriented Programming,面向切面编程)是Spring框架的重要组成部分,它提供了一种在程序运行时动态插入代码的能力,以实现跨切面的关注点,如日志、事务管理、权限控制等。通过AOP,开发者可以将...

    第五章:Spring AOP 在 Spring Framework 内部应用1

    Spring缓存支持在方法调用级别进行缓存,提高性能,而本地调度则允许定时任务的设定,两者都利用AOP来实现动态的增强。 面试题精选部分可能涵盖了Spring AOP的常见问题,如如何配置和使用AOP,事务管理的原理等,...

    Flex整合Spring实现aop日志管理

    本文将深入探讨如何使用Flex与Spring框架整合,实现AOP(面向切面编程)来达到日志管理的目标。AOP允许我们在不修改原有业务逻辑的情况下,插入日志记录代码,使得日志功能的实现更加灵活和高效。 首先,让我们理解...

    基于java的企业级应用开发:Spring AOP简介.ppt

    它主要由Beans模块、Core模块、Context模块、Context-support模块和SpEL(Spring Expression Language,Spring表达式语言)模块组成,具体介绍如下: Beans模块:提供了BeanFactory,是工厂模式的经典实现,Spring将...

    Spring Aop实例(AOP 如此简单)@Aspect、@Around 注解方式配置

    Spring AOP 实例(AOP 如此简单)@Aspect、@Around 注解方式...本实例展示了 Spring AOP 的基本概念和应用,展示了如何使用 @Aspect 和 @Around 注解来实现一个简单的时间监控功能,从而提高了系统的灵活性和可维护性。

    spring注解方式实现aop

    通过以上步骤,我们可以使用Spring注解方式实现AOP,将关注点如日志、缓存等与业务逻辑分离,提高代码的可读性和可维护性。在实际项目中,可以灵活运用这些知识来优化代码结构,降低复杂性。希望本教程对理解和应用...

    spring AOP(声明式事务管理)小程序

    Spring AOP,全称Aspect-Oriented Programming(面向切面编程),是Spring框架的重要组成部分,它为应用程序提供了声明式的企业级服务,如日志、安全控制、缓存和事务管理等。在本“spring AOP(声明式事务管理)小...

    spring AOP 小例子

    在本示例中,我们将通过两个小例子来深入理解Spring AOP的核心概念和使用方法。 首先,我们来看第一个例子——Spring_AOP_Demo01。在这个例子中,我们可能会涉及到以下几个关键知识点: 1. **AOP核心概念**:AOP...

Global site tag (gtag.js) - Google Analytics