Skip to content

Commit 5325352

Browse files
committed
init
1 parent afd5135 commit 5325352

File tree

61 files changed

+4603
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

61 files changed

+4603
-0
lines changed

.gitignore

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,27 @@
2121

2222
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
2323
hs_err_pid*
24+
25+
# Eclipse
26+
.classpath
27+
.project
28+
/.settings/
29+
/target/
30+
31+
# IntelliJ IDEA
32+
.idea
33+
*.iml
34+
35+
# Maven Submodules
36+
**/target/
37+
38+
# PID
39+
*.pid
40+
41+
# Versions Maven Plugin
42+
*.xml.versionsBackup
43+
44+
# Spring Profiles
45+
application-*.yaml
46+
47+
*.log

pom.xml

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
3+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<modelVersion>4.0.0</modelVersion>
6+
<parent>
7+
<groupId>org.springframework.boot</groupId>
8+
<artifactId>spring-boot-dependencies</artifactId>
9+
<version>2.6.8</version>
10+
</parent>
11+
<groupId>io.github.ms100</groupId>
12+
<artifactId>cache-as-multi</artifactId>
13+
<version>1.0-SNAPSHOT</version>
14+
15+
<name>cache-as-multi</name>
16+
<!-- FIXME change it to the project's website -->
17+
<url>http://https://github.com/ms100/cache-as-multi</url>
18+
19+
<properties>
20+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
21+
<maven.compiler.source>1.8</maven.compiler.source>
22+
<maven.compiler.target>1.8</maven.compiler.target>
23+
</properties>
24+
25+
<dependencies>
26+
<dependency>
27+
<groupId>org.projectlombok</groupId>
28+
<artifactId>lombok</artifactId>
29+
</dependency>
30+
<!-- spring -->
31+
<dependency>
32+
<groupId>org.springframework.boot</groupId>
33+
<artifactId>spring-boot</artifactId>
34+
<scope>provided</scope>
35+
</dependency>
36+
<dependency>
37+
<groupId>org.springframework</groupId>
38+
<artifactId>spring-core</artifactId>
39+
<scope>provided</scope>
40+
</dependency>
41+
<dependency>
42+
<groupId>org.springframework.boot</groupId>
43+
<artifactId>spring-boot-starter-cache</artifactId>
44+
<scope>provided</scope>
45+
</dependency>
46+
<dependency>
47+
<groupId>org.springframework.boot</groupId>
48+
<artifactId>spring-boot-starter-aop</artifactId>
49+
<scope>provided</scope>
50+
</dependency>
51+
<dependency>
52+
<groupId>org.springframework.boot</groupId>
53+
<artifactId>spring-boot-starter-data-redis</artifactId>
54+
<optional>true</optional>
55+
</dependency>
56+
<dependency>
57+
<groupId>javax.cache</groupId>
58+
<artifactId>cache-api</artifactId>
59+
<scope>provided</scope>
60+
</dependency>
61+
<dependency>
62+
<groupId>org.springframework</groupId>
63+
<artifactId>spring-context-support</artifactId>
64+
<optional>true</optional>
65+
</dependency>
66+
<dependency>
67+
<groupId>junit</groupId>
68+
<artifactId>junit</artifactId>
69+
<scope>test</scope>
70+
</dependency>
71+
72+
</dependencies>
73+
74+
<build>
75+
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
76+
<plugins>
77+
<!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle -->
78+
<plugin>
79+
<artifactId>maven-clean-plugin</artifactId>
80+
<version>3.1.0</version>
81+
</plugin>
82+
<!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging -->
83+
<plugin>
84+
<artifactId>maven-resources-plugin</artifactId>
85+
<version>3.0.2</version>
86+
</plugin>
87+
<plugin>
88+
<artifactId>maven-compiler-plugin</artifactId>
89+
<version>3.8.0</version>
90+
</plugin>
91+
<plugin>
92+
<artifactId>maven-surefire-plugin</artifactId>
93+
<version>2.22.1</version>
94+
</plugin>
95+
<plugin>
96+
<artifactId>maven-jar-plugin</artifactId>
97+
<version>3.0.2</version>
98+
</plugin>
99+
<plugin>
100+
<artifactId>maven-install-plugin</artifactId>
101+
<version>2.5.2</version>
102+
</plugin>
103+
<plugin>
104+
<artifactId>maven-deploy-plugin</artifactId>
105+
<version>2.8.2</version>
106+
</plugin>
107+
<!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle -->
108+
<plugin>
109+
<artifactId>maven-site-plugin</artifactId>
110+
<version>3.7.1</version>
111+
</plugin>
112+
<plugin>
113+
<artifactId>maven-project-info-reports-plugin</artifactId>
114+
<version>3.0.0</version>
115+
</plugin>
116+
</plugins>
117+
</pluginManagement>
118+
</build>
119+
</project>
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package io.github.ms100.cacheasmulti.cache;
2+
3+
import org.springframework.cache.Cache;
4+
5+
import java.util.Collection;
6+
import java.util.Map;
7+
8+
/**
9+
* 对Spring的{@link Cache}的增强接口,增加了批量的方法
10+
* 注意与javax的{@link javax.cache.Cache}的区别
11+
*
12+
* @author zhumengshuai
13+
*/
14+
public interface EnhancedCache extends Cache {
15+
/**
16+
* 批量查询
17+
*
18+
* @param keys 缓存keys
19+
* @return key-value对
20+
*/
21+
Map<Object, ValueWrapper> multiGet(Collection<?> keys);
22+
23+
/**
24+
* 批量更新
25+
*
26+
* @param map key-value对
27+
*/
28+
void multiPut(Map<?, ?> map);
29+
30+
/**
31+
* 批量删除
32+
*
33+
* @param keys 缓存keys
34+
*/
35+
void multiEvict(Collection<?> keys);
36+
}
Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
package io.github.ms100.cacheasmulti.cache.annotation;
2+
3+
import org.springframework.cache.annotation.CacheEvict;
4+
import org.springframework.cache.annotation.CachePut;
5+
import org.springframework.cache.annotation.Cacheable;
6+
import org.springframework.cache.interceptor.KeyGenerator;
7+
8+
import javax.cache.annotation.CacheKey;
9+
import javax.cache.annotation.CacheKeyGenerator;
10+
import javax.cache.annotation.CacheRemove;
11+
import javax.cache.annotation.CacheResult;
12+
import javax.cache.annotation.CacheValue;
13+
import java.lang.annotation.ElementType;
14+
import java.lang.annotation.Retention;
15+
import java.lang.annotation.RetentionPolicy;
16+
import java.lang.annotation.Target;
17+
import java.lang.reflect.Method;
18+
19+
/**
20+
* 本注解需要与下面两套注解搭配使用,以实现对被注解参数所在的方法进行批量的缓存操作。
21+
* <ol>
22+
* <li>Spring的缓存注解{@link Cacheable @Cacheable}、{@link CachePut @CachePut}、{@link CacheEvict @CacheEvict}
23+
* <li>JSR-107的注解{@link CacheResult @CacheResult}、{@link javax.cache.annotation.CachePut JSR-107的@CachePut}、{@link CacheRemove @CacheRemove}、{@link CacheKey @CacheKey}
24+
* </ol>
25+
* <p><b>使用说明:</b></p>
26+
* <p>
27+
* 假设已有获取单个对象的方法,如下:
28+
* <pre><code>
29+
* public Foo getFoo(Integer fooId) {
30+
* ...
31+
* }
32+
* </code></pre>
33+
* 此时如果需要获取批量对象的方法,通常会是下面两种写法:
34+
* <pre><code>
35+
* public Map&lt;Integer, Foo&gt; getMultiFoo(Collection&lt;Integer&gt; fooIds) {
36+
* ...
37+
* }
38+
*
39+
* public List&lt;Foo&gt; getMultiFoo(List&lt;Integer&gt; fooIds) {
40+
* ...
41+
* }
42+
* </code></pre>
43+
* 获取批量对象的方法相对于获取单个对象的方法会有两点变化:
44+
* <ol>
45+
* <li>入参从单个对象(以下称【对象参数】)变为对象集合(以下称【对象集合参数】),例如 Integer 变为 Collection&lt;Integer&gt; 或 Set&lt;Integer&gt; 或 List&lt;Integer&gt;。</li>
46+
* <li>返回值从单个对象变为 Map&lt;K,V&gt; 或者 List&lt;V&gt; 。例如 Map&lt;Integer,Foo&gt; 或 List&lt;Foo&gt;,若返回的是 List,那应与【对象集合参数】大小相同并顺序一致。</li>
47+
* </ol></p>
48+
* <p>
49+
* 在上面例子中,如果需要对获取单个对象的方法做缓存,会使用 {@link Cacheable @Cacheable} 或 {@link CacheResult @CacheResult} 注解:
50+
* (PS: 这里将 &#64;CacheResult 和 &#64;Cacheable 放在一起举例子,实际使用时通常只用其中的一个)
51+
* <pre><code>
52+
* &#64;Cacheable(cacheNames = "foo")
53+
* &#64;CacheResult(cacheName = "foo")
54+
* public Foo getFoo(Integer fooId) {
55+
* // 用 fooId 生成缓存 key 和计算 condition、unless 条件,用 Foo 为缓存值
56+
* }
57+
* </code></pre>
58+
* 如果对获取批量对象的方法直接加上 {@link Cacheable @Cacheable} 或 {@link CacheResult @CacheResult},则会使用【对象集合参数】整体生成一个缓存 key,将返回的 Map 或 List 整体作为一个缓存值。
59+
* 但通常我们会希望它能变为多个 fooId => Foo 的缓存,即:使用【对象集合参数】中每个【元素】和它对应的值分别作缓存。此时只需要在【对象集合参数】上加上 {@link CacheAsMulti @CacheAsMulti} 注解即可实现我们想要的缓存方式。
60+
* <pre><code>
61+
* &#64;Cacheable(cacheNames = "foo")
62+
* &#64;CacheResult(cacheName = "foo")
63+
* public Map&lt;Integer, Foo&gt; getMultiFoo(&#64;CacheAsMulti Collection&lt;Integer&gt; fooIds) {
64+
* // 为 fooIds 集合中每个元素分别生成缓存 key 和计算 condition、unless 条件,用 Map 中对应的值作为缓存值
65+
* }
66+
*
67+
* &#64;Cacheable(cacheNames = "foo")
68+
* &#64;CacheResult(cacheName = "foo")
69+
* public List&lt;Foo&gt; getMultiFoo(&#64;CacheAsMulti List&lt;Integer&gt; fooIds) {
70+
* // 为 fooIds 集合中每个元素分别生成缓存 key 和计算 condition、unless 条件,用 List 中对应的值作为缓存值
71+
* // 之后的例子中,返回 List 和 返回 Map 的处理方式都一样,就不再单独举例
72+
* }
73+
* </code></pre>
74+
* 当方法有多个参数时,示例如下:
75+
* <ol><li>
76+
* 使用 {@link Cacheable} 时 {@link Cacheable#key()} 未配置、使用 {@link CacheResult} 时参数中没有 {@link CacheKey @CacheKey}
77+
* <pre><code>
78+
* &#64;Cacheable(cacheNames = "foo", key="")
79+
* &#64;CacheResult(cacheName = "foo")
80+
* public Foo getFoo(Integer fooId, String arg1) {
81+
* // 用 fooId 和 arg1 两个参数生成缓存的 key,用返回值作为缓存值
82+
* }
83+
*
84+
* &#64;Cacheable(cacheNames = "foo", key="")
85+
* &#64;CacheResult(cacheName = "foo")
86+
* public Map&lt;Integer, Foo&gt; getMultiFoo(&#64;CacheAsMulti Collection&lt;Integer&gt; fooIds, String arg1) {
87+
* // 用 fooIds 中的每个【元素】分别和 arg1 参数生成缓存的 key,用返回 Map 中【元素】对应的值作为缓存值
88+
* }
89+
* </code></pre>
90+
* </li><li>
91+
* 使用 {@link Cacheable} 时 {@link Cacheable#key()} 只配置了【对象参数】的引用、使用 {@link CacheResult} 时只有【对象参数】上有 {@link CacheKey}
92+
* <pre><code>
93+
* &#64;Cacheable(cacheNames = "foo", key="#fooId")
94+
* &#64;CacheResult(cacheName = "foo")
95+
* public Foo getFoo(&#64;CacheKey Integer fooId, String arg1) {
96+
* // 用 fooId 生成缓存的 key,用返回值作为缓存值
97+
* }
98+
*
99+
* &#64;Cacheable(cacheNames = "foo", key="#fooIds")
100+
* &#64;CacheResult(cacheName = "foo")
101+
* public Map&lt;Integer, Foo&gt; getMultiFoo(&#64;CacheAsMulti &#64;CacheKey Collection&lt;Integer&gt; fooIds, String arg1) {
102+
* // 用 fooIds 中的每个【元素】分别生成缓存的 key,用返回 Map 中【元素】对应的值作为缓存值
103+
* }
104+
* </code></pre>
105+
* </li><li>
106+
* 使用 {@link Cacheable} 时 {@link Cacheable#key()} 配置了若干参数的引用、使用 {@link CacheResult} 时参数中有若干 {@link CacheKey @CacheKey}
107+
* <pre><code>
108+
* &#64;Cacheable(cacheNames = "foo", key="#fooId+#arg1")
109+
* &#64;CacheResult(cacheName = "foo")
110+
* public Foo getFoo(&#64;CacheKey Integer fooId, &#64;CacheKey String arg1, Float arg2) {
111+
* // 用 fooId 和 arg1 两个参数生成缓存的 key,用返回值作为缓存值
112+
* }
113+
*
114+
* &#64;Cacheable(cacheNames = "foo", key="#fooIds+#arg1")
115+
* &#64;CacheResult(cacheName = "foo")
116+
* public Map&lt;Integer, Foo&gt; getMultiFoo(&#64;CacheAsMulti &#64;CacheKey Collection&lt;Integer&gt; fooIds, &#64;CacheKey String arg1, Float arg2) {
117+
* // 用 fooIds 中的每个【元素】分别和 arg1 参数生成缓存的 key,用返回 Map 中【元素】对应的值作为缓存值
118+
* // 注意此时【对象集合参数】需要在 {@link Cacheable#key()} 中,需要有 &#64;CacheKey 注解
119+
* }
120+
* </code></pre>
121+
* </li></ol></p>
122+
* <p>
123+
* <b>与其他注解搭配使用时的说明:</b>
124+
* <ul>
125+
* <li>与 {@link CachePut Spring 的 CachePut} 搭配时,同样符合上面的例子。</li>
126+
* <li>与 {@link CacheEvict} 搭配时,若注解的 key 参数中没有 #result,对【方法】返回类型无要求;若 key 中有 #result,【方法】返回类型需要是 Map 或 List。</li>
127+
* <li>与 Spring 的 `@CachePut`、`@CacheEvict` 搭配时,若 key 参数中有 `#result`, 则可以没有【对象集合参数】的引用。</li>
128+
* <li>与 {@link CacheRemove} 搭配时,对【方法】返回类型无要求。</li>
129+
* <li>与 {@link javax.cache.annotation.CachePut JSR-107 的 CachePut} 搭配时,对【方法】返回类型无要求,可参照下面的示例:
130+
* <ul><li>单个参数做key,未配置 {@link CacheKey @CacheKey}:
131+
* <pre><code>
132+
* &#64;CachePut(cacheName = "foo")
133+
* public void putFoo(Integer fooId, &#64;CacheValue String value) {
134+
* // 用 fooId 参数生成缓存的 key,用 value 作为缓存值
135+
* }
136+
*
137+
* &#64;CachePut(cacheName = "foo")
138+
* public void putMultiFoo(&#64;CacheAsMulti &#64;CacheValue Map<Integer, String> fooIdValueMap) {
139+
* // 此时方法的 @CacheValue 参数必须为 Map 类型
140+
* // 用 fooIdValueMap 中的每个 Entry 的 key 分别生成缓存的 key,用 Entry 的 value 作为缓存值
141+
* }
142+
* </code></pre>
143+
* </li><li>多个参数做key,未配置 {@link CacheKey @CacheKey}:
144+
* <pre><code>
145+
* &#64;CachePut(cacheName = "foo")
146+
* public void putFoo(Integer fooId, String arg1, &#64;CacheValue String value) {
147+
* // 用 fooId 和 arg1 两个参数生成缓存的 key,用 value 作为缓存值
148+
* }
149+
*
150+
* &#64;CachePut(cacheName = "foo")
151+
* public void putMultiFoo(&#64;CacheAsMulti &#64;CacheValue Map<Integer, String> fooIdValueMap, String arg1) {
152+
* // 此时方法的 @CacheValue 参数必须为 Map 类型
153+
* // 用 fooIdValueMap 中的每个 Entry 的 key 分别和 arg1 参数生成缓存的 key,用 Entry 的 value 作为缓存值
154+
* }
155+
* </code></pre>
156+
* </li><li>只有【对象参数】上有 {@link CacheKey @CacheKey}:
157+
* <pre><code>
158+
* &#64;CachePut(cacheName = "foo")
159+
* public void putFoo(&#64;CacheKey Integer fooId, String arg1, &#64;CacheValue String value) {
160+
* // 用 fooId 参数生成缓存的 key,用 value 作为缓存值
161+
* }
162+
*
163+
* &#64;CachePut(cacheName = "foo")
164+
* public void putMultiFoo(&#64;CacheAsMulti &#64;CacheKey &#64;CacheValue Map<Integer, String> fooIdValueMap, String arg1) {
165+
* // 此时方法的 @CacheValue 参数必须为 Map 类型
166+
* // 用 fooIdValueMap 中的每个 Entry 的 key 分别生成缓存的 key,用 Entry 的 value 作为缓存值
167+
* }
168+
* </code></pre>
169+
* </li><li>若干参数上有 {@link CacheKey @CacheKey}:
170+
* <pre><code>
171+
* &#64;CachePut(cacheName = "foo")
172+
* public void putFoo(&#64;CacheKey Integer fooId, &#64;CacheKey String arg1, String arg2, &#64;CacheValue String value) {
173+
* // 用 fooId 和 arg1 两个参数生成缓存的 key,用 value 作为缓存值
174+
* }
175+
*
176+
* &#64;CachePut(cacheName = "foo")
177+
* public void putMultiFoo(&#64;CacheAsMulti &#64;CacheKey &#64;CacheValue Map<Integer, String> fooIdValueMap, &#64;CacheKey String arg1, String arg2) {
178+
* // 此时方法的 @CacheValue 参数必须为 Map 类型
179+
* // 用 fooIdValueMap 中的每个 Entry 的 key 分别和 arg1 参数生成缓存的 key,用 Entry 的 value 作为缓存值
180+
* }
181+
* </code></pre>
182+
* </li></li></ul>
183+
* </p>
184+
* <p>
185+
* <b>总结和补充:</b>
186+
* <ol>
187+
* <li>@CacheAsMulti 注解不能替代 Spring 缓存注解中的 key 参数,例如:{@link Cacheable#key()},也不能替代 {@link CacheKey}、{@link CacheValue} 注解。</li>
188+
* <li>如果使用自定义的 {@link KeyGenerator},则会用【对象集合参数】的每个【元素】和其他参数组成 Object[] 传入 {@link KeyGenerator#generate(Object, Method, Object...)} 计算缓存 key;自定义的 {@link CacheKeyGenerator} 也一样。</li>
189+
* <li>与生成缓存的注解搭配使用时,若方法的返回类型是 Map,【元素】在 Map 中对应的值为 null 就会缓存 null,【元素】在 Map 中不存在就不缓存。</li>
190+
* <li>与 {@link CachePut} 和 {@link CacheEvict} 搭配,注解的 key 参数配置了 #result 时,若方法的返回类型是 Map,对于 Map 中不存在的【元素】会使用 null 作为缺省值来计算缓存 key 和 condition、unless 条件。</li>
191+
* <li>{@link Cacheable#condition()}、{@link Cacheable#unless()} 等条件表达式是用【对象集合参数】中的每个【元素】分别计算,只将不符合的【元素】排除,而不是整个集合。</li>
192+
* </ol>
193+
* </p>
194+
*
195+
* @author zhumengshuai
196+
*/
197+
@Target(ElementType.PARAMETER)
198+
@Retention(RetentionPolicy.RUNTIME)
199+
public @interface CacheAsMulti {
200+
201+
}
202+
203+

0 commit comments

Comments
 (0)