时间原因,这里只贴代码,见谅。
package com.rd.ifaes.common.annotation;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.asm.*;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 切面编程工具类
* @author lh
* @version 3.0
* @since 2016-8-26
*/
public class AopUtils {
private static final Logger LOGGER = LoggerFactory.getLogger(AopUtils.class);
private static final String DESC_DOUBLE = "D";
private static final String DESC_SHORT = "J";
private AopUtils() { }
/**
* <p>获取方法的参数名</p>
*
* @param m
* @return
*/
public static String[] getMethodParamNames(final Method m) {
final String[] paramNames = new String[m.getParameterTypes().length];
final String n = m.getDeclaringClass().getName();
final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
String className = m.getDeclaringClass().getSimpleName();
ClassReader cr = null;
InputStream resourceAsStream = null;
try {
resourceAsStream = Class.forName(n).getResourceAsStream(className + ".class");
cr = new ClassReader(resourceAsStream);
} catch (IOException | ClassNotFoundException e) {
LOGGER.warn(e.getMessage(), e);
} finally {
if (resourceAsStream != null) {
try {
resourceAsStream.close();
} catch (IOException e) {
LOGGER.warn(e.getMessage(), e);
}
}
}
if (cr != null) {
cr.accept(new ClassVisitor(Opcodes.ASM4, cw) {
@Override
public MethodVisitor visitMethod(final int access,
final String name, final String desc,
final String signature, final String[] exceptions) {
final Type[] args = Type.getArgumentTypes(desc);
// 方法名相同并且参数个数相同
if (!name.equals(m.getName())
|| !sameType(args, m.getParameterTypes())) {
return super.visitMethod(access, name, desc, signature,
exceptions);
}
MethodVisitor v = cv.visitMethod(access, name, desc, signature,
exceptions);
return new MethodVisitor(Opcodes.ASM4, v) {
int fixCount = 0;//步长修正计数器
@Override
public void visitLocalVariable(String name, String desc,
String signature, Label start, Label end, int index) {
int i = index - 1;
// 如果是静态方法,则第一就是参数
// 如果不是静态方法,则第一个是"this",然后才是方法的参数
if (Modifier.isStatic(m.getModifiers())) {
i = index;
}
if (i > fixCount) {
i -= fixCount;
}
if(desc.equals(DESC_SHORT) || desc.equals(DESC_DOUBLE)){
fixCount++;
}
if (i >= 0 && i < paramNames.length) {
paramNames[i] = name;
}
super.visitLocalVariable(name, desc, signature, start,
end, index);
}
};
}
}, 0);
}
return paramNames;
}
/**
* <p>比较参数类型是否一致</p>
*
* @param types asm的类型({@link Type})
* @param clazzes java 类型({@link Class})
* @return
*/
private static boolean sameType(Type[] types, Class<?>[] clazzes) {
// 个数不同
if (types.length != clazzes.length) {
return false;
}
for (int i = 0; i < types.length; i++) {
if (!Type.getType(clazzes[i]).equals(types[i])) {
return false;
}
}
return true;
}
/**
* 取得切面调用的方法
* @param pjp
* @return
*/
public static Method getMethod(ProceedingJoinPoint pjp){
Signature sig = pjp.getSignature();
if (!(sig instanceof MethodSignature)) {
throw new IllegalArgumentException("该注解只能用于方法");
}
MethodSignature msig = (MethodSignature) sig;
Object target = pjp.getTarget();
Method currentMethod = null;
try {
currentMethod = target.getClass().getMethod(msig.getName(), msig.getParameterTypes());
} catch (NoSuchMethodException | SecurityException e) {
LOGGER.warn(e.getMessage(), e);
}
return currentMethod;
}
public static List<String> getMatcher(String regex, String source) {
List<String> list = new ArrayList<>();
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(source);
while (matcher.find()) {
list.add(matcher.group());
}
return list;
}
/**
* 取得注解参数
(?=exp) 匹配exp前面的位置
(?<=exp) 匹配exp后面的位置
(?!exp) 匹配后面跟的不是exp的位置
(?<!exp) 匹配前面不是exp的位置
* @param managers
* @return
*/
public static List<String> getAnnoParams(String source){
String regex = "(?<=\\{)(.+?)(?=\\})";
return getMatcher(regex, source);
}
}
package com.rd.ifaes.common.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import com.rd.ifaes.common.dict.ExpireTime;
/**
* 添加缓存
* @author lh
*
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Cacheable {
/**
* 缓存key
* @return
*/
public String key() default "";
/**
* 缓存时效,默认无限期
* @return
*/
public ExpireTime expire() default ExpireTime.NONE;
}
package com.rd.ifaes.common.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import com.rd.ifaes.common.dict.ExpireTime;
/**
* 缓存清除
* @author lh
* @version 3.0
* @since 2016-8-28
*
*/
@Target({ ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface CacheEvict {
/**
* 缓存key
* @return
*/
String key() default "";
/**
* 缓存key数组
* @return
*/
String[] keys() default{};
/**
* 操作之间的缓存时间(秒)
* @author FangJun
* @date 2016年9月9日
* @return 默认0,不做限制
*/
ExpireTime interval() default ExpireTime.NONE;
}
package com.rd.ifaes.common.annotation;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import com.rd.ifaes.common.dict.ExpireTime;
import com.rd.ifaes.common.util.ReflectionUtils;
import com.rd.ifaes.common.util.StringUtils;
import com.rd.ifaes.core.core.constant.CacheConstant;
import com.rd.ifaes.core.core.constant.Constant;
import com.rd.ifaes.core.core.util.CacheUtils;
/**
* 缓存操作切面
* @author lh
*
*/
@Aspect
@Component
public class CacheAspect {
private static final Logger LOGGER = LoggerFactory.getLogger(CacheAspect.class);
@SuppressWarnings("rawtypes")
@Autowired
private RedisTemplate redisTemplate;
/**
* 添加缓存
* @param pjp
* @param cache
* @return
* @throws Throwable
*/
@Around("@annotation(cache)")
public Object cacheable(final ProceedingJoinPoint pjp, Cacheable cache)throws Throwable {
String key = getCacheKey(pjp, cache.key());
//使用redisTemplate操作缓存
@SuppressWarnings("unchecked")
ValueOperations<String, Object> valueOper = redisTemplate.opsForValue();
Object value = valueOper.get(key); // 从缓存获取数据
if (value != null) {
return value; // 如果有数据,则直接返回
}
value = pjp.proceed();
if(LOGGER.isInfoEnabled()){
LOGGER.info("cachePut, key={}", key);
}
// 缓存,到后端查询数据
if (cache.expire().getTime() <= 0) { // 如果没有设置过期时间,则无限期缓存
valueOper.set(key, value);
} else { // 否则设置缓存时间
valueOper.set(key, value, cache.expire().getTime(), TimeUnit.SECONDS);
}
return value;
}
@Around("@annotation(evict)")
public Object cacheEvict(final ProceedingJoinPoint pjp, CacheEvict evict)throws Throwable {
Object value;
// 执行方法
value = pjp.proceed();
//单个key操作
if (StringUtils.isNotBlank(evict.key())) {
String keyname = evict.key();
evictByKeyname(pjp, keyname,evict.interval());
}
//批量key操作
if (evict.keys() != null && evict.keys().length > 0) {
for (String keyname : evict.keys()) {
evictByKeyname(pjp, keyname,evict.interval());
}
}
return value;
}
@SuppressWarnings("unchecked")
private void evictByKeyname(final ProceedingJoinPoint pjp, final String keyname, ExpireTime interval) {
final String key = getCacheKey(pjp, keyname);
//操作间隔判断
if (!ExpireTime.NONE.equals(interval)) {
final String intervalKey = CacheConstant.KEY_PREFIX_CACHE_EVICT + key;
if (CacheUtils.incr(intervalKey, Constant.DOUBLE_ONE) > Constant.DOUBLE_ONE) {
return;
}
CacheUtils.expire(intervalKey, interval);
}
if(LOGGER.isInfoEnabled()){
LOGGER.info("cacheEvict, key={}", key);
}
//使用redisTemplate操作缓存
if (keyname.equals(key)) {// 支持批量删除
Set<String> keys = redisTemplate.keys(key.concat("*"));
if (!CollectionUtils.isEmpty(keys)) {
redisTemplate.delete(keys);
}
} else {
redisTemplate.delete(key);
}
}
/**
* 获取缓存的key值
*
* @param pjp
* @param key
* @return
*/
private String getCacheKey(final ProceedingJoinPoint pjp, final String key) {
StringBuilder buf = new StringBuilder();
final Object[] args = pjp.getArgs();
if(StringUtils.isNotBlank(key)){
buf.append(key);
List<String> annoParamNames = AopUtils.getAnnoParams(key);
String[] methodParamNames = AopUtils.getMethodParamNames(AopUtils.getMethod(pjp));
if(!CollectionUtils.isEmpty(annoParamNames)){
for (String ap : annoParamNames) {
buf = replaceParam(buf, args, methodParamNames, ap);
}
}
}else{
buf.append(pjp.getSignature().getDeclaringTypeName()).append(":").append(pjp.getSignature().getName());
for (Object arg : args) {
buf.append(":").append(arg.toString());
}
}
return buf.toString();
}
/**
* 替换占位参数
* @param buf
* @param args
* @param methodParamNames
* @param ap
* @return
*/
private StringBuilder replaceParam(StringBuilder buf, final Object[] args, String[] methodParamNames, String ap) {
StringBuilder builder = new StringBuilder(buf);
String paramValue = "";
for (int i = 0; i < methodParamNames.length; i++) {
if(ap.startsWith(methodParamNames[i])){
final Object arg = args[i];
if (ap.contains(".")) {
paramValue = String.valueOf(ReflectionUtils.invokeGetter(arg, ap.substring(ap.indexOf('.') + 1)));
} else {
paramValue = String.valueOf(arg);
}
break;
}
}
int start = builder.indexOf("{" + ap);
int end = start + ap.length() + 2;
builder =builder.replace(start, end, paramValue);
return builder;
}
}
spring相关配置如下:
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig"> <property name="maxIdle" value="${redis.pool.maxIdle}" /> <!-- 最大能够保持idel状态的对象数 --> <property name="maxTotal" value="${redis.pool.maxTotal}" /> <!-- 最大分配的对象数 --> <property name="testOnBorrow" value="${redis.pool.testOnBorrow}" /> <!-- 当调用borrow Object方法时,是否进行有效性检查 --> </bean> <!-- sprin_data_redis 单机配置 --> <bean id="jedisConnFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" > <property name="hostName" value="${redis.host}" /> <property name="port" value="${redis.port}" /> <property name="timeout" value="${redis.timeout}" /> <property name="password" value="${redis.password}" /> <property name="poolConfig" ref="jedisPoolConfig" /> </bean> <!-- key序列化 --> <bean id="stringRedisSerializer" class="org.springframework.data.redis.serializer.StringRedisSerializer" /> <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate" p:connectionFactory-ref="jedisConnFactory" p:keySerializer-ref="stringRedisSerializer" /> <!-- spring自己的缓存管理器 --> <bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager"> <property name="caches"> <set> <bean class="com.rd.ifaes.common.jedis.RdRedisCache" p:redis-template-ref="redisTemplate" p:name="sysCache"/> </set> </property> </bean> <!-- 启用缓存注解功能,这个是必须的,否则注解不会生效,另外,该注解一定要声明在spring主配置文件中才会生效 --> <cache:annotation-driven cache-manager="cacheManager" proxy-target-class="true" key-generator="rdKeyGenerator"/> <!-- 自定义主键生成策略 --> <bean id="rdKeyGenerator" class="com.rd.ifaes.common.jedis.RdKeyGenerator"/>