缓存Cache

前序:Spring Cache对Cache进行了抽象,提供了@Cacheable、@CachePut、@CacheEvict等注解。可用于大型系统或者分布式系统。应用系统需要通过Cache来缓存不经常改变的数据已提高系统性能,减少跟数据库的交互来增加系统的吞吐量。

一、Spring Boot Cache


Spring Boot本身提供了一个基于ConcurrentHashMap的缓存机制,也集成了ExCache2.x、JCache(JSR-107、EhCache3.x、Hazelcast、Infunispan),还有Couchbase、Redis等。Spring Boot应用通过注解的方式使用同一缓存,只需再方法上使用缓存注解即可,其缓存的具体实现依赖于选择的目标缓存管理器,使用@Cacheble

@Service("cacheServiceImpl")
public class CacheServiceImpl implements CacheService {


    @Override
    @Cacheable("user")
    public UserDO getUser(Integer id) throws Exception {
        return null;
    }

}

CacheService 作为一个容器管理Bean,Spring将生成代理类,在实际调用CacheService.getUser()方法前,会调用缓存管理器,取名为“user”的缓存,此时,缓存项的Key就是方法参数Id,如果缓存命中,则返回此值,如果没有找到,则进入实际的CacheService.getUser()方法。在返回调用结果给调用之前,还会将查询结果缓存以备下次使用。
集成Spring Cache

		<!-- Spring Cache缓存 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-cache</artifactId>
		</dependency>

要是使用Spring自带的内存缓存管理器,需要再application.properties中配置属性

spring.cache.type=Simple

Simple只适合单体应用或者开发环境,再或者是一个小系统。
需要使用注解@EnableCaching打开缓存功能。

@SpringBootApplication
@EnableCaching
public class ElectricityApplication {

	public static void main(String[] args) {
		SpringApplication.run(ElectricityApplication.class, args);
	}
	
	/**
     * 跨域
     */
    @Bean
    public WebMvcConfigurer corsConfigurer() {
        return new WebMvcConfigurerAdapter() {
            @Override
            public void addCorsMappings(CorsRegistry registry) {
                registry.addMapping("/**").allowedOrigins("*");
            }
        };
    }
}

一旦配置好Spring Boot缓存,就可以在Spring管理的Bean中使用缓存注解,通常可以直接放在Service类上

  • @Cacheable,作用在方法上,触发缓存读取操作。
  • @CacheEvivt,作用在方法上,触发缓存失效操作。
  • @CachePut,作用在方法上,触发缓存更新操作。
  • @Cache,作用在方法上,综合上面的各种操作,在有些场景下,调用业务会触发多种缓存操作。
  • @CacheConfig,在类上设置当前缓存的一些公共设置。

1.@Cacheable


注解Cacheable声明了方法的结果是缓存的,如果缓存存在,则目标方法不被调用,直接取出缓存,可以为方法声明多个缓存,如果至少有一个缓存项,则其缓存项将被返回
在这里我们先打印一下数据库com.xiaofeixia.lcc.dao这里是自己写的Mysql路径

logging.level.com.xiaofeixia.lcc.dao=debug

@RestController
public class CacheController {

    @Autowired
    private CacheService cacheService;
    
    @GetMapping("/cache/{id}")
    public UserDO cache(
            @PathVariable Integer id
            ) throws Exception{
        UserDO userDO = cacheService.getUser(id);
        return userDO;
    }
    
}

注意:对于不同的缓存实现,要将对象序列化,这样无缝切换到分布式缓存系统

public class UserDO implements Serializable  {...........}

访问接口http://localhost:8080/cache/2
第一次请求,这里显示访问了数据库,和前台得到的数据。
在这里插入图片描述
在这里插入图片描述

第二次访问 我们看到日志没有任何打印东西,页面还是显示这样数据,是直接读取的缓存,没有操作数据库

2.Key生成器


缓存的Key非常重要,Spring使用了KeyGenerator类来根据方法参数生成Key,有以下规则。
如果只有一个参数,这个参数就是Key

@Override
    @Cacheable("user")
    public UserDO getUser(Integer id) throws Exception {
        return userDAO.getUserById(id);
    }

要是没有参数,则返回SimpleKey.EMPTY,比如构造系统的菜单树,因为系统只有唯一的一个菜单树,所以不用指定参数,Key值是SimpleKey.EMPTY。

@Override
    @Cacheable("user")
    public UserDO getUser(  ) throws Exception {
       . . . . . . . . . . . . . . . . . . . .
    }

要是有多个参数,则返回包含多个参数的SimpleKey。

@Override
    @Cacheable("user")
    public UserDO getUser(Integer Id, Integer shopId,Integer goodId) throws Exception {
       . . . . . . . . . . . . . . . . . . . .
    }

Spring使用SimpleKeyGenerator来实现上述Key的生成:

public class SimpleKeyGenerator implements KeyGenerator {

	@Override
	public Object generate(Object target, Method method, Object... params) {
		return generateKey(params);
	}

	/**
	 * Generate a key based on the specified parameters.
	 */
	public static Object generateKey(Object... params) {
		if (params.length == 0) {
			return SimpleKey.EMPTY;
		}
		if (params.length == 1) {
			Object param = params[0];
			if (param != null && !param.getClass().isArray()) {
				return param;
			}
		}
		return new SimpleKey(params);
	}

}

也可以自定义KeyGenerator方法。

@Component("myKeyGenerator")
public class MyKeyGenerator implements KeyGenerator {
    @Override
    public Object generate(Object target, Method method, Object... params) {
        //返回后缀名
        //return method.getName();
        
        //注意,这里不能返回null,否则会报错
        return "";
    }
}

注解使用

@Cacheable(value = "listAll", keyGenerator = "myKeyGenerator")

也可以通过条件缓存,比如id大于100的都需要缓存

@Override
    @Cacheable(cacheNames = "user",condition = "#id>100")
    public UserDO getUser(Integer id) throws Exception {
        return userDAO.getUserById(id);
    }

返回值status为0,不缓存

@Override
    @Cacheable(cacheNames = "user",condition = "#result.status==0")
    public UserDO getUser(Integer id) throws Exception {
        return userDAO.getUserById(id);
    }

3.@CachePut


注解CachePut总是会执行的方法体,并且使用返回的结果来更新缓存,同Cacheable一样,支持condition、unless、key选项,也支持KeyGenerator。

    @Override
    @CachePut(cacheNames="user")
    public Integer updateUser(Integer id) throws Exception {
        
        return null;
    }

更新完之后,再次请求查询接口,就会再次请求数据库

4.@CacheEvict


注解CacheEvict用于删除缓存项或者清空缓存,CacheEvict可以指定多个缓存名字来清空多个缓存

    @Override
    @CachePut(cacheNames="user",key=“#id”)
    public Integer updateUser(Integer id) throws Exception {
        
        return null;
    }

清空“user”缓存中键值为id的缓存项。比如一个缓存用不到了,就删掉吧。

5.@Caching


注解Caching可以混合以上的各种注解,可以在Caching标签中混合@Cacheable、@CachePut、@CacheEvict。比如一个修改同时需要失效对应的用户缓存和用户扩展信息缓存。

    @Override
    @Caching(evict = {@CacheEvict(cacheNames = "user"),@CacheEvict(cacheNames = "test")})
    @CachePut(cacheNames="user")
    public Integer updateUser(Integer id) throws Exception {
        
        return null;
    }

6.@CacheConfig


到目前为止,所有的Cache注解都需要提供Cache名称,如果每个Service方法上都包含Cache名称,可能写起来重复。注解CacheConfig作用于类上,可以为此类的方法的缓存注解提供默认值

  • 缓存的默认名称
  • 缓存的KeyGenerator
@Service("cacheServiceImpl")
@CacheConfig(cacheNames = "user")
public class CacheServiceImpl implements CacheService {

    @Autowired
    private UserDAO userDAO;

    @Override
    @Cacheable
    public UserDO getUser(Integer id) throws Exception {
        return userDAO.getUserById(id);
    }

    @Override
    @CachePut
    public Integer updateUser(Integer id) throws Exception {
        // TODO 自动生成的方法存根
        return null;
    }
    
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值