add: jitpack, MissingParameterException, proper error handling, @Init annotation with useful params, instead of generic ones

This commit is contained in:
Tulis
2025-08-13 19:17:06 +02:00
parent e7f1b4dd5e
commit e769991440
7 changed files with 286 additions and 60 deletions

129
README.md Normal file
View File

@ -0,0 +1,129 @@
<div style="text-align: center;">
<a href="https://packagist.org/packages/kanashimo/phpwaifu" target="_blank">
<img src="img.png" width="594" alt="@TuliAutoInitializer">
</a>
</div>
<div style="text-align: center;">
<img alt="GitHub Created At" src="https://img.shields.io/github/created-at/Tulis12/TuliAutoInitializer">
<img alt="GitHub last commit" src="https://img.shields.io/github/last-commit/Tulis12/TuliAutoInitializer">
<a href="https://jitpack.io/#Tulis12/TuliAutoInitializer" target="_blank"><img alt="Jitpack" src="https://jitpack.io/v/Tulis12/TuliAutoInitializer.svg"></a>
<img alt="GitHub Release Date" src="https://img.shields.io/github/release-date/Tulis12/TuliAutoInitializer">
<img alt="Made by Tulis" src="https://img.shields.io/badge/Made_by-Tulis-blue">
</div>
## What is it?
It's a Java project for initializing classes automatically,
a little like Spring uses Beans. You can avoid a mess like this:
```java
new Command();
new Command2();
new Event();
new Event2();
```
and instead use `@Init` annotation to initialize the class (using your constructor configuration) automatically:
```java
new AutomaticInitializer.Builder()
.setPackageName("(the main package to look for classes)")
.addInitVariable(Integer.class, 123) // You can add as many as you want
.addInitVariable(MyMainClass.class, mainClassObject) // Or none if you want the default constructor
.run();
```
and for the class:
```java
@Init
public class Test {
public Test(Integer i, MyMainClass myMainClass) {
System.out.println("Hi from test! My int value: " + i);
myMainClass.registerEvent(this); // You can initialize the event here for example!
}
}
```
## Installation
Install TuliAutoInitializer by Maven or Gradle using Jitpack, or my self-hosted gitea (on git.tulisiowice.top):
### Repos:
```xml
<repositories>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories>
```
```xml
<repositories>
<repository>
<id>gitea</id>
<url>https://git.tulisiowice.top/api/packages/Tulis/maven</url>
</repository>
</repositories>
```
### The package:
```xml
<dependency> // Jitpack
<groupId>com.github.Tulis12</groupId>
<artifactId>TuliAutoInitializer</artifactId>
<version>latest-version</version> // If you don't know it, check the Jitpack badge!
</dependency>
```
```xml
<dependency> // Gitea
<groupId>dev.tulis</groupId>
<artifactId>TuliAutoInitializer</artifactId>
<version>latest-version</version>
</dependency>
```
## All annotation's parameters
### `initializeWithoutParameters = true`
Tries to use the default constructor of class (the one without parameters), defaults to false (using the configuration of the builder).
```java
@Init(initializeWithoutParameters = true)
public class Test {
public Test() {
System.out.println("Will use this instead!");
}
public Test(Integer i1, Integer i2) {
System.out.println("Won't use this!");
}
}
```
The Initializer won't force use of default constructor (for example, if its private), will instead throw `NoSuchMethodException`, `IllegalAccessException` respectively.
### `initializeOnlyWith = { Integer.class, MyMainClass.class }`
Will use only those two, even if there are more. In case of multiple values with the same class, will take the one first added in the builder, defaults to an empty array (will use the configuration of builder).
```java
@Init(initializeOnlyWith = { Integer.class, MyMainClass.class })
public class Test {
public Test(Integer integer, MyMainClass myMainClass) {
System.out.println("Will use this, even if in builder there is more.");
}
public Test(Integer integer, String string, MyMainClass myMainClass) {
System.out.println("Won't use this!");
}
}
```
Remember, if there is no class (with a value) provided in the builder, but you will try to use it here, the builder will throw `MissingParameterException`.
## License
Released under the MIT license.

BIN
img.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

10
pom.xml
View File

@ -5,8 +5,8 @@
<modelVersion>4.0.0</modelVersion>
<groupId>dev.tulis</groupId>
<artifactId>TuliAutoInitializer</artifactId>
<version>1.0-SNAPSHOT</version>
<artifactId>tuliautoinitializer</artifactId>
<version>1.1</version>
<properties>
<maven.compiler.source>21</maven.compiler.source>
@ -38,6 +38,10 @@
<artifactId>reflections</artifactId>
<version>0.10.2</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.17</version>
</dependency>
</dependencies>
</project>

View File

@ -7,5 +7,7 @@ import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface GenericInitializer {
public @interface Init {
boolean initializeWithoutParameters() default false;
Class<?>[] initializeOnlyWith() default {};
}

View File

@ -1,12 +0,0 @@
package dev.tulis.autoinitializer.Annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface InitializeCommand {
String commandName();
}

View File

@ -1,73 +1,169 @@
package dev.tulis.autoinitializer;
import dev.tulis.autoinitializer.Annotations.GenericInitializer;
import dev.tulis.autoinitializer.Annotations.InitializeCommand;
import dev.tulis.autoinitializer.Annotations.Init;
import org.reflections.Reflections;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.Constructor;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
public class AutomaticInitializer {
String packageName;
Class<?> mainClass;
Object mainClassObject;
private final Logger logger = Logger.getLogger("dev.tulis.autoinitializer");
private boolean loggerEnabled = true;
private final String packageName;
private final LinkedHashMap<Class<?>, List<Object>> classes;
private final Logger log = LoggerFactory.getLogger(AutomaticInitializer.class);
public AutomaticInitializer(String packageName, Class<?> mainClass, Object mainClassObject) {
this.packageName = packageName;
this.mainClass = mainClass;
this.mainClassObject = mainClassObject;
private AutomaticInitializer(Builder builder) throws NoSuchMethodException, IllegalAccessException, RuntimeException {
packageName = builder.getPackageName();
classes = builder.getClasses();
System.setProperty("org.slf4j.simpleLogger.log.org.reflections", "off");
Logger.getLogger("org.reflections").setLevel(Level.OFF);
init();
}
private <T> T castObject(Object obj, Class<T> clazz) {
return clazz.cast(obj);
}
public void setLoggerEnabled(boolean enabled) {
loggerEnabled = enabled;
if(!enabled) logger.setLevel(Level.OFF); else logger.setLevel(Level.WARNING);
}
public boolean isLoggerEnabled() {
return loggerEnabled;
}
public void initializeCommands() {
public void init() throws NoSuchMethodException, IllegalAccessException, RuntimeException {
Reflections reflections = new Reflections(packageName);
Set<Class<?>> autoInitClasses = reflections.getTypesAnnotatedWith(InitializeCommand.class);
Set<Class<?>> autoInitClasses = reflections.getTypesAnnotatedWith(Init.class);
List<Class<?>> paramTypes = new ArrayList<>();
List<Object> objects = new ArrayList<>();
for(Map.Entry<Class<?>, List<Object>> entry : classes.entrySet()) {
for(Object object : entry.getValue()) {
paramTypes.add(entry.getKey());
objects.add(castObject(object, entry.getKey()));
}
}
for (Class<?> clazz : autoInitClasses) {
Init init = clazz.getAnnotation(Init.class);
if(init.initializeOnlyWith().length != 0) {
List<Class<?>> paramTypesLocal = new ArrayList<>();
List<Object> objectsLocal = new ArrayList<>();
for(Class<?> initClazz : init.initializeOnlyWith()) {
if(!classes.containsKey(initClazz)) {
throw new MissingParameterException(
String.format(
"You didn't provide a value for: %s (requested by: %s).",
initClazz.getName(),
clazz.getName()
)
);
}
for(Object object : classes.get(initClazz)) {
paramTypesLocal.add(initClazz);
objectsLocal.add(castObject(object, initClazz));
}
}
try {
Constructor<?> ctor = clazz.getDeclaredConstructor(
paramTypesLocal.toArray(new Class<?>[0])
);
ctor.newInstance(objectsLocal.toArray());
log.debug(
"Initialized: {}, but only with: {}.",
clazz.getSimpleName(),
Arrays.toString(paramTypesLocal.toArray())
);
} catch (Exception e) {
log.warn(e.getMessage());
}
continue;
}
if(init.initializeWithoutParameters()) {
try {
Constructor<?> ctor = clazz.getConstructor();
ctor.newInstance();
} catch (Exception e) {
log.warn(e.getMessage());
}
continue;
}
try {
Constructor<?> ctor = clazz.getDeclaredConstructor(mainClass);
ctor.newInstance(castObject(mainClassObject, mainClass));
Constructor<?> ctor = clazz.getDeclaredConstructor(paramTypes.toArray(new Class<?>[0]));
ctor.newInstance(objects.toArray());
InitializeCommand annotation = clazz.getAnnotation(InitializeCommand.class);
logger.info("Zainicjalizowano komendę: " + annotation.commandName());
} catch (Exception e) {
logger.warning(e.getMessage());
log.debug("Initialized: {}", clazz.getSimpleName());
} catch (NoSuchMethodException e) {
throw new NoSuchMethodException(
String.format(
"Class %s has no constructor for: %s.",
clazz.getName(),
Arrays.toString(paramTypes.toArray())
)
);
} catch (InstantiationException e) {
throw new RuntimeException(
String.format(
"Class %s is not a proper class (eg. is abstract or is a interface!).",
clazz.getName()
)
);
} catch (InvocationTargetException e) {
throw new RuntimeException(
String.format(
"Class %s occurred an exception (%s) when being initialized.",
clazz.getName(),
e.getCause().fillInStackTrace()
)
);
} catch (IllegalAccessException e) {
throw new IllegalAccessException(
String.format(
"Class %s has private or protected access to its constructor.",
clazz.getName()
)
);
}
}
}
public void initializeGenerics() {
Reflections reflections = new Reflections(packageName);
Set<Class<?>> autoInitClasses = reflections.getTypesAnnotatedWith(GenericInitializer.class);
public static class Builder {
private String packageName;
private final LinkedHashMap<Class<?>, List<Object>> classes = new LinkedHashMap<>();
for (Class<?> clazz : autoInitClasses) {
try {
Constructor<?> ctor = clazz.getDeclaredConstructor(mainClass);
ctor.newInstance(castObject(mainClassObject, mainClass));
public Builder setPackageName(String packageName) {
this.packageName = packageName;
return this;
}
logger.info("Zainicjalizowano klasę ogólną: " + clazz.getSimpleName());
} catch (Exception e) {
logger.warning(e.getMessage());
}
public Builder addInitVariable(Class<?> classInit, Object objectInit) {
List<Object> objects = classes.getOrDefault(classInit, new ArrayList<>());
objects.add(objectInit);
classes.put(classInit, objects);
return this;
}
public String getPackageName() {
return packageName;
}
public LinkedHashMap<Class<?>, List<Object>> getClasses() {
return classes;
}
public AutomaticInitializer run() throws NoSuchMethodException, IllegalAccessException, RuntimeException {
return new AutomaticInitializer(this);
}
public AutomaticInitializer build() throws NoSuchMethodException, IllegalAccessException, RuntimeException {
return run();
}
}
}

View File

@ -0,0 +1,7 @@
package dev.tulis.autoinitializer;
public class MissingParameterException extends RuntimeException {
public MissingParameterException(String message) {
super(message);
}
}