ehcache redis 双缓存(aop + 自定义注解)
查询数据:先查询ehcache缓存,如果没有数据,再查询redis缓存;如果查询不到,查询后端数据库
插入数据:后端数据库插入数据后,同时插入ehcache、redis缓存
更新数据:后端数据库更新数据后,同时更新ehcache、redis缓存
删除数据:后端数据库删除数据时,同时删除ehcache、redis缓存
****************************
示例
**********************
pojo 层
Person
@Data
public class Person implements Serializable {
private Integer id;
private String name;
private Integer age;
}
**********************
myannotation 层
CustomCacheCacheable:自定义查询注解
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomCacheable { //缓存查询注解
String value() default "";
/*
key为方法参数名、参数名的属性
Person fun(String name,Person person){} //key可用格式:"#name"、“#person.id”
*/
String key() default "";
}
CustomCachePut:自定义缓存添加注解
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomCachePut { //缓存添加注解
String value() default "";
/*
key为方法参数名、参数名的属性、返回值、返回值的属性
Person fun(String name,Person person){} //key可用格式:"#name"、“#person.id”、“#result”、"#result.id"
*/
String key() default "";
}
CustomCacheEvict:自定义缓存删除注解
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomCacheEvict { //缓存删除注解
String value() default "";
/*
key为方法参数名、参数名的属性
Person fun(String name,Person person){} //key可用格式:"#name"、“#person.id”、“#result”、“#result.id”
*/
String key() default "";
}
**********************
aop 层
CustomAspect
@Aspect
@Component
public class CustomAspect {
private final Logger logger= LoggerFactory.getLogger(this.getClass().getName());
@Resource(name = "ehcacheCacheManager")
private CacheManager cacheManager;
@Resource(name = "redisCacheManager")
private RedisCacheManager redisCacheManager;
@Pointcut("@annotation(com.example.demo.myannotation.CustomCacheable)")
public void fun(){
}
@Pointcut("@annotation(com.example.demo.myannotation.CustomCachePut)" +
"||@annotation(com.example.demo.myannotation.CustomCacheEvict)")
public void fun2(){
}
@Around("fun()")
public Person CustomCacheable(ProceedingJoinPoint joinPoint){
Person result=null;
Method method=((MethodSignature)joinPoint.getSignature()).getMethod();
CustomCacheable cacheable=method.getDeclaredAnnotation(CustomCacheable.class);
String value=cacheable.value();
String key=getKeyValue(joinPoint,method,cacheable.key());
Cache<String, Person> cache=cacheManager.getCache(value,String.class,Person.class);
result=cache.get(key);
if (result!=null){
return result;
}else {
org.springframework.cache.Cache redisCache=redisCacheManager.getCache(value);
assert redisCache != null;
result=redisCache.get(key,Person.class);
if (result!=null){
return result;
}else {
try {
result=(Person) joinPoint.proceed();
cache.put(key,result);
redisCache.put(key,result);
}catch (Throwable e){
e.printStackTrace();
}
}
}
return result;
}
@Around("fun2()")
public Person CustomCachePutOrEvict(ProceedingJoinPoint joinPoint){
Person result=null;
Method method=((MethodSignature)joinPoint.getSignature()).getMethod();
String keyValue;
if (method.isAnnotationPresent(CustomCachePut.class)){
CustomCachePut cachePut=method.getDeclaredAnnotation(CustomCachePut.class);
String value=cachePut.value();
String key=cachePut.key();
Cache<String, Person> cache=cacheManager.getCache(value,String.class,Person.class);
org.springframework.cache.Cache redisCache=redisCacheManager.getCache(value);
try {
result=(Person) joinPoint.proceed();
keyValue=getKeyValue2(joinPoint,method,key,result);
cache.put(keyValue,result);
assert redisCache != null;
redisCache.put(keyValue,result);
}catch (Throwable e){
e.printStackTrace();
}
}else {
CustomCacheEvict cacheEvict=method.getDeclaredAnnotation(CustomCacheEvict.class);
String value=cacheEvict.value();
String key=cacheEvict.key();
Cache<String,Person> cache=cacheManager.getCache(value,String.class,Person.class);
org.springframework.cache.Cache redisCache=redisCacheManager.getCache(value);
try {
result=(Person) joinPoint.proceed();
keyValue=getKeyValue2(joinPoint,method,key,result);
cache.remove(keyValue);
assert redisCache != null;
redisCache.evictIfPresent(keyValue);
}catch (Throwable e){
e.printStackTrace();
}
}
return result;
}
private String getKeyValue(ProceedingJoinPoint joinPoint,Method method,String key) {
if (key==null||!key.startsWith("#")){
return null;
}
String result=null;
if (!key.contains(".")){
String name=key.substring(1);
Object[] args=joinPoint.getArgs();
Parameter[] parameters=method.getParameters();
for (int i=0;i<parameters.length;i++){
if (parameters[i].getName().equals(name)){
result=args[i].toString();
break;
}
}
}else {
String[] s=key.substring(1).split("\\.");
Object[] args = joinPoint.getArgs();
Parameter[] parameters=method.getParameters();
for (int i=0;i<parameters.length;i++){
if (parameters[i].getName().equals(s[0])){
try {
Field field=parameters[i].getType().getDeclaredField(s[1]);
field.setAccessible(true);
result= field.get(args[i]).toString();
}catch (Exception e){
e.printStackTrace();
}
}
}
}
return result;
}
private String getKeyValue2(ProceedingJoinPoint joinPoint,Method method,String key,Person returnValue) {
if (key==null||!key.startsWith("#")){
return null;
}
String result=null;
if (!key.contains(".")){
String name=key.substring(1);
if (!name.equals("result")){
Object[] args=joinPoint.getArgs();
Parameter[] parameters=method.getParameters();
for (int i=0;i<parameters.length;i++){
if (parameters[i].getName().equals(name)){
result=args[i].toString();
break;
}
}
}else {
Class<?> returnType=method.getReturnType();
try {
Field field=returnType.getDeclaredField(name);
field.setAccessible(true);
result = field.get(returnValue).toString();
}catch (Exception e){
e.printStackTrace();
}
}
}else {
String[] s=key.substring(1).split("\\.");
if (!s[0].equals("result")){
Object[] args = joinPoint.getArgs();
Parameter[] parameters=method.getParameters();
for (int i=0;i<parameters.length;i++){
if (parameters[i].getName().equals(s[0])){
try {
Field field=parameters[i].getType().getDeclaredField(s[1]);
field.setAccessible(true);
result= field.get(args[i]).toString();
}catch (Exception e){
e.printStackTrace();
}
}
}
}else {
Class<?> returnType=method.getReturnType();
try {
Field field=returnType.getDeclaredField(s[1]);
field.setAccessible(true);
result = field.get(returnValue).toString();
}catch (Exception e){
e.printStackTrace();
}
}
}
return result;
}
}
**********************
config 层
CacheConfig
@EnableCaching
@Configuration
public class CacheConfig {
@Bean("ehcacheCacheManager")
public CacheManager initEhcacheCacheManager(){
return CacheManagerBuilder.newCacheManagerBuilder()
.withCache("custom", CacheConfigurationBuilder
.newCacheConfigurationBuilder(String.class, Person.class,
ResourcePoolsBuilder.newResourcePoolsBuilder()
.heap(10, MemoryUnit.MB)
)).build(true);
}
@Bean("redisCacheManager")
public RedisCacheManager initRedisCacheManager(RedisConnectionFactory connectionFactory){
RedisCacheConfiguration cacheConfiguration=RedisCacheConfiguration.defaultCacheConfig()
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new JdkSerializationRedisSerializer()))
.entryTtl(Duration.ofMinutes(2L))
.disableCachingNullValues();
RedisCacheConfiguration defaultCacheConfiguration=RedisCacheConfiguration.defaultCacheConfig()
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new JdkSerializationRedisSerializer()))
.entryTtl(Duration.ofMinutes(1L))
.disableCachingNullValues();
Map<String,RedisCacheConfiguration> map=new HashMap<>();
map.put("custom",cacheConfiguration);
map.put("default",defaultCacheConfiguration);
return RedisCacheManager.builder(connectionFactory)
.cacheDefaults(defaultCacheConfiguration)
.withInitialCacheConfigurations(map)
.transactionAware()
.build();
}
}
**********************
service 层
HelloService
public interface HelloService {
Person getById(Integer id);
Person getByPerson(Person person);
Person put(Integer id,String name,Integer age);
Person put2(Person person);
Person deleteById(Integer id);
Person delete2(Person person);
}
**********************
serviceImpl 层
HelloServiceImpl
@Service
public class HelloServiceImpl implements HelloService {
private final Map<String,Person> map=new HashMap<>();
@Override
@CustomCacheable(value = "custom",key = "#id")
public Person getById(Integer id) {
System.out.println("查询数据:getById");
if (map.containsKey(id.toString())){
return map.get(id.toString());
}
return new Person();
}
@Override
@CustomCacheable(value = "custom",key = "#person.id")
public Person getByPerson(Person person) {
System.out.println("查询数据:getByPerson");
if (!map.containsKey(person.getId().toString())){
return new Person();
}
return map.get(person.getId().toString());
}
@Override
@CustomCachePut(value = "custom",key = "#id")
public Person put(Integer id, String name, Integer age) {
System.out.println("插入数据:id、name、age");
Person person=new Person();
person.setId(id);
person.setName(name);
person.setAge(age);
map.put(id.toString(),person);
return person;
}
@Override
@CustomCachePut(value = "custom",key = "#person.id")
public Person put2(Person person) {
System.out.println("插入数据:person");
map.put(person.getId().toString(),person);
return person;
}
@Override
@CustomCacheEvict(value = "custom",key = "#id")
public Person deleteById(Integer id) {
System.out.println("删除数据:deleteById");
Person person=map.get(id.toString());
map.remove(id.toString());
return person;
}
@Override
@CustomCacheEvict(value = "custom",key = "#result.id")
public Person delete2(Person person) {
System.out.println("删除数据:person");
map.remove(person.getId().toString());
return person;
}
}
**********************
controller 层
HelloController
@RestController
public class HelloController {
@Resource
private HelloService helloService;
@RequestMapping("/get")
public Person get(){
return helloService.getById(1);
}
@RequestMapping("/get2")
public Person getBYPerson(){
Person person=new Person();
person.setId(2);
person.setName("瓜田李下 2");
person.setAge(21);
return helloService.getByPerson(person);
}
@RequestMapping("/put")
public Person put(){
return helloService.put(1,"瓜田李下",20);
}
@RequestMapping("/put2")
public Person put2(){
Person person=new Person();
person.setId(2);
person.setName("瓜田李下 2");
person.setAge(21);
return helloService.put2(person);
}
@RequestMapping("/delete")
public Person delete(){
return helloService.deleteById(1);
}
@RequestMapping("/delete2")
public Person delete2(){
Person person=new Person();
person.setId(2);
person.setName("瓜田李下 2");
person.setAge(21);
return helloService.delete2(person);
}
}
****************************
使用测试
缓存插入 byId:localhost:8080/get、localhost:8080/put、localhost:8080/get,控制台输出:
查询数据:getById
插入数据:id、name、age
第一次查询:缓存里面没有数据,执行方法体
第二次查询:缓存里面有数据,直接返回缓存里面的数据
缓存删除 byId:localhost:8080/delete、loclahost:8080/get,控制台输出:
删除数据:deleteById
查询数据:getById
由于删除了缓存,查询时执行方法体返回数据
缓存插入 byPerson:loclahost:8080/get2、localhost:8080/put2、loclahost:8080/get2,控制台输出
查询数据:getByPerson
插入数据:person
第一次查询:缓存里面没有数据,执行方法体
第二次查询:缓存里面存有数据,直接从缓存里面获取数据
缓存删除 byPerson:localhost:8080/delete2、localhost:8080/get2,控制台输出:
删除数据:person
查询数据:getByPerson
由于缓存删除了,查询时执行方法体查询数据