springbootによって提供されるキャッシュ注釈
クエリデータベースサービスを作成するときは、redisに必要なデータのキャッシュがあるかどうかをクエリする必要があります。キャッシュがある場合は、データベースにクエリを実行する必要がないため、クエリ速度が向上し、データベースへの負荷が軽減されます。
springbootを使用してredisを統合する場合、誰もが@Cacheableアノテーションに精通していると思います。
たとえば、サービスがデータベースにクエリを実行する前に、キャッシュでクエリを実行する必要があります。データベースにクエリを実行しない場合は、クエリを実行しません。redisTemplateを構成するだけで済みます。次のように、@ EnableCachingアノテーションをクラスに追加し、SpringCacheアノテーション関数を構成します。
package com.sunyuqi.config;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
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.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
@EnableCaching
class RedisTemplateConfig {
@Bean
public LettuceConnectionFactory redisConnectionFactory() {
return new LettuceConnectionFactory(new RedisStandaloneConfiguration("127.0.0.1", 6379));
}
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate redisTemplate = new RedisTemplate();
redisTemplate.setConnectionFactory(redisConnectionFactory);
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer());
return redisTemplate;
}
// 配置Spring Cache注解功能
@Bean
public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory);
RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig();
RedisCacheManager cacheManager = new RedisCacheManager(redisCacheWriter, redisCacheConfiguration);
return cacheManager;
}
}
次に、サービスメソッドに注釈を追加します
@Cacheable(cacheManager = "cacheManager", value = "testcache", key = "#userId")
public User findUserById(String userId) throws Exception {
//此处编写查询数据库代码
User user = ....
return user;
}
メソッドが実行される前に、redisキャッシュが照会されます。結果が見つかった場合、メソッドは実行されません。結果がない場合、メソッドが実行されます。
キャッシュクエリロジックをカスタマイズする必要がある場合は、注釈を定義する必要があります。
カスタムキャッシュアノテーション
関連する依存関係をインポートする
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.9</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.9</version>
</dependency>
まず、注釈を定義する必要があります
package com.sunyuqi.mycache.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* cache注解
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Cache {
String key();
}
注釈はメソッドで定義され、パラメータキーを渡す必要があります。
AOPを定義する
package com.sunyuqi.mycache.aop;
import com.sunyuqi.mycache.annotations.Cache;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
@Component
@Aspect
public class CacheAspect {
@Autowired
private RedisTemplate redisTemplate;
@Pointcut("@annotation(com.sunyuqi.mycache.annotations.Cache)")
public void cachePointcut() {
}
// 定义相应的事件
@Around("cachePointcut()")
public Object doCache(ProceedingJoinPoint joinPoint) {
Object value = null;
try {
// 获取当前方法上注解的内容
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
// 获取被注解的方法
Method method = joinPoint.getTarget().getClass().getMethod(signature.getName(), signature.getMethod().getParameterTypes());
Cache cacheAnnotation = method.getAnnotation(Cache.class);
// 获取到传入注解中的key的值 即"#userID"(我们需要EL解析)
String keyEl = cacheAnnotation.key();
// 创建解析器
ExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression(keyEl);
EvaluationContext context = new StandardEvaluationContext();
// 添加参数
Object[] args = joinPoint.getArgs();
DefaultParameterNameDiscoverer discover = new DefaultParameterNameDiscoverer();
String[] parameterNames = discover.getParameterNames(method);
for (int i = 0; i < parameterNames.length; i++) {
context.setVariable(parameterNames[i], args[i].toString());
}
// 解析EL表达式
String key = expression.getValue(context).toString();
// 查询缓存,并判定缓存中是否存在
value = redisTemplate.opsForValue().get(key);
if (value != null) {
System.out.println("从缓存中查询到结果:" + value);
return value;
}
// 缓存中没有结果,执行被注解的方法
value = joinPoint.proceed();
// 将结果存入缓存
redisTemplate.opsForValue().set(key, value);
} catch (Throwable throwable) {
throwable.printStackTrace();
}
return value;
}
}
メソッドにカスタム注釈を使用する
@Cache(key = "#userId")
public User findUserById(String userId) throws Exception {
//此处编写查询数据库代码
User user = ....
return user;
}
使いやすいですか?
もちろん、redistemplateを構成することを忘れないでください
package com.sunyuqi.mycache.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.context.annotation.Profile;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
@EnableAspectJAutoProxy
class MyRedisConfig {
@Bean
public LettuceConnectionFactory redisConnectionFactory() {
return new LettuceConnectionFactory(new RedisStandaloneConfiguration("127.0.0.1", 6379));
}
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate redisTemplate = new RedisTemplate();
redisTemplate.setConnectionFactory(redisConnectionFactory);
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer());
return redisTemplate;
}
}
ここに、すべての人に思い出させるポイントが
あります。redisに格納するオブジェクトは、実際にはシリアル化された文字列です。
デシリアライズの過程で、つまり、redisからキャッシュを照会してオブジェクトに変換するときに、オブジェクトの現在のパッケージパスが、格納されているオブジェクトのパッケージパスと異なる場合、デシリアライズは失敗します。