refactor: optimize string concatenation and add startup statistics

- Replace string concatenation with .formatted() method for better readability
- Add StartupStatistics class to track initialization metrics
- Add detailed startup logging with timing information
  - Scan time
  - IOC time
  - Injection time
  - PostConstruct time
  - Total startup time
- Add banner output support (banner.txt or defBanner.txt)
- Add @Lazy annotation support for lazy initialization

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Timi
2026-01-13 01:41:49 +08:00
parent 93bcc9b5c6
commit f5c6dcd275
10 changed files with 890 additions and 659 deletions

1
.gitignore vendored
View File

@ -1,6 +1,7 @@
/.claude /.claude
/CLAUDE.md /CLAUDE.md
/AGENTS.md /AGENTS.md
/logs
target/ target/
!.mvn/wrapper/maven-wrapper.jar !.mvn/wrapper/maven-wrapper.jar

View File

@ -9,93 +9,88 @@ import java.util.concurrent.ConcurrentHashMap;
* Bean 容器上下文 * Bean 容器上下文
* *
* @author 夜雨 * @author 夜雨
* @since 2026-01-12 23:37
*/ */
public class BeanContext { public class BeanContext {
private final Map<String, BeanDefinition> definitions = new ConcurrentHashMap<>(); private final Map<String, Object> singletons = new ConcurrentHashMap<>();
private final Map<String, Object> singletons = new ConcurrentHashMap<>(); private final Map<String, BeanDefinition> definitions = new ConcurrentHashMap<>();
private final Map<Class<?>, List<String>> typeIndex = new ConcurrentHashMap<>(); private final Map<Class<?>, List<String>> typeIndex = new ConcurrentHashMap<>();
/** /**
* 注册 Bean 定义 * 注册 Bean 定义
*/ */
public void registerDefinition(BeanDefinition definition) { public void registerDefinition(BeanDefinition definition) {
String name = definition.getName(); String name = definition.getName();
if (definitions.containsKey(name)) { if (definitions.containsKey(name)) {
throw new InjectException("Bean '" + name + "' already registered"); throw new InjectException("Bean [%s] already registered".formatted(name));
} }
definitions.put(name, definition);
typeIndex.computeIfAbsent(definition.getType(), k -> new ArrayList<>()).add(name);
}
definitions.put(name, definition); /**
typeIndex.computeIfAbsent(definition.getType(), k -> new ArrayList<>()).add(name); * 注册单例 Bean 实例
} */
public void registerSingleton(String name, Object bean) {
if (bean == null) {
throw new InjectException("Bean [%s] cannot be null".formatted(name));
}
singletons.put(name, bean);
if (!definitions.containsKey(name)) {
BeanDefinition definition = BeanDefinition.builder().name(name).type(bean.getClass()).scope(ScopeType.SINGLETON).build();
definitions.put(name, definition);
typeIndex.computeIfAbsent(bean.getClass(), k -> new ArrayList<>()).add(name);
}
}
/** /**
* 注册单例 Bean 实例 * 获取 Bean 定义
*/ */
public void registerSingleton(String name, Object bean) { public BeanDefinition getDefinition(String name) {
if (bean == null) { return definitions.get(name);
throw new InjectException("Cannot register null bean: " + name); }
}
singletons.put(name, bean);
if (!definitions.containsKey(name)) { /**
BeanDefinition definition = BeanDefinition.builder() * 获取单例 Bean
.name(name) */
.type(bean.getClass()) public Object getSingleton(String name) {
.scope(ScopeType.SINGLETON) return singletons.get(name);
.build(); }
definitions.put(name, definition);
typeIndex.computeIfAbsent(bean.getClass(), k -> new ArrayList<>()).add(name);
}
}
/** /**
* 获取 Bean 定义 * 设置单例 Bean
*/ */
public BeanDefinition getDefinition(String name) { public void setSingleton(String name, Object bean) {
return definitions.get(name); singletons.put(name, bean);
} }
/** /**
* 获取单例 Bean * 根据类型查找 Bean 名称列表
*/ */
public Object getSingleton(String name) { public List<String> getBeanNamesByType(Class<?> type) {
return singletons.get(name); List<String> names = new ArrayList<>();
}
/** for (Map.Entry<Class<?>, List<String>> entry : typeIndex.entrySet()) {
* 设置单例 Bean if (type.isAssignableFrom(entry.getKey())) {
*/ names.addAll(entry.getValue());
public void setSingleton(String name, Object bean) { }
singletons.put(name, bean); }
}
/** return names;
* 根据类型查找 Bean 名称列表 }
*/
public List<String> getBeanNamesByType(Class<?> type) {
List<String> names = new ArrayList<>();
for (Map.Entry<Class<?>, List<String>> entry : typeIndex.entrySet()) { /**
if (type.isAssignableFrom(entry.getKey())) { * 获取所有 Bean 定义
names.addAll(entry.getValue()); */
} public Collection<BeanDefinition> getAllDefinitions() {
} return definitions.values();
}
return names; /**
} * 检查是否包含指定名称的 Bean
*/
/** public boolean containsBean(String name) {
* 获取所有 Bean 定义 return definitions.containsKey(name);
*/ }
public Collection<BeanDefinition> getAllDefinitions() {
return definitions.values();
}
/**
* 检查是否包含指定名称的 Bean
*/
public boolean containsBean(String name) {
return definitions.containsKey(name);
}
} }

View File

@ -4,6 +4,8 @@ import com.imyeyu.inject.annotation.ScopeType;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Collections;
import java.util.List;
/** /**
* Bean 定义元数据 * Bean 定义元数据
@ -12,121 +14,144 @@ import java.lang.reflect.Method;
*/ */
public class BeanDefinition { public class BeanDefinition {
private final String name; private final String name;
private final Class<?> type; private final Class<?> type;
private final ScopeType scope; private final ScopeType scope;
private final boolean primary; private final boolean primary;
private final Constructor<?> constructor; private final boolean lazy;
private final Method factoryMethod; private final Constructor<?> constructor;
private final Object configInstance; private final Method factoryMethod;
private final Method postConstruct; private final Object configInstance;
private final List<Method> postConstructs;
private BeanDefinition(Builder builder) { private BeanDefinition(Builder builder) {
this.name = builder.name; this.name = builder.name;
this.type = builder.type; this.type = builder.type;
this.scope = builder.scope; this.scope = builder.scope;
this.primary = builder.primary; this.primary = builder.primary;
this.constructor = builder.constructor; this.lazy = builder.lazy;
this.factoryMethod = builder.factoryMethod; this.constructor = builder.constructor;
this.configInstance = builder.configInstance; this.factoryMethod = builder.factoryMethod;
this.postConstruct = builder.postConstruct; this.configInstance = builder.configInstance;
} this.postConstructs = Collections.unmodifiableList(builder.postConstructs);
}
public String getName() { public String getName() {
return name; return name;
} }
public Class<?> getType() { public Class<?> getType() {
return type; return type;
} }
public ScopeType getScope() { public ScopeType getScope() {
return scope; return scope;
} }
public boolean isPrimary() { public boolean isPrimary() {
return primary; return primary;
} }
public Constructor<?> getConstructor() { public boolean isLazy() {
return constructor; return lazy;
} }
public Method getFactoryMethod() { public Constructor<?> getConstructor() {
return factoryMethod; return constructor;
} }
public Object getConfigInstance() { public Method getFactoryMethod() {
return configInstance; return factoryMethod;
} }
public Method getPostConstruct() { public Object getConfigInstance() {
return postConstruct; return configInstance;
} }
public boolean isFactoryBean() { public List<Method> getPostConstructs() {
return factoryMethod != null; return postConstructs;
} }
public static Builder builder() { public boolean isFactoryBean() {
return new Builder(); return factoryMethod != null;
} }
public static class Builder { public static Builder builder() {
private String name; return new Builder();
private Class<?> type; }
private ScopeType scope = ScopeType.SINGLETON;
private boolean primary = false;
private Constructor<?> constructor;
private Method factoryMethod;
private Object configInstance;
private Method postConstruct;
public Builder name(String name) { /**
this.name = name; *
return this; *
} * @author 夜雨
* @since 2026-01-12 23:38
*/
public static class Builder {
public Builder type(Class<?> type) { private String name;
this.type = type; private Class<?> type;
return this; private ScopeType scope = ScopeType.SINGLETON;
} private boolean primary = false;
private boolean lazy = false;
private Constructor<?> constructor;
private Method factoryMethod;
private Object configInstance;
private List<Method> postConstructs = Collections.emptyList();
public Builder scope(ScopeType scope) { public Builder name(String name) {
this.scope = scope; this.name = name;
return this; return this;
} }
public Builder primary(boolean primary) { public Builder type(Class<?> type) {
this.primary = primary; this.type = type;
return this; return this;
} }
public Builder constructor(Constructor<?> constructor) { public Builder scope(ScopeType scope) {
this.constructor = constructor; this.scope = scope;
return this; return this;
} }
public Builder factoryMethod(Method method) { public Builder primary(boolean primary) {
this.factoryMethod = method; this.primary = primary;
return this; return this;
} }
public Builder configInstance(Object instance) { public Builder lazy(boolean lazy) {
this.configInstance = instance; this.lazy = lazy;
return this; return this;
} }
public Builder postConstruct(Method method) { public Builder constructor(Constructor<?> constructor) {
this.postConstruct = method; this.constructor = constructor;
return this; return this;
} }
public BeanDefinition build() { public Builder factoryMethod(Method method) {
if (name == null || type == null) { this.factoryMethod = method;
throw new IllegalStateException("Bean name and type are required"); return this;
} }
return new BeanDefinition(this);
} public Builder configInstance(Object instance) {
} this.configInstance = instance;
return this;
}
public Builder postConstructs(List<Method> methods) {
if (methods == null || methods.isEmpty()) {
this.postConstructs = Collections.emptyList();
return this;
}
this.postConstructs = List.copyOf(methods);
return this;
}
public BeanDefinition build() {
if (name == null || type == null) {
throw new IllegalStateException("Bean name and type are required");
}
return new BeanDefinition(this);
}
}
} }

View File

@ -1,216 +1,261 @@
package com.imyeyu.inject; package com.imyeyu.inject;
import com.imyeyu.inject.annotation.Inject;
import com.imyeyu.inject.annotation.Qualifier; import com.imyeyu.inject.annotation.Qualifier;
import com.imyeyu.inject.annotation.ScopeType; import com.imyeyu.inject.annotation.ScopeType;
import com.imyeyu.java.ref.Ref;
import com.imyeyu.utils.Time;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Parameter; import java.lang.reflect.Parameter;
import java.util.*; import java.util.LinkedHashSet;
import java.util.List;
/** /**
* Bean 工厂,负责创建和管理 Bean 实例 * Bean 工厂,负责创建和管理 Bean 实例
* *
* @author 夜雨 * @author 夜雨
* @since 2026-01-12 23:38
*/ */
public class BeanFactory { public class BeanFactory {
private static final Logger log = LoggerFactory.getLogger(BeanFactory.class); private static final Logger log = LoggerFactory.getLogger(BeanFactory.class);
private final BeanContext context; private final BeanContext context;
private final Set<String> creating = new HashSet<>(); private final StartupStatistics statistics;
private final LinkedHashSet<String> creating = new LinkedHashSet<>();
public BeanFactory(BeanContext context) { public BeanFactory(BeanContext context, StartupStatistics statistics) {
this.context = context; this.context = context;
} this.statistics = statistics;
}
/** /**
* 获取 Bean 实例(按名称) * 获取 Bean 实例
*/ *
public Object getBean(String name) { * @param name 名称
BeanDefinition definition = context.getDefinition(name); * @return 实例
if (definition == null) { */
throw new InjectException("No bean definition found for name: " + name); 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);
}
if (definition.getScope() == ScopeType.SINGLETON) { /**
Object singleton = context.getSingleton(name); * 获取 Bean 实例
if (singleton != null) { *
return singleton; * @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));
}
return createBean(definition); /**
} * 获取 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) {
* 获取 Bean 实例(按类型) String name = definition.getName();
*/ if (creating.contains(name)) {
@SuppressWarnings("unchecked") StringBuilder errorMsg = new StringBuilder("Circular dependency detected:\n");
public <T> T getBean(Class<T> type) { for (String bean : creating) {
List<String> candidates = context.getBeanNamesByType(type); 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);
}
if (definition.getScope() == ScopeType.SINGLETON) {
context.setSingleton(name, instance);
}
invokePostConstruct(instance, definition);
log.debug("Created bean: {} ({})", name, definition.getType().getName());
return instance;
if (candidates.isEmpty()) { } finally {
throw new InjectException("No bean found for type: " + type.getName()); creating.remove(name);
} }
}
if (candidates.size() == 1) { private Object createBeanFromConstructor(BeanDefinition definition) {
return (T) getBean(candidates.get(0)); 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);
}
}
String primaryBean = findPrimaryBean(candidates); private Object createBeanFromFactoryMethod(BeanDefinition definition) {
if (primaryBean != null) { Method factoryMethod = definition.getFactoryMethod();
return (T) getBean(primaryBean); factoryMethod.setAccessible(true);
}
throw new InjectException("Multiple beans found for type " + type.getName() + String configBeanName = extractConfigBeanName(factoryMethod.getDeclaringClass());
": " + candidates + ". Use @Qualifier or @Primary to specify which one to inject"); Object configInstance = getBean(configBeanName);
}
/** Parameter[] parameters = factoryMethod.getParameters();
* 获取 Bean 实例(按名称和类型) Object[] args = new Object[parameters.length];
*/
@SuppressWarnings("unchecked")
public <T> T getBean(String name, Class<T> type) {
Object bean = getBean(name);
if (!type.isInstance(bean)) {
throw new InjectException("Bean '" + name + "' is not of type " + type.getName());
}
return (T) bean;
}
private Object createBean(BeanDefinition definition) { for (int i = 0; i < parameters.length; i++) {
String name = definition.getName(); args[i] = resolveDependency(parameters[i]);
}
if (creating.contains(name)) { try {
throw new InjectException("Circular dependency detected: " + name + Object result = factoryMethod.invoke(configInstance, args);
" (creation chain: " + creating + ")"); 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);
}
}
creating.add(name); private Object resolveDependency(Parameter parameter) {
try { Qualifier qualifier = parameter.getAnnotation(Qualifier.class);
Object instance; if (qualifier != null) {
return getBean(qualifier.value());
}
Class<?> type = parameter.getType();
return getBean(type);
}
if (definition.isFactoryBean()) { private void invokePostConstruct(Object instance, BeanDefinition definition) {
instance = createBeanFromFactoryMethod(definition); List<Method> postConstructs = definition.getPostConstructs();
} else { if (postConstructs.isEmpty()) {
instance = createBeanFromConstructor(definition); 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;
}
if (definition.getScope() == ScopeType.SINGLETON) { private String findPrimaryBean(List<String> candidates) {
context.setSingleton(name, instance); 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;
}
invokePostConstruct(instance, definition); private String extractConfigBeanName(Class<?> configClass) {
return Character.toLowerCase(configClass.getSimpleName().charAt(0)) + configClass.getSimpleName().substring(1);
}
log.debug("Created bean: {} ({})", name, definition.getType().getName()); /**
return instance; * 初始化所有单例 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();
}
} finally { /**
creating.remove(name); * 对已存在的对象执行字段注入,适用于无法通过构造器创建的对象
} *
} * @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 createBeanFromConstructor(BeanDefinition definition) { private Object resolveDependencyForField(Field field) {
Constructor<?> constructor = definition.getConstructor(); Qualifier qualifier = field.getAnnotation(Qualifier.class);
constructor.setAccessible(true); if (qualifier != null) {
return getBean(qualifier.value());
Parameter[] parameters = constructor.getParameters(); }
Object[] args = new Object[parameters.length]; Class<?> type = field.getType();
return getBean(type);
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: " + 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: " + factoryMethod.getName());
}
return result;
} catch (Exception e) {
throw new InjectException("Failed to invoke @Bean method: " + 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();
return getBean(type);
}
private void invokePostConstruct(Object instance, BeanDefinition definition) {
Method postConstruct = definition.getPostConstruct();
if (postConstruct == null) {
return;
}
postConstruct.setAccessible(true);
try {
postConstruct.invoke(instance);
} catch (Exception e) {
throw new InjectException("Failed to invoke @PostConstruct method: " +
postConstruct.getName(), e);
}
}
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: " + primaryBean + ", " + name);
}
primaryBean = name;
}
}
return primaryBean;
}
private String extractConfigBeanName(Class<?> configClass) {
String simpleName = configClass.getSimpleName();
return Character.toLowerCase(simpleName.charAt(0)) + simpleName.substring(1);
}
/**
* 初始化所有单例 Bean
*/
public void initializeSingletons() {
for (BeanDefinition definition : context.getAllDefinitions()) {
if (definition.getScope() == ScopeType.SINGLETON) {
getBean(definition.getName());
}
}
}
} }

View File

@ -1,6 +1,15 @@
package com.imyeyu.inject; package com.imyeyu.inject;
import com.imyeyu.inject.annotation.*; import com.imyeyu.inject.annotation.Bean;
import com.imyeyu.inject.annotation.Component;
import com.imyeyu.inject.annotation.Configuration;
import com.imyeyu.inject.annotation.Inject;
import com.imyeyu.inject.annotation.Lazy;
import com.imyeyu.inject.annotation.PostConstruct;
import com.imyeyu.inject.annotation.Primary;
import com.imyeyu.inject.annotation.Scope;
import com.imyeyu.inject.annotation.ScopeType;
import com.imyeyu.utils.Time;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -10,7 +19,9 @@ import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.net.URL; import java.net.URL;
import java.util.*; import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry; import java.util.jar.JarEntry;
import java.util.jar.JarFile; import java.util.jar.JarFile;
@ -21,231 +32,236 @@ import java.util.jar.JarFile;
*/ */
public class BeanScanner { public class BeanScanner {
private static final Logger log = LoggerFactory.getLogger(BeanScanner.class); private static final Logger log = LoggerFactory.getLogger(BeanScanner.class);
private final BeanContext context; private final BeanContext context;
private final StartupStatistics statistics;
public BeanScanner(BeanContext context) { public BeanScanner(BeanContext context, StartupStatistics statistics) {
this.context = context; this.context = context;
} this.statistics = statistics;
}
/** /**
* 扫描指定包路径下的所有组件 * 扫描指定包路径下的所有组件
*/ */
public void scan(String... basePackages) { public void scan(String... basePackages) {
for (String pkg : basePackages) { statistics.scanStartTime = Time.now();
scanPackage(pkg); for (String pkg : basePackages) {
} statistics.scannedPackages.add(pkg);
} scanPackage(pkg);
}
statistics.scanEndTime = Time.now();
}
private void scanPackage(String basePackage) { private void scanPackage(String basePackage) {
String path = basePackage.replace('.', '/'); String path = basePackage.replace('.', '/');
try { try {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
Enumeration<URL> resources = classLoader.getResources(path); Enumeration<URL> resources = classLoader.getResources(path);
while (resources.hasMoreElements()) { while (resources.hasMoreElements()) {
URL resource = resources.nextElement(); URL resource = resources.nextElement();
String protocol = resource.getProtocol(); String protocol = resource.getProtocol();
if ("file".equals(protocol)) { if ("file".equals(protocol)) {
scanFileSystem(new File(resource.getFile()), basePackage); scanFileSystem(new File(resource.getFile()), basePackage);
} else if ("jar".equals(protocol)) { } else if ("jar".equals(protocol)) {
scanJar(resource, basePackage); scanJar(resource, basePackage);
} }
} }
} catch (IOException e) { } catch (IOException e) {
throw new InjectException("Failed to scan package: " + basePackage, e); throw new InjectException("Failed to scan package: %s".formatted(basePackage), e);
} }
} }
private void scanFileSystem(File directory, String basePackage) { private void scanFileSystem(File directory, String basePackage) {
if (!directory.exists() || !directory.isDirectory()) { if (!directory.exists() || !directory.isDirectory()) {
return; return;
} }
File[] files = directory.listFiles(); File[] files = directory.listFiles();
if (files == null) { if (files == null) {
return; return;
} }
for (File file : files) { for (File file : files) {
if (file.isDirectory()) { if (file.isDirectory()) {
scanFileSystem(file, basePackage + "." + file.getName()); scanFileSystem(file, "%s.%s".formatted(basePackage, file.getName()));
} else if (file.getName().endsWith(".class")) { } else if (file.getName().endsWith(".class")) {
String className = basePackage + "." + file.getName().replace(".class", ""); String className = "%s.%s".formatted(basePackage, file.getName().replace(".class", ""));
processClass(className); processClass(className);
} }
} }
} }
private void scanJar(URL resource, String basePackage) { private void scanJar(URL resource, String basePackage) {
String jarPath = resource.getPath().substring(5, resource.getPath().indexOf("!")); String jarPath = resource.getPath().substring(5, resource.getPath().indexOf("!"));
String packagePath = basePackage.replace('.', '/'); String packagePath = basePackage.replace('.', '/');
try (JarFile jar = new JarFile(jarPath)) { try (JarFile jar = new JarFile(jarPath)) {
Enumeration<JarEntry> entries = jar.entries(); Enumeration<JarEntry> entries = jar.entries();
while (entries.hasMoreElements()) { while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement(); JarEntry entry = entries.nextElement();
String name = entry.getName(); String name = entry.getName();
if (name.startsWith(packagePath) && name.endsWith(".class")) { if (name.startsWith(packagePath) && name.endsWith(".class")) {
String className = name.replace('/', '.').replace(".class", ""); String className = name.replace('/', '.').replace(".class", "");
processClass(className); processClass(className);
} }
} }
} catch (IOException e) { } catch (IOException e) {
throw new InjectException("Failed to scan jar: " + jarPath, e); throw new InjectException("Failed to scan jar: %s".formatted(jarPath), e);
} }
} }
private void processClass(String className) { private void processClass(String className) {
try { try {
Class<?> clazz = Class.forName(className); statistics.scannedClasses++;
processClass(clazz); Class<?> clazz = Class.forName(className);
} catch (ClassNotFoundException e) { processClass(clazz);
log.warn("Class not found: {}", className); } catch (ClassNotFoundException e) {
} catch (NoClassDefFoundError e) { log.warn("Class not found: {}", className);
log.debug("Skip class with missing dependencies: {}", className); } catch (NoClassDefFoundError e) {
} log.debug("Skip class with missing dependencies: {}", className);
} }
}
/** /**
* 处理单个类 * 处理单个类
*/ */
public void processClass(Class<?> clazz) { public void processClass(Class<?> clazz) {
if (clazz.isAnnotation() || clazz.isInterface() || clazz.isEnum()) { if (clazz.isAnnotation() || clazz.isInterface() || clazz.isEnum()) {
return; return;
} }
Annotation componentAnnotation = getComponentAnnotation(clazz); Annotation componentAnnotation = getComponentAnnotation(clazz);
if (componentAnnotation == null) { if (componentAnnotation == null) {
return; return;
} }
String beanName = extractBeanName(componentAnnotation, clazz); String beanName = extractBeanName(componentAnnotation, clazz);
ScopeType scope = extractScope(clazz); ScopeType scope = extractScope(clazz);
boolean primary = clazz.isAnnotationPresent(Primary.class); boolean primary = clazz.isAnnotationPresent(Primary.class);
Constructor<?> constructor = selectConstructor(clazz); boolean lazy = extractLazy(clazz);
Method postConstruct = findPostConstruct(clazz); Constructor<?> constructor = selectConstructor(clazz);
List<Method> postConstructs = findPostConstructs(clazz);
BeanDefinition definition = BeanDefinition.builder() BeanDefinition definition = BeanDefinition.builder().name(beanName).type(clazz).scope(scope).primary(primary).lazy(lazy).constructor(constructor).postConstructs(postConstructs).build();
.name(beanName)
.type(clazz)
.scope(scope)
.primary(primary)
.constructor(constructor)
.postConstruct(postConstruct)
.build();
context.registerDefinition(definition); context.registerDefinition(definition);
log.debug("Registered bean: {} ({})", beanName, clazz.getName()); statistics.registeredBeans++;
log.debug("Registered bean: {} ({})", beanName, clazz.getName());
if (clazz.isAnnotationPresent(Configuration.class)) { if (clazz.isAnnotationPresent(Configuration.class)) {
processBeanMethods(clazz); processBeanMethods(clazz);
} }
} }
private void processBeanMethods(Class<?> configClass) { private void processBeanMethods(Class<?> configClass) {
for (Method method : configClass.getDeclaredMethods()) { for (Method method : configClass.getDeclaredMethods()) {
if (!method.isAnnotationPresent(Bean.class)) { if (!method.isAnnotationPresent(Bean.class)) {
continue; continue;
} }
Bean beanAnnotation = method.getAnnotation(Bean.class); Bean beanAnnotation = method.getAnnotation(Bean.class);
String beanName = beanAnnotation.value().isEmpty() ? method.getName() : beanAnnotation.value(); String beanName = beanAnnotation.value().isEmpty() ? method.getName() : beanAnnotation.value();
Class<?> returnType = method.getReturnType(); Class<?> returnType = method.getReturnType();
if (returnType == void.class || returnType == Void.class) { if (returnType == void.class || returnType == Void.class) {
throw new InjectException("@Bean method cannot return void: " + method.getName()); throw new InjectException("@Bean method cannot return void: %s".formatted(method.getName()));
} }
BeanDefinition definition = BeanDefinition.builder() BeanDefinition definition = BeanDefinition.builder().name(beanName).type(returnType).scope(ScopeType.SINGLETON).factoryMethod(method).build();
.name(beanName)
.type(returnType)
.scope(ScopeType.SINGLETON)
.factoryMethod(method)
.build();
context.registerDefinition(definition); context.registerDefinition(definition);
log.debug("Registered bean from @Bean method: {} ({})", beanName, returnType.getName()); statistics.registeredBeans++;
} log.debug("Registered bean from @Bean method: {} ({})", beanName, returnType.getName());
} }
}
private Annotation getComponentAnnotation(Class<?> clazz) { private Annotation getComponentAnnotation(Class<?> clazz) {
if (clazz.isAnnotationPresent(Component.class)) { if (clazz.isAnnotationPresent(Component.class)) {
return clazz.getAnnotation(Component.class); return clazz.getAnnotation(Component.class);
} }
for (Annotation annotation : clazz.getAnnotations()) { for (Annotation annotation : clazz.getAnnotations()) {
if (annotation.annotationType().isAnnotationPresent(Component.class)) { if (annotation.annotationType().isAnnotationPresent(Component.class)) {
return annotation; return annotation;
} }
} }
return null; return null;
} }
private String extractBeanName(Annotation annotation, Class<?> clazz) { private String extractBeanName(Annotation annotation, Class<?> clazz) {
try { try {
Method valueMethod = annotation.annotationType().getMethod("value"); Method valueMethod = annotation.annotationType().getMethod("value");
String value = (String) valueMethod.invoke(annotation); String value = (String) valueMethod.invoke(annotation);
if (!value.isEmpty()) { if (!value.isEmpty()) {
return value; return value;
} }
} catch (Exception e) { } catch (Exception e) {
// ignore // ignore
} }
String simpleName = clazz.getSimpleName(); String simpleName = clazz.getSimpleName();
return Character.toLowerCase(simpleName.charAt(0)) + simpleName.substring(1); return Character.toLowerCase(simpleName.charAt(0)) + simpleName.substring(1);
} }
private ScopeType extractScope(Class<?> clazz) { private ScopeType extractScope(Class<?> clazz) {
if (clazz.isAnnotationPresent(Scope.class)) { if (clazz.isAnnotationPresent(Scope.class)) {
return clazz.getAnnotation(Scope.class).value(); return clazz.getAnnotation(Scope.class).value();
} }
return ScopeType.SINGLETON; return ScopeType.SINGLETON;
} }
private Constructor<?> selectConstructor(Class<?> clazz) { private boolean extractLazy(Class<?> clazz) {
Constructor<?>[] constructors = clazz.getDeclaredConstructors(); // 检查 @Lazy 注解
if (clazz.isAnnotationPresent(Lazy.class)) {
return true;
}
// 检查 @Scope 注解的 lazy 属性
if (clazz.isAnnotationPresent(Scope.class)) {
return clazz.getAnnotation(Scope.class).lazy();
}
return false;
}
Constructor<?> injectConstructor = null; private Constructor<?> selectConstructor(Class<?> clazz) {
for (Constructor<?> constructor : constructors) { Constructor<?>[] constructors = clazz.getDeclaredConstructors();
if (constructor.isAnnotationPresent(Inject.class)) { Constructor<?> injectConstructor = null;
if (injectConstructor != null) { for (Constructor<?> constructor : constructors) {
throw new InjectException("Multiple @Inject constructors found in " + clazz.getName()); if (constructor.isAnnotationPresent(Inject.class)) {
} if (injectConstructor != null) {
injectConstructor = constructor; throw new InjectException("Multiple @Inject constructors found in %s".formatted(clazz.getName()));
} }
} injectConstructor = constructor;
}
}
if (injectConstructor != null) {
return injectConstructor;
}
if (constructors.length == 1) {
return constructors[0];
}
try {
return clazz.getDeclaredConstructor();
} catch (NoSuchMethodException e) {
throw new InjectException("No suitable constructor found for %s. Please provide a no-arg constructor or mark one with @Inject".formatted(clazz.getName()), e);
}
}
if (injectConstructor != null) { private List<Method> findPostConstructs(Class<?> clazz) {
return injectConstructor; List<Method> methods = new ArrayList<>();
} for (Method method : clazz.getDeclaredMethods()) {
if (method.isAnnotationPresent(PostConstruct.class)) {
if (constructors.length == 1) { if (method.getParameterCount() != 0) {
return constructors[0]; throw new InjectException("@PostConstruct method must have no parameters: %s".formatted(method.getName()));
} }
methods.add(method);
try { }
return clazz.getDeclaredConstructor(); }
} catch (NoSuchMethodException e) { return methods;
throw new InjectException("No suitable constructor found for " + clazz.getName() + }
". Please provide a no-arg constructor or mark one with @Inject", e);
}
}
private Method findPostConstruct(Class<?> clazz) {
for (Method method : clazz.getDeclaredMethods()) {
if (method.isAnnotationPresent(PostConstruct.class)) {
if (method.getParameterCount() != 0) {
throw new InjectException("@PostConstruct method must have no parameters: " + method.getName());
}
return method;
}
}
return null;
}
} }

View File

@ -0,0 +1,28 @@
package com.imyeyu.inject;
import java.util.ArrayList;
import java.util.List;
/**
* 启动统计信息
*
* @author 夜雨
* @since 2026-01-13 01:29
*/
class StartupStatistics {
final List<String> scannedPackages = new ArrayList<>();
int scannedClasses = 0;
int registeredBeans = 0;
int injectedFields = 0;
int postConstructInvocations = 0;
long scanStartTime = 0;
long scanEndTime = 0;
long iocStartTime = 0;
long iocEndTime = 0;
long injectionTotalTime = 0;
long injectionCurrentStart = 0;
long postConstructTotalTime = 0;
long postConstructCurrentStart = 0;
}

View File

@ -2,10 +2,19 @@ package com.imyeyu.inject;
import com.imyeyu.inject.annotation.Import; import com.imyeyu.inject.annotation.Import;
import com.imyeyu.inject.annotation.TimiInjectApplication; 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.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.util.*; 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 控制反转框架 * Timi 控制反转框架
@ -14,138 +23,226 @@ import java.util.*;
*/ */
public class TimiInject { public class TimiInject {
private static final Logger log = LoggerFactory.getLogger(TimiInject.class); private static final Logger log = LoggerFactory.getLogger(TimiInject.class);
private final BeanContext context; private final BeanContext context;
private final BeanFactory factory; private final BeanFactory factory;
private TimiInject(BeanContext context, BeanFactory factory) { private TimiInject(BeanContext context, BeanFactory factory) {
this.context = context; this.context = context;
this.factory = factory; this.factory = factory;
} }
/** /**
* 启动应用并初始化容器 * 启动应用并初始化容器
*/ */
public static TimiInject run(Class<?> applicationClass, String... args) { public static TimiInject run(Class<?> applicationClass) {
long startTime = System.currentTimeMillis(); return run(applicationClass, null);
log.info("Starting {} ...", applicationClass.getSimpleName()); }
TimiInjectApplication annotation = applicationClass.getAnnotation(TimiInjectApplication.class); /**
if (annotation == null) { * 启动应用并初始化容器(支持初始化前注册外部 Bean
throw new InjectException("Application class must be annotated with @TimiInjectApplication"); *
} * @param initializer 初始化前回调
*/
public static TimiInject run(Class<?> applicationClass, CallbackArg<TimiInject> initializer) {
printBanner();
BeanContext context = new BeanContext(); long startTime = Time.now();
BeanFactory factory = new BeanFactory(context); StartupStatistics statistics = new StartupStatistics();
BeanScanner scanner = new BeanScanner(context);
String[] basePackages = annotation.value(); log.info("Initializing TimiInject for {} ...", applicationClass.getSimpleName());
if (basePackages.length == 0) {
basePackages = new String[]{applicationClass.getPackage().getName()};
}
scanner.scan(basePackages); TimiInjectApplication annotation = applicationClass.getAnnotation(TimiInjectApplication.class);
processImports(applicationClass, scanner, context); if (annotation == null) {
factory.initializeSingletons(); throw new InjectException("Application class must be annotated with @TimiInjectApplication");
}
long elapsed = System.currentTimeMillis() - startTime; BeanContext context = new BeanContext();
log.info("Started {} in {} ms ({} beans)", applicationClass.getSimpleName(), elapsed, BeanFactory factory = new BeanFactory(context, statistics);
context.getAllDefinitions().size()); BeanScanner scanner = new BeanScanner(context, statistics);
return new TimiInject(context, factory); String[] basePackages = annotation.value();
} if (basePackages.length == 0) {
basePackages = new String[] {applicationClass.getPackage().getName()};
}
/** log.info("Scanning packages: {}", String.join(", ", basePackages));
* 依赖注入 - 获取 Bean 实例(按类型) scanner.scan(basePackages);
*/ processImports(applicationClass, scanner, context);
public <T> T di(Class<T> type) { log.info("Scanned {} classes, registered {} beans", statistics.scannedClasses, statistics.registeredBeans);
return factory.getBean(type);
}
/** TimiInject inject = new TimiInject(context, factory);
* 依赖注入 - 获取 Bean 实例(按名称和类型) if (initializer != null) {
*/ initializer.handler(inject);
public <T> T di(String name, Class<T> type) { }
return factory.getBean(name, type);
}
/** log.info("Performing inversion of control...");
* 控制反转 - 手动注册 Bean 实例 factory.initializeSingletons();
*/ log.info("initialization completed!");
public void ioc(String name, Object bean) { log.info("\tTotal beans: {}", statistics.registeredBeans);
context.registerSingleton(name, bean); log.info("\tInjected fields: {}", statistics.injectedFields);
log.debug("Registered singleton bean: {} ({})", name, bean.getClass().getName()); 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 String exportDependencyGraph() { public <T> T getBean(Class<T> type) {
StringBuilder graph = new StringBuilder(); return factory.getBean(type);
graph.append("=== Dependency Graph ===\n\n"); }
for (BeanDefinition definition : context.getAllDefinitions()) { /**
graph.append(formatBeanInfo(definition)); * 获取 Bean 实例(按名称和类型)
graph.append("\n"); */
} public <T> T getBean(String name, Class<T> type) {
return factory.getBean(name, type);
}
return graph.toString(); /**
} * 注册 Bean 实例
*/
public void registerBean(String name, Object bean) {
context.registerSingleton(name, bean);
log.debug("Registered singleton bean: {} ({})", name, bean.getClass().getName());
}
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"); * @param target 需要注入依赖的目标对象
info.append(" Scope: ").append(definition.getScope()).append("\n"); */
info.append(" Primary: ").append(definition.isPrimary()).append("\n"); public void inject(Object target) {
factory.injectFields(target);
}
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()) public String exportDependencyGraph() {
.append(" parameters\n"); StringBuilder graph = new StringBuilder();
} graph.append("=== Dependency Graph ===\n\n");
if (definition.getPostConstruct() != null) { for (BeanDefinition definition : context.getAllDefinitions()) {
info.append(" PostConstruct: ").append(definition.getPostConstruct().getName()).append("()\n"); graph.append(formatBeanInfo(definition));
} graph.append("\n");
}
return info.toString(); return graph.toString();
} }
private static void processImports(Class<?> applicationClass, BeanScanner scanner, BeanContext context) { private String formatBeanInfo(BeanDefinition definition) {
if (!applicationClass.isAnnotationPresent(Import.class)) { StringBuilder info = new StringBuilder();
return; 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");
Import importAnnotation = applicationClass.getAnnotation(Import.class); if (definition.isFactoryBean()) {
Class<?>[] importClasses = importAnnotation.value(); 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");
}
for (Class<?> importClass : importClasses) { List<Method> postConstructs = definition.getPostConstructs();
if (Module.class.isAssignableFrom(importClass)) { if (!postConstructs.isEmpty()) {
try { info.append(" PostConstruct: ");
Module module = (Module) importClass.getDeclaredConstructor().newInstance(); for (int i = 0; i < postConstructs.size(); i++) {
module.configure(context); if (i > 0) {
log.debug("Loaded module: {}", importClass.getName()); info.append(", ");
} catch (Exception e) { }
throw new InjectException("Failed to instantiate module: " + importClass.getName(), e); info.append(postConstructs.get(i).getName()).append("()");
} }
} else { info.append("\n");
// 检查是否已经通过包扫描注册 }
String beanName = extractBeanName(importClass);
if (!context.containsBean(beanName)) {
scanner.processClass(importClass);
log.debug("Imported configuration: {}", importClass.getName());
} else {
log.debug("Skip importing already registered class: {}", importClass.getName());
}
}
}
}
private static String extractBeanName(Class<?> clazz) { return info.toString();
String simpleName = clazz.getSimpleName(); }
return Character.toLowerCase(simpleName.charAt(0)) + simpleName.substring(1);
} 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;
}
}
} }

View File

@ -6,11 +6,11 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import java.lang.annotation.Target;
/** /**
* 标记用于依赖注入的构造器 * 标记用于依赖注入的构造器或字段
* *
* @author 夜雨 * @author 夜雨
*/ */
@Target(ElementType.CONSTRUCTOR) @Target({ElementType.CONSTRUCTOR, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
public @interface Inject { public @interface Inject {
} }

View File

@ -0,0 +1,17 @@
package com.imyeyu.inject.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 标记 Bean 为懒加载
* 等同于 @Scope(value = ScopeType.SINGLETON, lazy = true)
*
* @author 夜雨
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Lazy {
}

View File

@ -18,4 +18,11 @@ public @interface Scope {
* 作用域类型 * 作用域类型
*/ */
ScopeType value() default ScopeType.SINGLETON; ScopeType value() default ScopeType.SINGLETON;
/**
* 是否懒加载(仅对 SINGLETON 作用域有效)
* true - 首次使用时才创建
* false - 启动时立即创建(默认)
*/
boolean lazy() default false;
} }