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
至此我们实现采用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>