After rolling out the Spring source code, I wrote a distributed cache plug-in for Spring, which is now open source and is about to crack!! (recommended Collection)

Hello, I'm glacier~~

After three months, I finally finished the Spring source code, which is about to crack!! Subsequently, the distributed caching framework was opened source!!!

The frame address is as follows:

GitHub: https://github.com/sunshinelyz/mykit-cache

Gitee: https://gitee.com/binghe001/mykit-cache

Next, I'll introduce this framework to you. If this framework is helpful to you, please open Github and Gitee links to give this project a big Star and let more small partners benefit. You can also like, watch and forward this article~~

Framework description

The independent mykit cache component in the mykit architecture encapsulates various cache operations under the mykit architecture. Users only need to introduce the relevant Jar package to realize the easy operation of the cache.

Frame structure description

It encapsulates the cache operation, supports distributed cache databases such as Memcached, Redis and Ehcache, and supports Spring annotations. Through Spring annotations, you can set the cache expiration time and actively refresh the cache.

The overall structure is as follows.

mykit-cache-memcached

Components related to Memcached cache under mykit cache architecture

mykit-cache-memcached-spring

Under mykit cache Memcached, Spring integrates components related to Memcached operations, and supports setting the cache effective time through annotations

mykit-cache-memcached-spring-simple

Under mykit cache memcached spring, the simple spring memcached kernel is mainly used to implement annotation caching components, and it supports setting the cache effective time through annotations.

When the compatible Memcached server goes down or cannot connect to the Memcached server for other reasons, the main method is to throw relevant exception information and continue to execute the original method.

mykit-cache-memcached-spring-simple-core

The core module under mykit cache memcached spring simple provides core configuration items

mykit-cache-memcached-spring-simple-xml

Mykit cache Memcached Spring simple manages the plug-in classes of Spring container in XML, and provides the core configuration of Spring container to manage Memcached,

Other projects or projects only need to introduce this plug-in and load the Memcached core configuration of this component in their own Spring configuration file.

mykit-cache-memcached-spring-simple-test

The general test project mainly tests the cache operation with simple spring memcached as the kernel under mykit cache memcached spring. This plug-in module mainly provides the main test case encapsulation classes.

mykit-cache-memcached-spring-simple-test-xml

Under mykit cache Memcached spring, test simple spring Memcached as the entry project for kernel cache operation. The test entry class is io.mykit.cache.test.Memcached.test.xml.Memcached test. Meanwhile, the simple.memcache.server property in the classpath:properties/memcached.properties file under this project needs to be configured as the IP and port of its own Memcached server.

mykit-cache-redis

Components related to Redis cache under mykit cache architecture

mykit-cache-redis-java

Under mykit cache Redis, Redis cache encapsulation is used separately in Java.

mykit-cache-redis-spring

Mykit cache Redis mainly integrates Redis operation related components with Spring, and supports setting the cache effective time and actively refreshing the cache through annotations

mykit-cache-redis-spring-core

Mykit cache Redis Spring mainly provides general tools and methods for Spring to integrate Redis, and the core implementation is provided by this module

mykit-cache-redis-spring-annotation

Mykit cache Redis Spring mainly integrates Redis operation related components with Spring, supports setting the cache effective time and actively refreshing the cache through annotations, mainly realizes the management operation of Spring container in the form of Java annotations, and is compatible with the situation when the Redis cluster is down or cannot be connected to the Redis cluster for other reasons.

If the Redis cluster is down or unable to connect to the Redis cluster for other reasons, print the relevant logs and continue to execute the original method downward.

mykit-cache-redis-spring-xml

Mykit cache Redis Spring mainly integrates Redis operation related components with Spring, supports setting the cache effective time and actively refreshing the cache through annotations, and mainly realizes the management operation of Spring container in the form of XML configuration. It is incompatible with the situation when the Redis cluster goes down or cannot connect to the Redis cluster for other reasons, If the Redis cluster is down or unable to connect to the Redis cluster for other reasons, an exception is thrown and execution is exited.

mykit-cache-redis-spring-test

Mykit cache Redis Spring integrates the core test case classes of Redis and provides the main test encapsulation;

mykit-cache-redis-spring-test-annotation

Under mykit cache redis Spring, the test entry of Spring container is managed in the form of Java annotation, and unit test cases are provided for mykit cache redis Spring annotation.

The test entry is io.mykit.cache.test.redis.spring.annotation.test.TestRedisConfig. Before executing the test method, you need to configure the classpath:properties/redis.properties file according to your own Redis cluster, and modify the node IP and port of Redis cluster in redis.properties to the IP and port of your own Redis cluster node

mykit-cache-redis-spring-test-xml

Mykit cache redis Spring tests manage the test entry of Spring container in the form of XML configuration. The test module of mykit cache redis Spring XML provides unit test cases for mykit cache redis Spring XML.

The test entry is io.mykit.cache.test.redis.spring.test.xml.RedisTest. Before executing the test method, you need to configure the classpath:properties/redis.properties file according to your own Redis cluster, and modify the node IP and port of Redis cluster in redis.properties to the IP and port of your own Redis cluster node

mykit-cache-ehcache

Components related to ehcache in mykit cache architecture

mykit-cache-ehcache-spring

Mykit cache Ehcache mainly integrates components related to Ehcache operation with Spring, and supports setting cache effective time through annotation

The usage scenario is as follows:

1. Redis needs to be operated directly using Java

1) Add the following configuration to Maven's pom.xml file:

<dependency>
    <groupId>io.mykit.cache</groupId>
    <artifactId>mykit-cache-redis-java</artifactId>
    <version>1.0.0-SNAPSHOT</version>
</dependency>

2) Create the Redis configuration file redis.properties file in the resources directory of the project

For Redis stand-alone mode, the contents of redis.properties file are as follows.

redis.host=10.2.2.231
redis.port=6379
redis.max_idle=200
redis.max_wait=10000
redis.max_total=1024
redis.timeout=3000
redis.test_on_borrow=true

For Redis cluster mode, the contents of redis.properties file are as follows.

#Redis cluster mode
redis.cluster.password=
redis.cluster.max.total=100
redis.cluster.max.idle=20
redis.cluster.min.idle=10
redis.cluster.timeout=2000
redis.cluster.maxAttempts=100
redis.cluster.redisDefaultExpiration=3600
redis.cluster.usePrefix=true
redis.cluster.blockWhenExhausted=true
redis.cluster.maxWaitMillis=3000
redis.cluster.testOnBorrow=false
redis.cluster.testOnReturn=false
redis.cluster.testWhileIdle=true
redis.cluster.minEvictableIdleTimeMillis=60000
redis.cluster.timeBetweenEvictionRunsMillis=30000
redis.cluster.numTestsPerEvictionRun=-1
redis.cluster.defaultExpirationKey=defaultExpirationKey
redis.cluster.expirationSecondTime=300
redis.cluster.preloadSecondTime=280

# virsual env
redis.cluster.node.one=192.168.175.151
redis.cluster.node.one.port=7001

redis.cluster.node.two=192.168.175.151
redis.cluster.node.two.port=7002

redis.cluster.node.three=192.168.175.151
redis.cluster.node.three.port=7003

redis.cluster.node.four=192.168.175.151
redis.cluster.node.four.port=7004

redis.cluster.node.five=192.168.175.151
redis.cluster.node.five.port=7005

redis.cluster.node.six=192.168.175.151
redis.cluster.node.six.port=7006

redis.cluster.node.seven=192.168.175.151
redis.cluster.node.seven.port=7006

be careful:

When configuring the redis.properties file, you can modify the Redis IP and port number, but the Key in the file must be the same as the Key given in the above example, otherwise the Redis client cannot connect to the Redis server.

3) Using Redis cache in Java programs

If the configuration is in stand-alone mode, Redis cache is used as follows

Jedis jedis = RedisBuilder.getInstance();
jedis.set("name", "binghe");
String value = jedis.get("name");
System.out.println(value);

If the cluster environment is configured, Redis cache is used as follows

JedisCluster jedisCluster = RedisClusterBuilder.getInstance();
jedisCluster.set("name", "binghe");
String value = jedisCluster.get("name");
System.out.println(value);

2. You need to use Spring+Redis cluster to configure cache:

1) When a compatible Redis cluster is down or the Redis cluster cannot be connected for other reasons:

Add the following configuration to Maven's pom.xml:

 <dependency>
    <groupId>io.mykit.cache</groupId>
    <artifactId>mykit-cache-redis-spring-annotation</artifactId>
    <version>1.0.0-SNAPSHOT</version>
</dependency>

At this time, you also need to create Redis configuration classes in the appropriate modules of your project according to the specific situation. The main function is to provide Spring container management for configuring Spring and Redis cluster integration in the form of Java annotations.

The sample program is io.mykit.cache.test.redis.spring.annotation.config.AnnotationConfig class in mykit cache redis spring test annotation test module.

package io.mykit.cache.test.redis.spring.annotation.config;
 
 import io.mykit.cache.redis.spring.annotation.config.CacheRedisConfig;
 import org.springframework.cache.annotation.EnableCaching;
 import org.springframework.context.annotation.ComponentScan;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.context.annotation.EnableAspectJAutoProxy;
 import org.springframework.context.annotation.PropertySource;
 
 /**
  * @author binghe
  * @version 1.0.0
  * @description Provides Spring container management for configuring Spring and Redis cluster integration in the form of Java annotations
  */
 @Configuration
 @EnableCaching
 @EnableAspectJAutoProxy(proxyTargetClass = true)
 @ComponentScan(value = {"io.mykit.cache"})
 @PropertySource(value = {"classpath:properties/redis-default.properties", "classpath:properties/redis.properties"})
 public class AnnotationConfig extends CacheRedisConfig {
 }

2) When a compatible Redis cluster is not required to be down or the Redis cluster cannot be connected for other reasons:

Add the following configuration to Maven's pom.xml:

<dependency>
    <groupId>io.mykit.cache</groupId>
    <artifactId>mykit-cache-redis-spring-xml</artifactId>
    <version>1.0.0-SNAPSHOT</version>
</dependency>

At this time, you also need to make relevant configuration in the spring configuration file of your own project according to the specific situation. The main configuration items are: start spring annotation scanning and proxy, add io.mykit.cache package to the scanned basic classes, and load them in order

classpath*:properties/redis-default.properties, classpath*:properties/redis.properties file. The specific example is: classpath:spring/spring-context.xml configuration file under mykit cache redis spring test XML:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	   xmlns:p="http://www.springframework.org/schema/p"
	   xmlns:aop="http://www.springframework.org/schema/aop"
	   xmlns:context="http://www.springframework.org/schema/context"
	   xmlns:cache="http://www.springframework.org/schema/cache"
	   xsi:schemaLocation="http://www.springframework.org/schema/beans
                        http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
                        http://www.springframework.org/schema/context
                        http://www.springframework.org/schema/context/spring-context-4.2.xsd
                        http://www.springframework.org/schema/aop
                        http://www.springframework.org/schema/aop/spring-aop-4.2.xsd
                        http://www.springframework.org/schema/cache
                        http://www.springframework.org/schema/cache/spring-cache-4.2.xsd">

	<context:annotation-config />
	<aop:aspectj-autoproxy/>
	<context:component-scan base-package="io.mykit.cache"/>
	   <!-- Import profile -->
	  <context:property-placeholder location="classpath*:properties/redis-default.properties, classpath*:properties/redis.properties" system-properties-mode="FALLBACK"/>
	  <context:annotation-config />
	  <context:component-scan base-package="io.mykit.cache" />
 	 <import resource="classpath:redis/spring-redis.xml"/>
</beans>

be careful:

(1) Regardless of whether the Redis cluster needs to be compatible or not, or when the Redis cluster cannot be connected for other reasons, you need to create the redis.properties file in the classpath directory of your project to configure the IP and port of your Redis cluster node.

At present, mykit cache Redis spring annotation and mykit cache Redis spring XML support up to 7 Redis clusters, which can be expanded according to their actual situation;

If there are less than 7 Redis clusters, you can configure the IP and port of duplicate Redis cluster nodes in the redis.properties file;

(2) Create the redis.properties file in the classpath directory of your project. This file name is not mandatory and can be replaced by other file names. However, this file name must be consistent with the redis configuration file name loaded in the spring configuration file and the redis configuration file name loaded in the configuration class that manages the spring container in the form of Java annotations;

(3) The configuration item names of cluster node IP and port in the customized Redis cluster configuration file must be the same as those in the instance classpath:properties/redis.properties configuration file;

(4) Configuration instance:

For example, under classpath:properties in my own project, the redis cluster configuration file is redis.properties. The details are as follows:

#redis cluster config
redis.cluster.defaultExpirationKey=defaultExpirationKey
redis.cluster.expirationSecondTime=300000
redis.cluster.preloadSecondTime=280000

#node info
redis.cluster.node.one=10.2.2.231
redis.cluster.node.one.port=7001

redis.cluster.node.two=10.2.2.231
redis.cluster.node.two.port=7002

redis.cluster.node.three=10.2.2.231
redis.cluster.node.three.port=7003

redis.cluster.node.four=10.2.2.231
redis.cluster.node.four.port=7004

redis.cluster.node.five=10.2.2.231
redis.cluster.node.five.port=7005

redis.cluster.node.six=10.2.2.231
redis.cluster.node.six.port=7006

redis.cluster.node.seven=10.2.2.231
redis.cluster.node.seven.port=7006

The configuration files to be loaded in the spring configuration file of my project are:

<context:property-placeholder location="classpath*:properties/redis-default.properties, classpath*:properties/redis.properties" system-properties-mode="FALLBACK"/>

Or in the configuration class of my project, the annotation of the configuration file to be loaded is:

@PropertySource(value = {"classpath:properties/redis-default.properties", "classpath:properties/redis.properties"})

That is, the classpath:properties/redis-default.properties file should be written in front of the customized configuration file, and the framework will load classpath:properties/redis-default.properties first.

Then load the customized configuration file. If the customized configuration file has the same property configuration as the classpath:properties/redis-default.properties file, the framework will overwrite the same property in classpath:properties/redis-default.properties with the customized configuration property

(5) Specific use

1) Add @ Cacheable annotation without key attribute to relevant query methods:

@Cacheable(value={"test#10#2"})

The key attribute of @ Cacheable is not configured. At this time, the key attribute value of @ Cacheable is generated according to a certain policy, that is, the HashCode of the current class name (full package name + class name) + method name + method type list + method parameter list is the key attribute of the Current @ Cacheable. The specific key generation policy class is io.mykit.cache.redis.spring.cache.CacheKeyGenerator in mykit cache redis spring core;

2) Add the @ Cacheable annotation with the key attribute to the relevant query methods

@Cacheable(value={"test#10#2"} key="key" + ".#defaultValue")

The key property of @ Cacheable is configured. At this time, the key property value of @ Cacheable is the HashCode value of the result of the value of the key splicing parameter defaultValue.

be careful:

(1) If there is no key attribute in the @ Cacheable annotation, the framework will generate a key attribute for @ Cacheable, that is, the key attribute is not required;

(2) If the @ Cacheable annotation is not configured with the key attribute, the HashCode of the current class name (full package name + class name) + method name + method type list + method parameter list is the key attribute of the current @ Cacheable;

(3) If the @ Cacheable annotation is configured with the key attribute, the HashCode of the current key is used as the key attribute of the current @ Cacheable;

(4) In the value attribute of @ Cacheable, the value we configured is test#10#2. At this time, it means that the cache name of @ Cacheable is test, where 10 represents the effective length of cache (in seconds), and 2 represents the remaining length of time from cache invalidation (in seconds),

That is, the configuration format of the value attribute of @ Cacheable is: cache name #expireTime#reloadTime. The framework stipulates that # must be used as the separator

  • expireTime: indicates the effective duration of the cache, in seconds;
  • reloadTime: indicates the remaining time from cache invalidation, in seconds;
  • expireTime needs to be greater than reloadTime, otherwise it is meaningless

(5) Description of the value attribute of @ Cacheable

  • After configuring the value attribute of @ Cacheable in the format of cache name #expireTime#reloadTime, the framework will put the query results into the cache. The effective length is expireTime seconds and the remaining length from cache invalidation is reloadTime seconds;
  • When the time range from 0 seconds to (expireTime reloadtime) seconds elapses when the data is stored in the cache, call the method again to obtain the data directly from the cache;
  • When the data is stored in the cache, the method is called again after the time range of reloadTime seconds - expireTime seconds, and the framework will actively call the original method through proxy and reflection to refresh the cache after obtaining data from the real data source;
  • When the data is stored in the cache for more than expireTime seconds, the cache will become invalid. If the method is called again, the original method will be executed to query the data, and the framework will automatically store the query results in the cache;
  • When the framework actively calls the original method through proxy and reflection to refresh the cache after obtaining data from the real data source, in order to prevent multiple threads requesting to refresh the cache at the same time, the framework provides a distributed lock to ensure that only one thread performs the cache refresh operation;
  • After the framework actively calls the original method to obtain data from the real data source, the operation of refreshing the cache is asynchronous with the user's request operation, which will not affect the performance of the user's request;
  • After the framework actively calls the original method to obtain data from the real data source, the operation of refreshing the cache is transparent to the user's request, that is, the user cannot perceive the operation of the framework actively refreshing the cache;

other:

1) When the Value of @ Cacheable is only configured with the cache name, for example, @ Cacheable(value = "test")

At this time, the expireTime defaults to the redis.cluster.expirationSecondTime property value of the redis configuration file, with the unit of seconds; reloadTime is the redis.cluster.preloadSecondTime attribute value of the redis configuration file by default, and the unit is seconds;

The loading order of attribute values is: first load the redis.cluster.expirationSecondTime attribute value and redis.cluster.preloadSecondTime attribute value of the customized redis configuration file. If the customized redis configuration file has no relevant attribute value; Load the redis-default.properties file from the framework default redis configuration file;

2) When the Value of @ Cacheable is configured with the cache name and expiration time, for example, @ Cacheable(value = "test#10")

At this time, the reloadTime defaults to the redis.cluster.preloadSecondTime attribute value of the redis configuration file, in seconds;

The loading order of attribute values is: first load the redis.cluster.preloadSecondTime attribute value of the customized redis configuration file. If the customized redis configuration file has no relevant attribute value; Load the redis-default.properties file from the framework default redis configuration file;

3) When the Value of @ Cacheable configures the cache name, expiration time and remaining time from cache expiration, for example, @ Cacheable(value = "test#10#2")

At this time, the default expireTime and reloadTime will not be loaded. The framework will directly use the expireTime and reloadTime configured by the value attribute in the @ Cacheable annotation;

4) No matter whether the value property of @ Cacheable is configured with cache duration information or not, only reloadTime will not be configured without expireTime. The format of the value property specified by the framework is: cache name #expireTime#reloadTime

That is, only the following format will appear:

  • Cache name
  • Cache name #expireTime
  • Cache name #expireTime#reloadTime

reloadTime does not appear separately. The cache name #expireTime is configured, and reloadTime is configured using the default duration of the configuration file;

matters needing attention

1. Mykit cache redis spring XML reference and mykit cache redis spring annotation reference are mutually exclusive, that is, in a project, mykit cache redis spring XML and mykit cache redis spring annotation can only reference one at the same time;

2. The functions of mykit cache Redis spring XML and mykit cache Redis spring annotation are the same, but the mykit cache Redis spring annotation project is compatible with the situation when the Redis cluster is down or unable to connect to the Redis cluster for other reasons;

3. If the Redis cluster is down or cannot be connected for other reasons, mykit cache Redis spring XML will throw an exception and exit the execution; Mykit cache Redis spring annotation will print relevant exception information and continue to execute the original method downward.
4. If Spring container and Spring MVC are configured in the way of XML configuration in your project, and you want to configure the cache in the way of compatible Redis cluster downtime or failure to connect to Redis cluster for other reasons, you can go through the following configuration:

1) Add the following configuration classes to the project:

SpringContextConfig: configure Spring container:

package io.mykit.cache.redis.spring.utils.config;

import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.context.annotation.ImportResource;
import org.springframework.context.annotation.PropertySource;

import io.mykit.cache.redis.spring.annotation.config.CacheRedisConfig;

/**
 * @ClassName SpringContextConfig
 * @Description Spring Java to configure
 * @author binghe
 */
@Configuration
@EnableCaching
@EnableAspectJAutoProxy(proxyTargetClass = true)
@ComponentScan(value = {"io.mykit.cache"})
@PropertySource(value = {"classpath:properties/redis-default.properties", "classpath:properties/redis.properties"})
@ImportResource("classpath:spring/applicationContext.xml")
public class SpringContextConfig extends CacheRedisConfig{

}

Spring MVC config: configure spring MVC:

package io.mykit.cache.redis.spring.utils.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;

/**
 * @ClassName SpringMVCConfig
 * @Description SpringMVC Java to configure
 * @author binghe
 */
@Configuration
@ImportResource("classpath:spring/SpringMVC-servlet.xml")
public class SpringMVCConfig {

}

2) The web.xml of the web project is modified as follows:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
	version="3.0">
	  <!-- to configure spring monitor -->
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>
	
	<context-param>
		<param-name>contextClass</param-name>
		<param-value>
			org.springframework.web.context.support.AnnotationConfigWebApplicationContext
		</param-value>
    </context-param>
    
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>io.mykit.cache.redis.spring.utils.config.SpringContextConfig</param-value>
	</context-param>
	<listener>
		<listener-class>org.springframework.web.util.IntrospectorCleanupListener</listener-class>
	</listener>
	
	<servlet>
		<servlet-name>SpringMVC</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		
		<init-param>
			<param-name>contextClass</param-name>
			<param-value>
				org.springframework.web.context.support.AnnotationConfigWebApplicationContext
			</param-value>
		</init-param>
		
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>io.mykit.cache.redis.spring.utils.config.SpringMVCConfig</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
	</servlet>
	<servlet-mapping>
		<servlet-name>SpringMVC</servlet-name>
		<url-pattern>/</url-pattern>
	</servlet-mapping>
</web-app>

3. You need to use Spring+Memcached cluster to configure cache

1. It needs to be referenced in pom.xml of the project

<dependency>
    <groupId>io.mykit.cache</groupId>
    <artifactId>mykit-cache-memcached-spring-simple-xml</artifactId>
    <version>1.0.0-SNAPSHOT</version>
</dependency>

Note: This module of the framework does not support active cache refresh. The underlying core uses the simple spring memcached kernel.

2. Method of use

1) Create a new Memcached configuration file in the classpath:properties directory of your project, such as the memcached.properties file, and configure the properties of connecting to Memcached;

The properties are configured as follows:

#simple memcached config
simple.memcache.server=127.0.0.1:12000
simple.memcache.consistenthashing=true
simple.memcache.connectionpoolsize=1
simple.memcache.optimizeget=false
simple.memcache.optimizemergebuffer=false
simple.memcache.mergefactor=50
simple.memcache.usebinaryprotocol=true
simple.memcache.connectiontimeout=3000
simple.memcache.operationtimeout=2000
simple.memcache.enableheartbeat=true
simple.memcache.failureMode=false

Note: the properties of the customized memcached file must be the same as the default configured property key of memcached-default.properties, that is, the same as the above configured key, but you don't need to overwrite the above complete configuration,

You can configure only:

simple.memcache.server=192.168.209.121:12000

To override the simple.memcache.server attribute

2) Create a new spring configuration file in the classpath directory of your project, such as spring-context.xml. The configuration contents are as follows:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	   xmlns:p="http://www.springframework.org/schema/p"
	   xmlns:aop="http://www.springframework.org/schema/aop"
	   xmlns:context="http://www.springframework.org/schema/context"
	   xmlns:cache="http://www.springframework.org/schema/cache"
	   xsi:schemaLocation="http://www.springframework.org/schema/beans
                        http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
                        http://www.springframework.org/schema/context
                        http://www.springframework.org/schema/context/spring-context-4.2.xsd
                        http://www.springframework.org/schema/aop
                        http://www.springframework.org/schema/aop/spring-aop-4.2.xsd
                        http://www.springframework.org/schema/cache
                        http://www.springframework.org/schema/cache/spring-cache-4.2.xsd">

	<context:annotation-config />
	<aop:aspectj-autoproxy/>
	<context:component-scan base-package="io.mykit.cache"/>
	   <!-- Import profile -->
	  <context:property-placeholder location="classpath*:properties/memcached-default.properties, classpath*:properties/memcached.properties" system-properties-mode="FALLBACK"/>
	  <context:annotation-config />
	  <context:component-scan base-package="io.mykit.cache" />
 	 <import resource="classpath:memcached/memcached-simple.xml"/>
</beans>

According to the order of loading the properties file according to the above configuration, the framework will overwrite the properties of the memcached-default.properties file with the custom memcached.properties file properties.

If there are properties in the memcached-default.properties file that do not exist in memcached.properties, the framework will use the default properties in memcached-default.properties.

At this point, you can configure the cache using the annotations provided by simple spring memcached.

3. Introduction to simple spring memcached

3-1. Basic introduction

Simple spring memcached essentially uses AOP to call and manage the cache. Its core component declares some Advice. When encountering the corresponding entry point, it will execute these Advice to manage memcached.

Pointcuts are declared in the form of labels. During project development, corresponding label descriptions are usually given on DAO methods to represent the interception of the method by components. The pointcuts provided by components mainly include the following:

ReadThroughSingleCache,ReadThroughMultiCache,ReadThroughAssignCache

1) When these pointcuts are declared by the query method, the component will first read the data from the cache, and then skip the query method and return directly. If the data cannot be retrieved, execute the query method, and put the query results into the cache for next retrieval. InvalidateSingleCache,InvalidateMultiCache,InvalidateAssignCache

2) When these pointcuts are declared by the deletion method, the component will delete the corresponding entities in the cache so that the data status read from the cache next time is the latest UpdateSingleCache, UpdateMultiCache and updateasigncache

3-2 notes

Detailed description of each Annotation

  • ReadThroughSingleCache

Function: read the data in the Cache. If it does not exist, store the read data in the Cachekey. Generation rules: the parameter specified by ParameterValueKeyProvider. If the parameter object contains the method annotated by CacheKeyMethod, call its method, otherwise call toString method

@ReadThroughSingleCache(namespace = "Alpha", expiration = 30)
public String getDateString(@ParameterValueKeyProvider final String key) {
   final Date now = new Date();
   try {
       Thread.sleep(1500);
   } catch (InterruptedException ex) {
   		
   }
   return now.toString() + ":" + now.getTime();
}
  • InvalidateSingleCache

Function: invalidate data in Cache

key generation rules:

1) When using ParameterValueKeyProvider annotation, it is consistent with ReadThroughSingleCache

2) When using the ReturnValueKeyProvider annotation, the key is generated by the CacheKeyMethod or toString method of the returned object

@InvalidateSingleCache(namespace = "Charlie")
public void updateRandomString(@ParameterValueKeyProvider final Long key) {
    // Nothing really to do here.
}

@InvalidateSingleCache(namespace = "Charlie")
@ReturnValueKeyProvider
public Long updateRandomStringAgain(final Long key) {
    return key;
}
  • UpdateSingleCache

Function: update data in Cache

key generation rule: specified by ParameterValueKeyProvider

1)ParameterDataUpdateContent: the data in the method parameter is used as the data to update the cache

2)ReturnDataUpdateContent: the data generated after the method call is used as the data to update the cache

Note: the above two annotations must be used together with the annotation of Update * series

@UpdateSingleCache(namespace = "Alpha", expiration = 30)
public void overrideDateString(final int trash, @ParameterValueKeyProvider final String key,
       @ParameterDataUpdateContent final String overrideData) {
}

@UpdateSingleCache(namespace = "Bravo", expiration = 300)
@ReturnDataUpdateContent
public String updateTimestampValue(@ParameterValueKeyProvider final Long key) {
   try {
       Thread.sleep(100);
   } catch (InterruptedException ex) {
   }
   final Long now = new Date().getTime();
   final String result = now.toString() + "-U-" + key.toString();
   return result;
}
  • ReadThroughAssignCache

Function: read the data in the Cache. If it does not exist, store the read data in the Cache

key generation rule: specified by the assignedKey field in the ReadThroughAssignCache annotation

@ReadThroughAssignCache(assignedKey = "SomePhatKey", namespace = "Echo", expiration = 3000)
public List<String> getAssignStrings() {
    try {
        Thread.sleep(500);
    } catch (InterruptedException ex) {
    }
    final List<String> results = new ArrayList<String>();
    final long extra = System.currentTimeMillis() % 20;
    final String base = System.currentTimeMillis() + "";
    for (int ix = 0; ix < 20 + extra; ix++) {
        results.add(ix + "-" + base);
    }
    return results;
}
  • InvalidateAssignCache

Function: invalidate the data of the specified key in the cache

key generation rule: specified in the assignedKey field

@InvalidateAssignCache(assignedKey = "SomePhatKey", namespace = "Echo")
public void invalidateAssignStrings() {
	
}
  • UpdateAssignCache

Role: updates the specified cache

key generation rule: specified in the assignedKey field

@UpdateAssignCache(assignedKey = "SomePhatKey", namespace = "Echo", expiration = 3000)
public void updateAssignStrings(int bubpkus, @ParameterDataUpdateContent final List<String> newData) {
	
}

4. You need to use Spring + Ehcache cluster to configure cache

This module of the framework will not be implemented for the time being. Because the integration of Spring and Ehcache is too simple, you can implement the integration of Spring and Ehcache by yourself. This module does not provide encapsulation.

Spring 4 configuring annotation based ehcache caching

1. ehcache configuration file ehcache.xml

<?xml version="1.0" encoding="UTF-8"?>
<ehcache name="es">
    <diskStore path="java.io.tmpdir"/>
    <defaultCache
        maxElementsInMemory="10000" 
		eternal="false"
		timeToIdleSeconds="30" 
		timeToLiveSeconds="30" 
		overflowToDisk="true">
    </defaultCache>
 
 <!-- Configure custom cache 
	maxElementsInMemory: Maximum number of objects allowed to be created in the cache 
	eternal: Whether the object in the cache is permanent. If so, the timeout setting will be ignored and the object will never expire. 
	timeToIdleSeconds: The passivation time of cached data, that is, the maximum time interval between two accesses before an element dies, which is valid only when the element does not reside permanently, 
	If the value is 0, it means that the element can pause for an infinite time.
	timeToLiveSeconds: The lifetime of cached data, that is, the maximum time interval between the construction and extinction of an element, 
	This is only valid if the element does not reside permanently. If the value is 0, it means that the element can pause for an infinite time. 
	overflowToDisk: Whether to enable disk caching when memory is low. 
	memoryStoreEvictionPolicy: The elimination algorithm after the cache is full. 
-->

<cache name="statisticServiceCache" 
	maxElementsInMemory="1000"
	eternal="false" 
	overflowToDisk="true" 
	timeToIdleSeconds="900"
	timeToLiveSeconds="1800" 
	diskPersistent="false"
	memoryStoreEvictionPolicy="LFU" />
</ehcache>

2. Spring cache annotation and ehcache bean configuration

<cache:annotation-driven cache-manager="cacheManager"/>
<!-- cacheManager Factory class, specifying ehcache.xml Location of -->
<bean id="ehcache" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
	<property name="configLocation"  value="/WEB-INF/ehcache.xml"/>
	<!-- <property name="shared"  value="true"/>   -->  
</bean>
<!-- statement cacheManager -->
<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager">
	<property name="cacheManager" ref="ehcache" />
</bean>

3. Confirm that spring aop support is enabled

<aop:aspectj-autoproxy/>

4. Use of spring cache annotation

(1)@Cacheable

@Main parameters of Cacheable

  • value

The name of the cache, defined in the spring configuration file, must specify at least one

For example:

@Cacheable(value="mycache") perhaps 
@Cacheable(value={"cache1","cache2"}
  • key

The cached key can be empty. If it is specified to be written according to the spiel expression, if it is not specified, it will be combined according to all the parameters of the method by default

For example:

@Cacheable(value="testcache",key="#userName")
  • condition

The cache condition can be null. It is written in spiel and returns true or false. It can be cached only when it is true

For example:

@Cacheable(value="testcache",condition="#userName.length()

Examples are as follows:

@Cacheable(value = "statisticServiceCache", key = "'activityChartData_' + #urlID")
public ResultInfo getActivityChartData(String urlID, Date startMonth,Date endMonth) {
    ...
}

This annotation is used to check whether there is a cache with key value in the current cache system when calling the annotated method. If it exists, the cache object is returned directly without executing the method. If it does not exist, the method is called and the resulting return value is written to the cache.

(2)@CachePut

@Main parameters of CachePut

  • value

The name of the cache, defined in the spring configuration file, must specify at least one

For example:

@Cacheable(value="mycache") perhaps 
@Cacheable(value={"cache1","cache2"}
  • key

The cached key can be empty. If it is specified to be written according to the spiel expression, if it is not specified, it will be combined according to all the parameters of the method by default

For example:

@Cacheable(value="testcache",key="#userName")
  • condition

The cache condition can be null. It is written in spiel and returns true or false. It can be cached only when it is true

For example:

@Cacheable(value="testcache",condition="#userName.leng

@CachePut is used to write to the cache, but unlike @ Cacheable, the method of @ CachePut annotation is always executed, and then the return value of the method is written to the cache. This annotation is mainly used to add or update the cache.

(3) @CacheEvict

@Main parameters of CacheEvict

  • value

The name of the cache, defined in the spring configuration file, must specify at least one

For example:

@CachEvict(value="mycache") perhaps 
@CachEvict(value={"cache1","cache2"}
  • key

The cached key can be empty. If it is specified to be written according to the spiel expression, if it is not specified, it will be combined according to all the parameters of the method by default

For example:

@CachEvict(value="testcache",key="#userName")
  • condition

The cache condition can be empty. It is written in spiel and returns true or false. Only when it is true will the cache be cleared

For example:

@CachEvict(value="testcache", condition="#userName.length()>2")
  • allEntries

Whether to clear all cache contents. The default value is false. If true is specified, all caches will be cleared immediately after the method is called

For example:

@CachEvict(value="testcache",allEntries=true)
  • beforeInvocation

Whether to clear the method before execution. The default is false. If it is specified as true, the cache will be cleared before the method is executed. By default, if the method throws an exception, the cache will not be cleared

For example:

@CachEvict(value="testcache",beforeInvocation=true)

@CacheEvict is used to delete the cache

matters needing attention

No matter which module is used, the ApplicationContext needs to be configured into the SpringContextWrapper in the relevant project.

The example code is as follows:

package io.mykit.cache.test.redis.spring.utils;

import io.mykit.cache.redis.spring.context.SpringContextWrapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import redis.clients.util.Hashing;

/**
 * @author binghe
 * @version 1.0.0
 * @description Save the Spring ApplicationContext as a static variable, and you can get the ApplicaitonContext from any code, anywhere and at any time
 */
@Slf4j
@Component
public class SpringContext implements ApplicationContextAware {
    private static ApplicationContext applicationContext;

    /**
     * Implement the context injection function of ApplicationContextAware interface and store it into static variables
     */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) {
        SpringContext.applicationContext = applicationContext; // NOSONAR
        log.debug(SpringContext.class.getName() + " Path to class load:" + this.getClass().getResource("/").getPath() + ", hashcode:" + Hashing.MURMUR_HASH.hash(this.getClass().getResource("/").getPath()));
        log.debug(SpringContext.class.getName() + " applicationContext===>>>" + applicationContext);
        SpringContextWrapper.setApplicationContext(SpringContextWrapper.getContextKey(this.getClass()), applicationContext);
    }

    /**
     * Get the ApplicationContext stored in the static variable
     * @return ApplicationContext object
     */
    public static ApplicationContext getApplicationContext() {
        checkApplicationContext();
        return applicationContext;
    }

    /**
     * Get the Bean from the static variable ApplicationContext and automatically convert it to the type of the assigned object
     * @param name Spring The name of the Bean in the
     * @return Generic object
     */
    @SuppressWarnings("unchecked")
    public static <T> T getBean(String name) {
        checkApplicationContext();
        return (T) applicationContext.getBean(name);
    }

    /**
     * Get the Bean from the static variable ApplicationContext and automatically convert it to the type of the assigned object
     * @param clazz Specified clazz object
     * @return Generic object
     */
    public static <T> T getBean(Class<T> clazz) {
        checkApplicationContext();
        return (T) applicationContext.getBean(clazz);
    }

    /**
     * Clear the applicationContext static variable
     */
    public static void cleanApplicationContext() {
        applicationContext = null;
    }

    private static void checkApplicationContext() {
        if (applicationContext == null) {
            throw new IllegalStateException("applicaitonContext Not injected,Please in applicationContext.xml Defined in SpringContextHolder");
        }
    }
}

The project is still under development and has not been added to Maven central warehouse at present. It will be added to Maven central warehouse after subsequent development.

If this framework is helpful to you, please open Github and Gitee links to give this project a big Star and let more small partners benefit. You can also like, watch and forward this article~~

Write at the end

If you want to enter a big factory, want to be promoted and raised, or are confused about your existing work, you can communicate with me privately. I hope some of my experience can help you~~

Recommended reading:

Well, that's all for today. Let's praise, collect and comment. Let's walk up three times with one button. I'm glacier. I'll see you next time~~

Tags: Redis Programmer

Posted by noirsith on Thu, 23 Sep 2021 05:23:48 +0530