缓存
Spring 3开始提供了强大的基于注解的缓存支持,可以通过注解配置方式低侵入的给原有Spring应用增加缓存功能,提高数据访问性能。
引入缓存
第一步:在pom.xml
中引入cache依赖,添加如下内容:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
第二步:在Spring Boot主类中增加@EnableCaching
注解开启缓存功能
@EnableCaching
@SpringBootApplication
public class SmtDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SmtDemoApplication.class, args);
}
第三步:在数据访问接口中,增加缓存配置注解
@Cacheable
@Select("select * from student where name=#{name}")
Student FindByName(@Param("name") String name);
在第一次调用FindByName
函数之后,CacheManager
将这个查询结果保存了下来,所以在第二次访问的时候,就能匹配上而不需要再访问数据库了。
Cache配置注解详解
@CacheConfig
:主要用于配置该类中会用到的一些共用的缓存配置。在这里@CacheConfig(cacheNames = "Student")
:配置了该数据访问对象中返回的内容将存储于名为Student的缓存对象中,可以不使用该注解,直接通过@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() < 4")
,表示只有当第一个参数的长度小于3的时候才会被缓存,若做此配置上面的缓存测试用户就不会被缓存。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时,会在调用方法之前移除数据。
EhCache缓存
Spring Boot根据下面的顺序去侦测缓存提供者:(可以通过配置属性spring.cache.type来强制指定。)
- Generic
- JCache (JSR-107) (EhCache 3, Hazelcast, Infinispan, and others)
- EhCache 2.x
- Hazelcast
- Infinispan
- Couchbase
- Redis
- Caffeine
- Simple
当我们不指定具体其他第三方实现的时候,Spring Boot的Cache模块会使用ConcurrentHashMap
来存储。
修改成ehcache缓存
第一步:在pom.xml
中引入ehcache依赖
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
</dependency>
第二步:在src/main/resources
目录下创建:ehcache.xml
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="ehcache.xsd">
<cache name="Student"
maxEntriesLocalHeap="200"
timeToLiveSeconds="600">
</cache>
</ehcache>
可以看到已经修改成功:
使用EhCache缓存集群
由于没有多机环境以及不了解打包部署相关配置,没有进行实际操作
第一步:为需要同步的缓存对象(即实体-Student类)实现Serializable
接口
注意:如果没有做这一步,后续缓存集群通过过程中,因为要传输Student对象,会导致序列化与反序列化相关的异常
第二步:重新组织ehcache的配置文件。用手工组建集群的方式,不同实例在网络相关配置上会产生不同的配置信息,所以我们建立不同的配置文件给不同的实例使用。比如下面这样:
实例1,使用ehcache-1.xml
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="ehcache.xsd">
<cache name="users"
maxEntriesLocalHeap="200"
timeToLiveSeconds="600">
<cacheEventListenerFactory
class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"
properties="replicateAsynchronously=true,
replicatePuts=true,
replicateUpdates=true,
replicateUpdatesViaCopy=false,
replicateRemovals=true "/>
</cache>
<cacheManagerPeerProviderFactory
class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory" properties="hostName=10.10.0.100,
port=40001,
socketTimeoutMillis=2000,
peerDiscovery=manual,
rmiUrls=//10.10.0.101:40001/users" />
</ehcache>
实例2,使用ehcache-2.xml
由于只有服务器ip地址的区别,不重复编写
配置说明:
cache
标签中定义名为users的缓存,这里我们增加了一个子标签定义cacheEventListenerFactory
,这个标签主要用来定义缓存事件监听的处理策略,它有以下这些参数用来设置缓存的同步策略:- replicatePuts:当一个新元素增加到缓存中的时候是否要复制到其他的peers。默认是true。
- replicateUpdates:当一个已经在缓存中存在的元素被覆盖时是否要进行复制。默认是true。
- replicateRemovals:当元素移除的时候是否进行复制。默认是true。
- replicateAsynchronously:复制方式是异步的指定为true时,还是同步的,指定为false时。默认是true。
- replicatePutsViaCopy:当一个新增元素被拷贝到其他的cache中时是否进行复制指定为true时为复制,默认是true。
- replicateUpdatesViaCopy:当一个元素被拷贝到其他的cache中时是否进行复制指定为true时为复制,默认是true。
- 新增了一个
cacheManagerPeerProviderFactory
标签的配置,用来指定组建的集群信息和要同步的缓存信息,其中:- hostName:是当前实例的主机名
- port:当前实例用来同步缓存的端口号
- socketTimeoutMillis:同步缓存的Socket超时时间
- peerDiscovery:集群节点的发现模式,有手工与自动两种,这里采用了手工指定的方式
- rmiUrls:当peerDiscovery设置为manual的时候,用来指定需要同步的缓存节点,如果存在多个用
|
连接
第三步:打包部署与启动。打包没啥大问题,主要缓存配置内容存在一定差异,所以在指定节点的模式下,需要单独拿出来,然后使用启动参数来控制读取不同的配置文件。比如这样:
-Dspring.cache.ehcache.config=classpath:ehcache-1.xml
-Dspring.cache.ehcache.config=classpath:ehcache-2.xml
集中式缓存Redis
在一些要求高一致性(任何数据变化都能及时的被查询到)的系统和应用中,不能再使用EhCache来解决了,使用集中式缓存就可以很好的解决缓存数据的一致性问题。
第一步:pom.xml
中增加相关依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
第二步:配置文件中增加配置信息,以本地运行为例,比如:
spring.redis.host=localhost
spring.redis.port=6379
spring.redis.lettuce.pool.max-idle=8
spring.redis.lettuce.pool.max-active=8
spring.redis.lettuce.pool.max-wait=-1ms
spring.redis.lettuce.pool.min-idle=0
spring.redis.lettuce.shutdown-timeout=100ms
关于连接池的配置,注意几点:
- Redis的连接池配置在1.x版本中前缀为
spring.redis.pool
与Spring Boot 2.x有所不同。 - 在1.x版本中采用jedis作为连接池,而在2.x版本中采用了lettuce作为连接池
- 以上配置均为默认值,实际上生产需进一步根据部署情况与业务要求做适当修改.
第三步:为需要同步的缓存对象(即实体-Student类)实现Serializable
接口(否则会报错)