@Cacheable加缓存导致的跳过校验 & self自调用

@Cacheable加缓存导致的跳过校验 & self自调用

@Cacheable加缓存导致的跳过校验

java 复制代码
@Service
public class DataServiceImpl implements DataService {

    // 这个属性指向当前类的代理对象
    @Autowired
    private DataService self;


    @Override
    @Cacheable(value = "dataCache", key = "#param")
    public String getData(String param) {
        if (!isValid(param)) {
            throw new IllegalArgumentException("Invalid parameter");
        }
        // 通过代理对象调用getData方法,确保缓存逻辑被执行
        
        return findData(param);
    }

    private String findData(String param) {
        // 数据查询逻辑
        return "Data for " + param;
    }

    private boolean isValid(String param) {
        // 参数校验逻辑
        return param != null && !param.isEmpty();
    }
}

上面的代码在getData方法前面加了@Cacheable注解,实现了加缓存的操作;不过仔细梳理逻辑就会发现:

当一次调用该方法请求数据;cache中没有数据,于是调用getData方法获取数据,并且将查到的数据存在缓存当中;当第二次查数据的时候,会直接从cache里面找数据,此时不会调用getData方法;因此也就直接跳过了校验。导致后面查找数据时不需要校验就可以直接从缓存里面获取数据。

改进办法:

java 复制代码
@Service
public class DataServiceImpl implements DataService {

    // 这个属性指向当前类的代理对象
    @Autowired
    private DataService self;

    @Override
    public String getDataWithValidation(String param) {
        if (!isValid(param)) {
            throw new IllegalArgumentException("Invalid parameter");
        }
        // 通过代理对象调用getData方法,确保缓存逻辑被执行
        return self.getData(param);
    }

    @Override
    @Cacheable(value = "dataCache", key = "#param")
    public String getData(String param) {
        return findData(param);
    }

    private String findData(String param) {
        // 数据查询逻辑
        return "Data for " + param;
    }

    private boolean isValid(String param) {
        // 参数校验逻辑
        return param != null && !param.isEmpty();
    }
}

我们在getData 方法外又封装了一层方法,于是不管是从数据库中还是从缓存中调取数据,都必须要首先进行校验;便避免了上面提到的问题。

此外,在getDataWithValidation方法中返回getData方法需要使用self关键字来进行调用,否则@cacheable关键字就会失效;


self自调用

在Java中,使用self.func()这样的自引用调用(其中self是当前对象的引用)通常不是必需的,但在特定情况下,这种调用方式是必要的,特别是在使用Spring框架和面向切面编程(AOP)时。以下是一些使用self.func()调用的具体场景:

1. Spring AOP和代理

当你在Spring框架中使用AOP特性,如@Cacheable@Transactional等注解时,直接在同一个类内部调用被注解的方法不会触发AOP逻辑,因为这种调用不会经过Spring代理。为了确保AOP逻辑被正确触发,你需要通过代理对象(self)来调用方法。

java

java 复制代码
@Service
public class MyService {

    private final MyService self = this;

    @Cacheable(value = "cache", key = "#id")
    public String getCachedData(Long id) {
        // 模拟数据库查询
        return "Data for " + id;
    }

    public String getDataWithCacheCheck(Long id) {
        if (someCondition()) {
            // 直接调用不会触发缓存逻辑
            return getCachedData(id);
        }
        // 使用self引用来调用,确保经过Spring代理
        return self.getCachedData(id);
    }
}

2. 确保线程安全

在多线程环境中,如果你需要确保对共享资源的访问是线程安全的,可能会使用synchronized关键字。在这种情况下,自引用调用可以确保所有的调用都经过同步块。

java

java 复制代码
public class Counter {
    private int count = 0;

    public synchronized void increment() {
        count++;
    }

    public void incrementSafely() {
        // 使用self引用来确保线程安全
        this.self.increment();
    }

    private Counter self = this;
}

3. 避免无限递归

在某些情况下,你可能需要在方法内部调用自身,但又不希望陷入无限递归。通过使用self引用,你可以控制递归的深度。

java

java 复制代码
public class RecursiveFunction {
    private final RecursiveFunction self = this;

    public void recursiveCall(int depth) {
        if (depth > 0) {
            // 使用self引用来避免无限递归
            self.recursiveCall(depth - 1);
        }
    }
}

4. 模拟外部调用

在单元测试中,你可能需要模拟外部对类的调用,以测试类的行为。通过使用self引用,你可以确保测试覆盖了所有通过代理的路径。

java

java 复制代码
public class ServiceUnderTest {
    private final ServiceUnderTest self = this;

    public void someBusinessLogic() {
        // 业务逻辑
    }

    public void testExternalCall() {
        // 使用self引用来模拟外部调用
        self.someBusinessLogic();
    }
}

总的来说,self.func()调用在Java中不是常规做法,但在特定的框架使用(如Spring AOP)和特定的编程模式(如线程安全控制、递归控制)中,它是一种确保代码按预期行为执行的有效手段。

相关推荐
AC赳赳老秦1 分钟前
企业级合规审计体系:用 OpenClaw 落地采集全链路留痕,自动生成合规审计报告
java·python·django·beautifulsoup·php·deepseek·openclaw
吃杠碰小鸡1 小时前
VsCode中开发Java项目
java·vscode
宠友信息1 小时前
内容社区源码开发实践解析,用Spring Boot打造1:1仿小红书源码平台
java·spring boot·redis·websocket·mysql·spring·uni-app
行百里er1 小时前
加个依赖就生效?一行搞定 Spring Boot Starter 自动装配
java·后端·监控
月华路2 小时前
G1 新生代对象晋升老年代:实现机制与 GC 日志
java·jvm·算法
AI人工智能+电脑小能手2 小时前
大白话说Java设计模式-34-命令模式(业务实战篇)
java·spring·设计模式·命令模式·异步任务·撤销重做·事务封装
净重21克2 小时前
万字长文!TLS协议升级避坑手册:JDK老项目适配TLS1.2/1.3全流程拆解
java·springboot·java安全
一直C2 小时前
【数据结构】哈希表+算法复杂度与经典排序查找(C语言)
java·linux·开发语言·数据结构·算法·ubuntu·散列表
paopaokaka_luck2 小时前
基于springboot3+vue3的车间生产管理系统(Echarts图形化分析、BI报表)
java·前端·spring boot·学习·echarts
Zane19943 小时前
遍历时删元素为什么报错:modCount 这个"计数器"在背后盯着你
java·后端