设计模式导致分层—如何实现参数传递(文末附彩蛋)?
2022-03-01 本文已影响0人
小胖学编程
一个项目/模块中,有时候会将生命周期划分成:校验层->转换层->持久化层->后置处理层。
这样划分无疑可以使得各层级职责更加明确,但是会引入一个新的问题,即参数如何传递?
例如:在“校验层”中需要通过IO查询校验一个字段是否合法,但是在“转换层”又需要再次使用IO查询来填充数据(“转换层”不推荐抛出异常)。
那么为了节约性能,有两种方案:
- 扩展方法入参对象,这种方式可以通过泛型来实现。
- 通过ThreadLocal来进行传递。
无论哪一种方案,都是可以解决跨层级参数传递的问题,但是两种在后期的可读性都不是很高。
那么有没有一种方案,既能保证了性能,又能保证可读性???
这里说一种解决方案:隐式的ThreadLocal传递,即线程级别的缓存方案。
@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 //自定义注解,目的是自动调用remove方法,防止内存泄露
private static ThreadLocal<Map<String, Object>> requestCache = ThreadLocal.withInitial(HashMap::new);
@PostConstruct
public void init() {
//可读取启动参数,此处也可以读取Spring的配置参数
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("执行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("执行test(Integer id, String name, String ex)方法啦");
return String.join("$", id + "", name, ex, new Date().toString());
}
}
实现起来是比较简单的,即通过ThreadLocal进行线程级别的缓存。这样在第二次调用的时候,就会减少性能的损耗,且可以跨层级的去获取参数。
彩蛋:@ThreadLocalLifeCycle注解是如何实现的,详看:https://www.jianshu.com/p/494de3bf076b