diff --git a/annotation/README.md b/annotation/README.md
index ec832b8..b5517ab 100644
--- a/annotation/README.md
+++ b/annotation/README.md
@@ -71,5 +71,6 @@ We only need to get `AnnotationProcessingService` through dependency injection a
Let's focus on `annotationProcessingService.processAnnotations(basePackage, false, this.getClassLoader())`.
The first parameter is the package we need to scan.
-The second is whether the processed class should be injected into the singleton mode by the Fairy framework. If it is false, it will be created through parameterless reflection.
+The second is whether the processed class should be injected into the singleton mode by the Fairy framework. If it is
+false, it will be created through parameterless reflection.
The third is the classloader that needs to be scanned.
\ No newline at end of file
diff --git a/cache/README.md b/cache/README.md
index 800456c..58012db 100644
--- a/cache/README.md
+++ b/cache/README.md
@@ -1,9 +1,11 @@
### cache
-The module is a caching solution that supports `Caffeine`'s `Cache` and `AsyncCache`,
-as well as an in-memory `Redis` database implementation, offering functional programming support and automatic lock handling.
+The module is a caching solution that supports `Caffeine`'s `Cache` and `AsyncCache`,
+as well as an in-memory `Redis` database implementation, offering functional programming support and automatic lock
+handling.
-The original purpose of this module was to design a L1 cache for the `mongodb` module, but now it is **general-purpose**.
+The original purpose of this module was to design a L1 cache for the `mongodb` module, but now it is **general-purpose
+**.
### usage
diff --git a/cache/src/main/java/me/qwqdev/library/cache/factory/CacheServiceFactory.java b/cache/src/main/java/me/qwqdev/library/cache/factory/CacheServiceFactory.java
index a948e0e..09a6cb9 100644
--- a/cache/src/main/java/me/qwqdev/library/cache/factory/CacheServiceFactory.java
+++ b/cache/src/main/java/me/qwqdev/library/cache/factory/CacheServiceFactory.java
@@ -22,22 +22,28 @@
@UtilityClass
public final class CacheServiceFactory {
/**
- * Creates a Redis cache service with the specified configuration.
+ * Creates a {@link RedisCacheService} with the specified configuration.
*
* @param config the Redis configuration
- * @return a new Redis cache service instance
+ * @return a new {@link RedisCacheService} instance
+ * @see RedisCacheService
+ * @see RedisCacheServiceInterface
+ * @see Config
*/
public static RedisCacheServiceInterface createRedisCache(Config config) {
return new RedisCacheService(config);
}
/**
- * Creates a Caffeine synchronous cache service with custom configuration.
+ * Creates a {@link CaffeineCacheService} with custom configuration.
*
* @param cache the Caffeine cache
* @param The injection is done via reflection, which allows this class to work without explicitly hardcoding the
+ * dependencies. However, this approach introduces some overhead and may not be suitable for high-performance use cases.
+ * The injection process works in two ways:
+ *
+ *
+ *
If using a static method for injection, the class containing the static method must be loadable by the current + * thread's context {@link ClassLoader}. The method should be accessible and match the required signature for retrieving + * the {@link VarHandle}.
+ * + *Due to the use of reflection, the fields will be made accessible even if they are private, ensuring that injection + * can occur regardless of visibility modifiers.
+ * + *This class implements the {@link StaticInjectorInterface} interface, which defines the contract for injecting + * dependencies (in this case, {@link VarHandle} instances) into static fields of a class.
+ * + * @author qwq-dev + * @since 2024-12-23 16:15 + */ +public class VarHandleReflectionInjector implements StaticInjectorInterface { + /** + * {@inheritDoc} + *+ * Injects {@link VarHandle} instances into static fields of the given class that are annotated with + * {@link VarHandleAutoInjection}. This method uses reflection to find the appropriate {@link VarHandle} + * for each annotated field and assigns it to the field. + * + *
The method performs the injection by either using the default method of + * {@link MethodHandles#privateLookupIn(Class, MethodHandles.Lookup)} to + * locate the {@link VarHandle} for the specified static field or by using a static method if defined in the + * annotation {@link VarHandleAutoInjection}. + * + *
The specified static method must contain two parameters: {@link String} and {@link Class} + * + *
If a static method is specified in the annotation, it will be called to retrieve the {@link VarHandle} + * for the field. The class containing the static method must be loadable by the current thread's context + * {@link ClassLoader}.
+ * + * @param clazz the class into which {@link VarHandle} instances will be injected into its static fields + * @throws IllegalStateException if the injection fails due to reflection issues or invalid annotations + */ + @Override + public void inject(Class> clazz) { + Field[] fields = clazz.getDeclaredFields(); + + for (Field field : fields) { + VarHandleAutoInjection annotation = field.getAnnotation(VarHandleAutoInjection.class); + + if (annotation == null) { + continue; + } + + String targetFieldName = annotation.fieldName(); + String staticMethodName = annotation.staticMethodName(); + String staticMethodPackage = annotation.staticMethodPackage(); + boolean useStaticMethod = !staticMethodName.isEmpty() && !staticMethodPackage.isEmpty(); + + try { + VarHandle varHandle; + + if (useStaticMethod) { + Class> staticClass = Class.forName(staticMethodPackage); + Method method = staticClass.getDeclaredMethod(staticMethodName, String.class, Class.class); + method.setAccessible(true); + varHandle = (VarHandle) method.invoke(null, targetFieldName, field.getType()); + } else { + varHandle = MethodHandles.privateLookupIn(clazz, MethodHandles.lookup()) + .unreflectVarHandle(clazz.getDeclaredField(targetFieldName)); + } + + field.setAccessible(true); + field.set(null, varHandle); + } catch (Exception exception) { + throw new IllegalStateException("Failed to inject VarHandle for field: " + field.getName(), exception); + } + } + } +} diff --git a/commons/src/main/java/me/qwqdev/library/commons/injector/annotation/VarHandleAutoInjection.java b/commons/src/main/java/me/qwqdev/library/commons/injector/annotation/VarHandleAutoInjection.java new file mode 100644 index 0000000..bc2423b --- /dev/null +++ b/commons/src/main/java/me/qwqdev/library/commons/injector/annotation/VarHandleAutoInjection.java @@ -0,0 +1,42 @@ +package me.qwqdev.library.commons.injector.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.invoke.VarHandle; + +/** + * Annotation to mark a field for automatic {@link VarHandle} injection. + * + * @author qwq-dev + * @since 2024-12-23 16:15 + */ +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +public @interface VarHandleAutoInjection { + /** + * The name of the target field for which the {@link VarHandle} is being generated. + * + * @return the field name + */ + String fieldName(); + + /** + * The name of the static method used to generate the {@link VarHandle}. + * + *The specified static method must contain two parameters: {@link String} and {@link Class} + * + * @return the static method name, or an empty string if not specified + */ + String staticMethodName() default ""; + + /** + * The fully qualified name of the class containing the static method used to generate the {@link VarHandle}. + * + *
The class must be loadable by the current thread's context {@link ClassLoader}. + * + * @return the fully qualified class name, or an empty string if not specified + */ + String staticMethodPackage() default ""; +} diff --git a/configuration/src/main/java/me/qwqdev/library/configuration/factory/SimplixBuilderFactory.java b/configuration/src/main/java/me/qwqdev/library/configuration/factory/SimplixBuilderFactory.java index 228d760..873a802 100644 --- a/configuration/src/main/java/me/qwqdev/library/configuration/factory/SimplixBuilderFactory.java +++ b/configuration/src/main/java/me/qwqdev/library/configuration/factory/SimplixBuilderFactory.java @@ -20,7 +20,7 @@ * @since 2024-12-18 13:48 */ @UtilityClass -public class SimplixBuilderFactory { +public final class SimplixBuilderFactory { /** * Creates a {@link SimplixBuilder} instance from a file. * @@ -28,6 +28,8 @@ public class SimplixBuilderFactory { * * @param file the file to create the {@link SimplixBuilder} from * @return a configured {@link SimplixBuilder} instance + * @see File + * @see SimplixBuilder */ public static SimplixBuilder createSimplixBuilderFromFile(File file) { return configureSimplixBuilder(SimplixBuilder.fromFile(file)); @@ -40,6 +42,8 @@ public static SimplixBuilder createSimplixBuilderFromFile(File file) { * * @param file the directory to create the {@link SimplixBuilder} from * @return a configured {@link SimplixBuilder} instance + * @see File + * @see SimplixBuilder */ public static SimplixBuilder createSimplixBuilderFromDirectory(File file) { return configureSimplixBuilder(SimplixBuilder.fromDirectory(file)); @@ -52,6 +56,8 @@ public static SimplixBuilder createSimplixBuilderFromDirectory(File file) { * * @param path the path to create the {@link SimplixBuilder} from * @return a configured {@link SimplixBuilder} instance + * @see Path + * @see SimplixBuilder */ public static SimplixBuilder createSimplixBuilderFromPath(Path path) { return configureSimplixBuilder(SimplixBuilder.fromPath(path)); @@ -65,6 +71,7 @@ public static SimplixBuilder createSimplixBuilderFromPath(Path path) { * @param name the name of the file * @param path the path to the file * @return a configured {@link SimplixBuilder} instance + * @see SimplixBuilder */ public static SimplixBuilder createSimplixBuilder(String name, String path) { return configureSimplixBuilder(SimplixBuilder.fromPath(name, path)); diff --git a/mongodb/README.md b/mongodb/README.md index eca85ca..944e7d9 100644 --- a/mongodb/README.md +++ b/mongodb/README.md @@ -1,8 +1,10 @@ ### mongodb -This module only encapsulates a more convenient MongoConfig based on [Morphia](https://morphia.dev/landing/index.html), and its implementation is very simple. +This module only encapsulates a more convenient MongoConfig based on [Morphia](https://morphia.dev/landing/index.html), +and its implementation is very simple. -We recommend using Datastore directly for any CRUD operations, using [Morphia](https://morphia.dev/landing/index.html) for index creation or using the [aggregation](https://morphia.dev/morphia/2.4/aggregations.html). +We recommend using Datastore directly for any CRUD operations, using [Morphia](https://morphia.dev/landing/index.html) +for index creation or using the [aggregation](https://morphia.dev/morphia/2.4/aggregations.html). ### cache @@ -57,7 +59,7 @@ public class Example { Filters.eq("name", "Alice"), // name eq Filters.gt("age", 35) // age > 35, it's time to lay off employees lol )) - + // It should be noted that iterators are not thread-safe .iterator(); // get iterator diff --git a/mongodb/src/main/java/me/qwqdev/library/mongodb/factory/MongoDBConnectionConfigFactory.java b/mongodb/src/main/java/me/qwqdev/library/mongodb/factory/MongoDBConnectionConfigFactory.java index b6ecb54..2c0c4ed 100644 --- a/mongodb/src/main/java/me/qwqdev/library/mongodb/factory/MongoDBConnectionConfigFactory.java +++ b/mongodb/src/main/java/me/qwqdev/library/mongodb/factory/MongoDBConnectionConfigFactory.java @@ -13,7 +13,7 @@ * @since 2024-12-20 12:18 */ @UtilityClass -public class MongoDBConnectionConfigFactory { +public final class MongoDBConnectionConfigFactory { /** * Creates a default {@link MongoDBConnectionConfig} instance using the standard UUID representation. * @@ -21,6 +21,7 @@ public class MongoDBConnectionConfigFactory { * @param mongoURL the MongoDB connection URL, e.g., "mongodb://localhost:27017" * @return a configured {@link MongoDBConnectionConfig} instance * @throws IllegalArgumentException if either the database name or MongoDB URL is null or empty + * @see MongoDBConnectionConfig */ public static MongoDBConnectionConfig create(String databaseName, String mongoURL) { return new MongoDBConnectionConfig(databaseName, mongoURL); @@ -34,6 +35,8 @@ public static MongoDBConnectionConfig create(String databaseName, String mongoUR * @param uuidRepresentation the UUID representation to be used in MongoDB documents * @return a configured {@link MongoDBConnectionConfig} instance * @throws IllegalArgumentException if either the database name or MongoDB URL is null or empty + * @see MongoDBConnectionConfig + * @see UuidRepresentation */ public static MongoDBConnectionConfig create(String databaseName, String mongoURL, UuidRepresentation uuidRepresentation) { return new MongoDBConnectionConfig(databaseName, mongoURL, uuidRepresentation); @@ -45,6 +48,8 @@ public static MongoDBConnectionConfig create(String databaseName, String mongoUR * @param mongoClientSettings the custom {@link MongoClientSettings} to configure the MongoClient * @return a configured {@link MongoDBConnectionConfig} instance * @throws IllegalArgumentException if {@link MongoClientSettings} is null + * @see MongoDBConnectionConfig + * @see MongoClientSettings */ public static MongoDBConnectionConfig create(MongoClientSettings mongoClientSettings) { return new MongoDBConnectionConfig(mongoClientSettings);