一.常见错误
- 反射调用失败
通常是有成员变量没有注入成功,出现再AopHelper代理生成的时候 - 反射调用失败lang.IllegalArgumentException: argument type mismatch
request获得的参数类型跟方法中的参数类型不一致
package com.hikvision.sro.core.config; import com.google.common.collect.Maps; import com.hikvision.sro.common.annotation.CacheDuration; import org.springframework.beans.BeansException; import org.springframework.beans.factory.InitializingBean; import org.springframework.cache.Cache; import org.springframework.cache.annotation.CacheConfig; import org.springframework.cache.annotation.Cacheable; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.data.redis.cache.RedisCacheManager; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.util.ReflectionUtils; import java.lang.reflect.Method; import java.util.Map; import java.util.Set; import static com.google.common.collect.Sets.newHashSet; import static org.springframework.core.annotation.AnnotationUtils.findAnnotation; import static org.springframework.util.StringUtils.isEmpty; /** * 新写了一个SpringRedisCacheManager,继承自RedisCacheManager, * 用于对@CacheDuration解析及有效期的设置 * 程序初始化后即写入注解给定的过期时间 * @author lobin * @date 2018/10/17 */ public class SpringRedisCacheManager extends RedisCacheManager implements ApplicationContextAware, InitializingBean { private ApplicationContext applicationContext; private Map<String,Long> expireMap; public SpringRedisCacheManager(RedisTemplate redisTemplate) { super(redisTemplate); } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } @Override public void afterPropertiesSet() { /** * 获取所有CacheDuration注解的 */ expireMap= Maps.newHashMap(); String[] beanNames = applicationContext.getBeanNamesForType(Object.class); for (String beanName : beanNames) { if(beanName.contains("com.hikvision.sro.services.client.BasicInfoService")){ System.out.println(beanName); } final Class clazz = applicationContext.getType(beanName); ReflectionUtils.doWithMethods(clazz, method -> { ReflectionUtils.makeAccessible(method); CacheDuration cacheDuration = findCacheDuration(clazz, method); Cacheable cacheable = findAnnotation(method, Cacheable.class); CacheConfig cacheConfig = findAnnotation(clazz, CacheConfig.class); if(null==cacheDuration||null==cacheable){ return; } Set<String> cacheNames = findCacheNames(cacheConfig, cacheable); for (String cacheName : cacheNames) { expireMap.put(cacheName, cacheDuration.duration()); } }, method -> null != findAnnotation(method, Cacheable.class)); } if(!expireMap.isEmpty()){ super.setExpires(expireMap); } } /** * CacheDuration标注的有效期,优先使用方法上标注的有效期 * @param clazz * @param method * @return */ private CacheDuration findCacheDuration(Class clazz, Method method) { CacheDuration methodCacheDuration = findAnnotation(method, CacheDuration.class); if (null != methodCacheDuration) { return methodCacheDuration; } CacheDuration classCacheDuration = findAnnotation(clazz, CacheDuration.class); if (null != classCacheDuration) { return classCacheDuration; } return null; } private Set<String> findCacheNames(CacheConfig cacheConfig, Cacheable cacheable) { return isEmpty(cacheable.value()) ? newHashSet(cacheConfig.cacheNames()) : newHashSet(cacheable.value()); } @Override protected Cache getMissingCache(String name) { long expiration = computeExpiration(name); return new CustomizedRedisCache(name, (this.isUsePrefix() ? this.getCachePrefix().prefix(name) : null), this.getRedisOperations(), expiration); } }
package com.hikvision.sro.core.config; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.parser.ParserConfig; import com.hikvision.sro.common.util.MD5; import com.hikvision.sro.core.services.ThreadLocalUser; import org.springframework.cache.CacheManager; import org.springframework.cache.annotation.CachingConfigurerSupport; import org.springframework.cache.annotation.EnableCaching; import org.springframework.cache.interceptor.KeyGenerator; import org.springframework.cache.interceptor.SimpleKeyGenerator; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.cache.RedisCacheManager; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.transaction.annotation.EnableTransactionManagement; import java.lang.reflect.Method; /** * @author tongxiabin */ @Configuration @EnableCaching @EnableTransactionManagement(proxyTargetClass = true) public class RedisConfig extends CachingConfigurerSupport { /** * 定义缓存数据 key 生成策略的bean * 包名+类名+方法名+所有参数 */ @Bean("wiselyKeyGenerator") public KeyGenerator withUserKeyGenerator(){ return (target, method, params) -> { StringBuilder sb = new StringBuilder(); sb.append(target.getClass().getName()); sb.append(method.getName()); sb.append(JSON.toJSONString(ThreadLocalUser.getUser())); sb.append(JSON.toJSONString(params)); return MD5.encodeByMD5(sb.toString()); }; } /** * 公共缓存key生成,不区分用户 * @return */ @Bean("commonKeyGenerator") public KeyGenerator commonKeyGenerator() { return (Object target, Method method, Object... params) -> { StringBuilder sb = new StringBuilder(); sb.append(target.getClass().getName()); sb.append(method.getName()); sb.append(JSON.toJSONString(params)); return MD5.encodeByMD5(sb.toString()); }; } /** * 要启用spring缓存支持,需创建一个 CacheManager的 bean,CacheManager 接口有很多实现,这里Redis 的集成,用 RedisCacheManager这个实现类 * Redis 不是应用的共享内存,它只是一个内存服务器,就像 MySql 似的, * 我们需要将应用连接到它并使用某种“语言”进行交互,因此我们还需要一个连接工厂以及一个 Spring 和 Redis 对话要用的 RedisTemplate, * 这些都是 Redis 缓存所必需的配置,把它们都放在自定义的 CachingConfigurerSupport 中 */ @Bean public CacheManager cacheManager( @SuppressWarnings("rawtypes") RedisTemplate redisTemplate) { RedisCacheManager cacheManager = new SpringRedisCacheManager(redisTemplate); cacheManager.setUsePrefix(true); //设置默认缓存时间为20min cacheManager.setDefaultExpiration(20*60L); return cacheManager; } /** * 显示声明缓存key生成器 * * @return */ @Override @Bean public KeyGenerator keyGenerator() { return new SimpleKeyGenerator(); } //1.项目启动时此方法先被注册成bean被spring管理 @Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) { RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(redisConnectionFactory); FastJsonRedisSerializer<Object> fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class); // 全局开启AutoType ParserConfig.getGlobalInstance().setAutoTypeSupport(true); // 设置值(value)的序列化采用FastJsonRedisSerializer。 redisTemplate.setValueSerializer(fastJsonRedisSerializer); redisTemplate.setHashValueSerializer(fastJsonRedisSerializer); // 设置键(key)的序列化采用StringRedisSerializer。 redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setHashKeySerializer(new StringRedisSerializer()); redisTemplate.afterPropertiesSet(); return redisTemplate; } }
package com.hikvision.sro.core.config;
import com.google.common.collect.Maps;
import com.hikvision.sro.common.annotation.CacheDuration;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.cache.Cache;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.ReflectionUtils;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.Set;
import static com.google.common.collect.Sets.newHashSet;
import static org.springframework.core.annotation.AnnotationUtils.findAnnotation;
import static org.springframework.util.StringUtils.isEmpty;
/**
* 新写了一个SpringRedisCacheManager,继承自RedisCacheManager,
* 用于对@CacheDuration解析及有效期的设置
* 程序初始化后即写入注解给定的过期时间
* @author lobin
* @date 2018/10/17
*/
public class SpringRedisCacheManager extends RedisCacheManager implements ApplicationContextAware, InitializingBean {
private ApplicationContext applicationContext;
private Map<String,Long> expireMap;
public SpringRedisCacheManager(RedisTemplate redisTemplate) {
super(redisTemplate);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
@Override
public void afterPropertiesSet() {
/**
* 获取所有CacheDuration注解的
*/
expireMap= Maps.newHashMap();
String[] beanNames = applicationContext.getBeanNamesForType(Object.class);
for (String beanName : beanNames) {
if(beanName.contains("com.hikvision.sro.services.client.BasicInfoService")){
System.out.println(beanName);
}
final Class clazz = applicationContext.getType(beanName);
ReflectionUtils.doWithMethods(clazz, method -> {
ReflectionUtils.makeAccessible(method);
CacheDuration cacheDuration = findCacheDuration(clazz, method);
Cacheable cacheable = findAnnotation(method, Cacheable.class);
CacheConfig cacheConfig = findAnnotation(clazz, CacheConfig.class);
if(null==cacheDuration||null==cacheable){
return;
}
Set<String> cacheNames = findCacheNames(cacheConfig, cacheable);
for (String cacheName : cacheNames) {
expireMap.put(cacheName, cacheDuration.duration());
}
}, method -> null != findAnnotation(method, Cacheable.class));
}
if(!expireMap.isEmpty()){
super.setExpires(expireMap);
}
}
/**
* CacheDuration标注的有效期,优先使用方法上标注的有效期
* @param clazz
* @param method
* @return
*/
private CacheDuration findCacheDuration(Class clazz, Method method) {
CacheDuration methodCacheDuration = findAnnotation(method, CacheDuration.class);
if (null != methodCacheDuration) {
return methodCacheDuration;
}
CacheDuration classCacheDuration = findAnnotation(clazz, CacheDuration.class);
if (null != classCacheDuration) {
return classCacheDuration;
}
return null;
}
private Set<String> findCacheNames(CacheConfig cacheConfig, Cacheable cacheable) {
return isEmpty(cacheable.value()) ?
newHashSet(cacheConfig.cacheNames()) : newHashSet(cacheable.value());
}
@Override
protected Cache getMissingCache(String name) {
long expiration = computeExpiration(name);
return new CustomizedRedisCache(name,
(this.isUsePrefix() ? this.getCachePrefix().prefix(name) : null),
this.getRedisOperations(),
expiration);
}
}