implement lightweight IOC/DI framework

- 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>
This commit is contained in:
Timi
2026-01-11 12:26:14 +08:00
parent 05606eda74
commit 71be7c07c0
43 changed files with 1429 additions and 1129 deletions

View File

@ -0,0 +1,101 @@
package com.imyeyu.inject;
import com.imyeyu.inject.annotation.ScopeType;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
/**
* Bean 容器上下文
*
* @author 夜雨
*/
public class BeanContext {
private final Map<String, BeanDefinition> definitions = new ConcurrentHashMap<>();
private final Map<String, Object> singletons = new ConcurrentHashMap<>();
private final Map<Class<?>, List<String>> typeIndex = new ConcurrentHashMap<>();
/**
* 注册 Bean 定义
*/
public void registerDefinition(BeanDefinition definition) {
String name = definition.getName();
if (definitions.containsKey(name)) {
throw new InjectException("Bean '" + name + "' already registered");
}
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("Cannot register null bean: " + 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 定义
*/
public BeanDefinition getDefinition(String name) {
return definitions.get(name);
}
/**
* 获取单例 Bean
*/
public Object getSingleton(String name) {
return singletons.get(name);
}
/**
* 设置单例 Bean
*/
public void setSingleton(String name, Object bean) {
singletons.put(name, bean);
}
/**
* 根据类型查找 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())) {
names.addAll(entry.getValue());
}
}
return names;
}
/**
* 获取所有 Bean 定义
*/
public Collection<BeanDefinition> getAllDefinitions() {
return definitions.values();
}
/**
* 检查是否包含指定名称的 Bean
*/
public boolean containsBean(String name) {
return definitions.containsKey(name);
}
}

View File

@ -0,0 +1,132 @@
package com.imyeyu.inject;
import com.imyeyu.inject.annotation.ScopeType;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
/**
* Bean 定义元数据
*
* @author 夜雨
*/
public class BeanDefinition {
private final String name;
private final Class<?> type;
private final ScopeType scope;
private final boolean primary;
private final Constructor<?> constructor;
private final Method factoryMethod;
private final Object configInstance;
private final Method postConstruct;
private BeanDefinition(Builder builder) {
this.name = builder.name;
this.type = builder.type;
this.scope = builder.scope;
this.primary = builder.primary;
this.constructor = builder.constructor;
this.factoryMethod = builder.factoryMethod;
this.configInstance = builder.configInstance;
this.postConstruct = builder.postConstruct;
}
public String getName() {
return name;
}
public Class<?> getType() {
return type;
}
public ScopeType getScope() {
return scope;
}
public boolean isPrimary() {
return primary;
}
public Constructor<?> getConstructor() {
return constructor;
}
public Method getFactoryMethod() {
return factoryMethod;
}
public Object getConfigInstance() {
return configInstance;
}
public Method getPostConstruct() {
return postConstruct;
}
public boolean isFactoryBean() {
return factoryMethod != null;
}
public static Builder builder() {
return new Builder();
}
public static class Builder {
private String name;
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;
}
public Builder type(Class<?> type) {
this.type = type;
return this;
}
public Builder scope(ScopeType scope) {
this.scope = scope;
return this;
}
public Builder primary(boolean primary) {
this.primary = primary;
return this;
}
public Builder constructor(Constructor<?> constructor) {
this.constructor = constructor;
return this;
}
public Builder factoryMethod(Method method) {
this.factoryMethod = method;
return this;
}
public Builder configInstance(Object instance) {
this.configInstance = instance;
return this;
}
public Builder postConstruct(Method method) {
this.postConstruct = method;
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

@ -0,0 +1,216 @@
package com.imyeyu.inject;
import com.imyeyu.inject.annotation.Qualifier;
import com.imyeyu.inject.annotation.ScopeType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.*;
/**
* Bean 工厂,负责创建和管理 Bean 实例
*
* @author 夜雨
*/
public class BeanFactory {
private static final Logger log = LoggerFactory.getLogger(BeanFactory.class);
private final BeanContext context;
private final Set<String> creating = new HashSet<>();
public BeanFactory(BeanContext context) {
this.context = context;
}
/**
* 获取 Bean 实例(按名称)
*/
public Object getBean(String name) {
BeanDefinition definition = context.getDefinition(name);
if (definition == null) {
throw new InjectException("No bean definition found for name: " + name);
}
if (definition.getScope() == ScopeType.SINGLETON) {
Object singleton = context.getSingleton(name);
if (singleton != null) {
return singleton;
}
}
return createBean(definition);
}
/**
* 获取 Bean 实例(按类型)
*/
@SuppressWarnings("unchecked")
public <T> T getBean(Class<T> type) {
List<String> candidates = context.getBeanNamesByType(type);
if (candidates.isEmpty()) {
throw new InjectException("No bean found for type: " + type.getName());
}
if (candidates.size() == 1) {
return (T) getBean(candidates.get(0));
}
String primaryBean = findPrimaryBean(candidates);
if (primaryBean != null) {
return (T) getBean(primaryBean);
}
throw new InjectException("Multiple beans found for type " + type.getName() +
": " + candidates + ". Use @Qualifier or @Primary to specify which one to inject");
}
/**
* 获取 Bean 实例(按名称和类型)
*/
@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) {
String name = definition.getName();
if (creating.contains(name)) {
throw new InjectException("Circular dependency detected: " + name +
" (creation chain: " + creating + ")");
}
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;
} 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: " + 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

@ -0,0 +1,251 @@
package com.imyeyu.inject;
import com.imyeyu.inject.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
/**
* Bean 扫描器
*
* @author 夜雨
*/
public class BeanScanner {
private static final Logger log = LoggerFactory.getLogger(BeanScanner.class);
private final BeanContext context;
public BeanScanner(BeanContext context) {
this.context = context;
}
/**
* 扫描指定包路径下的所有组件
*/
public void scan(String... basePackages) {
for (String pkg : basePackages) {
scanPackage(pkg);
}
}
private void scanPackage(String basePackage) {
String path = basePackage.replace('.', '/');
try {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
Enumeration<URL> resources = classLoader.getResources(path);
while (resources.hasMoreElements()) {
URL resource = resources.nextElement();
String protocol = resource.getProtocol();
if ("file".equals(protocol)) {
scanFileSystem(new File(resource.getFile()), basePackage);
} else if ("jar".equals(protocol)) {
scanJar(resource, basePackage);
}
}
} catch (IOException e) {
throw new InjectException("Failed to scan package: " + basePackage, e);
}
}
private void scanFileSystem(File directory, String basePackage) {
if (!directory.exists() || !directory.isDirectory()) {
return;
}
File[] files = directory.listFiles();
if (files == null) {
return;
}
for (File file : files) {
if (file.isDirectory()) {
scanFileSystem(file, basePackage + "." + file.getName());
} else if (file.getName().endsWith(".class")) {
String className = basePackage + "." + file.getName().replace(".class", "");
processClass(className);
}
}
}
private void scanJar(URL resource, String basePackage) {
String jarPath = resource.getPath().substring(5, resource.getPath().indexOf("!"));
String packagePath = basePackage.replace('.', '/');
try (JarFile jar = new JarFile(jarPath)) {
Enumeration<JarEntry> entries = jar.entries();
while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
String name = entry.getName();
if (name.startsWith(packagePath) && name.endsWith(".class")) {
String className = name.replace('/', '.').replace(".class", "");
processClass(className);
}
}
} catch (IOException e) {
throw new InjectException("Failed to scan jar: " + jarPath, e);
}
}
private void processClass(String className) {
try {
Class<?> clazz = Class.forName(className);
processClass(clazz);
} catch (ClassNotFoundException e) {
log.warn("Class not found: {}", className);
} catch (NoClassDefFoundError e) {
log.debug("Skip class with missing dependencies: {}", className);
}
}
/**
* 处理单个类
*/
public void processClass(Class<?> clazz) {
if (clazz.isAnnotation() || clazz.isInterface() || clazz.isEnum()) {
return;
}
Annotation componentAnnotation = getComponentAnnotation(clazz);
if (componentAnnotation == null) {
return;
}
String beanName = extractBeanName(componentAnnotation, clazz);
ScopeType scope = extractScope(clazz);
boolean primary = clazz.isAnnotationPresent(Primary.class);
Constructor<?> constructor = selectConstructor(clazz);
Method postConstruct = findPostConstruct(clazz);
BeanDefinition definition = BeanDefinition.builder()
.name(beanName)
.type(clazz)
.scope(scope)
.primary(primary)
.constructor(constructor)
.postConstruct(postConstruct)
.build();
context.registerDefinition(definition);
log.debug("Registered bean: {} ({})", beanName, clazz.getName());
if (clazz.isAnnotationPresent(Configuration.class)) {
processBeanMethods(clazz);
}
}
private void processBeanMethods(Class<?> configClass) {
for (Method method : configClass.getDeclaredMethods()) {
if (!method.isAnnotationPresent(Bean.class)) {
continue;
}
Bean beanAnnotation = method.getAnnotation(Bean.class);
String beanName = beanAnnotation.value().isEmpty() ? method.getName() : beanAnnotation.value();
Class<?> returnType = method.getReturnType();
if (returnType == void.class || returnType == Void.class) {
throw new InjectException("@Bean method cannot return void: " + method.getName());
}
BeanDefinition definition = BeanDefinition.builder()
.name(beanName)
.type(returnType)
.scope(ScopeType.SINGLETON)
.factoryMethod(method)
.build();
context.registerDefinition(definition);
log.debug("Registered bean from @Bean method: {} ({})", beanName, returnType.getName());
}
}
private Annotation getComponentAnnotation(Class<?> clazz) {
if (clazz.isAnnotationPresent(Component.class)) {
return clazz.getAnnotation(Component.class);
}
for (Annotation annotation : clazz.getAnnotations()) {
if (annotation.annotationType().isAnnotationPresent(Component.class)) {
return annotation;
}
}
return null;
}
private String extractBeanName(Annotation annotation, Class<?> clazz) {
try {
Method valueMethod = annotation.annotationType().getMethod("value");
String value = (String) valueMethod.invoke(annotation);
if (!value.isEmpty()) {
return value;
}
} catch (Exception e) {
// ignore
}
String simpleName = clazz.getSimpleName();
return Character.toLowerCase(simpleName.charAt(0)) + simpleName.substring(1);
}
private ScopeType extractScope(Class<?> clazz) {
if (clazz.isAnnotationPresent(Scope.class)) {
return clazz.getAnnotation(Scope.class).value();
}
return ScopeType.SINGLETON;
}
private Constructor<?> selectConstructor(Class<?> clazz) {
Constructor<?>[] constructors = clazz.getDeclaredConstructors();
Constructor<?> injectConstructor = null;
for (Constructor<?> constructor : constructors) {
if (constructor.isAnnotationPresent(Inject.class)) {
if (injectConstructor != null) {
throw new InjectException("Multiple @Inject constructors found in " + 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 " + 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

@ -1,68 +0,0 @@
package com.imyeyu.inject;
import com.imyeyu.java.bean.CallbackArg;
import java.util.ArrayList;
import java.util.List;
/**
* @author 夜雨
* @since 2024-10-10 15:20
*/
public final class InjectApp {
Object obj;
Class<?> clazz;
TimiInject injector;
boolean enableBanner = true;
/** 全局注入后监听,包括静态注入 */
final List<CallbackArg<TimiInject>> afterInjectCallbackList = new ArrayList<>();
public InjectApp(Object obj) {
this.obj = obj;
this.clazz = obj.getClass();
}
public InjectApp(Class<?> clazz) {
this.clazz = clazz;
}
public InjectApp disableBanner() {
enableBanner = false;
return this;
}
public TimiInject injector() {
return injector;
}
/**
* 添加全局注入后监听
*
* @param listener 监听器
*/
public synchronized void addAfterInjectListener(CallbackArg<TimiInject> listener) {
afterInjectCallbackList.add(listener);
}
/**
* 移除全局注入后监听
*
* @param listener 监听器
*/
public synchronized void removeAfterInjectListener(CallbackArg<TimiInject> listener) {
afterInjectCallbackList.remove(listener);
}
public boolean isEnableBanner() {
return enableBanner;
}
public void setEnableBanner(boolean enableBanner) {
this.enableBanner = enableBanner;
}
}

View File

@ -0,0 +1,17 @@
package com.imyeyu.inject;
/**
* IOC/DI 框架异常
*
* @author 夜雨
*/
public class InjectException extends RuntimeException {
public InjectException(String message) {
super(message);
}
public InjectException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@ -1,50 +0,0 @@
package com.imyeyu.inject;
/**
* 注入工厂,顶层接口
*
* @author 夜雨
* @version 2022-03-04 23:18
*/
interface InjectFactory {
/**
* 根据 ID 获取反转对象
*
* @param id ID
* @return 反转对象
*/
<T> T di(String id, Class<T> clazz);
/**
* 根据类型获取反转对象
*
* @param classType 类型
* @return 反转对象
* @param <T> 对象类型
*/
<T> T di(Class<T> classType);
/**
* 强制控制反转并重新注入
*
* @param object 反转对象
*/
void ioc(Object object);
/**
* 强制控制反转并重新注入
*
* @param clazz 反转类型
* @param object 反转对象
*/
void ioc(Class<?> clazz, Object object);
/**
* 强制控制反转并重新注入
*
* @param id 对象 ID
* @param object 反转对象
*/
void ioc(String id, Object object);
}

View File

@ -0,0 +1,16 @@
package com.imyeyu.inject;
/**
* 模块扩展接口,用于手动配置 Bean
*
* @author 夜雨
*/
public interface Module {
/**
* 配置 Bean 上下文
*
* @param context Bean 上下文
*/
void configure(BeanContext context);
}

View File

@ -1,766 +1,140 @@
package com.imyeyu.inject;
import com.imyeyu.inject.annotation.Bean;
import com.imyeyu.inject.annotation.Component;
import com.imyeyu.inject.annotation.Controller;
import com.imyeyu.inject.annotation.IOCAsync;
import com.imyeyu.inject.annotation.IOCPriority;
import com.imyeyu.inject.annotation.IOCReturn;
import com.imyeyu.inject.annotation.Inject;
import com.imyeyu.inject.annotation.InvokeForInjected;
import com.imyeyu.inject.annotation.Resources;
import com.imyeyu.inject.annotation.Service;
import com.imyeyu.inject.annotation.StaticInject;
import com.imyeyu.inject.annotation.SuperIOC;
import com.imyeyu.inject.annotation.SuperInject;
import com.imyeyu.inject.annotation.Import;
import com.imyeyu.inject.annotation.TimiInjectApplication;
import com.imyeyu.inject.annotation.Util;
import com.imyeyu.io.IO;
import com.imyeyu.java.TimiJava;
import com.imyeyu.java.bean.CallbackArg;
import com.imyeyu.utils.Time;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.*;
/**
* Timi 控制反转框架
*
* @author 夜雨
* @version 2022-03-04 23:14
*/
public class TimiInject implements InjectFactory {
public class TimiInject {
/** 日志 */
private static final Logger log = LoggerFactory.getLogger(TimiInject.class);
private static final Logger log = LoggerFactory.getLogger(TimiInject.class);
/** 类路径 */
private final List<String> paths;
private final BeanContext context;
private final BeanFactory factory;
/** 控制反转对象 */
private final Map<String, Object> objects;
private TimiInject(BeanContext context, BeanFactory factory) {
this.context = context;
this.factory = factory;
}
/** 依赖注入字段集 */
private final List<Field> fields;
/**
* 启动应用并初始化容器
*/
public static TimiInject run(Class<?> applicationClass, String... args) {
long startTime = System.currentTimeMillis();
log.info("Starting {} ...", applicationClass.getSimpleName());
// 注入后调度 Map<对象, Map<方法, 调度顺序>>
private final Map<Object, Map<Method, Integer>> invokeForInjected;
TimiInjectApplication annotation = applicationClass.getAnnotation(TimiInjectApplication.class);
if (annotation == null) {
throw new InjectException("Application class must be annotated with @TimiInjectApplication");
}
/** 控制反转监听 */
private final Map<Class<?>, List<CallbackArg<Object>>> iocListeners;
BeanContext context = new BeanContext();
BeanFactory factory = new BeanFactory(context);
BeanScanner scanner = new BeanScanner(context);
/** 静态注入类列表 */
private final List<Class<?>> staticInject;
String[] basePackages = annotation.value();
if (basePackages.length == 0) {
basePackages = new String[]{applicationClass.getPackage().getName()};
}
/** 控制反转类对象 */
private final List<IOCClass> classes;
scanner.scan(basePackages);
processImports(applicationClass, scanner, context);
factory.initializeSingletons();
// 异步控制反转参数
private int iocAsyncRunning = 0;
private boolean hasAsyncIOC = false;
private final Object iocAsyncLock = new Object();
long elapsed = System.currentTimeMillis() - startTime;
log.info("Started {} in {} ms ({} beans)", applicationClass.getSimpleName(), elapsed,
context.getAllDefinitions().size());
private TimiInject(InjectApp app) {
if (TimiJava.isEmpty(app)) {
throw new NullPointerException("empty inject app");
}
app.injector = this;
// 初始化
long initAt = Time.now();
// Logo
if (app.enableBanner) {
if (IO.resourceExist(getClass(), "banner.txt")) {
log.info(IO.resourceToString(getClass(), "banner.txt"));
} else {
log.info(IO.resourceToString(getClass(), "defBanner.txt"));
}
}
log.info("Starting TimiInject {} ..", app.clazz);
List<String> scanPathList = new ArrayList<>();
objects = new HashMap<>();
iocListeners = new HashMap<>();
paths = new ArrayList<>();
fields = new ArrayList<>();
invokeForInjected = new LinkedHashMap<>();
staticInject = new ArrayList<>();
classes = new ArrayList<>();
try {
// 扫描
long scanAt = Time.now();
{
// 扫描包
log.info("Scanning..");
TimiInjectApplication appAnnotation = app.clazz.getAnnotation(TimiInjectApplication.class);
if (appAnnotation == null) {
throw new NullPointerException("TimiInjectApplication can not be null, used annotation for scanner class");
}
if (TimiJava.isEmpty(appAnnotation.value())) {
scanPathList.add(app.clazz.getPackageName());
} else {
scanPathList.addAll(List.of(appAnnotation.value()));
}
// 扫描类
URI baseURI = app.clazz.getProtectionDomain().getCodeSource().getLocation().toURI();
// baseURI: 可执行 jar 或编译 classes 路径
for (int j = 0; j < scanPathList.size(); j++) {
scan(new File(baseURI).getPath(), scanPathList.get(j));
}
}
// 控制反转
log.info("Running IOC..");
long iocAt = Time.now();
{
// Application IOC
if (app.obj != null) {
objects.put(app.clazz.getName(), app.obj);
// 控制反转方法返回
objects.putAll(iocReturn(app.clazz, app.obj));
// 收集此对象需要注入后调度的方法
invokeForInjected.put(app.obj, invokeForInjected(app.clazz));
}
// 自 IOC
objects.put(getClass().getName(), this);
// 其他注解 IOC
for (int j = 0; j < paths.size(); j++) {
generateIOCClass(Class.forName(paths.get(j)));
}
classes.sort(Comparator.comparingInt(o -> o.level));
for (int j = 0; j < classes.size(); j++) {
final IOCClass iocClass = classes.get(j);
try {
// 控制反转
if (iocClass.clazz.isAnnotationPresent(IOCAsync.class)) {
// 异步实例化
iocAsyncRunning++;
hasAsyncIOC = true;
return new TimiInject(context, factory);
}
new Thread(() -> {
try {
newInstance(iocClass);
synchronized (iocAsyncLock) {
iocAsyncRunning--;
if (iocAsyncRunning < 1) {
iocAsyncLock.notifyAll();
}
}
} catch (Exception e) {
synchronized (iocAsyncLock) {
iocAsyncLock.notifyAll();
}
throw new RuntimeException(e);
}
}).start();
} else {
// 同步实例化
newInstance(iocClass);
}
} catch (Exception e) {
throw new RuntimeException("IOC fail id: %s, class: %s".formatted(iocClass.id, iocClass.clazz), e);
}
}
if (hasAsyncIOC && iocAsyncRunning != 0) {
// 存在异步控制反转,且至少有一个类正在进行
synchronized (iocAsyncLock) {
iocAsyncLock.wait();
}
}
}
// 依赖注入
log.info("Running inject..");
long injectAt = Time.now();
{
Collection<Object> objects = this.objects.values();
for (Object object : objects) {
inject(object, object.getClass());
}
// 静态注入
for (int j = 0; j < staticInject.size(); j++) {
inject(null, staticInject.get(j));
}
/**
* 依赖注入 - 获取 Bean 实例(按类型)
*/
public <T> T di(Class<T> type) {
return factory.getBean(type);
}
// 完成注入
for (int j = 0; j < app.afterInjectCallbackList.size(); j++) {
app.afterInjectCallbackList.get(j).handler(this);
}
}
log.info("Invoking injected callback..");
// 注入调度
long invokeForInjectedAt = Time.now();
{
for (Map.Entry<Object, Map<Method, Integer>> item : invokeForInjected.entrySet()) {
for (Method method : item.getValue().keySet()) {
try {
long start = -1;
if (log.isDebugEnabled()) {
start = Time.now();
}
method.invoke(item.getKey());
if (log.isDebugEnabled()) {
log.debug("invoke injected event {} ms for {}#{}", "%4s".formatted(Time.now() - start), item.getKey().getClass().getName(), method.getName());
}
} catch (Exception e) {
throw new RuntimeException("invoke method fail: " + item.getKey().getClass().getName() + "#" + method.getName(), e);
}
}
}
// 静态
for (int j = 0; j < staticInject.size(); j++) {
Method[] methods = staticInject.get(j).getDeclaredMethods();
for (int k = 0; k < methods.length; k++) {
if (Modifier.isStatic(methods[k].getModifiers())) {
methods[k].setAccessible(true);
if (methods[k].getDeclaredAnnotation(InvokeForInjected.class) != null) {
try {
methods[k].invoke(null);
} catch (Exception e) {
throw new RuntimeException("invoke method fail: " + methods[k].getDeclaringClass().getName() + "#" + methods[k].getName(), e);
}
}
}
}
}
}
/**
* 依赖注入 - 获取 Bean 实例(按名称和类型)
*/
public <T> T di(String name, Class<T> type) {
return factory.getBean(name, type);
}
long now = Time.now();
log.info("Completed!");
log.info("IOC objects: " + objects.size());
log.info("Inject fields: " + fields.size());
log.info("Build Time: ");
log.info("{} ms for Initialization", "%8s".formatted(scanAt - initAt));
log.info("{} ms for Scan Classes", "%8s".formatted(iocAt - scanAt));
log.info("{} ms for IOC", "%8s".formatted(injectAt - iocAt));
log.info("{} ms for Inject", "%8s".formatted(invokeForInjectedAt - injectAt));
log.info("{} ms for Invoke Callback", "%8s".formatted(now - invokeForInjectedAt));
log.info("{} ms for Total", "%8s".formatted(now - initAt));
} catch (Exception e) {
log.error("TimiInject run error", e);
}
}
/**
* 控制反转 - 手动注册 Bean 实例
*/
public void ioc(String name, Object bean) {
context.registerSingleton(name, bean);
log.debug("Registered singleton bean: {} ({})", name, bean.getClass().getName());
}
/**
* 扫描包
*
* @param basePath 基本文件位置,可执行 jar 或编译 classes 路径。路径为系统分隔符
* @param scanPath 扫描位置,源码包路径,分隔符为 '.'
*/
private void scan(String basePath, String scanPath) throws IOException {
if (basePath.endsWith(".jar")) {
// 扫描 jar class
JarFile jarFile = new JarFile(basePath);
Enumeration<JarEntry> entries = jarFile.entries();
scanPath = scanPath.replace('.', '/'); // jar 内路径始终为 '/' 分隔符
while (entries.hasMoreElements()) {
String name = entries.nextElement().getName();
if (name.startsWith(scanPath) && name.endsWith(".class") && !name.contains("$")) {
paths.add(name.substring(0, name.length() - 6).replace('/', '.'));
}
}
jarFile.close();
} else {
// 扫描本地文件 class
scanPath = scanPath.replace('.', File.separatorChar);
scanFile(new File(basePath + File.separator + scanPath), basePath.length());
}
}
/**
* 导出依赖图
*/
public String exportDependencyGraph() {
StringBuilder graph = new StringBuilder();
graph.append("=== Dependency Graph ===\n\n");
/**
* 扫描本地 class 文件
*
* @param directory 文件夹
* @param clip 剪切位置
*/
private void scanFile(File directory, int clip) {
if (directory.isDirectory()) {
File[] files = directory.listFiles();
if (files != null) {
for (int i = 0; i < files.length; i++) {
if (files[i].isFile()) {
String path = files[i].getPath();
if (path.endsWith(".class") && !path.contains("$")) {
paths.add(path.substring(clip + 1, path.length() - 6).replace(File.separatorChar, '.'));
}
} else {
scanFile(files[i], clip);
}
}
}
}
}
for (BeanDefinition definition : context.getAllDefinitions()) {
graph.append(formatBeanInfo(definition));
graph.append("\n");
}
/**
* 生成类的控制反转封装,如果此类没有标记控制反转则忽略
*
* @param clazz 类对象
*/
private void generateIOCClass(Class<?> clazz) {
boolean isBean = clazz.isAnnotationPresent(Bean.class);
boolean isComponent = clazz.isAnnotationPresent(Component.class);
boolean isService = clazz.isAnnotationPresent(Service.class);
boolean isController = clazz.isAnnotationPresent(Controller.class);
boolean isResources = clazz.isAnnotationPresent(Resources.class);
boolean isUtil = clazz.isAnnotationPresent(Util.class);
// 注入
boolean isInject = clazz.isAnnotationPresent(Inject.class);
boolean isStaticInject = clazz.isAnnotationPresent(StaticInject.class);
return graph.toString();
}
// 获取反转对象 ID
if (!(isBean || isComponent || isService || isController || isResources || isUtil || isInject)) {
// 非控制反转相关类
if (isStaticInject) {
// 静态注入
staticInject.add(clazz);
}
}
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");
String id = null;
if (isBean) {
id = clazz.getAnnotation(Bean.class).value();
}
if (isComponent) {
id = clazz.getAnnotation(Component.class).value();
}
if (isService) {
id = clazz.getAnnotation(Service.class).value();
}
if (isController) {
id = clazz.getAnnotation(Controller.class).value();
}
if (isResources) {
id = clazz.getAnnotation(Resources.class).value();
}
if (isUtil) {
id = clazz.getAnnotation(Util.class).value();
}
if (isInject) {
id = clazz.getAnnotation(Inject.class).value();
}
if (id != null) {
IOCClass iocClass = new IOCClass(id, clazz.getName(), clazz.isAnnotationPresent(IOCAsync.class), clazz);
if (clazz.isAnnotationPresent(IOCPriority.class)) {
// IOCPriority 注解类和异步控制反转优先实例化
iocClass.level = clazz.getAnnotation(IOCPriority.class).value();
}
classes.add(iocClass);
// 静态子类和父级静态子类控制反转
Class<?> superClass = clazz;
do {
Class<?>[] declaredClasses = superClass.getDeclaredClasses();
for (int i = 0; i < declaredClasses.length; i++) {
if (Modifier.isStatic(declaredClasses[i].getModifiers())) {
generateIOCClass(declaredClasses[i]);
}
}
} while (superClass.isAnnotationPresent(SuperIOC.class) && (superClass = superClass.getSuperclass()) != null);
}
}
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");
}
/**
* 实例化控制反转对象
*
* @param iocClass 控制反转类
* @throws NoSuchMethodException 不存在构造方法异常
* @throws InvocationTargetException 调用异常
* @throws InstantiationException 实例化异常
* @throws IllegalAccessException 访问异常
*/
private void newInstance(IOCClass iocClass) throws Exception {
long start = -1;
if (log.isDebugEnabled()) {
start = Time.now();
}
if (definition.getPostConstruct() != null) {
info.append(" PostConstruct: ").append(definition.getPostConstruct().getName()).append("()\n");
}
Constructor<?> constructor = iocClass.clazz.getDeclaredConstructor();
constructor.setAccessible(true);
Object object = constructor.newInstance();
if (TimiJava.isEmpty(iocClass.id)) {
objects.put(iocClass.path, object);
} else {
objects.put(iocClass.id, object);
}
return info.toString();
}
if (log.isDebugEnabled()) {
if (iocClass.isAsync) {
log.debug("IOC {} ms(async) for {}: {}", "%4s".formatted(Time.now() - start), iocClass.id, iocClass.clazz.getName());
} else {
log.debug("IOC {} ms for {}: {}", "%4s".formatted(Time.now() - start), iocClass.id, iocClass.clazz.getName());
}
}
private static void processImports(Class<?> applicationClass, BeanScanner scanner, BeanContext context) {
if (!applicationClass.isAnnotationPresent(Import.class)) {
return;
}
// 控制反转方法返回
objects.putAll(iocReturn(object.getClass(), object));
// 收集此对象需要注入后调度的方法
invokeForInjected.put(object, invokeForInjected(object.getClass()));
}
Import importAnnotation = applicationClass.getAnnotation(Import.class);
Class<?>[] importClasses = importAnnotation.value();
/**
* 控制反转方法返回对象
*
* @param clazz 所属类
* @param object 类对象
* @return 控制反转对象
*/
private Map<String, Object> iocReturn(Class<?> clazz, Object object) {
Map<String, Object> iocObjects = new HashMap<>();
// 控制反转方法返回
Method[] methods = clazz.getDeclaredMethods();
for (int i = 0; i < methods.length; i++) {
try {
methods[i].setAccessible(true);
IOCReturn iocReturn = methods[i].getAnnotation(IOCReturn.class);
if (iocReturn != null) {
if (methods[i].isAnnotationPresent(IOCAsync.class)) {
iocAsyncRunning++;
hasAsyncIOC = true;
final int j = i;
new Thread(() -> {
try {
long start = -1;
if (log.isDebugEnabled()) {
start = Time.now();
}
Object returnObject = methods[j].invoke(object);
if (returnObject == null) {
throw new NullPointerException("return IOC object can not be null: " + clazz.getSimpleName() + "#" + methods[j].getName());
}
String id = iocReturn.value();
if (TimiJava.isEmpty(id)) {
id = returnObject.getClass().getName() + "#" + methods[j].getName();
}
objects.put(id, returnObject);
if (log.isDebugEnabled()) {
log.debug("IOC {} ms(async) for {}: {}", "%4s".formatted(Time.now() - start), id, returnObject.getClass().getName());
}
synchronized (iocAsyncLock) {
iocAsyncRunning--;
if (iocAsyncRunning < 1) {
iocAsyncLock.notifyAll();
}
}
} catch (Exception e) {
synchronized (iocAsyncLock) {
iocAsyncLock.notifyAll();
}
log.error("IOC fail class: %s#%s".formatted(clazz.getName(), methods[j].getName()), e);
}
}).start();
} else {
long start = -1;
if (log.isDebugEnabled()) {
start = Time.now();
}
Object returnObject = methods[i].invoke(object);
if (returnObject == null) {
throw new NullPointerException("return IOC object can not be null: " + clazz.getSimpleName() + "#" + methods[i].getName());
}
String id = iocReturn.value();
if (TimiJava.isEmpty(id)) {
id = returnObject.getClass().getName() + "#" + methods[i].getName();
}
iocObjects.put(id, returnObject);
if (log.isDebugEnabled()) {
log.debug("IOC {} ms for {}: {}", "%4s".formatted(Time.now() - start), id, returnObject.getClass().getName());
}
}
}
} catch (InvocationTargetException | IllegalAccessException e) {
log.error("IOC fail class: %s#%s".formatted(clazz.getName(), methods[i].getName()), e);
}
}
return iocObjects;
}
/**
* 向变量注入对象
*
* @param object 控制反转对象
* @param clazz 该变量类型(存在父级注入时递归调用)
*/
private void inject(Object object, Class<?> clazz) throws IllegalAccessException {
if (clazz.isAnnotationPresent(SuperInject.class)) {
// 父级注入
inject(object, clazz.getSuperclass());
}
injectFields(object, clazz.getDeclaredFields());
}
/**
* 向变量字段注入对象
*
* @param object 目标
* @param fields 字段
*/
private void injectFields(Object object, Field... fields) throws IllegalAccessException {
for (int i = 0; i < fields.length; i++) {
if (object == null) {
if (!Modifier.isStatic(fields[i].getModifiers())) {
// 静态注入,忽略非静态属性
continue;
}
}
Inject inject = fields[i].getAnnotation(Inject.class);
if (inject != null) {
fields[i].setAccessible(true);
if (fields[i].getType().equals(TimiInject.class)) {
// 注入注射器
fields[i].set(object, this);
} else {
String value = inject.value();
// 执行注入
if (TimiJava.isEmpty(value)) {
String id = fields[i].getType().getName() + "#" + fields[i].getName();
log.debug("Injecting {}", id);
Object diObject = di(id, Object.class);
try {
if (diObject == null) {
fields[i].set(object, di(fields[i].getType()));
} else {
fields[i].set(object, diObject);
}
} catch (Exception e) {
if (object == null) {
throw new RuntimeException("inject field fail: null#" + fields[i].getName(), e);
} else {
throw new RuntimeException("inject field fail: " + object.getClass().getName() + "#" + fields[i].getName(), e);
}
}
} else {
fields[i].set(object, objects.get(value));
}
// 添加到集合
this.fields.add(fields[i]);
}
}
}
}
/**
* 收集此对象需要注入后调度的方法
*
* @param clazz 对象类
*/
private Map<Method, Integer> invokeForInjected(Class<?> clazz) {
Map<Method, Integer> map = new HashMap<>();
if (clazz.isAnnotationPresent(SuperInject.class)) {
// 父级调度
map.putAll(invokeForInjected(clazz.getSuperclass()));
}
Method[] methods = clazz.getDeclaredMethods();
for (int i = 0; i < methods.length; i++) {
methods[i].setAccessible(true);
InvokeForInjected invokeForInjected = methods[i].getAnnotation(InvokeForInjected.class);
if (invokeForInjected != null) {
map.put(methods[i], invokeForInjected.value());
}
}
// 排序
List<Map.Entry<Method, Integer>> list = new ArrayList<>(map.entrySet());
list.sort(Comparator.comparingDouble(o -> o.getValue().doubleValue()));
LinkedHashMap<Method, Integer> result = new LinkedHashMap<>();
for (Map.Entry<Method, Integer> entry : list) {
result.put(entry.getKey(), entry.getValue());
}
return result;
}
/**
* 运行 TimiInject 注入框架,此方法应运行且只运行一次
*
* @param clazz TimiInjectApplication 注解的类
*/
public static synchronized TimiInject run(Class<?> clazz) {
return run(new InjectApp(clazz));
}
/**
* 运行 TimiInject 注入框架,此方法应运行且只运行一次
*
* @param obj TimiInjectApplication 注解的对象
*/
public static synchronized TimiInject run(Object obj) {
return run(new InjectApp(obj));
}
/**
* 运行 TimiInject 注入框架,此方法应运行且只运行一次
*
* @param app 控制反转应用
*/
public static synchronized TimiInject run(InjectApp app) {
return new TimiInject(app);
}
/**
* 根据 ID 获取依赖注入对象
*
* @param id ID
* @return 控制反转的对象
*/
@Override
public <T> T di(String id, Class<T> clazz) {
return clazz.cast(objects.get(id));
}
/**
* 根据类型获取依赖注入对象
*
* @param classType 类型
* @param <T> 控制反转类型
* @return 控制反转对象
*/
@Override
public <T> T di(Class<T> classType) {
for (Object next : objects.values()) {
if (next.getClass().getName().equals(classType.getName()) || classType.isAssignableFrom(next.getClass())) {
return classType.cast(next);
}
}
return null;
}
@Override
public void ioc(Object object) {
ioc(object.getClass(), object);
}
@Override
public void ioc(Class<?> clazz, Object object) {
ioc(clazz.getName(), object);
}
@Override
public void ioc(String id, Object object) {
try {
log.info("Call IOC: " + id);
// ---------- 控制反转 ----------
long iocAt = Time.now();
// 本类对象
objects.put(id, object);
// 方法返回对象
Map<String, Object> iocReturnObjects = iocReturn(object.getClass(), object);
objects.putAll(iocReturnObjects);
// ---------- 注入 ----------
long injectAt = Time.now();
// 本类对象字段
inject(object, object.getClass());
// 其他类的本类对象注入
for (int i = 0; i < fields.size(); i++) {
if (fields.get(i).getType().equals(object.getClass())) {
fields.get(i).set(di(fields.get(i).getDeclaringClass()), object);
}
}
// 方法返回对象注入
for (Map.Entry<String, Object> iocObject : iocReturnObjects.entrySet()) {
for (int i = 0; i < fields.size(); i++) {
if (fields.get(i).getType().equals(iocObject.getValue().getClass())) {
fields.get(i).set(di(fields.get(i).getDeclaringClass()), iocObject.getValue());
}
}
}
long afterInjectingAt = Time.now();
// 一般注入后调度
Map<Method, Integer> methods = invokeForInjected(object.getClass());
for (Map.Entry<Method, Integer> method : methods.entrySet()) {
method.getKey().invoke(object);
}
long callIOCListenersAt = Time.now();
// 控制反转监听
if (iocListeners.get(object.getClass()) != null) {
List<CallbackArg<Object>> listeners = iocListeners.get(object.getClass());
for (int i = 0; i < listeners.size(); i++) {
listeners.get(i).handler(object);
}
}
long now = Time.now();
log.info("Completed for {} IOC object in {} ms", (iocReturnObjects.size() + 1), now - iocAt);
log.debug("Build Time Detail: ");
log.debug("{} ms for IOC", "%8s".formatted(injectAt - iocAt));
log.debug("{} ms for Inject", "%8s".formatted(afterInjectingAt - injectAt));
log.debug("{} ms for Injected Event", "%8s".formatted(callIOCListenersAt - afterInjectingAt));
log.debug("{} ms for IOC Listener Event", "%8s".formatted(now - callIOCListenersAt));
log.debug("{} ms for Total", "%8s".formatted(now - iocAt));
} catch (Exception e) {
log.error("call ioc fail: " + id, e);
}
}
/**
* 添加控制反转监听,这通常是为强制控制反转的类监听,因为第一轮控制反转是监听不到的
*
* @param clazz 控制反转类
* @param listener 监听器
*/
public synchronized void addIOCListener(Class<?> clazz, CallbackArg<Object> listener) {
iocListeners.computeIfAbsent(clazz, k -> new ArrayList<>()).add(listener);
}
/**
* 移除控制反转监听
*
* @param clazz 控制反转类
* @param listener 监听器
*/
public synchronized void removeIOCListener(Class<?> clazz, CallbackArg<Object> listener) {
iocListeners.computeIfAbsent(clazz, k -> new ArrayList<>()).remove(listener);
}
/**
* 控制反转类
*
* <p>夜雨 创建于 2022-10-11 09:42
*/
private static class IOCClass {
/** id */
String id;
/** 类路径 */
String path;
/** true 为异步实例化 */
boolean isAsync;
/** 类对象 */
Class<?> clazz;
/** 优先级 */
int level;
public IOCClass(String id, String path, boolean isAsync, Class<?> clazz) {
this.id = id;
this.path = path;
this.isAsync = isAsync;
this.clazz = clazz;
this.level = Integer.MAX_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());
}
}
}
}

View File

@ -1,26 +1,21 @@
package com.imyeyu.inject.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 控制反转(通用对象),注解在类上后该类将被 TimiInject 实例化并控制反转,后续执行注入
* Bean 工厂方法注解,用于 @Configuration 类中
*
* @author 夜雨
* @since 2024-07-19 15:48
*/
@Target({ElementType.TYPE})
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Bean {
/**
* 对象名称,非空时注入需要明确执行
*
* @return 对象名称
*/
String value() default "";
/**
* Bean 名称,默认为方法名
*/
String value() default "";
}

View File

@ -1,26 +1,21 @@
package com.imyeyu.inject.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 控制反转(组件),注解在类上后该类将被 TimiInject 实例化并控制反转,后续执行注入
* 通用组件注解
*
* @author 夜雨
* @version 2022-03-04 23:16
*/
@Target({ElementType.TYPE})
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Component {
/**
* 组件名称,非空时注入需要明确执行
*
* @return 组件名称
*/
String value() default "";
/**
* Bean 名称,默认为类名首字母小写
*/
String value() default "";
}

View File

@ -1,19 +1,22 @@
package com.imyeyu.inject.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 父级控制反转通常用于父级存在控制反转子类时使用父级可以继续使用此注解
* 配置类注解
*
* @author 夜雨
* @version 2023-02-28 11:42
*/
@Target({ElementType.TYPE})
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SuperIOC {
@Component
public @interface Configuration {
/**
* Bean 名称默认为类名首字母小写
*/
String value() default "";
}

View File

@ -1,26 +1,22 @@
package com.imyeyu.inject.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 控制反转(控制器),注解在类上后该类将被 TimiInject 实例化并控制反转,后续执行注入
* 控制器组件注解
*
* @author 夜雨
* @version 2022-03-04 23:16
*/
@Target({ElementType.TYPE})
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller {
/**
* 控制器名称,非空时注入需要明确执行
*
* @return 控制器名称
*/
String value() default "";
/**
* Bean 名称,默认为类名首字母小写
*/
String value() default "";
}

View File

@ -1,22 +0,0 @@
package com.imyeyu.inject.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* <p>异步控制反转,控制反转的类或 {@link IOCReturn} 方法使用此注解将会在异步线程中实例化或调用。
* 此注解不影响注入流程。
* <p>此注解对实例化比较耗时的动作有明显的速度提升,有 {@link IOCPriority} 的效果,对实例化耗时较短的动作使用将会适得其反。
* <p><u>注意JavaFX 组件不可用,因为它只有一个 UI 线程</u>
*
* @author 夜雨
* @version 2022-10-10 17:05
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface IOCAsync {
}

View File

@ -1,27 +0,0 @@
package com.imyeyu.inject.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 控制反转类使用此注解时将会优先执行实例化,对不存在异步控制反转的类使用不会提升效率。
* <p>使用此注解后,{@link InvokeForInjected} 的方法也会优先于其他类触发
*
* @author 夜雨
* @version 2022-10-10 23:17
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface IOCPriority {
/**
* 优先级,默认所有使用此注解的类都会往前挤,除非使用指定等级,数值小的优先执行
*
* @return 优先等级,默认 {@link Integer#MAX_VALUE} / 2非此注解的类的优先级为 {@link Integer#MAX_VALUE}
*/
int value() default Integer.MAX_VALUE / 2;
}

View File

@ -1,30 +0,0 @@
package com.imyeyu.inject.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 控制反转(方法返回对象),注解在方法上,该方法所在类也必须控制反转到 TimiInject 中,经过此注解的方法返回值也会控制反转到 TimiInject 中并后续执行注入
*
* <p><b><u>注意:注解参数为空时 {@link Inject} 注入对象的变量名需要和此方法名一致</u></b></p>
*
* @author 夜雨
* @version 2022-03-04 23:16
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface IOCReturn {
/**
* 对象名称,非空时注入需要明确指定
*
* <p><b><u>为空时注入对象的变量名需要和此方法名一致</u></b></p>
*
* @return 对象名称
*/
String value() default "";
}

View File

@ -0,0 +1,21 @@
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;
/**
* 引入外部配置类或模块
*
* @author 夜雨
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Import {
/**
* 要引入的配置类或模块类
*/
Class<?>[] value();
}

View File

@ -6,25 +6,11 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 注入,使用在类变量上,该变量的类需要在 TimiInject 控制反转中才能执行对象注入
*
* <p>注意
* <ul>
* <li><u>如果该类使用 {@link StaticInject} 静态注入,此变量必须为静态变量</u></li>
* <li><u>如果是 {@link IOCReturn} 控制反转的对象,并且控制反转时没有指定名称,此变量名称需要和控制反转方法名一致</u></li>
* </ul>
* 标记用于依赖注入的构造器
*
* @author 夜雨
* @version 2022-03-04 23:15
*/
@Target({ElementType.FIELD})
@Target(ElementType.CONSTRUCTOR)
@Retention(RetentionPolicy.RUNTIME)
public @interface Inject {
/**
* 注入名称,控制反转明确指定名称时此属性也需要明确指定
*
* @return 注入名称
*/
String value() default "";
}

View File

@ -1,43 +0,0 @@
package com.imyeyu.inject.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 此注解使用在方法上TimiInject 完成注入后自动执行,对 {@link StaticInject} 和 {@link SuperInject} 有效,方法访问权限无限制。
* <p>注意:{@link StaticInject} 注解类的方法使用此注解时,该方法也必须是静态方法
*
* <pre>
* &#064;Component
* public class Demo {
*
* &#064;InvokeForInjected
* public void hello() {
* System.out.println("hello");
* }
*
* &#064;InvokeForInjected
* public void timiInject() {
* System.out.println("timi-inject");
* }
* }
* </pre>
*
* @author 夜雨
* @version 2022-09-23 22:25
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface InvokeForInjected {
/**
* 执行顺序,默认 0数值小的先执行
*
* @return 执行顺序
*/
int value() default 0;
}

View File

@ -0,0 +1,16 @@
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 初始化后回调方法注解
*
* @author 夜雨
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface PostConstruct {
}

View File

@ -0,0 +1,16 @@
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多实现时
*
* @author 夜雨
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Primary {
}

View File

@ -0,0 +1,21 @@
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;
/**
* 按名称限定依赖注入
*
* @author 夜雨
*/
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface Qualifier {
/**
* Bean 名称
*/
String value();
}

View File

@ -1,26 +1,22 @@
package com.imyeyu.inject.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 控制反转(资源),注解在类上后该类将被 TimiInject 实例化并控制反转托管,后续执行注入
* 资源层组件注解
*
* @author 夜雨
* @version 2022-03-04 23:16
*/
@Target({ElementType.TYPE})
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Resources {
/**
* 资源名称,非空时注入需要明确执行
*
* @return 资源名称
*/
String value() default "";
/**
* Bean 名称,默认为类名首字母小写
*/
String value() default "";
}

View File

@ -0,0 +1,21 @@
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 作用域注解
*
* @author 夜雨
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Scope {
/**
* 作用域类型
*/
ScopeType value() default ScopeType.SINGLETON;
}

View File

@ -0,0 +1,19 @@
package com.imyeyu.inject.annotation;
/**
* Bean 作用域类型
*
* @author 夜雨
*/
public enum ScopeType {
/**
* 单例模式(默认)
*/
SINGLETON,
/**
* 原型模式(每次获取创建新实例)
*/
PROTOTYPE
}

View File

@ -1,26 +1,22 @@
package com.imyeyu.inject.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 控制反转(服务),注解在类上后该类将被 TimiInject 实例化并控制反转托管,后续执行注入
* 服务层组件注解
*
* @author 夜雨
* @version 2022-03-04 23:16
*/
@Target({ElementType.TYPE})
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Service {
/**
* 服务名称,非空时注入需要明确执行
*
* @return 服务名称
*/
String value() default "";
/**
* Bean 名称,默认为类名首字母小写
*/
String value() default "";
}

View File

@ -1,27 +0,0 @@
package com.imyeyu.inject.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 静态注入,此类的<u>静态属性</u>将会在控制反转完成后进行注入,<u>此注解的类不会控制反转实例到 TimiInject 中</u>
* <ul>
* <li>{@link InvokeForInjected} 对本注解有效,但必须是静态方法</li>
* </ul>
* <pre>
* &#064;InvokeForInjected
* public static void injected() {
* }
* </pre>
*
* @author 夜雨
* @version 2022-06-24 11:29
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface StaticInject {
}

View File

@ -1,21 +0,0 @@
package com.imyeyu.inject.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 父级注入,用在控制反转的类上,对控制反转的对象父级的变量也执行注入对象,父级也可以继续使用此注解
*
* <p>*对 {@link StaticInject} 无效
*
* @author 夜雨
* @version 2022-03-04 23:16
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SuperInject {
}

View File

@ -1,29 +1,21 @@
package com.imyeyu.inject.annotation;
import com.imyeyu.inject.TimiInject;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* TimiInject 注入程序,{@link TimiInject} 核心将基于此注解类的位置(或注解参数)进行包扫描和控制反转
* 标记应用入口类并指定组件扫描路径
*
* @author 夜雨
* @version 2022-03-04 23:32
*/
@Target({ElementType.TYPE})
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TimiInjectApplication {
/**
* 扫描包位置com.imyeyu.inject此包下的所有类将检测控制反转和注入留空则使用注解的类所在包作为扫描位置。
* <p><u>TimiInject 的所有注解在此包扫描范围内有效</u></p>
*
* @return 扫描包位置
*/
String[] value() default {};
/**
* 组件扫描的包路径
*/
String[] value() default {};
}

View File

@ -1,26 +1,22 @@
package com.imyeyu.inject.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 控制反转(工具),注解在类上后该类将被 TimiInject 实例化并控制反转,后续执行注入
* 工具类组件注解
*
* @author 夜雨
* @version 2023-05-18 00:36
*/
@Target({ElementType.TYPE})
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Util {
/**
* 工具名称,非空时注入需要明确执行
*
* @return 工具名称
*/
String value() default "";
}
/**
* Bean 名称,默认为类名首字母小写
*/
String value() default "";
}

View File

@ -1,2 +0,0 @@
/** 控制反转和注入注解 */
package com.imyeyu.inject.annotation;

View File

@ -1,2 +0,0 @@
/** 主程序 */
package com.imyeyu.inject;