日期:2014-05-16  浏览次数:20950 次

使用Spring中的aspect或advisor实现方法拦截,模拟缓存实现

AOP是一种将通用逻辑与具体业务分离的技术,能够弥补OO在横向代码复用不足的问题,很好的实现separation of concerns (SoC)。缓存是改善系统性能的一种常用技术,采取以空间换时间的策略。缓存就是与具体业务无关的,如果我们设计一个缓存框架,那么应该是可插拔的,对系统业务代码无侵入的,这很符合AOP的适用场景。我们的项目采用了Ehcache缓存框架作为底层支撑,采用Spring框架的AOP进行方法拦截,将耗时方法的返回结果,进行缓存。现在我们使用spring的aop模拟这种实现。

首先假设我们有一个已经编写好的,比较耗时的类

package net.aty.dao;

public class DaoImpl
{
	public String query(int id)
	{
		// 模拟耗时的数据库查询操作
		try
		{
			System.out.println("query begin...");
			Thread.sleep(100 * id);
			System.out.println("query over...");
		} catch (InterruptedException e)
		{
			e.printStackTrace();
		}

		return "result:" + id * 10;
	}
}

下面是我们编写的缓存实现类

package net.aty.cache;

import java.util.HashMap;
import java.util.Map;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;

public class CacheQueryResult
{
	private Map<String, Object> buffer = new HashMap<String, Object>();

	public Object around(ProceedingJoinPoint point) throws Throwable
	{
		String key = uniqueKey(point);
		
		Object returnValue = buffer.get(key);
		if(returnValue != null)
		{
			return returnValue;
		}

		Object object = point.proceed();
		buffer.put(key, object);
		return object;
	}

	private String uniqueKey(ProceedingJoinPoint point)
	{
		Object target = point.getTarget();

		Signature signature = point.getSignature();
		String methodSignature = signature.toString();

		String key = target.hashCode() + methodSignature;

		return key;
	}

}

现在我们在spring.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:context="http://www.springframework.org/schema/context"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-3.1.xsd
           http://www.springframework.org/schema/aop
           http://www.springframework.org/schema/aop/spring-aop-3.1.xsd">

	<!-- 需要被拦截的目标对象 -->
	<bean id="dao" class="net.aty.dao.DaoImpl"></bean>

	<!-- 方法拦截器和环绕增强-->
	<bean id="cacheInterceptor" class="net.aty.cache.CacheQueryResult"></bean>

	<aop:config proxy-target-class="true">
		<aop:pointcut id="cachePointcut" expression="execution(public * net.aty.dao.DaoImpl.*(..))" />
		<aop:aspect id="cacheAspect" ref="cacheInterceptor">
			<aop:around method="around" pointcut-ref="cachePointcut" />
		</aop:aspect>
	</aop:config>

</beans>

测试类如下:

package net.aty;

import net.aty.dao.DaoImpl;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestMain
{
	private static ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
			"spring.xml");

	public static void main(String[] args)
	{
		for (int i = 0; i < 3; i++)
		{
			testDao();
		}