随着工夫的积攒,利用的应用用户一直减少,数据规模也越来越大,往往数据库查问操作会成为影响用户应用体验的瓶颈,此时应用缓存往往是解决这一问题十分好的伎俩之一。Spring 3 开始提供了弱小的基于注解的缓存反对,能够通过注解配置形式低侵入的给原有 Spring 利用减少缓存性能,进步数据拜访性能。
在 Spring Boot 中对于缓存的反对,提供了一系列的自动化配置,使咱们能够十分不便的应用缓存。上面咱们通过一个简略的例子来展现,咱们是如何给一个既有利用减少缓存性能的。
疾速入门
上面咱们将应用应用 Spring Data JPA 拜访 MySQL 一文的案例为根底。这个案例中蕴含了应用 Spring Data JPA 拜访 User 数据的操作,利用这个根底,咱们为其增加缓存,来缩小对数据库的 IO,以达到拜访减速的作用。如果您还不相熟如何实现对 MySQL 的读写操作,那么倡议先浏览前文,实现这个根底案例的编写。
先简略回顾下这个案例的根底内容:
User 实体的定义
@Entity
@Data
@NoArgsConstructor
public class User {
@Id
@GeneratedValue
private Long id;
private String name;
private Integer age;
public User(String name, Integer age) {
this.name = name;
this.age = age;
}
}
User 实体的数据拜访实现
public interface UserRepository extends JpaRepository<User, Long> {User findByName(String name);
User findByNameAndAge(String name, Integer age);
@Query("from User u where u.name=:name")
User findUser(@Param("name") String name);
}
为了更好的了解缓存,咱们先对该工程做一些简略的革新。
-
application.properties
文件中新增spring.jpa.show-sql=true
,开启 hibernate 对 sql 语句的打印。如果是 1.x 版本,应用spring.jpa.properties.hibernate.show_sql=true
参数。 - 批改单元测试类,插入 User 表一条用户名为 AAA,年龄为 10 的数据。并通过 findByName 函数实现两次查问,具体代码如下:
@RunWith(SpringRunner.class)
@SpringBootTest
public class Chapter51ApplicationTests {
@Autowired
private UserRepository userRepository;
@Test
public void test() throws Exception {
// 创立 1 条记录
userRepository.save(new User("AAA", 10));
User u1 = userRepository.findByName("AAA");
System.out.println("第一次查问:" + u1.getAge());
User u2 = userRepository.findByName("AAA");
System.out.println("第二次查问:" + u2.getAge());
}
}
在没有退出缓存之前,咱们能够先执行一下这个案例,能够看到如下的日志:
Hibernate: select user0_.id as id1_0_, user0_.age as age2_0_, user0_.name as name3_0_ from user user0_ where user0_.name=?
第一次查问:10
Hibernate: select user0_.id as id1_0_, user0_.age as age2_0_, user0_.name as name3_0_ from user user0_ where user0_.name=?
第二次查问:10
两次 findByName
查问都执行了两次 SQL,都是对 MySQL 数据库的查问。
引入缓存
第一步:在 pom.xml
中引入 cache 依赖,增加如下内容:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
第二步:在 Spring Boot 主类中减少 @EnableCaching
注解开启缓存性能,如下:
@EnableCaching
@SpringBootApplication
public class Chapter51Application {public static void main(String[] args) {SpringApplication.run(Chapter51Application.class, args);
}
}
第三步:在数据拜访接口中,减少缓存配置注解,如:
@CacheConfig(cacheNames = "users")
public interface UserRepository extends JpaRepository<User, Long> {
@Cacheable
User findByName(String name);
}
第四步:再来执行以下单元测试,能够在控制台中输入了上面的内容
Hibernate: insert into user (age, name, id) values (?, ?, ?)
Hibernate: select user0_.id as id1_0_, user0_.age as age2_0_, user0_.name as name3_0_ from user user0_ where user0_.name=?
第一次查问:10
第二次查问:10
到这里,咱们能够看到,在调用第二次 findByName
函数时,没有再执行 select 语句,也就间接缩小了一次数据库的读取操作。
为了能够更好的察看,缓存的存储,咱们能够在单元测试中注入CacheManager
。
@Autowired
private CacheManager cacheManager;
应用 debug 模式运行单元测试,察看 CacheManager
中的缓存集 users 以及其中的 User 对象的缓存加深了解。
能够看到,在第一次调用 findByName
函数之后,CacheManager
将这个查问后果保留了下来,所以在第二次拜访的时候,就能匹配上而不须要再拜访数据库了。
Cache 配置注解详解
回过头来咱们再来看这里应用到的两个注解别离作了什么事件:
-
@CacheConfig
:次要用于配置该类中会用到的一些共用的缓存配置。在这里@CacheConfig(cacheNames = "users")
:配置了该数据拜访对象中返回的内容将存储于名为 users 的缓存对象中,咱们也能够不应用该注解,间接通过@Cacheable
本人配置缓存集的名字来定义。 -
@Cacheable
:配置了 findByName 函数的返回值将被退出缓存。同时在查问时,会先从缓存中获取,若不存在才再发动对数据库的拜访。该注解次要有上面几个参数:-
value
、cacheNames
:两个等同的参数(cacheNames
为 Spring 4 新增,作为value
的别名),用于指定缓存存储的汇合名。因为 Spring 4 中新增了@CacheConfig
,因而在 Spring 3 中本来必须有的value
属性,也成为非必须项了 -
key
:缓存对象存储在 Map 汇合中的 key 值,非必须,缺省依照函数的所有参数组合作为 key 值,若本人配置需应用 SpEL 表达式,比方:@Cacheable(key = "#p0")
:应用函数第一个参数作为缓存的 key 值,更多对于 SpEL 表达式的具体内容可参考官网文档 -
condition
:缓存对象的条件,非必须,也需应用 SpEL 表达式,只有满足表达式条件的内容才会被缓存,比方:@Cacheable(key = "#p0", condition = "#p0.length() < 3")
,示意只有当第一个参数的长度小于 3 的时候才会被缓存,若做此配置下面的 AAA 用户就不会被缓存,读者可自行试验尝试。 -
unless
:另外一个缓存条件参数,非必须,需应用 SpEL 表达式。它不同于condition
参数的中央在于它的判断机会,该条件是在函数被调用之后才做判断的,所以它能够通过对 result 进行判断。 -
keyGenerator
:用于指定 key 生成器,非必须。若须要指定一个自定义的 key 生成器,咱们须要去实现org.springframework.cache.interceptor.KeyGenerator
接口,并应用该参数来指定。须要留神的是:该参数与key
是互斥的 -
cacheManager
:用于指定应用哪个缓存管理器,非必须。只有当有多个时才须要应用 -
cacheResolver
:用于指定应用那个缓存解析器,非必须。需通过org.springframework.cache.interceptor.CacheResolver
接口来实现本人的缓存解析器,并用该参数指定。
-
除了这里用到的两个注解之外,还有上面几个外围注解:
-
@CachePut
:配置于函数上,可能依据参数定义条件来进行缓存,它与@Cacheable
不同的是,它每次都会真是调用函数,所以次要用于数据新增和批改操作上。它的参数与@Cacheable
相似,具体性能可参考上面对@Cacheable
参数的解析 -
@CacheEvict
:配置于函数上,通常用在删除办法上,用来从缓存中移除相应数据。除了同@Cacheable
一样的参数之外,它还有上面两个参数:-
allEntries
:非必须,默认为 false。当为 true 时,会移除所有数据 -
beforeInvocation
:非必须,默认为 false,会在调用办法之后移除数据。当为 true 时,会在调用办法之前移除数据。
-
代码示例
本文的相干例子能够查看上面仓库中的 chapter5-1
目录:
- Github:https://github.com/dyc87112/SpringBoot-Learning/
- Gitee:https://gitee.com/didispace/SpringBoot-Learning/
如果您感觉本文不错,欢送 Star
反对,您的关注是我保持的能源!
本文首发:Spring Boot 2.x 基础教程:过程内缓存的应用与 Cache 注解详解,转载请注明出处。
欢送关注我的公众号:程序猿 DD,取得独家整顿的学习资源和日常干货推送。
如果您对我的其余专题内容感兴趣,中转我的集体博客:didispace.com。