基于@AspectJ的Aop来实现缓存控制报NoSuchMethodException

项目中基于@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){

           代码略..
        }

 获取还有更好的办法,暂时先用上吧

猜你喜欢

转载自wokeke.iteye.com/blog/2210812