缓存是大多数应用程序的主要组成部分,只要我们设法避免磁盘访问,缓存就会保持强劲。 Spring对各种配置的缓存提供了强大的支持 。 您可以根据需要简单地开始,然后进行更多可定制的操作。
这将是spring提供的最简单的缓存形式的示例。
Spring默认带有一个内存缓存,它很容易设置。
让我们从gradle文件开始。
group 'com.gkatzioura'
version '1.0-SNAPSHOT'
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:1.4.2.RELEASE")
}
}
apply plugin: 'java'
apply plugin: 'idea'
apply plugin: 'org.springframework.boot'
repositories {
mavenCentral()
}
sourceCompatibility = 1.8
targetCompatibility = 1.8
dependencies {
compile("org.springframework.boot:spring-boot-starter-web")
compile("org.springframework.boot:spring-boot-starter-cache")
compile("org.springframework.boot:spring-boot-starter")
testCompile("junit:junit")
}
bootRun {
systemProperty "spring.profiles.active", "simple-cache"
}
由于同一项目将用于不同的缓存提供程序,因此会有多个spring配置文件。 本教程的Spring配置文件将是简单缓存,因为我们将使用基于ConcurrentMap的缓存,该缓存恰好是默认缓存。
我们将实现一个应用程序,该应用程序将从本地文件系统中获取用户信息。 该信息应位于users.json文件中
[
{"userName":"user1","firstName":"User1","lastName":"First"},
{"userName":"user2","firstName":"User2","lastName":"Second"},
{"userName":"user3","firstName":"User3","lastName":"Third"},
{"userName":"user4","firstName":"User4","lastName":"Fourth"}
]
我们还将为要检索的数据指定一个简单的模型。
package com.gkatzioura.caching.model;
/**
* Created by gkatzioura on 1/5/17.
*/
public class UserPayload {
private String userName;
private String firstName;
private String lastName;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
然后,我们将添加一个将读取信息的bean。
package com.gkatzioura.caching.config;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.gkatzioura.caching.model.UserPayload;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.core.io.Resource;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
* Created by gkatzioura on 1/5/17.
*/
@Configuration
@Profile("simple-cache")
public class SimpleDataConfig {
@Autowired
private ObjectMapper objectMapper;
@Value("classpath:/users.json")
private Resource usersJsonResource;
@Bean
public List<UserPayload> payloadUsers() throws IOException {
try(InputStream inputStream = usersJsonResource.getInputStream()) {
UserPayload[] payloadUsers = objectMapper.readValue(inputStream,UserPayload[].class);
return Collections.unmodifiableList(Arrays.asList(payloadUsers));
}
}
}
显然,为了访问信息,我们将使用实例化的Bean包含所有用户信息。
下一步将是创建一个存储库接口,以指定将使用的方法。
package com.gkatzioura.caching.repository;
import com.gkatzioura.caching.model.UserPayload;
import java.util.List;
/**
* Created by gkatzioura on 1/6/17.
*/
public interface UserRepository {
List<UserPayload> fetchAllUsers();
UserPayload firstUser();
UserPayload userByFirstNameAndLastName(String firstName,String lastName);
}
现在,让我们深入研究将包含所需缓存注释的实现。
package com.gkatzioura.caching.repository;
import com.gkatzioura.caching.model.UserPayload;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Optional;
/**
* Created by gkatzioura on 12/30/16.
*/
@Repository
@Profile("simple-cache")
public class UserRepositoryLocal implements UserRepository {
@Autowired
private List<UserPayload> payloadUsers;
private static final Logger LOGGER = LoggerFactory.getLogger(UserRepositoryLocal.class);
@Override
@Cacheable("alluserscache")
public List<UserPayload> fetchAllUsers() {
LOGGER.info("Fetching all users");
return payloadUsers;
}
@Override
@Cacheable(cacheNames = "usercache",key = "#root.methodName")
public UserPayload firstUser() {
LOGGER.info("fetching firstUser");
return payloadUsers.get(0);
}
@Override
@Cacheable(cacheNames = "usercache",key = "{#firstName,#lastName}")
public UserPayload userByFirstNameAndLastName(String firstName,String lastName) {
LOGGER.info("fetching user by firstname and lastname");
Optional<UserPayload> user = payloadUsers.stream().filter(
p-> p.getFirstName().equals(firstName)
&&p.getLastName().equals(lastName))
.findFirst();
if(user.isPresent()) {
return user.get();
} else {
return null;
}
}
}
包含@Cacheable的方法将触发缓存填充,这与包含@CacheEvict的方法将触发缓存逐出相反。 通过使用@Cacheable而不是仅指定将存储我们的值的缓存映射,我们还可以基于方法名称或方法参数来指定键。
因此,我们实现了方法缓存。 例如,方法firstUser使用方法名称作为键,而方法userByFirstNameAndLastName使用方法参数以创建键。
带有@CacheEvict批注的两种方法将清空指定的缓存。
LocalCacheEvict将是处理驱逐的组件。
package com.gkatzioura.caching.repository;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Component;
/**
* Created by gkatzioura on 1/7/17.
*/
@Component
@Profile("simple-cache")
public class LocalCacheEvict {
@CacheEvict(cacheNames = "alluserscache",allEntries = true)
public void evictAllUsersCache() {
}
@CacheEvict(cacheNames = "usercache",allEntries = true)
public void evictUserCache() {
}
}
由于我们使用非常简单的缓存形式,因此不支持驱逐ttl。 因此,我们将仅针对此特定情况添加一个调度程序,该调度程序将在一定时间后退出缓存。
package com.gkatzioura.caching.scheduler;
import com.gkatzioura.caching.repository.LocalCacheEvict;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Profile;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
/**
* Created by gkatzioura on 1/7/17.
*/
@Component
@Profile("simple-cache")
public class EvictScheduler {
@Autowired
private LocalCacheEvict localCacheEvict;
private static final Logger LOGGER = LoggerFactory.getLogger(EvictScheduler.class);
@Scheduled(fixedDelay=10000)
public void clearCaches() {
LOGGER.info("Invalidating caches");
localCacheEvict.evictUserCache();
localCacheEvict.evictAllUsersCache();
}
}
最后,我们将使用控制器来调用指定的方法
package com.gkatzioura.caching.controller;
import com.gkatzioura.caching.model.UserPayload;
import com.gkatzioura.caching.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* Created by gkatzioura on 12/30/16.
*/
@RestController
public class UsersController {
@Autowired
private UserRepository userRepository;
@RequestMapping(path = "/users/all",method = RequestMethod.GET)
public List<UserPayload> fetchUsers() {
return userRepository.fetchAllUsers();
}
@RequestMapping(path = "/users/first",method = RequestMethod.GET)
public UserPayload fetchFirst() {
return userRepository.firstUser();
}
@RequestMapping(path = "/users/",method = RequestMethod.GET)
public UserPayload findByFirstNameLastName(String firstName,String lastName ) {
return userRepository.userByFirstNameAndLastName(firstName,lastName);
}
}
最后但并非最不重要的一点是,我们的Application类应包含两个额外的注释。 为了启用调度程序,需要@EnableScheduling;为了启用缓存,需要@EnableCaching
package com.gkatzioura.caching;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.scheduling.annotation.EnableScheduling;
/**
* Created by gkatzioura on 12/30/16.
*/
@SpringBootApplication
@EnableScheduling
@EnableCaching
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class,args);
}
}
您可以在github上找到源代码。
翻译自: https://www.javacodegeeks.com/2017/01/spring-boot-cache-abstraction.html