package com.imyeyu.inject; import com.imyeyu.inject.annotation.Import; import com.imyeyu.inject.annotation.TimiInjectApplication; import com.imyeyu.java.bean.CallbackArg; import com.imyeyu.utils.Text; import com.imyeyu.utils.Time; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; import java.lang.reflect.Method; import java.nio.charset.StandardCharsets; import java.util.List; import java.util.stream.Collectors; /** * 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) { return run(applicationClass, null); } /** * 启动应用并初始化容器(支持初始化前注册外部 Bean) * * @param initializer 初始化前回调 */ public static TimiInject run(Class applicationClass, CallbackArg initializer) { printBanner(); long startTime = Time.now(); StartupStatistics statistics = new StartupStatistics(); log.info("Initializing TimiInject for {} ...", 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, statistics); BeanScanner scanner = new BeanScanner(context, statistics); String[] basePackages = annotation.value(); if (basePackages.length == 0) { basePackages = new String[] {applicationClass.getPackage().getName()}; } log.info("Scanning packages: {}", String.join(", ", basePackages)); scanner.scan(basePackages); processImports(applicationClass, scanner, context); log.info("Scanned {} classes, registered {} beans", statistics.scannedClasses, statistics.registeredBeans); TimiInject inject = new TimiInject(context, factory); if (initializer != null) { initializer.handler(inject); } log.info("Performing inversion of control..."); factory.initializeSingletons(); log.info("initialization completed!"); log.info("\tTotal beans: {}", statistics.registeredBeans); log.info("\tInjected fields: {}", statistics.injectedFields); log.info("\tPostConstruct invocations: {}", statistics.postConstructInvocations); log.info("\tScan time: {} ms", statistics.scanEndTime - statistics.scanStartTime); log.info("\tIOC time: {} ms", statistics.iocEndTime - statistics.iocStartTime); log.info("\tInjection time: {} ms", statistics.injectionTotalTime); log.info("\tPostConstruct time: {} ms", statistics.postConstructTotalTime); log.info("\tTotal startup time: {} ms", Time.now() - startTime); return inject; } /** * 获取 Bean 实例(按类型) */ public T getBean(Class type) { return factory.getBean(type); } /** * 获取 Bean 实例(按名称和类型) */ public T getBean(String name, Class type) { return factory.getBean(name, type); } /** * 注册 Bean 实例 */ public void registerBean(String name, Object bean) { context.registerSingleton(name, bean); log.debug("Registered singleton bean: {} ({})", name, bean.getClass().getName()); } /** * 对已存在的对象执行字段注入,适用于无法通过构造器创建的对象 * * @param target 需要注入依赖的目标对象 */ public void inject(Object target) { factory.injectFields(target); } /** * 导出依赖图 */ 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"); } List postConstructs = definition.getPostConstructs(); if (!postConstructs.isEmpty()) { info.append(" PostConstruct: "); for (int i = 0; i < postConstructs.size(); i++) { if (i > 0) { info.append(", "); } info.append(postConstructs.get(i).getName()).append("()"); } info.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: %s".formatted(importClass.getName()), e); } } else { // 检查是否已经通过包扫描注册 String beanName = Text.camelCaseClassName(importClass); if (!context.containsBean(beanName)) { scanner.processClass(importClass); log.debug("Imported configuration: {}", importClass.getName()); } else { log.debug("Skip importing already registered class: {}", importClass.getName()); } } } } /** * 输出启动 Banner */ private static void printBanner() { String bannerContent = loadBanner(); if (!bannerContent.isEmpty()) { System.out.println(bannerContent); } } /** * 加载 Banner 内容 * 优先加载 banner.txt,如果不存在则加载 defBanner.txt * * @return Banner 内容 */ private static String loadBanner() { // 优先尝试加载自定义 banner.txt String banner = loadBannerFromResource("banner.txt"); if (banner != null) { return banner; } // 加载默认 banner banner = loadBannerFromResource("defBanner.txt"); if (banner != null) { return banner; } // 如果都加载失败,返回空字符串 return ""; } /** * 从资源文件加载 Banner * * @param resourceName 资源文件名 * @return Banner 内容,如果加载失败返回 null */ private static String loadBannerFromResource(String resourceName) { try { InputStream inputStream = TimiInject.class.getClassLoader().getResourceAsStream(resourceName); if (inputStream == null) { return null; } try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) { return reader.lines().collect(Collectors.joining("\n")); } } catch (Exception e) { log.debug("Failed to load banner from {}: {}", resourceName, e.getMessage()); return null; } } }