项目中基于@Aspect实现AOP,通过环绕增强(@Around)控制Dao的缓存(set/delete)。DAO接口及实现及AOP部分代码如下:
///////////////////////////////////////////////////
package cn.xxx.dao;
public interface FunModuleDao {
/**
* 更新用户视图布局
*
* @param userId 用户ID
* @param fmlList 布局对象列表
*/
public void updateFunModuleUsers(Long userId,List fmlList);
}
//////////////////////////////////////////////
package cn.xxx.dao.impl;
@Repository
public class FunModuleDaoImpl implements FunModuleDao {
@Override
@CacheEvict(prefix = "userCache", suffix="0")
public void updateFunModuleUsers(Long userId,List fmlList){
代码略..
}
////////////////////////////////////////////////
package cn.xxx.cache.aop;
@Component
@Aspect
public class CacheAop {
private static final Log log = LogFactory.getLog(CacheAop.class);
@Autowired
private SiteService siteService;
@Autowired
private MemKeyService memKeyService;
@Autowired
private MemCachedClient memCachedClient ;
@Around(value="@annotation(cn.xxx.cache.annotation.Cacheable)")
public Object cache(ProceedingJoinPoint call){
Object result = null;
Boolean cacheEnable = CustomizedPropertyPlaceholderConfigurer.getCacheEnabled();
//判断是否开启缓存
if(!cacheEnable){
try {
result= call.proceed();
} catch (Throwable e) {
e.printStackTrace();
}
return result;
}
Method method=getMethod(call);
Cacheable cacheable=method.getAnnotation(cn.ac.ucas.sep.cache.annotation.Cacheable.class);
String fieldKey =parseKey(cacheable.suffix(),method,call.getArgs());
String prefix = cacheable.prefix();
String cacheKey = prefix+"_"+fieldKey;
result =memCachedClient.get(cacheKey);
if(null == result){
try {
result = call.proceed();
long expiration = cacheable.expiration();//1000*60*60*48==48小时过期
Date expirationTime=new Date(System.currentTimeMillis()+expiration);
memCachedClient.set(cacheKey, result,expirationTime));
} catch (Throwable e) {
e.printStackTrace();
}
}
return result;
}
/**
* 定义清除缓存逻辑
*/
@Around(value="@annotation(cn.xxx.cache.annotation.CacheEvict)")
public Object evict(ProceedingJoinPoint call){
代码略...
}
/**
* 获取被拦截方法对象
*
* MethodSignature.getMethod() 获取的是顶层接口或者父类的方法对象
* 而缓存的注解在实现类的方法上
* 所以应该使用反射获取当前对象的方法对象
*/
public Method getMethod(ProceedingJoinPoint call){
//获取参数的类型
Object [] args=call.getArgs();
Class [] argTypes=new Class[call.getArgs().length];
for(int i=0;i<args.length;i++){
argTypes[i]=args[i].getClass();
}
Method method=null;
try {
method=call.getTarget().getClass().getMethod(call.getSignature().getName(),argTypes);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
}
return method;
}
/**
* 获取缓存的key
* key 定义在注解上,支持SPEL表达式
*/
private String parseKey(String key,Method method,Object [] args){
代码略...
}
}
运行调试过程中出现一个很诡异的问题,错误信息如下:
java.lang.NoSuchMethodException: cn.xxx.dao.impl.FunModuleDaoImpl.updateFunModuleUsers(java.lang.Long, java.util.ArrayList) at java.lang.Class.getMethod(Class.java:1607) at cn.ac.ucas.sep.cache.aop.CacheAop.getMethod(CacheAop.java:148) //CacheAop.java:148即method=call.getTarget().getClass().getMethod(call.getSignature().getName(),argTypes);
跟踪执行过程最终找到原因:
通过ProceedingJoinPoint.getArgs()[1].getClass()获取的第二个参数类型(java.util.ArrayList)与call.getTarget().getClass().getMethod(...)中的类型(java.util.List)不匹配造成的。
解决办法:
修改Dao接口和实现的参数类型由List变为ArrayList
/////////////////////////////////////////////////// package cn.xxx.dao; public interface FunModuleDao { /** * 更新用户视图布局 * * @param userId 用户ID * @param fmlList 布局对象列表 */ public void updateFunModuleUsers(Long userId,ArrayList fmlList); } ////////////////////////////////////////////// package cn.xxx.dao.impl; @Repository public class FunModuleDaoImpl implements FunModuleDao { @Override @CacheEvict(prefix = "userCache", suffix="0") public void updateFunModuleUsers(Long userId,ArrayList fmlList){ 代码略.. }
获取还有更好的办法,暂时先用上吧
相关推荐
NULL 博文链接:https://snowolf.iteye.com/blog/1481442
2.2 基于@AspectJ的AOP @AspectJ使用注解来描述切点。 1 添加依赖Jar包 使用@AspectJ,需要以下的额外Jar包 (1) aspectjweaver.jar和aspectjtools.jar(提供两个版本,低版本JDK6的使用aspectj1.6,高版本如JDK8的...
NULL 博文链接:https://samter.iteye.com/blog/410618
@AspectJ配置Spring AOP,文档,Aspect jar包, 可运行的demo,
一个基于@AspectJ的spring2.0 AOP应用实例,很小很简单,没有任何额外信息,最适合AOP入门学习。使用log4j打印信息。把项目直接import进myeclipse就可以使用啦......
Spring spectJ AOP 前置通知 后置通知 返回通知 异常通知 环绕通知
NULL 博文链接:https://quicker.iteye.com/blog/670885
Spring 使用AspectJ 实现 AOP(基于xml文件、基于注解)
NULL 博文链接:https://moshow.iteye.com/blog/1613947
Spring4 In Action-4.2-@AspectJ-切面,Spring4 In Action-4.2-@AspectJ-切面。Spring4 In Action-4.2-@AspectJ-切面
主要对Spring AOP的相关概念和简单的静态代理、动态代理以及常见的几种AOP配置方式做总结学习。主要包括:1. AOP的常见概念 2. 静态代理 3. jdk动态代理 4. Aspectj and Aspectjweaver 5. **aop-config** 6. CGLIB ...
demo1是aspectj的Aop开发,用于用户是否登录的验证,使用注解来实现,在切面类中配置好切入点。优点:方便快捷 demo2是aspectj的Aop开发,用于用户是否登录的验证,在xml中配置好切面。 优点:便于维护,不用修改源...
NULL 博文链接:https://rain1109.iteye.com/blog/1838100
Spring AOP之基于AspectJ注解总结与案例 ,具体效果和过程看博文 http://blog.csdn.net/evankaka/article/details/45394409
博文链接:https://shaqiang32.iteye.com/blog/201914
概述 AOP 工具当前的技术状况,比较对于该技术而言最成熟的一些方法:AspectJ、AspectWerkz、JBoss AOP、和 Spring AOP,并对比与每种方法的采用有关的问题。
spring5基于aspectj实现aop操作所需jar包 com.springsource.net.sf.cglib-2.2.0.jar com.springsource.org.aopalliance-1.0.ar com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar spring-aspects-5.2.6....
Spring 使用AspectJ 实现 AOP之前置通知小例子,实际跑过,验证可信。