package com.imyeyu.inject; import com.imyeyu.inject.annotation.Inject; import com.imyeyu.inject.annotation.Qualifier; import com.imyeyu.inject.annotation.ScopeType; import com.imyeyu.java.ref.Ref; import com.imyeyu.utils.Time; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Parameter; import java.util.LinkedHashSet; import java.util.List; /** * Bean 工厂,负责创建和管理 Bean 实例 * * @author 夜雨 * @since 2026-01-12 23:38 */ public class BeanFactory { private static final Logger log = LoggerFactory.getLogger(BeanFactory.class); private final BeanContext context; private final StartupStatistics statistics; private final LinkedHashSet creating = new LinkedHashSet<>(); public BeanFactory(BeanContext context, StartupStatistics statistics) { this.context = context; this.statistics = statistics; } /** * 获取 Bean 实例 * * @param name 名称 * @return 实例 */ public Object getBean(String name) { BeanDefinition definition = context.getDefinition(name); if (definition == null) { throw new InjectException("Bean [%s] not found".formatted(name)); } if (definition.getScope() == ScopeType.SINGLETON) { Object singleton = context.getSingleton(name); if (singleton != null) { return singleton; } } return createBean(definition); } /** * 获取 Bean 实例 * * @param type 类型 * @return 实例 * @param 实例类型 */ @SuppressWarnings("unchecked") public T getBean(Class type) { List candidates = context.getBeanNamesByType(type); if (candidates.isEmpty()) { throw new InjectException("Bean not found for type: %s".formatted(type.getName())); } if (candidates.size() == 1) { return (T) getBean(candidates.getFirst()); } String primaryBean = findPrimaryBean(candidates); if (primaryBean != null) { return (T) getBean(primaryBean); } throw new InjectException("Multiple beans found for type %s: %s - use @Qualifier or @Primary to specify which one to inject".formatted(type.getName(), candidates)); } /** * 获取 Bean 实例 * * @param name 名称 * @param type 类型 * @return 实例 * @param 实例类型 */ @SuppressWarnings("unchecked") public T getBean(String name, Class type) { Object bean = getBean(name); if (!type.isInstance(bean)) { throw new InjectException("Bean [%s] is not of type %s".formatted(name, type.getName())); } return (T) bean; } private Object createBean(BeanDefinition definition) { String name = definition.getName(); if (creating.contains(name)) { StringBuilder errorMsg = new StringBuilder("Circular dependency detected:\n"); for (String bean : creating) { errorMsg.append(" ").append(bean).append("\n -> "); } errorMsg.append(name).append(" (attempting to create again)"); throw new InjectException(errorMsg.toString()); } creating.add(name); try { Object instance; if (definition.isFactoryBean()) { instance = createBeanFromFactoryMethod(definition); } else { instance = createBeanFromConstructor(definition); } // 在构造器注入后执行字段注入 injectFields(instance); if (definition.getScope() == ScopeType.SINGLETON) { context.setSingleton(name, instance); } invokePostConstruct(instance, definition); log.debug("Created bean: {} ({})", name, definition.getType().getName()); return instance; } finally { creating.remove(name); } } private Object createBeanFromConstructor(BeanDefinition definition) { Constructor constructor = definition.getConstructor(); constructor.setAccessible(true); Parameter[] parameters = constructor.getParameters(); Object[] args = new Object[parameters.length]; for (int i = 0; i < parameters.length; i++) { args[i] = resolveDependency(parameters[i]); } try { return constructor.newInstance(args); } catch (Exception e) { throw new InjectException("Failed to instantiate Bean [%s]".formatted(definition.getName()), e); } } private Object createBeanFromFactoryMethod(BeanDefinition definition) { Method factoryMethod = definition.getFactoryMethod(); factoryMethod.setAccessible(true); String configBeanName = extractConfigBeanName(factoryMethod.getDeclaringClass()); Object configInstance = getBean(configBeanName); Parameter[] parameters = factoryMethod.getParameters(); Object[] args = new Object[parameters.length]; for (int i = 0; i < parameters.length; i++) { args[i] = resolveDependency(parameters[i]); } try { Object result = factoryMethod.invoke(configInstance, args); if (result == null) { throw new InjectException("@Bean method returned null: %s".formatted(factoryMethod.getName())); } return result; } catch (Exception e) { throw new InjectException("Failed to invoke @Bean method: %s".formatted(factoryMethod.getName()), e); } } private Object resolveDependency(Parameter parameter) { Qualifier qualifier = parameter.getAnnotation(Qualifier.class); if (qualifier != null) { return getBean(qualifier.value()); } Class type = parameter.getType(); String paramName = parameter.getName(); return getBeanWithFallback(type, paramName); } private void invokePostConstruct(Object instance, BeanDefinition definition) { List postConstructs = definition.getPostConstructs(); if (postConstructs.isEmpty()) { return; } statistics.postConstructCurrentStart = Time.now(); for (Method postConstruct : postConstructs) { postConstruct.setAccessible(true); try { postConstruct.invoke(instance); statistics.postConstructInvocations++; } catch (Exception e) { throw new InjectException("Failed to invoke @PostConstruct method: %s".formatted(postConstruct.getName()), e); } } statistics.postConstructTotalTime += Time.now() - statistics.postConstructCurrentStart; statistics.postConstructCurrentStart = 0; } private String findPrimaryBean(List candidates) { String primaryBean = null; for (String name : candidates) { BeanDefinition definition = context.getDefinition(name); if (definition.isPrimary()) { if (primaryBean != null) { throw new InjectException("Multiple @Primary beans found: Bean [%s] and Bean [%s]".formatted(primaryBean, name)); } primaryBean = name; } } return primaryBean; } private String extractConfigBeanName(Class configClass) { return Character.toLowerCase(configClass.getSimpleName().charAt(0)) + configClass.getSimpleName().substring(1); } /** * 初始化所有单例 Bean(跳过懒加载的单例) */ public void initializeSingletons() { statistics.iocStartTime = Time.now(); for (BeanDefinition definition : context.getAllDefinitions()) { if (definition.getScope() == ScopeType.SINGLETON && !definition.isLazy()) { getBean(definition.getName()); } } statistics.iocEndTime = Time.now(); } /** * 对已存在的对象执行字段注入,适用于无法通过构造器创建的对象 * * @param target 需要注入依赖的目标对象 */ public void injectFields(Object target) { if (target == null) { throw new InjectException("Cannot inject fields into null object"); } statistics.injectionCurrentStart = Time.now(); Class clazz = target.getClass(); for (Field field : Ref.listAllFields(target.getClass())) { if (field.isAnnotationPresent(Inject.class)) { field.setAccessible(true); try { Object dependency = resolveDependencyForField(field); field.set(target, dependency); statistics.injectedFields++; log.debug("Injected field: {}.{}", clazz.getSimpleName(), field.getName()); } catch (IllegalAccessException e) { throw new InjectException("Failed to inject field: %s".formatted(field.getName()), e); } } } statistics.injectionTotalTime += Time.now() - statistics.injectionCurrentStart; } private Object resolveDependencyForField(Field field) { Qualifier qualifier = field.getAnnotation(Qualifier.class); if (qualifier != null) { return getBean(qualifier.value()); } Class type = field.getType(); String fieldName = field.getName(); return getBeanWithFallback(type, fieldName); } /** * 按类型获取 bean,当存在多个候选时按名称回退匹配 * * @param type 类型 * @param name 字段名或参数名,用于歧义时的回退匹配 * @return bean 实例 */ private Object getBeanWithFallback(Class type, String name) { List candidates = context.getBeanNamesByType(type); if (candidates.isEmpty()) { throw new InjectException("Bean not found for type: %s".formatted(type.getName())); } if (candidates.size() == 1) { return getBean(candidates.getFirst()); } // 多个候选时,先尝试按名称精确匹配 if (candidates.contains(name)) { return getBean(name); } // 再尝试 @Primary String primaryBean = findPrimaryBean(candidates); if (primaryBean != null) { return getBean(primaryBean); } throw new InjectException("Multiple beans found for type %s: %s - use @Qualifier or @Primary to specify which one to inject".formatted(type.getName(), candidates)); } }