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

使用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();
		}
	}

	public static void testDao()
	{
		DaoImpl dao = (DaoImpl) context.getBean("dao");

		long begin = System.currentTimeMillis();
		String result = dao.query(2);
		long end = System.currentTimeMillis();

		System.out.println(result + ",cost time:" + (end - begin));
	}
}

通过执行结果发现,后面2次调用是直接从缓存中取的数据,这样就达到了我们模拟缓存的效果。

最后附上该工程的结构图和依赖的spring3.1.2的jar

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


至此我们实现采用aspect的方式,达到了方法拦截的效果。接下来使用MethodInteceptor和advisor完成相同的效果。

package net.aty.cache;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

public class CacheMethodInterceptor implements MethodInterceptor
{

	@Override
	public Object invoke(MethodInvocation invocation) throws Throwable
	{
		System.out.println("begin MethodInterceptor");
		
		Object result = invocation.proceed();
		
		System.out.println("end MethodInterceptor");
		
		return result;
	}

}

对应的xml配置如下:

<bean id="cacheMethodInterrupter" class="net.aty.cache.CacheMethodInterceptor" />
	<aop:config proxy-target-class="true">
		<aop:pointcut id="servicePointcut" expression="execution(public * net.aty.service.WeatherService.*(..))" />
		<aop:advisor advice-ref="cacheMethodInterrupter" pointcut-ref="servicePointcut" />
	</aop:config>