Spring引导缓存无法在@PostConstruct

时间:2017-10-30 16:04:09

标签: spring-boot concurrenthashmap spring-cache spring-boot-starter

我正在构建一个"类缓存" ,我希望稍后调用它们。

主要目标是每次需要类实例时我都不想扫描上下文。

# Model / Repository classes

@Getter
@RequiredArgsConstructor
public class Block implements Serializable {
    private final String className;
    private final Set<String> classCandidates = new HashSet<>();
    public boolean addCandidate(final String classCandidate) {
        return this.classCandidates.add(classCandidate);
    }
}

@Slf4j
@Component
@CacheConfig(cacheNames = ConstantsCache.CACHE_BLOCK)
public class BlockRepository {

    @Cacheable(key = "#className")
    public Block findByInputClass(final String className) {
        log.info("---> Loading classes for class '{}'", className);
        val block = new Block(className);
        findCandidates(block);
        return block;
    }
}

首先评估缓存,我已将缓存方法 @Autowired 放在 @RestController 中,这样可以正常工作。当我调用rest方法时,将填充缓存。

@RestController
public class Controller {

    @Autowired
    BlockRepository blockRepository;

    @RequestMapping("/findByInputClass")
    public Block findByInputClass(@RequestParam("className") final String className) {
        return blockRepository.findByInputClass(className);
    }
}

完成此操作后,我已将 @Autowired 对象移至 @Service ,从而创建了一个自行填充缓存的方法。但这不起作用。调用 @PostConstructor 方法时,不会填充缓存。

@Slf4j
@Component
public class BlockCacheService {

    @Autowired
    BlockRepository blockRepository;

    @PostConstruct
    private void postConstruct() {
        log.info("*** {} PostConstruct called.", this.getClass().getTypeName());

        val block = blockRepository.findByInputClass(ConstantsGenerics.BLOCK_PARENT_CLASS);

        final Set<String> inputClasses = getInputFromCandidates(block.getClassCandidates());
        appendClassesToCache(inputClasses);
    }

    private void appendClassesToCache(final Set<String> inputClasses) {
        for (val inputClass : inputClasses) {
            blockRepository.findByInputClass(inputClass);
        }
    }
}

如何使用必须从应用程序开始的服务或组件正确填充缓存。

提前致谢。

编辑:

我在这里找到了一个可能的解决方案:https://stackoverflow.com/a/28311225/1703546

我已经改变了 @Service 代码来手动放置缓存,而不是使用 @Cacheable 魔法抽象。

班级现在就是这样。

@Slf4j
@Component
public class BlockCacheService {

    @Autowired
    CacheManager cacheManager;

    @Autowired
    BlockRepository blockRepository;

    @PostConstruct
    private void postConstruct() {
        log.info("*** {} PostConstruct called.", this.getClass().getTypeName());

        val block = blockRepository.findByInputClass(ConstantsGenerics.BLOCK_PARENT_CLASS);

        final Set<String> inputClasses = getInputFromCandidates(block.getClassCandidates());
        appendClassesToCache(inputClasses);
    }

    private void appendClassesToCache(final Set<String> inputClasses) {
        for (val inputClass : inputClasses) {
            val block = blockRepository.findByInputClass(inputClass);
            cacheManager.getCache(ConstantsCache.CACHE_BLOCK).put(block.getClassName(), block);
        }
    }
}

现在缓存已正确填充,但问题是,这是最好的解决方案吗?

感谢。

1 个答案:

答案 0 :(得分:0)

您无法使用@PostConstruct中的某个方面,因为它可能尚未创建(以及is documented by the way)。

实现这项工作的一种可能方法是实现SmartInitializingBean,因为当所有单例都已完全初始化时(包括它们的方面),它会给出回调。更改原始服务上的内容应该有效。

话虽如此,您的此代码会对启动时间产生影响。为什么不让你的缓存懒散地填充?