一個(gè)項(xiàng)目/模塊中形病,有時(shí)候會(huì)將生命周期劃分成:校驗(yàn)層->轉(zhuǎn)換層->持久化層->后置處理層。
這樣劃分無疑可以使得各層級(jí)職責(zé)更加明確霞幅,但是會(huì)引入一個(gè)新的問題窒朋,即參數(shù)如何傳遞?
例如:在“校驗(yàn)層”中需要通過IO查詢校驗(yàn)一個(gè)字段是否合法蝗岖,但是在“轉(zhuǎn)換層”又需要再次使用IO查詢來填充數(shù)據(jù)(“轉(zhuǎn)換層”不推薦拋出異常)。
那么為了節(jié)約性能榔至,有兩種方案:
- 擴(kuò)展方法入?yún)?duì)象抵赢,這種方式可以通過泛型來實(shí)現(xiàn)。
- 通過ThreadLocal來進(jìn)行傳遞唧取。
無論哪一種方案铅鲤,都是可以解決跨層級(jí)參數(shù)傳遞的問題,但是兩種在后期的可讀性都不是很高枫弟。
那么有沒有一種方案邢享,既能保證了性能,又能保證可讀性淡诗?骇塘??
這里說一種解決方案:隱式的ThreadLocal傳遞韩容,即線程級(jí)別的緩存方案款违。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestCache {
}
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.annotation.PostConstruct;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
/**
*
*/
@Service
@Aspect
@Slf4j
public class RequestCacheAspect {
private boolean openRequestCache;
@ThreadLocalLifeCycle //自定義注解,目的是自動(dòng)調(diào)用remove方法群凶,防止內(nèi)存泄露
private static ThreadLocal<Map<String, Object>> requestCache = ThreadLocal.withInitial(HashMap::new);
@PostConstruct
public void init() {
//可讀取啟動(dòng)參數(shù)插爹,此處也可以讀取Spring的配置參數(shù)
openRequestCache = true;
}
@Around("@annotation(com.tellme.aop.RequestCache)")
public Object doSurround(ProceedingJoinPoint point) throws Throwable {
if (!openRequestCache) {
return point.proceed();
}
MethodSignature signature = (MethodSignature) point.getSignature();
Method method = signature.getMethod();
Object[] args = point.getArgs();
String methodName = method.getName();
String className = method.getDeclaringClass().getSimpleName();
String paramString = Stream.of(args).map(JSON::toJSONString).collect(Collectors.joining("#"));
String cacheKey = className + "#" + methodName + "#" + paramString;
String cacheKeyMd5 = DigestUtils.md5DigestAsHex(cacheKey.getBytes());
if (!requestCache.get().containsKey(cacheKeyMd5)) {
Object result = point.proceed();
requestCache.get().putIfAbsent(cacheKeyMd5, result);
return result;
} else {
return requestCache.get().get(cacheKeyMd5);
}
}
}
使用方式:
@Service
@Slf4j
public class RequestCacheService {
@RequestCache
public String test(Integer id, String name) {
log.info("執(zhí)行test(Integer id, String name)方法啦");
return String.join("$", id + "", name, new Date().toString());
}
@RequestCache
public String test(Integer id, String name, String ex) {
log.info("執(zhí)行test(Integer id, String name, String ex)方法啦");
return String.join("$", id + "", name, ex, new Date().toString());
}
}
實(shí)現(xiàn)起來是比較簡單的,即通過ThreadLocal進(jìn)行線程級(jí)別的緩存。這樣在第二次調(diào)用的時(shí)候赠尾,就會(huì)減少性能的損耗力穗,且可以跨層級(jí)的去獲取參數(shù)。
彩蛋:@ThreadLocalLifeCycle注解是如何實(shí)現(xiàn)的气嫁,詳看:http://www.reibang.com/p/494de3bf076b