Integration example of Redis cache + Spring
The article integrating spring 4 (including mvc, context, orm) + mybatis 3 example) briefly introduces the integration of Spring MVC, IOC, and MyBatis ORM in the latest version and declarative transaction processing. This article will introduce the integration of Redis cache + Spring based on this example. For details about how to build a Redis server, refer to Redhat5.8 compiling and installing Redis and registering it as a system service.
1. Install pom. xml in the dependency package:
org.springframework.data
spring-data-redis
1.6.0.RELEASE
redis.clients
jedis
2.7.3
2. To enable cache support for integrating Spring projects into the cache, we need to create a new CacheManager bean. The CacheManager interface has many implementations. This article demonstrates the integration with Redis. Naturally, RedisCacheManager is used. Redis is not the shared memory of an application. It is just a memory server. Like MySql, we need to connect the application to it and use a certain language for interaction, therefore, we also need a connection factory and a RedisTemplate for Spring and Redis conversations. These are all necessary configurations for Redis cache, and they are all placed in the Custom cachingassumersupport:
/** * File Name:RedisCacheConfig.java * * Copyright Defonds Corporation 2015 * All Rights Reserved * */package com.defonds.bdp.cache.redis;import org.springframework.cache.CacheManager;import org.springframework.cache.annotation.CachingConfigurerSupport;import org.springframework.cache.annotation.EnableCaching;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.RedisConnectionFactory;import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;import org.springframework.data.redis.core.RedisTemplate;/** * * Project Name:bdp * Type Name:RedisCacheConfig * Type Description: * Author:Defonds * Create Date:2015-09-21 * * @version * */@Configuration@EnableCachingpublic class RedisCacheConfig extends CachingConfigurerSupport {@Beanpublic JedisConnectionFactory redisConnectionFactory() {JedisConnectionFactory redisConnectionFactory = new JedisConnectionFactory();// DefaultsredisConnectionFactory.setHostName(192.168.1.166);redisConnectionFactory.setPort(6379);return redisConnectionFactory;}@Beanpublic RedisTemplate
redisTemplate(RedisConnectionFactory cf) {RedisTemplate
redisTemplate = new RedisTemplate
();redisTemplate.setConnectionFactory(cf);return redisTemplate;}@Beanpublic CacheManager cacheManager(RedisTemplate redisTemplate) {RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate);// Number of seconds before expiration. Defaults to unlimited (0)cacheManager.setDefaultExpiration(3000); // Sets the default expire time (in seconds)return cacheManager;}}
Of course, do not forget to inject these beans into Spring, or the configuration will not work. Add the following to applicationContext. xml:
3. cache the execution results of some methods. After setting the cache configuration, we can use the @ Cacheable annotation to cache the results of method execution, for example, the provinceCities Method for retrieving cities based on province names and the searchCity Method for retrieving cities based on city_code are as follows:
// R@Cacheable(provinceCities)public List
provinceCities(String province) {logger.debug(province= + province);return this.cityMapper.provinceCities(province);}// R@Cacheable(searchCity)public City searchCity(String city_code){logger.debug(city_code= + city_code);return this.cityMapper.searchCity(city_code);}
4. cache Data Consistency ensures that in the CRUD (Create creation, Retrieve read, Update, Delete) operation, except for the idempotence of R, when the other three occur, the cache results may be inconsistent with those of the database. To ensure cache data consistency, we need to update or clear the cache that may be affected during CUD operations.
// C@CacheEvict(value = { provinceCities}, allEntries = true)public void insertCity(String city_code, String city_jb, String province_code, String city_name,String city, String province) {City cityBean = new City();cityBean.setCityCode(city_code);cityBean.setCityJb(city_jb);cityBean.setProvinceCode(province_code);cityBean.setCityName(city_name);cityBean.setCity(city);cityBean.setProvince(province);this.cityMapper.insertCity(cityBean);}// U@CacheEvict(value = { provinceCities, searchCity }, allEntries = true)public int renameCity(String city_code, String city_name) {City city = new City();city.setCityCode(city_code);city.setCityName(city_name);this.cityMapper.renameCity(city);return 1;}// D@CacheEvict(value = { provinceCities, searchCity }, allEntries = true)public int deleteCity(String city_code) {this.cityMapper.deleteCity(city_code);return 1;}
In this example, @ CacheEvict is used to clear the cache. If your CUD can return to the City instance, you can also use @ CachePut to update the cache policy. I recommend that you use @ CachePut instead of @ CacheEvict, because the latter clears the cache of all related methods. For example, if any of the above three methods is called, all caches of the provinceCities method will be cleared.
5. Custom cache data key generation policy for the method using the @ Cacheable annotation, each cache key Generation Policy uses the parameter name + parameter value by default. For example:
@Cacheable(users)public User findByUsername(String username)
The cache of this method will be stored in the key as users ~ Under the cache of keys, for the cache whose username value is Zhao defang, the key is username-zhao defang. In general, there is no problem. In general, if the method key value is equal and the parameter name is the same, the problem occurs, such:
@Cacheable(users)public Integer getLoginCountByUsername(String username)
The cache of this method will also be stored in the key as users ~ The cache of keys. For the cache with the username value Zhao defang, the key is also username-zhao defang, And the cache of another method is overwritten.
The solution is to use a custom cache policy. For the same business (the same business logic processing method, even the cluster/Distributed System), the generated keys are always consistent, different services are inconsistent:
@Beanpublic KeyGenerator customKeyGenerator() {return new KeyGenerator() {@Overridepublic Object generate(Object o, Method method, Object... objects) {StringBuilder sb = new StringBuilder();sb.append(o.getClass().getName());sb.append(method.getName());for (Object obj : objects) {sb.append(obj.toString());}return sb.toString();}};}
Therefore, the two methods above, for the cache with the username value of Zhao defang, are still stored in the key as users ~ Keys cache, but because the key is class name-findByUsername-username-zhao defang and class name-getLoginCountByUsername-username-zhao defang, there is no problem.
This is very important for shared caching between cluster systems and distributed systems, and truly implements distributed caching.
I suggest: it is best to use the method name for the cache method @ Cacheable to avoid the @ Cacheable values of different methods being consistent, and then configure the above Cache Policy.
6. cache verification 6.1 cache verification in order to determine whether each cache method has been cached, we opened the SQL log output of MyBatis and cleared the Redis database for testing to demonstrate clearly.
Verify the provinceCities method cache first. After Eclipse starts tomcat to load the project, use JMeter to call the/bdp/city/province/cities. json interface:
The Eclipse console outputs the following:
This indicates that the request did not hit the cache, and the database query is used. JMeter requests again, and the Eclipse console outputs:
The following marked red part is the log of this request, which does not access the db log and cache hits. View the Redis storage status of this request:
You can also verify whether the cache of the searchCity method with city_code 1492 is valid:
The red part in the figure is the cache storage of searchCity.
6.2 verify the cache consistency first, verify the cache configuration of the insertCity method. JMeter calls the/bdp/city/create. json interface:
Check Redis storage later:
It can be seen that the cache of the provinceCities method has been cleared, and the cache of the insertCity method works.
Then verify the cache configuration of the renameCity method. JMeter calls the/bdp/city/rename. json interface:
Then let's look at Redis storage:
The cache of the searchCity method has been cleared, and the cache of the renameCity method also works.
7. note: The Java object to be cached must implement the Serializable interface, because Spring will serialize the object before saving it to Redis, such as com. defonds. bdp. city. bean. city class. If Serializable is not implemented, the following error occurs: nested exception is java. lang. illegalArgumentException: DefaultSerializer requires a Serializable payload but has ed an object of type [com. defonds. bdp. city. bean. city]. We can configure the cache lifecycle and then host Spring CacheManager. Do not try to manage the cache through the redis-cli command line. For example, if the provinceCities method is cached, the query results of a province will be stored in Redis in the form of key-value. The key is the generated key we just customized, and the value is the serialized object, this key will be placed in the key name provinceCities ~ For the keys key-value storage, refer to the provinceCities method in Redis cache. You can use the del command in redis-cli to convert provinceCities ~ Keys is deleted, but the cache of each province is not cleared. CacheManager must set the cache expiration time. Otherwise, the cache object will never expire. The reason for this is as follows: Avoid "permanent storage" of some wild data ". In addition, setting the cache expiration time also helps to maximize resource utilization, because the cache always retains hotspot data.