使用AOP自己实现SpringCache的功能

总结

  1. SpringCache定义了@Cacheable、@CachePut、@CacheEvict三个注解,分别对应查询、更新、删除时对缓存的处理
  2. 只需要在Server类添加该注解,就可以通过AOP自动代理实现缓存查询、缓存更新、缓存删除,使用形式如下,@Cacheable、@CachePut会在unless条件成立时将返回值添加进缓存,而@Cacheable在有缓存时不会执行Server类方法。@CacheEvict会直接删除缓存名为value的缓存中的键值对的键为key的键值对
 @Override
 @Cacheable(value = "userCache", key = "#id", unless = "#result == null")
 public User getUserById(Long id) {
     System.out.println("运行");
     return userMapper.selectById(id);
 }

 @Override
 @CachePut(value = "userCache", key = "#user.id", unless = "#result == null")
 public User updateUser(User user) {
     userMapper.updateById(user);
     return userMapper.selectById(user.getId());
 }

 @Override
 @CacheEvict(value = "userCache", key = "#id")
 public void deleteUserById(Long id) {
     userMapper.deleteById(id);
 }
  1. 缓存的name为userCache,也就是下面接口方法对应的缓存名字,key就是通过name获取后,该缓存类似Map结构,可以根据key存储值和取出值,@Cacheable也是根据每次需要执行的代码key是否在缓存中存在来决定是否执行,存在则不执行

  2. 而SpringCache通过注解实现缓存实际上的逻辑是在CacheManager和Cache中定义的,在CacheManager使用缓存名字value,获取缓存Cache,然后缓存Cache中定义了对键值对增删查改的方法,通过这种接口抽象,可以自定义缓存,也可以整合第三方缓存,如本地缓存Caffeine和分布式缓存Redisson

自己实现SpringCache的功能

项目地址

https://gitee.com/chen_yan_ting/springboot_cache

项目结构

在这里插入图片描述

自定义缓存

定义如下三个注解, @MyCacheable, @MyCacheable

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyCacheable {
    String value();
    String key();
}

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyCachePut {
    String value();
    String key();
}

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyCacheEvict {
    String value();
    String key();
}

AOP实现类

  1. 使用ProceedingJoinPoint获得被代理类的类对象,进而获取其方法列表Method[]
  2. 通过ProceedingJoinPoint的Signature可以获得被代理类运行的方法名,通过方法名在Method[]找到目标方法targetMethod
  3. 获取目标方法targetMethod上的@MyCacheable注解,再获取注解的value对应缓存的名字,获取key对应缓存中键值对的键,一个value对应一个缓存即一个Map,一个key对应Map中的键值对
  4. 再通过LocalVariableTableParameterNameDiscoverer获取方法参数名字,将key从名字替换成具体的参数值arg
  5. 通过value从cacheManager获取缓存cache,再通过具体的参数值arg作为键key往Map中添加键值对,如果存在键值对则无须执行原来的方法,直接返回缓存的值
@Component
@Aspect
public class CacheableAspact {
    @Autowired
    private CacheManager cacheManager;

    @Pointcut("@annotation(com.lolxxs.annotation.MyCacheable)")
    public void pointcut() {

    }

    @Around("pointcut()")
    public Object round(ProceedingJoinPoint point) {
        System.out.println("代理类运行1");
        // 获取被代理类的签名
        Signature signature = point.getSignature();
        // 获取被代理类对象
        Object target = point.getTarget();
        // 获取被代理类class对象
        Class declaringType = signature.getDeclaringType();
        // 获取类中的方法
        Method[] methods = declaringType.getMethods();
        Method targetMethod = null;
        for (Method method : methods) {
            // 如果该方法名和AOP代理切入点方法名相同

            if (method.getName().equals(signature.getName())) {
                targetMethod = method;
                break;
            }
        }
        Cache cache = null;
        Object arg = null;
        if (targetMethod != null) {
            MyCacheable myCacheable = (MyCacheable)targetMethod.getAnnotation(MyCacheable.class);
            if (myCacheable != null) {
                // 获取缓存名
                String value = myCacheable.value();
                // 获取缓存中的键,其实是参数名
                String key = myCacheable.key();

                cache = cacheManager.getCache(value);
                // 获取方法的参数列表
                Parameter[] parameters = targetMethod.getParameters();
                Object[] args = point.getArgs();
                LocalVariableTableParameterNameDiscoverer localVariableTableParameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();

                // 获取方法名称列表
                String[] parameterNames = localVariableTableParameterNameDiscoverer.getParameterNames(targetMethod);

                //遍历方法名称列表
                int i = 0;
                for (String parameter: parameterNames) {
                    if (parameter.equals(key)) {
                        //找到指定名称的参数
                        arg = args[i];
                    }
                    i++;
                }

                if (cache != null && arg != null) {
                    Cache.ValueWrapper valueWrapper = cache.get(arg);
                    if (valueWrapper != null) {
                        return valueWrapper.get();
                    }
                }
            }
        }

        // 正常执行原本方法逻辑
        Object proceed = null;
        Object[] args = point.getArgs();
        try {
            proceed = point.proceed(args);
            if (cache != null && arg != null) {
                cache.put(arg, proceed);
            }
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return proceed;
    }
}

自定义缓存实现类

主要实现官方提供的两个接口CacheManager接口和Cache接口
可以看我这篇文章 https://blog.csdn.net/weixin_45754452/article/details/123942273

使用

直接对Server类进行AOP切面, @MyCacheable走缓存则不执行指定方法对应GET操作,@MyCachePut必须执行指定方法且会更新缓存对应Update操作, @MyCacheEvict一定会执行指定方法,且会删除缓存对应Delete操作

@Service("userServiceImpl")
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
    @Autowired
    private UserMapper userMapper;

    @MyCacheable(value = "userCache", key = "id")
    public User getUserById(Long id) {
        System.out.println("运行");
        return userMapper.selectById(id);
    }

    @MyCachePut(value = "userCache", key = "id")
    public User updateUser(Long id, User user) {
        userMapper.updateById(user);
        return userMapper.selectById(user.getId());
    }

    @MyCacheEvict(value = "userCache", key = "id")
    public void deleteUserById(Long id) {
        userMapper.deleteById(id);
    }
}
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: 使用SpringAOP可以实现缓存功能。具体实现方式是,在需要缓存的方法上添加@Cacheable注解,指定缓存的key和缓存的名称。当方法被调用时,Spring会先检查缓存中是否存在该key对应的缓存数据,如果存在则直接返回缓存数据,否则执行方法并将返回结果缓存起来。同时,还可以使用@CachePut注解更新缓存数据,或者使用@CacheEvict注解清除缓存数据。这样可以有效地提高系统性能和响应速度。 ### 回答2: 使用SpringAOP实现缓存功能的步骤如下: 1. 首先,需要在Spring配置文件中启用AOP功能,可以通过添加`<aop:aspectj-autoproxy/>`来实现。 2. 然后,创建一个用于缓存方法调用结果的类,该类需要实现`org.springframework.cache.Cache`接口,并提供对缓存的读取、写入、删除等操作方法。 3. 还需要创建一个切面类,该类需要使用`@Aspect`注解进行标记,并在需要缓存的方法上添加`@Cacheable`注解。在切面类中,使用`@Before`和`@After`等注解来定义缓存操作的切点和通知。 4. 在Spring配置文件中,将切面类声明为一个bean,并在`<aop:config>`中指定要应用缓存的方法和切面。 5. 最后,配置`ehcache.xml`(或其他缓存配置文件),并将其指定为Spring配置文件中缓存管理器的实现类,例如`<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager"/>`。 这样,当被缓存的方法被调用时,AOP将切入切面类的通知方法,首先查询缓存是否存在该方法的结果,如果存在,则直接返回缓存中的结果;如果不存在,则调用原始方法,并将结果存入缓存中。在后续的调用中,如果参数相同,则直接从缓存中获取结果,从而减少了对原始方法的调用,提高了系统的性能和响应速度。 使用SpringAOP实现缓存功能可以大大简化代码,提高项目的可维护性和可扩展性,同时还能通过缓存数据减少对数据库等资源的访问,提升系统整体的性能。 ### 回答3: 使用SpringAOP可以很方便地实现缓存功能AOP(面向切面编程)是一种编程范式,通过在程序运行时动态地将横切逻辑(如日志记录、事务管理、异常处理等)插入到应用程序的特定位置,以提供更好的代码结构和模块化。 在使用SpringAOP实现缓存功能时,我们可以通过以下步骤来实现: 1. 定义一个缓存注解:可以使用Spring提供的@Cacheable注解来定义缓存的方法。这个注解可以应用在方法上,用于标记被缓存的方法。 2. 配置缓存切面:通过AOP切面配置,将缓存注解和具体的缓存实现关联起来。可以使用Spring的@Aspect注解来定义一个切面类,该类可以包含多个增强方法用于处理缓存操作。 3. 配置缓存策略:在切面类中,可以通过使用Spring的缓存管理器(如Ehcache、Redis等)来定义缓存的具体策略。可以配置缓存的过期时间、缓存的存储位置等。 4. 在目标方法中使用缓存注解:在需要被缓存的方法上添加之前定义的缓存注解。当方法被调用时,AOP切面会先检查缓存中是否存在对应的缓存数据,如果存在则直接返回缓存数据,否则执行方法逻辑并将结果存入缓存。 5. 测试缓存功能:执行目标方法,观察是否从缓存中获取数据以及方法执行的时间。 通过这种方式,我们可以很方便地在应用中加入缓存功能,提高系统性能和响应速度。同时,由于使用AOP的方式,可以很好地解耦和复用缓存相关的代码逻辑。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lolxxs

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值