Files
timi-inject/src/main/java/com/imyeyu/inject/BeanFactory.java
Timi f9c1a4aeb6 feat: add fallback bean resolution by parameter/field name
- Replace javafx-maven-plugin with maven-compiler-plugin
- Enable -parameters flag for runtime parameter name access
- Add getBeanWithFallback for name-based disambiguation when multiple candidates exist

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-15 18:38:31 +08:00

293 lines
9.1 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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<String> 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 <T> 实例类型
*/
@SuppressWarnings("unchecked")
public <T> T getBean(Class<T> type) {
List<String> 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 <T> 实例类型
*/
@SuppressWarnings("unchecked")
public <T> T getBean(String name, Class<T> 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<Method> 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<String> 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<String> 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));
}
}