Skip to content

Instantly share code, notes, and snippets.

@ozanaaslan
Created December 29, 2025 14:32
Show Gist options
  • Select an option

  • Save ozanaaslan/98c22738b91ef407a9784f2b3c7c5be1 to your computer and use it in GitHub Desktop.

Select an option

Save ozanaaslan/98c22738b91ef407a9784f2b3c7c5be1 to your computer and use it in GitHub Desktop.
Minimalist IoC
import lombok.Getter;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
public class ServiceManager {
@Getter private static final ServiceManager instance = new ServiceManager();
private final Map<Class<?>, Object> services = new ConcurrentHashMap<>();
private ServiceManager() {}
/**
* Scans an object for @ServiceBean annotations on methods and fields.
* Methods are executed once, and their return value is registered as a service.
*/
public void registerBeans(Object provider) {
// 1. Process Methods (Factory Methods)
for (Method method : provider.getClass().getDeclaredMethods()) {
if (method.isAnnotationPresent(ServiceBean.class)) {
try {
method.setAccessible(true);
Object bean = method.invoke(provider);
if (bean != null) {
// Register using the return type as the key
register((Class<Object>) method.getReturnType(), bean);
}
} catch (Exception e) {
System.err.println("Failed to register bean from method: " + method.getName());
e.printStackTrace();
}
}
}
// 2. Process Fields
for (Field field : provider.getClass().getDeclaredFields()) {
if (field.isAnnotationPresent(ServiceBean.class)) {
try {
field.setAccessible(true);
Object bean = field.get(provider);
if (bean != null) {
register((Class<Object>) field.getType(), bean);
}
} catch (Exception e) {
System.err.println("Failed to register bean from field: " + field.getName());
}
}
}
}
public <T> void register(Class<T> serviceInterface, T implementation) {
services.put(serviceInterface, implementation);
}
public <T> Optional<T> getService(Class<T> serviceInterface) {
return Optional.ofNullable(serviceInterface.cast(services.get(serviceInterface)));
}
public void inject(Object target) {
for (Field field : target.getClass().getDeclaredFields()) {
if (field.isAnnotationPresent(InjectService.class)) {
field.setAccessible(true);
getService(field.getType()).ifPresent(service -> {
try {
field.set(target, service);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
});
}
}
}
// --- Annotations ---
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.FIELD})
public @interface ServiceBean {}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface InjectService {}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment