设计模式导致分层—如何实现参数传递(文末附彩蛋)?

2022-03-01  本文已影响0人  小胖学编程

一个项目/模块中,有时候会将生命周期划分成:校验层->转换层->持久化层->后置处理层。

这样划分无疑可以使得各层级职责更加明确,但是会引入一个新的问题,即参数如何传递?

例如:在“校验层”中需要通过IO查询校验一个字段是否合法,但是在“转换层”又需要再次使用IO查询来填充数据(“转换层”不推荐抛出异常)。

那么为了节约性能,有两种方案:

  1. 扩展方法入参对象,这种方式可以通过泛型来实现
  2. 通过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

上一篇下一篇

猜你喜欢

热点阅读