Spring之路(49)–Spring缓存性能提升如升天?但你真的用对了吗?

兄弟们,今日头条搜索三线城市程序员老陈关注我,我将持续不断推出视频教程。

背景

Spring缓存,用了确实爽,性能的提升就像俺升天了那么爽快,但是如果理解不够深,不够准确的话,会带来灾难性的问题。

比如该使用缓存的时候,实际上并没有使用缓存,这种情况,相当于缓存无效。

比如不该使用缓存的时候,缓存却跳出来了,这种情况就可怕了,意味着你拿到了不该拿的数据。

所以本文就以实际的例子,演示下Spring缓存中那些需要注意的点。

同一缓存下,只看参数不看方法名

如下面的例子

	@Cacheable("blogs")
	public List<BlogDo> getListAsc() {
		System.out.println("升序获取blog列表");
		return null;
	}

	@Cacheable("blogs")
	public List<BlogDo> getListDesc() {
		System.out.println("降序获取blog列表");
		return null;
	}

本意是想有两个缓存,分别缓存升序的blog列表和降序的blog列表,但是由于这两个方法都是使用的名为blogs的缓存,且都没有参数,导致第二个方法会将第一个方法执行的缓存取出来:

	public static void main(String[] args) throws SQLException {
		// 获取容器
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
		// 获取blogService组件
		BlogService blogService = context.getBean("blogService", BlogService.class);
		//输出:升序获取blog列表
		blogService.getListAsc();
		//没有输出,因为直接取缓存了
		blogService.getListDesc();
	}

那么这种情况该如何处理呢,有以下几种处理办法:

  1. 改为不同的缓存名称,比如一个用@Cacheable("blogsAsc"),另一个用@Cacheable("blogsDesc")。这种方法十分不推荐,因为都是面向的blog这个表,用了两个缓存,那么清除缓存的时候咋办?很麻烦!
  2. 使用参数区分,加一个枚举类型表示升序和降序,方法改为getListAsc(SortEnum.ASC)getListAsc(SortEnum.ASC)。这种方法也不推荐,因为太麻烦了。
  3. 推荐方法是:既然都是取的博客列表,直接定义一个getList方法即可,然后对该方法添加缓存。至于排序的事情,自己取出结果后排序就是了。

缓存方法调用判断的是对象相等,而不是数值相等

如下面的例子:

	@Cacheable("blogs")
	public Long getLong(Long a) {
		System.out.println("getLong");
		return a+1;
	}
	@Cacheable("blogs")
	public Integer getInteger(Integer a) {
		System.out.println("getInteger");
		return a+2;
	}

感觉上,如果都是对数字1进行查询,应该能触发缓存,实际上并没有,就是因为这两个对象并不相等

public class Main {
	public static void main(String[] args) throws SQLException {
		// 获取容器
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
		// 获取blogService组件
		BlogService blogService = context.getBean("blogService", BlogService.class);
		//测试
		blogService.getLong(1L);// 输出getLong
		blogService.getLong(1L);// 没有输出,因为已经有缓存
		blogService.getInteger(1);// 输出getInteger因为参数并不相等
		Integer a = 1;
		Long b = 1L;
		System.out.println(a.equals(b));// 输出false,证明这两个参数实际上不相等
	}
}

如果参数是对象,一定要实现.equals和hashcode

在上面我们已经说明了,缓存参数的触发,是按对象是否相等来实现的,如果没有实现.equals和hashcode,就会出现:

	BlogDo blog1=new BlogDo();
   	blog1.setId(1L);
   	blogService.getByObject(blog1);
   	blogService.getByObject(blog1);//触发缓存
   	BlogDo blog2=new BlogDo();
   	blog2.setId(1L);
   	blogService.getByObject(blog2);//没触发缓存,因为blog1与blog2不同

注意,因为是map结构,务必要同时实现.equals和hashcode,否则判断也不准确!

总结

如果感觉还不清楚的话,还可以在调试模式下去查看CacheManager的具体内容,如下:

		// 获取容器
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
		// 获取blogService组件
		BlogService blogService = context.getBean("blogService", BlogService.class);
		//测试
		blogService.getLong(1L);// 输出getLong
		blogService.getLong(1L);// 没有输出,因为已经有缓存
		blogService.getInteger(1);// 输出getInteger因为参数并不相等
		//查看缓存
		CacheManager cacheManager=context.getBean("cacheManager", CacheManager.class);

此时我们查看缓存中的内容就好理解了,此处感兴趣的可以自己去试下,我不再详细分析了。
在这里插入图片描述

发布了376 篇原创文章 · 获赞 245 · 访问量 54万+

猜你喜欢

转载自blog.csdn.net/woshisangsang/article/details/104310240