一.@Cacheable,@CacheEvict,@CachePut,@CacheConfig等注解及其属性的用法
1.1 @Cacheable
@Cacheable的属性的意义
- cacheNames:指定缓存的名称
- key:定义组成的key值,如果不定义,则使用全部的参数计算一个key值。可以使用spring El表达式
- keyGenerator:定义key生成的类,和key的不能同时存在
- sync:如果设置sync=true:a. 如果缓存中没有数据,多个线程同时访问这个方法,则只有一个方法会执行到方法,其它方法需要等待; b. 如果缓存中已经有数据,则多个线程可以同时从缓存中获取数据
-
condition和unless 只满足特定条件才进行缓存:
1. condition: 在执行方法前,condition的值为true,则缓存数据
2.unless :在执行方法后,判断unless ,如果值为true,则不缓存数据
3.conditon和unless可以同时使用,则此时只缓存同时满足两者的记录
1.2 @CacheEvict
删除缓存
- allEntries = true: 清空缓存book1里的所有值
- allEntries = false: 默认值,此时只删除key对应的值
1.3 @CachePut
每次执行都会执行方法,无论缓存里是否有值,同时使用新的返回值的替换缓存中的值。这里不同@Cacheable:@Cacheable如果缓存没有值,从则执行方法并缓存数据,如果缓存有值,则从缓存中获取值
1.4 @CacheConfig
@CacheConfig: 类级别的注解:如果我们在此注解中定义cacheNames,则此类中的所有方法上 @Cacheable的cacheNames默认都是此值。当然@Cacheable也可以重定义cacheNames的值
二.写个使用SpringBoot + Redis实现数据缓存的工程
2.1 添加Maven依赖
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.weichai</groupId>
<artifactId>SpringCache</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>SpringCache</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>1.5.18.RELEASE</version>
</dependency>
<!-- 添加MyBatis依赖 -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>6.0.5</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
<version>1.5.18.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>1.5.18.RELEASE</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.24</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.8</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>27.0-jre</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.0</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
2.2 application.properties属性文件
spring.datasource.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.redis.database=0
spring.redis.host=localhost
spring.redis.port=6380
spring.redis.password=123456
spring.redis.pool.max-active=10
spring.redis.pool.max-wait=-1
spring.redis.pool.max-idle=10
spring.redis.pool.min-idle=0
spring.redis.timeout=2000
mybatis.mapper-locations=classpath:mappings/modules/user/*.xml
#spring.cache.cache-names=user
spring.cache.type=redis
server.port=8090
2.3 Redis缓存配置类
package com.weichai.SpringCache.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
/**
* Redis缓存配置类
* 初始化redis做数据缓存
* @author linhaiy
* @date 2019.03.05
*/
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {
@Value("${spring.redis.host}")
private String host;
@Value("${spring.redis.port}")
private int port;
@Value("${spring.redis.timeout}")
private int timeout;
private static int OverTime = 120;
/**
* 自定义缓存key生成策略
*/
@Bean
public KeyGenerator keyGenerator() {
return new KeyGenerator(){
public Object generate(Object target, java.lang.reflect.Method method, Object... params) {
StringBuffer sb = new StringBuffer();
sb.append(target.getClass().getName());
sb.append(method.getName());
for(Object obj:params){
sb.append(obj.toString());
}
return sb.toString();
}
};
}
/**
* 缓存管理器(注意:此方法在SpringBoot2.0以下版本才有效,2.0以后得版本参照
* https://blog.csdn.net/Mirt_/article/details/80934312 来写)
* @param redisTemplate
* @return
*/
@Bean
public CacheManager cacheManager(@SuppressWarnings("rawtypes") RedisTemplate redisTemplate) {
RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate);
cacheManager.setDefaultExpiration(OverTime); // 设置缓存过期时间
return cacheManager;
}
/**
* RedisTemplate序列化器之GenericJackson2JsonRedisSerializer
* @return
*/
@Bean(name = "springSessionDefaultRedisSerializer")
public GenericJackson2JsonRedisSerializer getGenericJackson2JsonRedisSerializer() {
return new GenericJackson2JsonRedisSerializer();
}
/**
* 设置RedisTemplate的序列化器。
* @param connectionFactory
* @return
*/
@Bean
public RedisTemplate<String, Object> getRedisTemplate(JedisConnectionFactory connectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
redisTemplate.setConnectionFactory(connectionFactory);
redisTemplate.setDefaultSerializer(new GenericJackson2JsonRedisSerializer());
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
redisTemplate.setKeySerializer(stringRedisSerializer);
redisTemplate.setHashKeySerializer(stringRedisSerializer);
return redisTemplate;
}
}
2.4 有缓存功能的接口实现
package com.weichai.SpringCache.entity;
import java.io.Serializable;
public class User implements Serializable {
private String id;
private String userName;
private String userPwd;
private Integer age;
private String address;
public User(String id, String userName, String userPwd, Integer age, String address) {
super();
this.id = id;
this.userName = userName;
this.userPwd = userPwd;
this.age = age;
this.address = address;
}
public User() {
super();
// TODO Auto-generated constructor stub
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getUserPwd() {
return userPwd;
}
public void setUserPwd(String userPwd) {
this.userPwd = userPwd;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "User [id=" + id + ", userName=" + userName + ", userPwd=" + userPwd + ", age=" + age + ", address="
+ address + "]";
}
}
package com.weichai.SpringCache.entity;
import java.io.Serializable;
/**
* 数据接口返回结果
* @author linhaiy
* @date 2019.03.01
* @param <T>
*/
public class ResponseResult<T> implements Serializable {
public static final int STATE_ERROR=-1;
public static final int STATE_OK=1;
private static final long serialVersionUID = 2158690201147047546L;
private int status; //返回状态
private String message; //返回信息
private T data; //返回数据
public ResponseResult() {
super();
// TODO Auto-generated constructor stub
}
public ResponseResult(int status, String message, T data) {
super();
this.status = status;
this.message = message;
this.data = data;
}
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((data == null) ? 0 : data.hashCode());
result = prime * result + ((message == null) ? 0 : message.hashCode());
result = prime * result + status;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ResponseResult other = (ResponseResult) obj;
if (data == null) {
if (other.data != null)
return false;
} else if (!data.equals(other.data))
return false;
if (message == null) {
if (other.message != null)
return false;
} else if (!message.equals(other.message))
return false;
if (status != other.status)
return false;
return true;
}
@Override
public String toString() {
return "ResponseResult [status=" + status + ", message=" + message + ", data=" + data + "]";
}
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.weichai.SpringCache.dao.UserDao">
<sql id="userColumns">
a.id as "id",
a.userName as "userName",
a.userPwd as
"userPwd",
a.age as "age",
a.address as "address"
</sql>
<sql id="userJoins">
</sql>
<!-- 查询用户表所有数据 -->
<select id="findList"
resultType="com.weichai.SpringCache.entity.User">
SELECT
<include refid="userColumns" />
FROM user a
<include refid="userJoins" />
</select>
<!-- 根据id查询用户表 -->
<select id="findById"
resultType="com.weichai.SpringCache.entity.User"
parameterType="java.lang.String">
SELECT
<include refid="userColumns" />
FROM user a
<include refid="userJoins" />
WHERE a.id = #{id}
</select>
<!-- 添加用户信息 -->
<insert id="AddUser">
INSERT INTO user(id,userName,userPwd,age,address)
VALUES
(#{id},#{userName},#{userPwd},#{age},#{address})
</insert>
<!-- 根据id删除对应的用户 -->
<delete id="deleteUser">
DELETE FROM user WHERE id = #{id}
</delete>
<!-- 修改用户密码 -->
<update id="updatePwdById">
UPDATE user SET userPwd = #{userPwd} WHERE id = #{id}
</update>
</mapper>
package com.weichai.SpringCache.dao;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import com.weichai.SpringCache.entity.User;
@Mapper
public interface UserDao {
public List<User> findList();
public User findById(String id);
public void AddUser(User user);
public void deleteUser(String id);
public void updatePwdById(User user);
}
package com.weichai.SpringCache.service;
import java.util.List;
import javax.annotation.Resource;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import com.weichai.SpringCache.dao.UserDao;
import com.weichai.SpringCache.entity.User;
/**
* 缓存机制是作用到Service层的,
* 而dao或者repository层缓存用注解,用key的话它会认定为null。
* 要用KeyGenerator来提前生成key的生成策略
* @author linhaiy
*
*/
@Service
@CacheConfig(cacheNames = "user")
public class UserService {
@Resource
private UserDao userDao;
@Cacheable(value="user")
public List<User> findList(){
return userDao.findList();
}
@Cacheable(cacheNames="user",key ="#id")
public User findById(String id) {
return userDao.findById(id);
}
/**
* acheNames 设置缓存的值
* key:指定缓存的key,这是指参数id值。 key可以使用spEl表达式,也可以是指定对象的成员变量
* @param user
*/
@CachePut(cacheNames="user",key = "#user.id")
public void AddUser(User user) {
userDao.AddUser(user);
}
//如果指定为 true,则方法调用后将立即清空所有缓存
@CacheEvict(key ="#id",allEntries=true)
public void deleteUser(String id) {
userDao.deleteUser(id);
}
@CachePut(cacheNames="user",key = "#user.id")
public void updatePwdById(User user) {
userDao.updatePwdById(user);
}
/**
* 条件缓存:
* 只有满足condition的请求才可以进行缓存,如果不满足条件,则跟方法没有@Cacheable注解的方法一样
* 如下面只有id < 3才进行缓存
* @param id
* @return
*/
@Cacheable(cacheNames = "user", condition = "T(java.lang.Integer).parseInt(#id) < 3 ")
public User queryUserByIdCondition(String id) {
return userDao.findById(id);
}
}
package com.weichai.SpringCache.controller;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import com.alibaba.fastjson.JSONObject;
import com.weichai.SpringCache.entity.ResponseResult;
import com.weichai.SpringCache.entity.User;
import com.weichai.SpringCache.service.UserService;
import com.weichai.SpringCache.util.StringUtils;
/**
* 用户缓存测试类
* @author linhaiy
* @date 2019.03.05
*/
@RestController
@RequestMapping(value = "/userCache")
public class UserController {
@Autowired
private UserService userService;
@RequestMapping("/findList")
@ResponseBody
public JSONObject findList(HttpServletRequest request){
JSONObject rr = new JSONObject();
List<User> list = userService.findList();
if(list !=null && list.size() >0) {
rr.put("state","0");
rr.put("msg","success");
rr.put("data", list);
}
return rr;
}
@RequestMapping("/findById")
@ResponseBody
public ResponseResult<User> findById(@RequestParam String id){
ResponseResult<User> resData = new ResponseResult<User>();
User user = userService.findById(id);
resData.setStatus(0);
resData.setMessage("成功获取数据");
resData.setData(user);
return resData;
}
@RequestMapping(value = "AddUser",method= RequestMethod.POST)
@ResponseBody
public JSONObject AddUser(User user){
JSONObject rr = new JSONObject();
try {
if(user !=null) {
userService.AddUser(user);
rr.put("state","0");
rr.put("msg","success");
}else {
rr.put("state","2");
rr.put("msg","参数错误");
}
} catch (Exception e) {
// TODO Auto-generated catch block
rr.put("state","2");
rr.put("msg","保存用户信息失败");
e.printStackTrace();
}
return rr;
}
@RequestMapping(value = "deleteUser",method= RequestMethod.POST)
@ResponseBody
public JSONObject deleteUser(@RequestParam String id){
JSONObject rr = new JSONObject();
try {
if(StringUtils.isNotBlank(id)) {
userService.deleteUser(id);
rr.put("state","0");
rr.put("msg","用户信息删除成功");
}else {
rr.put("state","2");
rr.put("msg","参数输入错误");
}
} catch (NumberFormatException e) {
// TODO Auto-generated catch block
rr.put("state","2");
rr.put("msg","用户操作错误");
}
return rr;
}
@RequestMapping(value = "updatePwdById",method= RequestMethod.POST)
@ResponseBody
public JSONObject updatePwdById(User user){
JSONObject rr = new JSONObject();
try {
if(user !=null) {
if(StringUtils.isNotBlank(user.getUserPwd())) {
userService.updatePwdById(user);
rr.put("state","0");
rr.put("msg","用户密码修改成功");
}else {
rr.put("state","2");
rr.put("msg","参数错误");
}
}else {
rr.put("state","2");
rr.put("msg","没有修改数据");
}
} catch (Exception e) {
// TODO Auto-generated catch block
rr.put("state","2");
rr.put("msg","用户操作错误");
e.printStackTrace();
}
return rr;
}
}
2.5 项目启动
package com.weichai.SpringCache;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.scheduling.annotation.EnableScheduling;
/**
* SpringBoot启动诶
* @EnableCaching: 启动缓存
* @author linhaiy
* @date 2019.03.05
*/
@SpringBootApplication
@EnableScheduling
@EnableCaching
public class App
{
public static void main( String[] args )
{
SpringApplication.run(App.class, args);
}
}
2.6 部分接口缓存功能测试
Redis服务器出现的缓存数据
修改之前的一条数据记录
其他接口就不测试了