- Add annotation system (@Component, @Service, @Configuration, @Bean, @Inject, @Qualifier, @Primary, @Scope, @PostConstruct, @Import, @TimiInjectApplication) - Implement BeanContext for managing bean instances and definitions - Implement BeanScanner for component scanning (file system and jar) - Implement BeanFactory with constructor injection and lifecycle support - Support @Configuration and @Bean factory methods - Support @Qualifier and @Primary for multi-implementation - Support @Scope (singleton/prototype) - Support @PostConstruct lifecycle callback - Support @Import and Module extension - Add circular dependency detection - Add dependency graph export - Add demo and test cases Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
141 lines
4.8 KiB
Java
141 lines
4.8 KiB
Java
package com.imyeyu.inject;
|
|
|
|
import com.imyeyu.inject.annotation.Import;
|
|
import com.imyeyu.inject.annotation.TimiInjectApplication;
|
|
import org.slf4j.Logger;
|
|
import org.slf4j.LoggerFactory;
|
|
|
|
import java.util.*;
|
|
|
|
/**
|
|
* Timi 控制反转框架
|
|
*
|
|
* @author 夜雨
|
|
*/
|
|
public class TimiInject {
|
|
|
|
private static final Logger log = LoggerFactory.getLogger(TimiInject.class);
|
|
|
|
private final BeanContext context;
|
|
private final BeanFactory factory;
|
|
|
|
private TimiInject(BeanContext context, BeanFactory factory) {
|
|
this.context = context;
|
|
this.factory = factory;
|
|
}
|
|
|
|
/**
|
|
* 启动应用并初始化容器
|
|
*/
|
|
public static TimiInject run(Class<?> applicationClass, String... args) {
|
|
long startTime = System.currentTimeMillis();
|
|
log.info("Starting {} ...", applicationClass.getSimpleName());
|
|
|
|
TimiInjectApplication annotation = applicationClass.getAnnotation(TimiInjectApplication.class);
|
|
if (annotation == null) {
|
|
throw new InjectException("Application class must be annotated with @TimiInjectApplication");
|
|
}
|
|
|
|
BeanContext context = new BeanContext();
|
|
BeanFactory factory = new BeanFactory(context);
|
|
BeanScanner scanner = new BeanScanner(context);
|
|
|
|
String[] basePackages = annotation.value();
|
|
if (basePackages.length == 0) {
|
|
basePackages = new String[]{applicationClass.getPackage().getName()};
|
|
}
|
|
|
|
scanner.scan(basePackages);
|
|
processImports(applicationClass, scanner, context);
|
|
factory.initializeSingletons();
|
|
|
|
long elapsed = System.currentTimeMillis() - startTime;
|
|
log.info("Started {} in {} ms ({} beans)", applicationClass.getSimpleName(), elapsed,
|
|
context.getAllDefinitions().size());
|
|
|
|
return new TimiInject(context, factory);
|
|
}
|
|
|
|
/**
|
|
* 依赖注入 - 获取 Bean 实例(按类型)
|
|
*/
|
|
public <T> T di(Class<T> type) {
|
|
return factory.getBean(type);
|
|
}
|
|
|
|
/**
|
|
* 依赖注入 - 获取 Bean 实例(按名称和类型)
|
|
*/
|
|
public <T> T di(String name, Class<T> type) {
|
|
return factory.getBean(name, type);
|
|
}
|
|
|
|
/**
|
|
* 控制反转 - 手动注册 Bean 实例
|
|
*/
|
|
public void ioc(String name, Object bean) {
|
|
context.registerSingleton(name, bean);
|
|
log.debug("Registered singleton bean: {} ({})", name, bean.getClass().getName());
|
|
}
|
|
|
|
/**
|
|
* 导出依赖图
|
|
*/
|
|
public String exportDependencyGraph() {
|
|
StringBuilder graph = new StringBuilder();
|
|
graph.append("=== Dependency Graph ===\n\n");
|
|
|
|
for (BeanDefinition definition : context.getAllDefinitions()) {
|
|
graph.append(formatBeanInfo(definition));
|
|
graph.append("\n");
|
|
}
|
|
|
|
return graph.toString();
|
|
}
|
|
|
|
private String formatBeanInfo(BeanDefinition definition) {
|
|
StringBuilder info = new StringBuilder();
|
|
info.append("Bean: ").append(definition.getName()).append("\n");
|
|
info.append(" Type: ").append(definition.getType().getName()).append("\n");
|
|
info.append(" Scope: ").append(definition.getScope()).append("\n");
|
|
info.append(" Primary: ").append(definition.isPrimary()).append("\n");
|
|
|
|
if (definition.isFactoryBean()) {
|
|
info.append(" Factory: @Bean ").append(definition.getFactoryMethod().getName()).append("()\n");
|
|
} else if (definition.getConstructor() != null) {
|
|
info.append(" Constructor: ").append(definition.getConstructor().getParameterCount())
|
|
.append(" parameters\n");
|
|
}
|
|
|
|
if (definition.getPostConstruct() != null) {
|
|
info.append(" PostConstruct: ").append(definition.getPostConstruct().getName()).append("()\n");
|
|
}
|
|
|
|
return info.toString();
|
|
}
|
|
|
|
private static void processImports(Class<?> applicationClass, BeanScanner scanner, BeanContext context) {
|
|
if (!applicationClass.isAnnotationPresent(Import.class)) {
|
|
return;
|
|
}
|
|
|
|
Import importAnnotation = applicationClass.getAnnotation(Import.class);
|
|
Class<?>[] importClasses = importAnnotation.value();
|
|
|
|
for (Class<?> importClass : importClasses) {
|
|
if (Module.class.isAssignableFrom(importClass)) {
|
|
try {
|
|
Module module = (Module) importClass.getDeclaredConstructor().newInstance();
|
|
module.configure(context);
|
|
log.debug("Loaded module: {}", importClass.getName());
|
|
} catch (Exception e) {
|
|
throw new InjectException("Failed to instantiate module: " + importClass.getName(), e);
|
|
}
|
|
} else {
|
|
scanner.processClass(importClass);
|
|
log.debug("Imported configuration: {}", importClass.getName());
|
|
}
|
|
}
|
|
}
|
|
}
|