首页 > 解决方案 > Spring 的 Cacheable Annotation 如何为通过 new 关键字初始化的类工作。(在类构造函数中,通过 Bean 初始化)

问题描述

CacheableService在我们的服务中,我们正在初始化一个 bean(比如“A”),并使用 -在内部构造一个Object new CacheableService()。据我所知@Cacheable,如果使用“new”关键字初始化类,spring 的注释将不适用于类方法。

那么缓存方法响应的替代方法或方法是什么?

设想 :

<bean class="com.package.src.A"/>
public class A {
    
    Map<String, CacheableService> map;
    public CacheableService2() {
        map = new HashedMap();
        map.put("a", new CacheableService());
    }
}
import org.springframework.cache.annotation.Cacheable;
    
public class CacheableService {
    
    
    @Cacheable(value = "entityCount", key = "#criteria.toString()")
    public int someEntityCount(final String criteria) {
        System.out.println("Inside function : " + criteria);
        return 5;
    }
}

标签: javaspringcaching

解决方案


这是一个演示使用 Spring Boot 进行缓存的最小示例。可以在此处找到以下示例的代码。

转到https://start.spring.io/并创建一个新的 Spring Boot 项目。确保包含“Spring 缓存抽象”,这会导致将此条目添加到您的 pom 中:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>

将 @EnableCaching 注解添加到您的应用程序:

package com.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;

@EnableCaching
@SpringBootApplication
public class CacheableApplication {
    public static void main(String[] args) {
        SpringApplication.run(CacheableApplication.class, args);
    }
}

您的服务:

package com.example;

import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

@Service
public class CacheableService {
    @Cacheable(value = "entityCount")
    public int someEntityCount(final String criteria) {
        System.out.print(String.format("Inside function: %s", criteria));
        return 5;
    }
}

A类:

package com.example;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class A {
    private CacheableService cacheableService;

    public A(@Autowired CacheableService cacheableService) {
        this.cacheableService = cacheableService;
    }

    public int getEntityCount(String criteria) {
        return cacheableService.someEntityCount(criteria);
    }
}

然后这是一个测试,证明缓存正在工作。正如您在测试中看到的那样,a.getEntityCount("foo") 被调用了两次,但在标准输出中我们只看到“Inside function: foo”被打印了一次。因此,我们验证了第二次调用导致缓存被用于产生结果。

package com.example;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.io.ByteArrayOutputStream;
import java.io.PrintStream;

import static org.junit.jupiter.api.Assertions.assertEquals;

@SpringBootTest
class CacheableTest {
    private final ByteArrayOutputStream outContent = new ByteArrayOutputStream();

    @Autowired
    private A a;

    @BeforeEach
    public void init() {
        System.setOut(new PrintStream(outContent));
    }

    @Test
    public void testCaching() {
        a.getEntityCount("foo");
        a.getEntityCount("foo");

        assertEquals("Inside function: foo", outContent.toString());
    }
}

编辑:如果您想将缓存移到 Spring 生命周期之外并手动管理它,那么我建议使用Caffeine。这是相同的示例,但现在不涉及任何 Spring。

您的服务:

package com.example.withoutspring;

import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;

import java.util.concurrent.TimeUnit;

public class CaffeineCachingService {
    private LoadingCache<String, Integer> entityCountCache = Caffeine.newBuilder()
            .expireAfterAccess(5, TimeUnit.MINUTES)
            .build(key -> someEntityCount(key));

    public int cachedEntityCount(final String criteria) {
        return entityCountCache.get(criteria);
    }

    private int someEntityCount(final String criteria) {
        System.out.print(String.format("Inside function: %s", criteria));
        return 5;
    }
}

B类:

package com.example.withoutspring;

public class B {
    private CaffeineCachingService cacheableService;

    public B() {
        cacheableService = new CaffeineCachingService();
    }

    public int getEntityCount(String criteria) {
        return cacheableService.cachedEntityCount(criteria);
    }
}

同样的测试,但没有 Spring:

package com.example.withoutspring;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.io.ByteArrayOutputStream;
import java.io.PrintStream;

import static org.junit.jupiter.api.Assertions.assertEquals;

public class CaffeineCacheableTest {
    private final ByteArrayOutputStream outContent = new ByteArrayOutputStream();

    private B b = new B();

    @BeforeEach
    public void init() {
        System.setOut(new PrintStream(outContent));
    }

    @Test
    public void testCaching() {
        b.getEntityCount("foo");
        b.getEntityCount("foo");

        assertEquals("Inside function: foo", outContent.toString());
    }
}

显然,您需要调整缓存以执行您想要的方式,因此可能在 5 分钟后驱逐缓存的值不是您想要的,但是如果您访问 Caffeine Github 页面,您将看到很多详细的示例如何配置缓存以满足你的用例。

希望这可以帮助!


推荐阅读