Skip to content

Commit da05cf6

Browse files
committed
reactify-cache
1 parent c1f23b0 commit da05cf6

File tree

8 files changed

+338
-70
lines changed

8 files changed

+338
-70
lines changed

reactify-cache/src/main/java/com/reactify/ApplicationContextProvider.java

+16-12
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,15 @@
2222

2323
/**
2424
* <p>
25-
* The {@code ApplicationContextProvider} class serves as a utility for accessing
26-
* the {@link ApplicationContext} from anywhere within the application.
25+
* The {@code ApplicationContextProvider} class serves as a utility for
26+
* accessing the {@link ApplicationContext} from anywhere within the
27+
* application.
2728
* </p>
2829
* <p>
29-
* This class implements {@link ApplicationContextAware} to store a static reference
30-
* to the {@link ApplicationContext}, enabling other components to retrieve Spring beans
31-
* programmatically without requiring dependency injection.
30+
* This class implements {@link ApplicationContextAware} to store a static
31+
* reference to the {@link ApplicationContext}, enabling other components to
32+
* retrieve Spring beans programmatically without requiring dependency
33+
* injection.
3234
* </p>
3335
* <p>
3436
* Use the {@link #getBean(Class)} method to obtain a bean instance by its type.
@@ -40,8 +42,8 @@
4042
public class ApplicationContextProvider implements ApplicationContextAware {
4143

4244
/**
43-
* The application context instance, stored as a static volatile variable
44-
* to ensure thread safety when accessing the context across multiple threads.
45+
* The application context instance, stored as a static volatile variable to
46+
* ensure thread safety when accessing the context across multiple threads.
4547
* <p>
4648
* The {@code volatile} keyword guarantees visibility of updates to the variable
4749
* across threads, and the double-checked locking mechanism ensures that the
@@ -53,15 +55,17 @@ public class ApplicationContextProvider implements ApplicationContextAware {
5355
/**
5456
* Retrieves a bean from the {@link ApplicationContext} by its type.
5557
* <p>
56-
* This method allows fetching beans from the Spring container without
57-
* using {@code @Autowired} or other dependency injection mechanisms.
58+
* This method allows fetching beans from the Spring container without using
59+
* {@code @Autowired} or other dependency injection mechanisms.
5860
* </p>
5961
*
60-
* @param clazz the class type of the desired bean
61-
* @param <T> the generic type of the bean
62+
* @param clazz
63+
* the class type of the desired bean
64+
* @param <T>
65+
* the generic type of the bean
6266
* @return an instance of the requested bean
6367
* @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
64-
* if no bean of the specified type is found
68+
* if no bean of the specified type is found
6569
*/
6670
public static <T> T getBean(Class<T> clazz) {
6771
return context.getBean(clazz);

reactify-cache/src/main/java/com/reactify/CacheAspect.java

+10-4
Original file line numberDiff line numberDiff line change
@@ -69,16 +69,22 @@ private void processAnnotation() {}
6969
public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
7070
Object[] args = joinPoint.getArgs();
7171
Object key = SimpleKeyGenerator.generateKey(args);
72-
String nameCache = ClassUtils.getUserClass(joinPoint.getTarget().getClass()).getSimpleName() + "."
73-
+ joinPoint.getSignature().getName();
72+
String nameCache = ClassUtils.getUserClass(joinPoint.getTarget().getClass())
73+
.getSimpleName() + "." + joinPoint.getSignature().getName();
7474
Cache<Object, Object> cache = CacheStore.getCache(nameCache);
7575
log.debug("Checking cache for method: {} with key: {}", nameCache, key);
76-
return CacheMono.lookup(k -> Mono.justOrEmpty(!DataUtil.isNullOrEmpty(cache) ? cache.getIfPresent(key) : Mono.empty()).map(Signal::next), key)
76+
return CacheMono.lookup(
77+
k -> Mono.justOrEmpty(!DataUtil.isNullOrEmpty(cache) ? cache.getIfPresent(key) : Mono.empty())
78+
.map(Signal::next),
79+
key)
7780
.onCacheMissResume(Mono.defer(() -> {
7881
try {
7982
Object result = joinPoint.proceed(args);
8083
if (!(result instanceof Mono<?>)) {
81-
log.warn("Method {} must return a Mono<?> but got: {}", nameCache, result.getClass().getSimpleName());
84+
log.warn(
85+
"Method {} must return a Mono<?> but got: {}",
86+
nameCache,
87+
result.getClass().getSimpleName());
8288
return Mono.error(new IllegalStateException("Method must return Mono<?>"));
8389
}
8490
@SuppressWarnings("unchecked")

reactify-cache/src/main/java/com/reactify/CacheStore.java

+26-18
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@
1818
import com.github.benmanes.caffeine.cache.Cache;
1919
import com.github.benmanes.caffeine.cache.Caffeine;
2020
import com.github.benmanes.caffeine.cache.Scheduler;
21+
import java.lang.reflect.Method;
22+
import java.time.Duration;
23+
import java.util.*;
24+
import javax.annotation.PostConstruct;
2125
import org.reflections.Reflections;
2226
import org.reflections.scanners.Scanners;
2327
import org.slf4j.Logger;
@@ -31,18 +35,14 @@
3135
import org.springframework.scheduling.annotation.Async;
3236
import org.springframework.stereotype.Component;
3337

34-
import javax.annotation.PostConstruct;
35-
import java.lang.reflect.Method;
36-
import java.time.Duration;
37-
import java.util.*;
38-
3938
/**
40-
* The {@code CacheStore} class is responsible for managing local caches using Caffeine.
41-
* It automatically initializes caches based on methods annotated with {@link LocalCache},
42-
* supports retrieving caches by name, and provides functionalities to clear specific or all caches.
39+
* The {@code CacheStore} class is responsible for managing local caches using
40+
* Caffeine. It automatically initializes caches based on methods annotated with
41+
* {@link LocalCache}, supports retrieving caches by name, and provides
42+
* functionalities to clear specific or all caches.
4343
* <p>
44-
* This class also supports auto-loading caches on application startup and integrates with Spring's
45-
* application context lifecycle events.
44+
* This class also supports auto-loading caches on application startup and
45+
* integrates with Spring's application context lifecycle events.
4646
* </p>
4747
*
4848
* @author hoangtien2k3
@@ -58,7 +58,9 @@ public class CacheStore implements ApplicationContextAware {
5858
/** Stores the caches mapped by their names. */
5959
private static final HashMap<String, Cache<Object, Object>> caches = new HashMap<>();
6060

61-
/** Stores methods annotated with {@link LocalCache} that require auto-loading. */
61+
/**
62+
* Stores methods annotated with {@link LocalCache} that require auto-loading.
63+
*/
6264
private static final Set<Method> autoLoadMethods = new HashSet<>();
6365

6466
/** The base package for scanning cache-related methods. */
@@ -127,7 +129,8 @@ public static List<String> getCaches() {
127129
/**
128130
* Clears the specified cache by name.
129131
*
130-
* @param cacheName the name of the cache to clear
132+
* @param cacheName
133+
* the name of the cache to clear
131134
* @return the number of cleared caches (0 or 1)
132135
*/
133136
public static int clearCachesByName(String cacheName) {
@@ -150,17 +153,19 @@ public static int clearAllCaches() {
150153
log.info("Clearing all caches");
151154
int count = 0;
152155
for (Map.Entry<String, Cache<Object, Object>> entry : caches.entrySet()) {
153-
count ++;
156+
count++;
154157
entry.getValue().invalidateAll();
155158
log.info("Cleared cache: {}", entry.getKey());
156159
}
157160
return count;
158161
}
159162

160163
/**
161-
* Automatically loads caches for methods annotated with {@link LocalCache} that support auto-loading.
164+
* Automatically loads caches for methods annotated with {@link LocalCache} that
165+
* support auto-loading.
162166
*
163-
* @param event the application context refresh event
167+
* @param event
168+
* the application context refresh event
164169
*/
165170
@Async
166171
@EventListener
@@ -177,10 +182,13 @@ public void autoLoad(ContextRefreshedEvent event) {
177182
}
178183

179184
/**
180-
* Sets the application context and determines the base package for scanning cache methods.
185+
* Sets the application context and determines the base package for scanning
186+
* cache methods.
181187
*
182-
* @param applicationContext the application context
183-
* @throws BeansException if an error occurs while setting the context
188+
* @param applicationContext
189+
* the application context
190+
* @throws BeansException
191+
* if an error occurs while setting the context
184192
*/
185193
@Override
186194
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {

reactify-cache/src/main/java/com/reactify/CacheUtils.java

+7-4
Original file line numberDiff line numberDiff line change
@@ -65,16 +65,19 @@ public static void invokeMethod(Method method) {
6565
if (result instanceof Mono<?> monoResult) {
6666
monoResult.subscribe(
6767
success -> log.debug("Successfully executed {}", methodName),
68-
error -> log.error("Error executing {}", methodName, error)
69-
);
68+
error -> log.error("Error executing {}", methodName, error));
7069
} else {
7170
log.warn("Method {} does not return a Mono<?>", methodName);
7271
}
7372
} catch (IllegalAccessException e) {
7473
log.error("Access violation when invoking method {}: {}", method.getName(), e.getMessage(), e);
7574
} catch (Exception e) {
76-
log.error("Error when autoload cache {}.{}.{}",
77-
method.getDeclaringClass().getSimpleName(), method.getName(), e.getMessage(), e);
75+
log.error(
76+
"Error when autoload cache {}.{}.{}",
77+
method.getDeclaringClass().getSimpleName(),
78+
method.getName(),
79+
e.getMessage(),
80+
e);
7881
}
7982
}
8083
}

reactify-cache/src/main/java/com/reactify/LocalCache.java

+34-21
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,20 @@
2121
import java.lang.annotation.Target;
2222

2323
/**
24-
* Annotation for enabling local caching on method results to enhance performance
25-
* by reducing redundant computations and minimizing repeated data retrievals from
26-
* external sources such as databases or APIs.
24+
* Annotation for enabling local caching on method results to enhance
25+
* performance by reducing redundant computations and minimizing repeated data
26+
* retrievals from external sources such as databases or APIs.
2727
*
2828
* <p>
29-
* This annotation provides a flexible caching mechanism with configurable properties:
29+
* This annotation provides a flexible caching mechanism with configurable
30+
* properties:
3031
* <ul>
31-
* <li><strong>durationInMinute</strong>: Defines the lifespan of cached entries in minutes.</li>
32-
* <li><strong>maxRecord</strong>: Specifies the maximum number of entries that can be stored in the cache.</li>
33-
* <li><strong>autoCache</strong>: Determines whether caching should be automatically applied when the method is invoked.</li>
32+
* <li><strong>durationInMinute</strong>: Defines the lifespan of cached entries
33+
* in minutes.</li>
34+
* <li><strong>maxRecord</strong>: Specifies the maximum number of entries that
35+
* can be stored in the cache.</li>
36+
* <li><strong>autoCache</strong>: Determines whether caching should be
37+
* automatically applied when the method is invoked.</li>
3438
* </ul>
3539
* </p>
3640
*
@@ -47,33 +51,40 @@
4751
* {@code
4852
* @LocalCache(durationInMinute = 15, maxRecord = 200, autoCache = true)
4953
* public List<User> fetchActiveUsers() {
50-
* // Retrieves a list of active users from the database
54+
* // Retrieves a list of active users from the database
5155
* }
5256
* }
5357
* </pre>
5458
*
5559
* <h3>Annotation Properties:</h3>
5660
* <dl>
5761
* <dt><strong>durationInMinute</strong></dt>
58-
* <dd>Specifies how long (in minutes) the cache entry remains valid. Default is 120 minutes.</dd>
62+
* <dd>Specifies how long (in minutes) the cache entry remains valid. Default is
63+
* 120 minutes.</dd>
5964
*
6065
* <dt><strong>maxRecord</strong></dt>
61-
* <dd>Limits the number of records stored in the cache at any given time. Default is 1000 entries.</dd>
66+
* <dd>Limits the number of records stored in the cache at any given time.
67+
* Default is 1000 entries.</dd>
6268
*
6369
* <dt><strong>autoCache</strong></dt>
64-
* <dd>If set to <code>true</code>, caching is applied automatically whenever the method is executed. Default is <code>false</code>.</dd>
70+
* <dd>If set to <code>true</code>, caching is applied automatically whenever
71+
* the method is executed. Default is <code>false</code>.</dd>
6572
* </dl>
6673
*
6774
* <h3>Best Practices:</h3>
6875
* <ul>
69-
* <li>Use on methods that return frequently accessed and computationally expensive results.</li>
70-
* <li>Avoid applying to methods with frequently changing data to prevent stale cache issues.</li>
71-
* <li>Adjust `durationInMinute` and `maxRecord` according to system load and data update frequency.</li>
76+
* <li>Use on methods that return frequently accessed and computationally
77+
* expensive results.</li>
78+
* <li>Avoid applying to methods with frequently changing data to prevent stale
79+
* cache issues.</li>
80+
* <li>Adjust `durationInMinute` and `maxRecord` according to system load and
81+
* data update frequency.</li>
7282
* </ul>
7383
*
7484
* <p>
75-
* This annotation is particularly useful in microservices and high-performance applications
76-
* where minimizing latency and optimizing resource utilization are crucial.
85+
* This annotation is particularly useful in microservices and high-performance
86+
* applications where minimizing latency and optimizing resource utilization are
87+
* crucial.
7788
* </p>
7889
*/
7990
@Target(ElementType.METHOD)
@@ -89,18 +100,20 @@
89100
int durationInMinute() default 120;
90101

91102
/**
92-
* Specifies the maximum number of records that can be stored in the cache.
93-
* Once this limit is reached, older entries may be evicted based on cache policies.
103+
* Specifies the maximum number of records that can be stored in the cache. Once
104+
* this limit is reached, older entries may be evicted based on cache policies.
94105
*
95106
* @return maximum cache size (default: 1000)
96107
*/
97108
int maxRecord() default 1000;
98109

99110
/**
100-
* Indicates whether caching should be automatically applied when the method is invoked.
101-
* If enabled, the method execution result will be stored in the cache for subsequent calls.
111+
* Indicates whether caching should be automatically applied when the method is
112+
* invoked. If enabled, the method execution result will be stored in the cache
113+
* for subsequent calls.
102114
*
103-
* @return <code>true</code> to enable automatic caching, <code>false</code> otherwise (default: false)
115+
* @return <code>true</code> to enable automatic caching, <code>false</code>
116+
* otherwise (default: false)
104117
*/
105118
boolean autoCache() default false;
106119
}

reactify-cache/src/main/java/com/reactify/request/LocalCacheRequest.java

+31-11
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,18 @@
1+
/*
2+
* Copyright 2024-2025 the original author Hoàng Anh Tiến.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
116
package com.reactify.request;
217

318
/**
@@ -6,18 +21,22 @@
621
* </p>
722
*
823
* <p>
9-
* This class is used to specify the cache clearing operation, allowing
10-
* users to delete cache at different levels based on the provided type.
24+
* This class is used to specify the cache clearing operation, allowing users to
25+
* delete cache at different levels based on the provided type.
1126
* </p>
1227
*
1328
* <ul>
14-
* <li><b>SERVICE_LEVEL</b>: Clears all cached entries related to a specific service.</li>
15-
* <li><b>METHOD_LEVEL</b>: Clears a specific cache by its unique name (absolute method path).</li>
16-
* <li><b>ALL_LEVEL</b>: Clears all caches across all services in the system.</li>
29+
* <li><b>SERVICE_LEVEL</b>: Clears all cached entries related to a specific
30+
* service.</li>
31+
* <li><b>METHOD_LEVEL</b>: Clears a specific cache by its unique name (absolute
32+
* method path).</li>
33+
* <li><b>ALL_LEVEL</b>: Clears all caches across all services in the
34+
* system.</li>
1735
* </ul>
1836
*
1937
* <p>
20-
* The `nameCache` field is required when using `SERVICE_LEVEL` or `METHOD_LEVEL` type.
38+
* The `nameCache` field is required when using `SERVICE_LEVEL` or
39+
* `METHOD_LEVEL` type.
2140
* </p>
2241
*
2342
* @author hoangtien2k3
@@ -29,9 +48,10 @@ public class LocalCacheRequest {
2948
* <p>
3049
* Accepted values:
3150
* <ul>
32-
* <li><b>SERVICE_LEVEL</b> - Clear cache for a specific service.</li>
33-
* <li><b>METHOD_LEVEL</b> - Clear a specific cache by its absolute method path.</li>
34-
* <li><b>ALL_LEVEL</b> - Clear all caches in the system.</li>
51+
* <li><b>SERVICE_LEVEL</b> - Clear cache for a specific service.</li>
52+
* <li><b>METHOD_LEVEL</b> - Clear a specific cache by its absolute method
53+
* path.</li>
54+
* <li><b>ALL_LEVEL</b> - Clear all caches in the system.</li>
3555
* </ul>
3656
* </p>
3757
*/
@@ -42,8 +62,8 @@ public class LocalCacheRequest {
4262
* <p>
4363
* This field is required when `type` is either:
4464
* <ul>
45-
* <li><b>SERVICE_LEVEL</b> - Specifies which service’s cache to clear.</li>
46-
* <li><b>METHOD_LEVEL</b> - Specifies the exact cache entry to remove.</li>
65+
* <li><b>SERVICE_LEVEL</b> - Specifies which service’s cache to clear.</li>
66+
* <li><b>METHOD_LEVEL</b> - Specifies the exact cache entry to remove.</li>
4767
* </ul>
4868
* For `ALL_LEVEL`, this field can be null.
4969
* </p>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
Copyright 2024-2025 the original author Hoàng Anh Tiến.
2+
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
7+
https://www.apache.org/licenses/LICENSE-2.0
8+
9+
Unless required by applicable law or agreed to in writing, software
10+
distributed under the License is distributed on an "AS IS" BASIS,
11+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
See the License for the specific language governing permissions and
13+
limitations under the License.

0 commit comments

Comments
 (0)