ThreadLocal加切面实现线程级别的方法缓存

这篇具有很好参考价值的文章主要介绍了ThreadLocal加切面实现线程级别的方法缓存。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1、实现效果

当一个请求线程多次请求A方法时,只会触发一次A方法的实际调用,会将方法结果缓存起来,避免多次调用。

2、实现过程

1. 需要一个注解ThreadLocalCache,在需要缓存的方法上加上该注解
2. 需要一个切面,借助ThreadLocal,将结果缓存起来,利用环绕通知来实现方法拦截从缓存中返回方法执行结果

3、代码实现

3.1、ThreadLocalCache注解创建

作用于方法级别

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ThreadLocalCache {
}

3.2、ThreadLocalTestAspect切面创建

@Aspect
@Component
public class ThreadLocalTestAspect {
    private ThreadLocal<Map<Object, Object>> threadLocal = new ThreadLocal<>();

    @Around("@annotation(com.example.test.ThreadLocalCache)")
    private Object myPointcut(ProceedingJoinPoint proceedingJoinPoint) {
        //获取方法的入参
        Object[] args = proceedingJoinPoint.getArgs();
        Signature signature = proceedingJoinPoint.getSignature();
        //获取目标方法名
        String name = signature.getName();
        //获取目标方法的类的完全限定名
        String declaringTypeName = signature.getDeclaringTypeName();
        //生成缓存key
        Object key = SimpleKeyGenerator.generateKey(args, declaringTypeName, name);
        if (Objects.isNull(threadLocal.get())) {
            threadLocal.set(new HashMap<>(8));
        }
        try {
            if (!threadLocal.get().containsKey(key)) {
                threadLocal.get().put(key, proceedingJoinPoint.proceed());
            }
        } catch (Throwable e) {
            //日志记录
            e.printStackTrace();
        }
        return threadLocal.get().get(key);
    }

    public void removeThreadLocal(){
        threadLocal.remove();
    }

}

4、测试过程

  1. 创建一个接口及实现
public interface ThreadLocalTestService {

    Long getParentIdByName(String name);
}
@Service
public class ThreadLocalTestServiceImpl implements ThreadLocalTestService{

    @ThreadLocalCache
    @Override
    public Long getParentIdByName(String name) {
        //根据name查询父级ID
        System.out.println("com.example.test.ThreadLocalTestServiceImpl.getParentIdByName 执行了");
        return 666L;
    }
}
  1. 方法调用
@RestController
@RequestMapping("/ThreadLocalTest")
public class ThreadLocalTest {

    @Autowired
    private ThreadLocalTestService threadLocalTestService;

    @Autowired
    private ThreadLocalTestAspect threadLocalTestAspect;


    @GetMapping("getParentIdByName")
    public Long getParentIdByName(String name){
        System.out.println(Thread.currentThread().getName());
        threadLocalTestService.getParentIdByName(name);
        threadLocalTestService.getParentIdByName(name);
        Long parentId = threadLocalTestService.getParentIdByName(name);
        threadLocalTestAspect.removeThreadLocal();
        return parentId;
    }

}

3.执行结果

http-nio-8087-exec-1
com.example.test.ThreadLocalTestServiceImpl.getParentIdByName 执行了
http-nio-8087-exec-2
com.example.test.ThreadLocalTestServiceImpl.getParentIdByName 执行了
http-nio-8087-exec-4
com.example.test.ThreadLocalTestServiceImpl.getParentIdByName 执行了
http-nio-8087-exec-5
com.example.test.ThreadLocalTestServiceImpl.getParentIdByName 执行了
http-nio-8087-exec-6
com.example.test.ThreadLocalTestServiceImpl.getParentIdByName 执行了
http-nio-8087-exec-7
com.example.test.ThreadLocalTestServiceImpl.getParentIdByName 执行了
http-nio-8087-exec-8
com.example.test.ThreadLocalTestServiceImpl.getParentIdByName 执行了
http-nio-8087-exec-10
com.example.test.ThreadLocalTestServiceImpl.getParentIdByName 执行了
http-nio-8087-exec-9
com.example.test.ThreadLocalTestServiceImpl.getParentIdByName 执行了
http-nio-8087-exec-3
com.example.test.ThreadLocalTestServiceImpl.getParentIdByName 执行了
http-nio-8087-exec-1
com.example.test.ThreadLocalTestServiceImpl.getParentIdByName 执行了
http-nio-8087-exec-2
com.example.test.ThreadLocalTestServiceImpl.getParentIdByName 执行了

http-nio-8087-exec-是线程名字,可以看到http-nio-8087-exec-1执行了两次,每次都调用四次getParentIdByName 方法,但getParentIdByName 方法实际至执行了一次,剩下的三次是从缓存中获取的。
这里需要注意的是:线程每次结束的时候都需要调用threadLocalTestAspect.removeThreadLocal();为的是把当前线程threadLocal里的缓存抹掉,因为同一个线程可能会被重复使用,所以不抹掉,可能会导致多次请求使用同一个线程,目标方法只会执行一次,和我们的最初的实现效果是违背的。
下面是不调用threadLocalTestAspect.removeThreadLocal();的执行结果

http-nio-8087-exec-1
com.example.test.ThreadLocalTestServiceImpl.getParentIdByName 执行了
http-nio-8087-exec-3
com.example.test.ThreadLocalTestServiceImpl.getParentIdByName 执行了
http-nio-8087-exec-8
com.example.test.ThreadLocalTestServiceImpl.getParentIdByName 执行了
http-nio-8087-exec-5
com.example.test.ThreadLocalTestServiceImpl.getParentIdByName 执行了
http-nio-8087-exec-6
com.example.test.ThreadLocalTestServiceImpl.getParentIdByName 执行了
http-nio-8087-exec-7
com.example.test.ThreadLocalTestServiceImpl.getParentIdByName 执行了
http-nio-8087-exec-4
com.example.test.ThreadLocalTestServiceImpl.getParentIdByName 执行了
http-nio-8087-exec-9
com.example.test.ThreadLocalTestServiceImpl.getParentIdByName 执行了
http-nio-8087-exec-10
com.example.test.ThreadLocalTestServiceImpl.getParentIdByName 执行了
http-nio-8087-exec-2
com.example.test.ThreadLocalTestServiceImpl.getParentIdByName 执行了
http-nio-8087-exec-1
http-nio-8087-exec-3
http-nio-8087-exec-8
http-nio-8087-exec-5

可以很清楚的看到http-nio-8087-exec-1、3、5、8再次请求的时候getParentIdByName 方法并没有执行了,因为之前的threadlocal缓存没有被remove导致的。文章来源地址https://www.toymoban.com/news/detail-846763.html

到了这里,关于ThreadLocal加切面实现线程级别的方法缓存的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • ThreadLocal:线程中的全局变量

    最近接了一个新需求,业务场景上需要在原有基础上新增2个字段,接口新增参数意味着很多类和方法的逻辑都需要改变,需要先判断是否属于该业务场景,再做对应的逻辑。原本的打算是在入口处新增变量,在操作数据的时候进行逻辑判断将变量进行存储或查询。 如果全链

    2024年02月10日
    浏览(41)
  • 【Java】线程数据共享和安全 -ThreadLocal

     🎄欢迎来到@边境矢梦°的csdn博文🎄  🎄本文主要梳理线程数据共享和安全 -ThreadLocal🎄 🌈我是边境矢梦°,一个正在为秋招和算法竞赛做准备的学生🌈 🎆喜欢的朋友可以关注一下 🫰🫰🫰 ,下次更新不迷路🎆 Ps: 月亮越亮说明知识点越重要 (重要性或者难度越大)🌑🌒

    2024年02月09日
    浏览(43)
  • 【Java 并发编程】Java 线程本地变量 ThreadLocal 详解

    先一起看一下 ThreadLocal 类的官方解释: 用大白话翻译过来,大体的意思是: ThreadLoal 提供给了 线程局部变量 。同一个 ThreadLocal 所包含的对象,在不同的 Thread 中有不同的副本。这里有几点需要注意: 因为每个 Thread 内有自己的实例副本,且 该副本只能由当前 Thread 使用 。

    2024年02月04日
    浏览(70)
  • 【项目日记(四)】第一层: 线程缓存的具体实现

    💓博主CSDN主页:杭电码农-NEO💓   ⏩专栏分类:项目日记-高并发内存池⏪   🚚代码仓库:NEO的学习日记🚚   🌹关注我🫵带你做项目   🔝🔝 开发环境: Visual Studio 2022 由于此项目需要创建多个文件 所以我直接在.h文件中既放声明 也存放实现,减少文件的数量 本章重点: 本篇

    2024年01月25日
    浏览(22)
  • dede首页调用留言本标题的方法总结

    第一种方法:用feedback标签直接调用。 代码如下: [field:username function=\\\"(@me==\\\'guest\\\' ? \\\'游客\\\' : @me)\\\"/] 评论 [field:title/] [field:msg/] {/dede:feedback} 第二种方法:利用织梦的loop标签或SQL标签。 要调用这个标签,我们在这之前,应该事先了解,留言本模块的数据表为#@_guestbook !换成SQL标

    2024年02月03日
    浏览(48)
  • springboot的缓存和redis缓存,入门级别教程

    一、springboot(如果没有配置)默认使用的是jvm缓存 1、Spring框架支持向应用程序透明地添加缓存。抽象的核心是将缓存应用于方法,从而根据缓存中可用的信息减少执行次数。缓存逻辑是透明地应用的,对调用者没有任何干扰。只要使用@EnableCaching注释启用了缓存支持,Spri

    2024年02月07日
    浏览(52)
  • Spring 多数据源方法级别注解实现

    Spring框架提供了多种数据源管理方式,其中多数据源管理是其中之一。多数据源管理允许应用程序使用多个数据源,而不是只使用一个数据源,从而提高了应用程序的灵活性和可靠性。 多数据源管理的主要目的是让应用程序能够在不同的数据库之间切换,以满足不同的业务需

    2024年02月15日
    浏览(60)
  • java八股文面试[多线程]——ThreadLocal底层原理和使用场景

    源码分析: ThreadLocal中定义了ThreadLocalMap静态内部类,该内部类中又定义了Entry内部类。 ThreadLocalMap定了 Entry数组。 Set方法: Get方法: Thread中定义了两个ThreaLocalMap成员变量: Spring使用ThreadLocal解决线程安全问题  我们知道在一般情况下,只有 无状态的Bean 才可以在多线程环

    2024年02月10日
    浏览(51)
  • dede5.7修改标题title长度方法总结

    当我们大家好DEDE5.7CMS系统以后,添加资料的时候回发现官方默认的标题长度非常的短,从截图我们能看到默认长度是60,这个不能满足我们发内容的需求,所以我们要针对性的修改一下。 这里按照小编的经验,大家可以直接修改成200-255之间的字符长度,能够满足网站的标题

    2024年02月02日
    浏览(47)
  • H3C无线路由的配置方法的经验总结

    在配置H3C无线路由器之前,首先要做的是将电脑与无线路由器用网线连接起来,网线的另一端要接到无线路由器的LAN口上,建议按照无线路由器配置页面中的向导引导进行配置。   一、 有些人在使用无线路由器上网时,速度时快时慢,笔记本与无线路由器的距离很近,其实

    2024年02月05日
    浏览(37)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包