SpringBoot 整合 Spring Cache
依赖
<!--Spring Cache-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
配置
在 启动类 加上注解
@EnableCaching
@SpringBootApplication
@MapperScan("com.ratel.hydra.**.mapper")
@EnableConfigurationProperties
@EnableCaching //开启 Spring Cache
public class HydraApplication {
public static void main(String[] args) {
MDC.put("TRACE_ID","-1");
SpringApplication.run(HydraApplication.class, args);
}
}
使用
接口
@CacheConfig(cacheNames = "role") //缓存名字
public interface RoleService {
@CacheEvict(key = "#p0") //表示用参数列表 第一参数作为缓存的 key
void delById(Long id);
@Cacheable(key = "#p0.query") //表示用参数列表 第一参数的query对象作为缓存的 key
IPage<Role> page(PageQuery<Role> query);
@Cacheable(key = "#p0") //表示用参数列表 第一参数作为缓存的 key
Role getById(Long id);
@CachePut(key = "#p0") //表示用参数列表 第一参数作为缓存的 key
void addOrUpdate(Role role);
}
实现类
@Slf4j
@Service
@Transactional(rollbackFor = Exception.class)
public class RoleServiceImpl extends ServiceImpl<RoleMapper, Role> implements RoleService {
@Autowired
private RoleStruct roleStruct;
@Override
public Role getById(Long id) {
return super.getById(id);
}
@Override
public void delById(Long id) {
removeById(id);
}
@Override
public IPage<Role> page(PageQuery query) {
LambdaQueryWrapper<Role> queryWrapper = new LambdaQueryWrapper<Role>().orderByDesc(Role::getModifyTime);
if (query.getQuery() != null){
Role role = (Role)query.getQuery();
if (StringUtils.isNoneBlank(role.getRoleName())) {
queryWrapper.likeRight(Role::getRoleName,role.getRoleName());
}
if (StringUtils.isNoneBlank(role.getRoleCode())){
queryWrapper.eq(Role::getRoleCode,role.getRoleCode());
}
if (StringUtils.isNoneBlank(role.getDescription())){
queryWrapper.like(Role::getDescription,role.getDescription());
}
}
return page(query.getPage(), queryWrapper);
}
@Override
public void addOrUpdate(Role role) {
saveOrUpdate(role);
}
}
Controller
非必须,仅用作于 测试
@Slf4j
@RestController
@RequestMapping("role")
@Api(tags = "角色")
public class RoleController extends BaseController {
@Autowired
private RoleService roleService;
@GetMapping("get/{id}")
@OperatingInfo(operation = "获取角色")
public WebResult getById(@PathVariable("id") Long id) {
return WebResultFactory.ok(roleService.getById(id));
}
@PostMapping("del/{id}")
@OperatingInfo(operation = "删除角色")
public WebResult delById(@PathVariable("id") Long id) {
roleService.delById(id);
return WebResultFactory.ok("操作成功");
}
@GetMapping("page")
@OperatingInfo(operation = "分页获取角色")
public WebResult page(PageQuery<Role> query, Role po) {
return WebResultFactory.ok(roleService.page(query.setQuery(po)));
}
}
注解描述
- @CacheConfig
主要用于配置当前类中会用到的一些共用的缓存配置。@CacheConfig(cacheNames = “role”):表示当前类中使用 @CachePut 修饰的方法的返回值,会存储至名字叫 role 的缓存对象中。当然也可以不使用该注解,直接通过
@Cacheable`自己配置缓存集的名字来定义; - @Cacheable
配置了该注解的page方法的返回值将被加入缓存。同时在查询时,会先从缓存中获取,若不存在才再发起对数据库的访问。该注解主要有下面几个参数:
-
value
、cacheNames
:两个等同的参数(cacheNames为Spring 4新增,作为value的别名),用于指定缓存存储的集合名。由于Spring 4中新增了@CacheConfig
,因此在Spring 3中原本必须有的value属性,也成为非必需项了; -
key
:缓存对象存储在Map集合中的key值,非必需,缺省按照函数的所有参数组合作为key值,若自己配置需使用SpEL表达式,比如:@Cacheable(key = "#p0")
:使用函数第一个参数作为缓存的key值,更多关于SpEL表达式的详细内容可参考https://docs.spring.io/spring/docs/current/spring-framework-reference/integration.html#cache; -
condition
:缓存对象的条件,非必需,也需使用SpEL表达式,只有满足表达式条件的内容才会被缓存,比如:@Cacheable(key = "#p0", condition = "#p0.length() < 3")
,表示只有当第一个参数的长度小于3的时候才会被缓存; -
unless
:另外一个缓存条件参数,非必需,需使用SpEL表达式。它不同于condition参数的地方在于它的判断时机,该条件是在函数被调用之后才做判断的,所以它可以通过对result进行判断; -
keyGenerator
:用于指定key生成器,非必需。若需要指定一个自定义的key生成器,我们需要去实现org.springframework.cache.interceptor.KeyGenerator
接口,并使用该参数来指定; -
cacheManager
:用于指定使用哪个缓存管理器,非必需。只有当有多个时才需要使用; -
cacheResolver
:用于指定使用那个缓存解析器,非必需。需通过org.springframework.cache.interceptor.CacheResolver接口来实现自己的缓存解析器,并用该参数指定;
- @CachePut
作用在方法上,能够根据参数定义条件来进行缓存,其缓存的是方法的返回值,它与@Cacheable
不同的是,它每次都会真实调用真实方法,所以主要用于数据新增和修改操作上。它的参数与@Cacheable
类似,具体功能可参考上面对@Cacheable
参数的解析; - @CacheEvict
作用在方法上,通常用在删除方法上,用来从缓存中移除相应数据。除了同@Cacheable
一样的参数之外,它还有下面两个参数:
-
allEntries
:非必需,默认为false。当为true时,会移除所有数据; -
beforeInvocation
:非必需,默认为false,会在调用方法之后移除数据。当为true时,会在调用方法之前移除数据。
使用 Redis 作为缓存
依赖
<!-- spring-boot redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
配置
application.yml
spring:
################# MySQL 配置 #################
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/ratel_singleton?useUnicode=true&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=GMT%2b8
username: root
password: 'Root@0804'
dbcp2:
min-idle: 5 #数据库连接池最小维持连接数
initial-size: 5 #初始化连接数
max-total: 5 #最大连接数
max-wait-millis: 200 #等待获取连接的最大超时时间
################# Redis 配置 #################
redis:
# Redis数据库索引(默认为0)
database: 0
# Redis服务器地址
host: 127.0.0.1
# Redis服务器连接端口
port: 6379
password: redis@0804
pool:
# 连接池最大连接数(使用负值表示没有限制)
max-active: 8
# 连接池最大阻塞等待时间(使用负值表示没有限制)
max-wait: -1
# 连接池中的最大空闲连接
max-idle: 8
# 连接池中的最小空闲连接
min-idle: 0
# 连接超时时间(毫秒)
timeout: 0
RedisConfiguration.java
/**
* Redis 配置类
* @author ratel
* @date 2020/7/14
*/
@Configuration
public class RedisConfiguration extends CachingConfigurerSupport {
/**
* @Description 自定义缓存 key 生成策略
* 这里仅用作测试,实际情况不建议使用超长 Key
* @Author ratel
* @Date 2020/7/14
* @return org.springframework.cache.interceptor.KeyGenerator
**/
@Bean
public KeyGenerator initKeyGenerator(){
return new KeyGenerator() {
@Override
public Object generate(Object o, Method method, Object... objects) {
StringBuilder sb = new StringBuilder();
sb.append(o.getClass().getSimpleName());
sb.append(method.getName());
for (Object object : objects) {
sb.append(object.toString());
}
return sb.toString();
}
};
}
/**
* @Description 缓存管理器
* @Author ratel
* @Date 2020/7/14
* @param redisConnectionFactory
* @return org.springframework.cache.CacheManager
**/
@Bean
public CacheManager initRedisCacheManager(RedisConnectionFactory redisConnectionFactory){
RedisCacheManager cacheManager = RedisCacheManager.RedisCacheManagerBuilder.fromConnectionFactory(redisConnectionFactory).build();
return cacheManager;
}
/**
* @Description Redis 模板类 功能类似 JDBC template 可以直接操作Redis
* @Author ratel
* @Date 2020/7/14
* @param factory
* @return org.springframework.data.redis.core.RedisTemplate<java.lang.String,java.lang.String>
**/
@Bean
public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory) {
StringRedisTemplate template = new StringRedisTemplate(factory);
setSerializer(template);// 设置序列化工具
template.afterPropertiesSet();
return template;
}
/**
* @Description 配置序列化策略
* @Author ratel
* @Date 2020/7/14
* @param template
* @return void
**/
private void setSerializer(StringRedisTemplate template) {
@SuppressWarnings({ "rawtypes", "unchecked" })
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
template.setValueSerializer(jackson2JsonRedisSerializer);
}
}
注意:
如果 缓存注解 作用在 service上会失效,失效原因是因为spring的事务切面会覆盖 cache 切面,(事务切面 order = 2147483647, cache 切面 order = 2147483647,两者order 一致时,会按 类名排序,所以会先添加 cache切面,再添加 事务切面),解决方案 修改 事务切面的优先级 @EnableTransactionManagement(order = 100)
ps:order 小于 cache就行
使用 Ehcache 作为缓存
todo
依赖
todo
配置
todo