まず、春に付属する注釈を最初に理解します
- まず、スタートアップクラスでこの関数を有効にする必要があります
package com.frame.util;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
@SpringBootApplication
@EnableCaching
@MapperScan("com.frame.util.dao")
public class UtilApplication {
public static void main(String[] args) {
SpringApplication.run(UtilApplication.class, args);
}
}
Springboot独自のアノテーションには、主に@ Cacheable、@ CacheEvict、@ CachePutが含まれ、主にAOPプロキシによって実装されます。
1.@Cacheable
- @Cacheableは、クラスまたはメソッドでマークできます。メソッドでマークされている場合は、メソッドがキャッシュをサポートしていることを意味します。クラスでマークされている場合は、クラスのすべてのメソッドがキャッシュをサポートしていることを意味します。このアノテーションが付いたメソッドの場合、Springは最初に、メソッドの同じパラメーターの前にキャッシュがあるかどうかを判断します。ある場合は、メソッドのロジックを経由せずに直接戻ります。ない場合は、メソッドを再実行する必要があります。 -実行され、メソッドが実行されます。結果はキャッシュに入れられます。次の例を考えてみましょう。
package com.frame.util.service.redis;
import com.frame.util.dao.TbPhoneDao;
import com.frame.util.entity.dataObject.TbPhone;
import com.frame.util.entity.vo.ResultVo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
/**
* @Author: towards
* @CreateTime: 2022-07-16 11:33
* @Description: 缓存注解
* @Version: 1.0
*/
@Slf4j
@Service
public class CacheAnnotionServiceimpl {
private final String phoneKey = "PHONE_";
@Resource
private TbPhoneDao tbPhoneDao;
@Autowired
private RedisService redisService;
/**
* @description: 通过手机名获取手机
* @author: towards
* @date: 2022/7/16 11:37
* @param: name
* @return: com.frame.util.vo.ResultVo
**/
@Cacheable(cacheNames = "phoneCache",key = "#id")
public <T> ResultVo getPhone(Integer id) throws Exception {
T value = redisService.getKey(keyGenorate(id));
if (ObjectUtils.isNotEmpty(value)){
// 如果缓存中有,直接返回
log.info("query phone from redis");
return ResultVo.success(value);
}
// 缓存中没有,查询数据不为空时,存入缓存
TbPhone tbPhone = tbPhoneDao.queryById(id);
if (!ObjectUtils.isEmpty(tbPhone)){
log.info("query from mysql ,and save to redis");
redisService.setKey(keyGenorate(id), tbPhone);
return ResultVo.success(tbPhone);
}
// 缓存和数据库都没有
throw new Exception("query phone in redis and mysql is not exist");
}
/**
* @description: 根据id生成redis的key
* @author: towards
* @date: 2022/7/16 12:12
* @param: id
* @return: java.lang.String
**/
public String keyGenorate(Integer id){
String redisKey = phoneKey+id;
return redisKey;
}
}
- getPhoneメソッドがid=1で初めてアクセスされると、getPhoneメソッドは内部ロジックを実行し、その結果をphoneCacheという名前のキャッシュに入れます。次にメソッドがid = 1でアクセスされると、メソッドは実行されません。
- さまざまなシナリオでの使用を容易にするために説明する必要のあるいくつかのプロパティがあります。
- cacheNames / value:キャッシュコンポーネントの名前を指定するために使用されます
- key:データをキャッシュするときに使用されるキー。指定に使用できます。デフォルトでは、メソッドパラメータの値を使用します。(このキーはspEL式を使用して書き込むことができます)
- keyGenerator:キーのジェネレーター。keyとkeyGeneratorを交互に使用する
- cacheManager:キャッシュマネージャーを指定するために使用できます。キャッシュを取得するキャッシュマネージャー。
- 条件:条件が満たされた場合にのみキャッシュがキャッシュされるように指定するために使用できます
- ない限り:キャッシングを無効にします。untilで指定された条件がtrueの場合、メソッドの戻り値はキャッシュされません。もちろん、判断する結果を得ることができます。(#resultを介してメソッドの結果を取得します)
- sync:非同期モードを使用するかどうか。
- spel式をサポートするキャッシュキーを取得するには、さまざまな方法があります。その一部を次に示します。ブログの引用:https ://blog.csdn.net/zl1zl2zl3/article/details/110987968
- キーはkeyGeneratorによって生成できます
- カスタムKeyGenerator
package com.frame.util.config;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.lang.reflect.Method;
import java.util.Arrays;
/**
* @Author: towards
* @CreateTime: 2022-07-16 16:45
* @Description: TODO
* @Version: 1.0
*/
@Configuration
public class KeyGenerateConfig {
@Bean("myKeyGenerate")
public KeyGenerator keyGenerator(){
return new KeyGenerator() {
@Override
public Object generate(Object target, Method method, Object... params) {
return method.getName()+"["+ Arrays.asList(params).toString()+"]";
}
};
}
}
- keyGenerateをキャッシュキーとして使用する
@Cacheable(cacheNames = "phoneCache",keyGenerator = "myKeyGenerate")
public <T> ResultVo getPhone(Integer id) throws Exception {
T value = redisService.getKey(keyGenorate(id));
if (ObjectUtils.isNotEmpty(value)){
// 如果缓存中有,直接返回
log.info("query phone from redis");
return ResultVo.success(value);
}
// 缓存中没有,查询数据不为空时,存入缓存
TbPhone tbPhone = tbPhoneDao.queryById(id);
if (!ObjectUtils.isEmpty(tbPhone)){
log.info("query from mysql ,and save to redis");
redisService.setKey(keyGenorate(id), tbPhone);
return ResultVo.success(tbPhone);
}
// 缓存和数据库都没有
throw new Exception("query phone in redis and mysql is not exist");
}
2.@CachePut
- @CachePutは、キャッシュ機能をサポートするメソッドを宣言することもできます。@Cacheableとの違いは、@ CachePutで注釈が付けられたメソッドは、実行前にキャッシュに以前に実行された結果があるかどうかをチェックせず、毎回メソッドを実行し、実行結果をキー値の形式で格納することです。指定されたキャッシュ内のペア。
- キーや値などの属性値は@Cacheableと同じであり、キーの取得方法も同じです
- 通常、更新されたシナリオで使用されるため、キャッシュを更新する必要があります。次に、コードの実装について説明します。
@CachePut(cacheNames = "phoneCache" , keyGenerator = "myKeyGenerate")
public <T> ResultVo getPhone(Integer id) throws Exception {
T value = redisService.getKey(keyGenorate(id));
if (ObjectUtils.isNotEmpty(value)){
// 如果缓存中有,直接返回
log.info("query phone from redis");
return ResultVo.success(value);
}
// 缓存中没有,查询数据不为空时,存入缓存
TbPhone tbPhone = tbPhoneDao.queryById(id);
if (!ObjectUtils.isEmpty(tbPhone)){
log.info("query from mysql ,and save to redis");
redisService.setKey(keyGenorate(id), tbPhone);
return ResultVo.success(tbPhone);
}
// 缓存和数据库都没有
throw new Exception("query phone in redis and mysql is not exist");
}
- キャッシュの有無に関係なく、メソッドロジックは毎回実行されることがわかります。
3. @ CacheEvict
- allEntriesは、キャッシュ内のすべての要素をクリアするかどうかを示すブール型です。デフォルトはfalseです。これは、必須ではないことを意味します。allEntriesがtrueとして指定されている場合、SpringCacheは指定されたキーを無視します。キャッシュ内のすべての要素を一度にクリアする必要がある場合があります。これは、要素を1つずつクリアするよりも効率的です。
- デフォルトでは、対応するメソッドが正常に実行された後にクリーンアップ操作がトリガーされます。つまり、例外がスローされたためにメソッドが正常に戻らなかった場合、クリーンアップ操作はトリガーされません。beforeInvocationを使用して、クリア操作がトリガーされる時間を変更します。このプロパティの値をtrueに指定すると、Springはこのメソッドを呼び出す前にキャッシュ内の指定された要素をクリアします。
4.@キャッシング
- @Cachingアノテーションを使用すると、メソッドまたはクラスで同時に複数のSpringCache関連のアノテーションを指定できます。これには、cacheable、put、およびevictの3つのプロパティがあり、それぞれ@ Cacheable、@ CachePut、および@CacheEvictを指定するために使用されます。
@Caching(cacheable = @Cacheable("users"), evict = {
@CacheEvict("cache2"),
@CacheEvict(value = "cache3", allEntries = true) })
public User find(Integer id) {
returnnull;
}
5.@CacheConfig
- このクラスの下の一般的な構成を抽出するために使用されます。以下はソースコードです
package org.springframework.cache.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({
ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CacheConfig {
String[] cacheNames() default {
};
String keyGenerator() default "";
String cacheManager() default "";
String cacheResolver() default "";
}
- このように、クラスにマークを付けることができます
2.上記はSpring独自のキャッシュを使用しています。キャッシュ構成を変更してredisに変更できます。以下にプロセスについて説明します。
- 構成、設定
package com.frame.util.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.*;
import java.time.Duration;
/**
* @Description:
* @date: 2022/5/25 15:21
* @author: towards
* @since JDK 1.8
*/
/**
* 自定义 RedisTemplate
*/
@Configuration
public class RedisConfig {
@Autowired
private LettuceConnectionFactory lettuceConnectionFactory;
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
template.setConnectionFactory(factory);
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.activateDefaultTyping(
LaissezFaireSubTypeValidator.instance,
ObjectMapper.DefaultTyping.NON_FINAL,
JsonTypeInfo.As.WRAPPER_ARRAY);
jackson2JsonRedisSerializer.setObjectMapper(om);
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
// key 采用 String 的序列化方式
template.setKeySerializer(stringRedisSerializer);
// hash 的 key 也采用 String 的序列化方式
template.setHashKeySerializer(stringRedisSerializer);
// value 序列化方式采用 jackson
template.setValueSerializer(jackson2JsonRedisSerializer);
// hash 的 value 序列化方式采用 jackson
template.setHashValueSerializer(jackson2JsonRedisSerializer);
template.afterPropertiesSet();
return template;
}
@Bean
public RedisCacheManager redisCacheManager(RedisTemplate redisTemplate){
RedisCacheConfiguration configuration = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMillis(30000))//设置缓存过期时间
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));
RedisCacheManager build = RedisCacheManager.builder(lettuceConnectionFactory).cacheDefaults(configuration)
.build();
return build;
}
}
- 注釈を使用する
@Cacheable(cacheNames = "phoneCache",cacheManager = "redisCacheManager",keyGenerator = "myKeyGenerate")
public <T> ResultVo getPhone(Integer id) throws Exception {
T value = redisService.getKey(keyGenorate(id));
if (ObjectUtils.isNotEmpty(value)){
// 如果缓存中有,直接返回
log.info("query phone from redis");
return ResultVo.success(value);
}
// 缓存中没有,查询数据不为空时,存入缓存
TbPhone tbPhone = tbPhoneDao.queryById(id);
if (!ObjectUtils.isEmpty(tbPhone)){
log.info("query from mysql ,and save to redis");
// redisService.setKey(keyGenorate(id), tbPhone);
return ResultVo.success(tbPhone);
}
// 缓存和数据库都没有
throw new Exception("query phone in redis and mysql is not exist");
}
- キャッシュを表示