缓存是网站架构中必不可少的关键点之一,特别是对于构建高性能高响应的网站来说更是不可或缺的技术。
在这里记录一下 我是如何在我的web应用中使用缓存的。
首先项目环境使用spring,framework版本4.x,缓存管理器选用ehcache+redis。
这里只讲解一下缓存在项目中的用法,不涉及缓存集群的搭建。不管缓存管理器是否是集群状态,这里都不会影响框架内的使用。
首先,配置ehcache缓存管理器,
新建一个CacheConfig.java做为spring的缓存容器配置类,配置net.sf.ehcache包内的CacheManager,这里使用spring内自带的EhCacheManagerFactoryBean来生成ehcache的CacheManager,
@Bean
public EhCacheManagerFactoryBean ehcache() {
EhCacheManagerFactoryBean ehCacheManagerFactoryBean = new EhCacheManagerFactoryBean();
ehCacheManagerFactoryBean.setConfigLocation(new ClassPathResource("org/fast/web/sys/cache/ehcache.xml"));
return ehCacheManagerFactoryBean;
}
这里需要注意一下,在org.springframework.cache包内,也有一个名为CacheManager的接口类,这个接口是做为spring内的唯一的缓存容器而存在的,我们需要为它注入一个实现,这个实现可以是net.sf.ehcache包内的CacheManager,这样做就表明我们的web应用程序只使用ehcache做为我们的缓存容器。例如如下代码:
@Bean
public EhCacheManagerFactoryBean ehcache() {
EhCacheManagerFactoryBean ehCacheManagerFactoryBean = new EhCacheManagerFactoryBean();
ehCacheManagerFactoryBean.setConfigLocation(new ClassPathResource("org/fast/web/sys/cache/ehcache.xml"));
return ehCacheManagerFactoryBean;
}
@Bean
public EhCacheCacheManager ehcacheManager(CacheManager cm) {
return new EhCacheCacheManager(cm);
}
这里使用了spring包内的EhCacheCacheManager类,这个类其实也是CacheManager接口的实现,只不过spring专门为ehcache做了一些优化,使用这个类需要构造器注入ehcache包内的cachemanager。
关于ehcache的配置文件ehcache.xml的配置方式,我会在另一篇文章内说明。
在这里我们不使用EhCacheCacheManager类作为spring的CacheManager接口的实现注入。我们先配置另一个缓存管理器---redis
这里我们使用spring内的redis连接工厂类JedisConnectionFactory来配置redis的连接信息,
@Bean
public JedisConnectionFactory redisConnectionFactory(JedisPoolConfig config) {
JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory();
jedisConnectionFactory.setPoolConfig(config);
jedisConnectionFactory.afterPropertiesSet();
return jedisConnectionFactory;
}
这里,我没有配置连接信息,使用连接工厂默认的参数。其中hostName,port,password等信息作为JedisConnectionFactory的成员属性,可以直接使用set方法自定义,这里不在赘述。
其中JedisPoolConfig是我注入的jedis连接池信息,用来细粒化redis连接配置信息,更多内容可以参考spring-data-redis的官方api文档
@Bean
public JedisPoolConfig config() {
JedisPoolConfig config = new JedisPoolConfig();
config.setMinEvictableIdleTimeMillis(1200000);//20分钟空闲超时断开连接
config.setMinIdle(1);//保留一个空闲连接
config.setTestOnBorrow(true);//检查连接有效性
return config;
}
Jedis连接工厂可以用来获取jedis对象,jedis对象是用来操作redis客户端的java api,这里有一篇详细的介绍可以参考:Redis客户端:Jedis
当然,我们不会使用这种方式来应用redis,因为你必须手动维护redis连接资源的开启和关闭以及线程安全等问题,如果空闲的redis连接不及时关闭,很快大量的缓存操作会将redis连接数占满,以至于报错。
因此,这里我使用spring提供的RedisTemplate类来操作redis,这与jdbcTemplate和jpaTemplate如出一辙,都是为了简化crud操作而生,redisTemplate内置的成员变量都是线程安全的,可以放心的将其注入为单例模式来使用。
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
有了redisTemplate之后,我们就可以注入一个RedisCacheManager(也实现了spring的CacheManager接口),将redis用作为我的web应用的缓存管理器。
不过这里我也不会这么做,接下来我会配置CompositeCacheManager类,将其作为spring的CacheManager的实现,注入到ioc容器中。
@Bean
public CacheManager cacheManager(net.sf.ehcache.CacheManager cm, RedisTemplate redisTemplate) {
CompositeCacheManager cacheManager = new CompositeCacheManager();
List caches = new ArrayList<Object>() {
{
add(new EhCacheCacheManager(cm));
add(new RedisCacheManager(redisTemplate));
}
};
cacheManager.setCacheManagers(caches);
return cacheManager;
}
可以很轻松的看出,这个bean集成了ehcache和redis,相当于联合使用两种缓存管理器(当然也可以是N种)。需要注意的是,应用存取缓存的顺序是按照这里的list内的顺序来决定的。
解决了缓存的配置之后,接下来就是在我的web应用中使用缓存了,我会使用最为便捷的spring缓存注解来构建最为便捷的缓存存取模式,当然也可以在适当的地方自定义缓存存取。
spring的缓存注解大部分都是围绕切面来展开使用的,例如 利用注解来创建切面,来拦截方法的调用,在方法调用之前返回缓存中的数据,因此使用注解缓存需要我们保证相同方法相同参数的每次返回值必须是相同的
主要是使用下面4种缓存注解:
asd
@Cacheable -> 表明spring在调用方法之前,首先应该在缓存中查找方法的返回值,如果这个值能够找到,就会返回缓存的值。否则,这个方法就会被调用,返回值会放入缓存中。
@CachePut ->表明spring应该将方法的返回值放入缓存中。在方法的调用前并不会检查缓存,方法始终都会被调用。
@CacheEvict ->表明spring应该在缓存中清除一个或多个条目
@Caching -> 这是一个分组的注解,能够同时应用多个其他的缓存注解