Skip to content

Commit

Permalink
Update README.md
Browse files Browse the repository at this point in the history
  • Loading branch information
MelodyAyane authored Jan 6, 2025
1 parent f70e0fa commit 92420ce
Showing 1 changed file with 66 additions and 63 deletions.
129 changes: 66 additions & 63 deletions annotation/README.md
Original file line number Diff line number Diff line change
@@ -1,68 +1,85 @@
### annotation
# 📚 Annotation Module

This module combines with the [reflections](https://github.com/ronmamo/reflections) library to implement a fully
automated annotation processor. This is very useful for repeatedly performing certain operations on classes.
> A fully automated annotation processor leveraging the [Reflections](https://github.com/ronmamo/reflections) library for efficient class operations.
### usage
[![JDK](https://img.shields.io/badge/JDK-17%2B-blue.svg)](https://www.oracle.com/java/technologies/javase/jdk17-archive-downloads.html)
[![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)

## 📋 Table of Contents

- [📚 Annotation Module](#-annotation-module)
- [📋 Table of Contents](#-table-of-contents)
- [✨ Features](#-features)
- [📝 Introduction](#-introduction)
- [💻 Usage](#-usage)
- [Dependencies](#dependencies)
- [Concept](#concept)
- [Example](#example)
- [Define an Annotation](#define-an-annotation)
- [Implement a Processor](#implement-a-processor)
- [Usage in a Plugin](#usage-in-a-plugin)
- [📚 ClassLoader Explanation](#-classloader-explanation)
- [Key Concepts](#key-concepts)
- [🔗 Additional Resources](#-additional-resources)
- [📄 License](#-license)

## ✨ Features

- 🔍 Automated annotation processing
- 📚 Supports multiple class loaders
- 🔄 Simplifies repetitive class operations

## 📝 Introduction

This module combines with the [Reflections](https://github.com/ronmamo/reflections) library to implement a fully automated annotation processor. It's particularly useful for performing repetitive operations on classes.

## 💻 Usage

### Dependencies

```kotlin
// Dependencies
dependencies {
// annotation module
// Annotation module
compileOnly(files("libs/annotation-1.0-SNAPSHOT.jar"))
}
```

The whole logic may be messy, but let's take it slow and think about a question. What is the meaning of `annotation`?

Imagine that you have a lot of books at home, and each book has some different stickers on the cover.
### Concept

When you want to find books with a certain type of sticker, you can ask a helper to look through all the books, see which books have the stickers you want, and then pick out these books.
Imagine you have many books at home, each with different stickers on the cover. When you want to find books with a specific type of sticker, you can ask a helper to look through all the books, identify those with the desired stickers, and pick them out.

After picking out these books, you want to operate on these books one by one, such as... They are too messy, tidy them up! Another helper!
- **AnnotationProcessingService**: Finds classes with specific annotations (like finding books with certain stickers).
- **CustomAnnotationProcessor**: Performs operations on these classes (like organizing the selected books).

Here, the helper who helps you find the books with the stickers you want is called `AnnotationProcessingService`. And the helper who helps you operate on these books with the stickers you want is called `CustomAnnotationProcessor`.
### Example

`AnnotationProcessingService` scans a lot of classes (like flipping books) to check whether the annotation it cares about is written on each class (like a sticker). If this annotation is found, the class will be taken out and handed over to the `CustomAnnotationProcessor` you specified for subsequent processing or operation.

For example, we want to get all classes annotated with `SimplixSerializerSerializableAutoRegister` and do some
processing on them.
#### Define an Annotation

```java

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface SimplixSerializerSerializableAutoRegister {
}
```

```java
#### Implement a Processor

```java
@AnnotationProcessor(SimplixSerializerSerializableAutoRegister.class)
public class SimplixSerializerSerializableAutoRegisterProcessor implements CustomAnnotationProcessor {
@Override
public void process(Class<?> clazz) throws Exception {
// do something
// Perform operations
}

@Override
public void exception(Class<?> clazz, Exception exception) {
// when exception
// Handle exceptions
}
}
```

We defined a runtime annotation, and defined a processor that implements the `CustomAnnotationProcessor` interface and
uses the `AnnotationProcessor` annotation.

This may be a little confusing, but let's take it slow.

The `SimplixSerializerSerializableAutoRegister` annotation is used to mark the class we need to process. The
`AnnotationProcessor` annotation needs to indicate that the annotation processor is dedicated to processing a certain
annotation. The `CustomAnnotationProcessor` interface is used to determine the processing method.

This is very simple, right? But now that the definition is complete, how does this all work?
What if we only want to process a few specific annotations?
#### Usage in a Plugin

```java
@FairyLaunch
Expand All @@ -73,50 +90,36 @@ public class Launcher extends Plugin {
@Override
public void onPluginEnable() {
List<String> basePackages = List.of(
// Packets expected to be processed
"org.example",

/*
* The package where the Processor of the annotation to be processed is located
* e.g. "annotation.serialize.net.legacy.library.configuration.SimplixSerializerSerializableAutoRegister"
* so the package name is "net.legacy.library.configuration.serialize.annotation"
*/
"net.legacy.library.configuration.serialize.annotation"
"org.example",
"net.legacy.library.configuration.serialize.annotation"
);

annotationProcessingService.processAnnotations(
basePackages,

// should Processors dependency injection by Fairy IoC
false,

/*
* All ClassLoaders used for basePackages scanning
* Here, we need to pass in not only the ClassLoader of the current class,
* but also the ClassLoader of the Launcher when we need to use the Processor that comes with legacy-lands-library
*/
this.getClassLoader(),
ConfigurationLauncher.class.getClassLoader() // configuration moduel's class loader
// or CommonsLauncher.class.getClassLoader(), its commons module's class loader
basePackages,
false,
this.getClassLoader(),
ConfigurationLauncher.class.getClassLoader()
);
}
}
```

Let's look at this, we just need to get the `AnnotationProcessingService` through dependency injection and then call `processAnnotations`.
## 📚 ClassLoader Explanation

Then there is a small question, why do we need to add the `ClassLoader` of the module `ConfigurationLauncher` when we use the annotation processor under `net.legacy.library.configuration.serialize.annotation`??
Each module operates as a plugin, akin to a library containing various books (classes). To access classes from other plugins, you need their `ClassLoader`.

That's because each module here runs as a plug-in, so if we want to get the class of other plug-ins, we certainly need to get its `ClassLoader`.
### Key Concepts

If you are still confused, let's go back to the book example!
- **ClassLoader**: Think of it as a library. Each plugin is an independent library storing classes it uses.
- **Multiple ClassLoaders**: Allow scanning across different plugins, ensuring all relevant classes are processed.

In fact, you can think of `ClassLoader` as a `library` - each plug-in is an independent `library`, which stores various books (that is, `classes`) that it needs to use.
By providing multiple `ClassLoaders`, `AnnotationProcessingService` can scan and match all classes managed by these loaders, handing over the correct classes to `CustomAnnotationProcessor` for processing.

When we want to use a class in a plug-in, we need to search in its own `library`. If we only search in our own `library`, we will definitely not find books from other plug-ins.
## 🔗 Additional Resources

Therefore, when `AnnotationProcessingService` scans (using `reflection`) all `classes` under the specified package, if it is only given one `library` (only one `ClassLoader`), it can only search for `classes` from this single source.
- [Reflections Library Documentation](https://github.com/ronmamo/reflections)
- [Java Annotation Processing](https://docs.oracle.com/javase/tutorial/java/annotations/processing.html)

But once you need to scan `classes` in other plug-ins (such as `classes` in the configuration module), you must tell it: "_Hey, these are other libraries, you can look for them together._" - that is, give it multiple `ClassLoaders`.
## 📄 License

In this way, it can scan and match all `classes` managed by these `ClassLoaders`, and finally hand over the correct class to `CustomAnnotationProcessor` for processing.
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details

0 comments on commit 92420ce

Please sign in to comment.