From 09705697387767e7369ebf02c2aa595d6e0a392f Mon Sep 17 00:00:00 2001 From: Timi Date: Mon, 14 Jul 2025 11:57:11 +0800 Subject: [PATCH] Initial project --- .gitignore | 128 +-- .idea/.gitignore | 3 + .idea/encodings.xml | 7 + .idea/misc.xml | 14 + .idea/uiDesigner.xml | 124 +++ .idea/vcs.xml | 6 + pom.xml | 30 + .../java/com/imyeyu/inject/InjectApp.java | 62 ++ .../java/com/imyeyu/inject/InjectFactory.java | 50 ++ .../java/com/imyeyu/inject/TimiInject.java | 765 ++++++++++++++++++ .../com/imyeyu/inject/annotation/Bean.java | 26 + .../imyeyu/inject/annotation/Component.java | 26 + .../imyeyu/inject/annotation/Controller.java | 26 + .../imyeyu/inject/annotation/IOCAsync.java | 22 + .../imyeyu/inject/annotation/IOCPriority.java | 27 + .../imyeyu/inject/annotation/IOCReturn.java | 30 + .../com/imyeyu/inject/annotation/Inject.java | 30 + .../inject/annotation/InvokeForInjected.java | 43 + .../imyeyu/inject/annotation/Resources.java | 26 + .../com/imyeyu/inject/annotation/Service.java | 26 + .../inject/annotation/StaticInject.java | 27 + .../imyeyu/inject/annotation/SuperIOC.java | 19 + .../imyeyu/inject/annotation/SuperInject.java | 21 + .../annotation/TimiInjectApplication.java | 29 + .../com/imyeyu/inject/annotation/Util.java | 26 + .../inject/annotation/package-info.java | 2 + .../java/com/imyeyu/inject/package-info.java | 2 + src/main/resources/defBanner.txt | 9 + src/main/resources/logback.xml | 29 + 29 files changed, 1541 insertions(+), 94 deletions(-) create mode 100644 .idea/.gitignore create mode 100644 .idea/encodings.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/uiDesigner.xml create mode 100644 .idea/vcs.xml create mode 100644 pom.xml create mode 100644 src/main/java/com/imyeyu/inject/InjectApp.java create mode 100644 src/main/java/com/imyeyu/inject/InjectFactory.java create mode 100644 src/main/java/com/imyeyu/inject/TimiInject.java create mode 100644 src/main/java/com/imyeyu/inject/annotation/Bean.java create mode 100644 src/main/java/com/imyeyu/inject/annotation/Component.java create mode 100644 src/main/java/com/imyeyu/inject/annotation/Controller.java create mode 100644 src/main/java/com/imyeyu/inject/annotation/IOCAsync.java create mode 100644 src/main/java/com/imyeyu/inject/annotation/IOCPriority.java create mode 100644 src/main/java/com/imyeyu/inject/annotation/IOCReturn.java create mode 100644 src/main/java/com/imyeyu/inject/annotation/Inject.java create mode 100644 src/main/java/com/imyeyu/inject/annotation/InvokeForInjected.java create mode 100644 src/main/java/com/imyeyu/inject/annotation/Resources.java create mode 100644 src/main/java/com/imyeyu/inject/annotation/Service.java create mode 100644 src/main/java/com/imyeyu/inject/annotation/StaticInject.java create mode 100644 src/main/java/com/imyeyu/inject/annotation/SuperIOC.java create mode 100644 src/main/java/com/imyeyu/inject/annotation/SuperInject.java create mode 100644 src/main/java/com/imyeyu/inject/annotation/TimiInjectApplication.java create mode 100644 src/main/java/com/imyeyu/inject/annotation/Util.java create mode 100644 src/main/java/com/imyeyu/inject/annotation/package-info.java create mode 100644 src/main/java/com/imyeyu/inject/package-info.java create mode 100644 src/main/resources/defBanner.txt create mode 100644 src/main/resources/logback.xml diff --git a/.gitignore b/.gitignore index c6d98d1..5ff6309 100644 --- a/.gitignore +++ b/.gitignore @@ -1,98 +1,38 @@ -# ---> JetBrains -# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider -# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 - -# User-specific stuff -.idea/**/workspace.xml -.idea/**/tasks.xml -.idea/**/usage.statistics.xml -.idea/**/dictionaries -.idea/**/shelf - -# AWS User-specific -.idea/**/aws.xml - -# Generated files -.idea/**/contentModel.xml - -# Sensitive or high-churn files -.idea/**/dataSources/ -.idea/**/dataSources.ids -.idea/**/dataSources.local.xml -.idea/**/sqlDataSources.xml -.idea/**/dynamic.xml -.idea/**/uiDesigner.xml -.idea/**/dbnavigator.xml - -# Gradle -.idea/**/gradle.xml -.idea/**/libraries - -# Gradle and Maven with auto-import -# When using Gradle or Maven with auto-import, you should exclude module files, -# since they will be recreated, and may cause churn. Uncomment if using -# auto-import. -# .idea/artifacts -# .idea/compiler.xml -# .idea/jarRepositories.xml -# .idea/modules.xml -# .idea/*.iml -# .idea/modules -# *.iml -# *.ipr - -# CMake -cmake-build-*/ - -# Mongo Explorer plugin -.idea/**/mongoSettings.xml - -# File-based project format -*.iws - -# IntelliJ -out/ - -# mpeltonen/sbt-idea plugin -.idea_modules/ - -# JIRA plugin -atlassian-ide-plugin.xml - -# Cursive Clojure plugin -.idea/replstate.xml - -# SonarLint plugin -.idea/sonarlint/ - -# Crashlytics plugin (for Android Studio and IntelliJ) -com_crashlytics_export_strings.xml -crashlytics.properties -crashlytics-build.properties -fabric.properties - -# Editor-based Rest Client -.idea/httpRequests - -# Android studio 3.1+ serialized cache file -.idea/caches/build_file_checksums.ser - -# ---> Maven target/ -pom.xml.tag -pom.xml.releaseBackup -pom.xml.versionsBackup -pom.xml.next -release.properties -dependency-reduced-pom.xml -buildNumber.properties -.mvn/timing.properties -# https://github.com/takari/maven-wrapper#usage-without-binary-jar -.mvn/wrapper/maven-wrapper.jar +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ -# Eclipse m2e generated files -# Eclipse Core -.project -# JDT-specific (Eclipse Java Development Tools) +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +*.iws +*.iml +*.ipr + +### Eclipse ### +.apt_generated .classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..aa00ffa --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..fdc35ea --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,14 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/uiDesigner.xml b/.idea/uiDesigner.xml new file mode 100644 index 0000000..2b63946 --- /dev/null +++ b/.idea/uiDesigner.xml @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..63f4b5a --- /dev/null +++ b/pom.xml @@ -0,0 +1,30 @@ + + + 4.0.0 + + com.imyeyu.inject + timi-inject + 0.0.1 + + + 21 + 21 + UTF-8 + true + + + + + com.imyeyu.io + timi-io + 0.0.1 + + + ch.qos.logback + logback-classic + 1.5.13 + + + diff --git a/src/main/java/com/imyeyu/inject/InjectApp.java b/src/main/java/com/imyeyu/inject/InjectApp.java new file mode 100644 index 0000000..e2c9119 --- /dev/null +++ b/src/main/java/com/imyeyu/inject/InjectApp.java @@ -0,0 +1,62 @@ +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; + + boolean enableBanner = true; + + /** 全局注入后监听,包括静态注入 */ + final List> 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; + } + + /** + * 添加全局注入后监听 + * + * @param listener 监听器 + */ + public synchronized void addAfterInjectListener(CallbackArg listener) { + afterInjectCallbackList.add(listener); + } + + /** + * 移除全局注入后监听 + * + * @param listener 监听器 + */ + public synchronized void removeAfterInjectListener(CallbackArg listener) { + afterInjectCallbackList.remove(listener); + } + + public boolean isEnableBanner() { + return enableBanner; + } + + public void setEnableBanner(boolean enableBanner) { + this.enableBanner = enableBanner; + } +} diff --git a/src/main/java/com/imyeyu/inject/InjectFactory.java b/src/main/java/com/imyeyu/inject/InjectFactory.java new file mode 100644 index 0000000..6828b3d --- /dev/null +++ b/src/main/java/com/imyeyu/inject/InjectFactory.java @@ -0,0 +1,50 @@ +package com.imyeyu.inject; + +/** + * 注入工厂,顶层接口 + * + * @author 夜雨 + * @version 2022-03-04 23:18 + */ +interface InjectFactory { + + /** + * 根据 ID 获取反转对象 + * + * @param id ID + * @return 反转对象 + */ + T di(String id, Class clazz); + + /** + * 根据类型获取反转对象 + * + * @param classType 类型 + * @return 反转对象 + * @param 对象类型 + */ + T di(Class 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); +} diff --git a/src/main/java/com/imyeyu/inject/TimiInject.java b/src/main/java/com/imyeyu/inject/TimiInject.java new file mode 100644 index 0000000..26fdb31 --- /dev/null +++ b/src/main/java/com/imyeyu/inject/TimiInject.java @@ -0,0 +1,765 @@ +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.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; + +/** + * Timi 控制反转框架 + * + * @author 夜雨 + * @version 2022-03-04 23:14 + */ +public class TimiInject implements InjectFactory { + + /** 日志 */ + private static final Logger log = LoggerFactory.getLogger(TimiInject.class); + + /** 类路径 */ + private final List paths; + + /** 控制反转对象 */ + private final Map objects; + + /** 依赖注入字段集 */ + private final List fields; + + // 注入后调度 Map<对象, Map<方法, 调度顺序>> + private final Map> invokeForInjected; + + /** 控制反转监听 */ + private final Map, List>> iocListeners; + + /** 静态注入类列表 */ + private final List> staticInject; + + /** 控制反转类对象 */ + private final List classes; + + // 异步控制反转参数 + private int iocAsyncRunning = 0; + private boolean hasAsyncIOC = false; + private final Object iocAsyncLock = new Object(); + + private TimiInject(InjectApp app) { + if (TimiJava.isEmpty(app)) { + throw new NullPointerException("empty inject app"); + } + // 初始化 + 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 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; + + 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 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)); + } + + // 完成注入 + 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> 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); + } + } + } + } + } + } + + 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); + } + } + + /** + * 扫描包 + * + * @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 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()); + } + } + + /** + * 扫描本地 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); + } + } + } + } + } + + /** + * 生成类的控制反转封装,如果此类没有标记控制反转则忽略 + * + * @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); + + // 获取反转对象 ID + if (!(isBean || isComponent || isService || isController || isResources || isUtil || isInject)) { + // 非控制反转相关类 + if (isStaticInject) { + // 静态注入 + staticInject.add(clazz); + } + } + + 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); + } + } + + /** + * 实例化控制反转对象 + * + * @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(); + } + + 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); + } + + 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()); + } + } + + // 控制反转方法返回 + objects.putAll(iocReturn(object.getClass(), object)); + // 收集此对象需要注入后调度的方法 + invokeForInjected.put(object, invokeForInjected(object.getClass())); + } + + /** + * 控制反转方法返回对象 + * + * @param clazz 所属类 + * @param object 类对象 + * @return 控制反转对象 + */ + private Map iocReturn(Class clazz, Object object) { + Map 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 invokeForInjected(Class clazz) { + Map 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> list = new ArrayList<>(map.entrySet()); + list.sort(Comparator.comparingDouble(o -> o.getValue().doubleValue())); + + LinkedHashMap result = new LinkedHashMap<>(); + for (Map.Entry 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 di(String id, Class clazz) { + return clazz.cast(objects.get(id)); + } + + /** + * 根据类型获取依赖注入对象 + * + * @param classType 类型 + * @param 控制反转类型 + * @return 控制反转对象 + */ + @Override + public T di(Class 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 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 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 methods = invokeForInjected(object.getClass()); + for (Map.Entry method : methods.entrySet()) { + method.getKey().invoke(object); + } + + long callIOCListenersAt = Time.now(); + // 控制反转监听 + if (iocListeners.get(object.getClass()) != null) { + List> 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 listener) { + iocListeners.computeIfAbsent(clazz, k -> new ArrayList<>()).add(listener); + } + + /** + * 移除控制反转监听 + * + * @param clazz 控制反转类 + * @param listener 监听器 + */ + public synchronized void removeIOCListener(Class clazz, CallbackArg listener) { + iocListeners.computeIfAbsent(clazz, k -> new ArrayList<>()).remove(listener); + } + + /** + * 控制反转类 + * + *

夜雨 创建于 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; + } + } +} diff --git a/src/main/java/com/imyeyu/inject/annotation/Bean.java b/src/main/java/com/imyeyu/inject/annotation/Bean.java new file mode 100644 index 0000000..fd7e991 --- /dev/null +++ b/src/main/java/com/imyeyu/inject/annotation/Bean.java @@ -0,0 +1,26 @@ +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 夜雨 + * @since 2024-07-19 15:48 + */ +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface Bean { + + /** + * 对象名称,非空时注入需要明确执行 + * + * @return 对象名称 + */ + String value() default ""; +} diff --git a/src/main/java/com/imyeyu/inject/annotation/Component.java b/src/main/java/com/imyeyu/inject/annotation/Component.java new file mode 100644 index 0000000..caec677 --- /dev/null +++ b/src/main/java/com/imyeyu/inject/annotation/Component.java @@ -0,0 +1,26 @@ +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}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface Component { + + /** + * 组件名称,非空时注入需要明确执行 + * + * @return 组件名称 + */ + String value() default ""; +} diff --git a/src/main/java/com/imyeyu/inject/annotation/Controller.java b/src/main/java/com/imyeyu/inject/annotation/Controller.java new file mode 100644 index 0000000..f101063 --- /dev/null +++ b/src/main/java/com/imyeyu/inject/annotation/Controller.java @@ -0,0 +1,26 @@ +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}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface Controller { + + /** + * 控制器名称,非空时注入需要明确执行 + * + * @return 控制器名称 + */ + String value() default ""; +} diff --git a/src/main/java/com/imyeyu/inject/annotation/IOCAsync.java b/src/main/java/com/imyeyu/inject/annotation/IOCAsync.java new file mode 100644 index 0000000..eaa9452 --- /dev/null +++ b/src/main/java/com/imyeyu/inject/annotation/IOCAsync.java @@ -0,0 +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; + +/** + *

异步控制反转,控制反转的类或 {@link IOCReturn} 方法使用此注解将会在异步线程中实例化或调用。 + * 此注解不影响注入流程。 + *

此注解对实例化比较耗时的动作有明显的速度提升,有 {@link IOCPriority} 的效果,对实例化耗时较短的动作使用将会适得其反。 + *

注意:JavaFX 组件不可用,因为它只有一个 UI 线程 + * + * @author 夜雨 + * @version 2022-10-10 17:05 + */ +@Target({ElementType.TYPE, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface IOCAsync { +} diff --git a/src/main/java/com/imyeyu/inject/annotation/IOCPriority.java b/src/main/java/com/imyeyu/inject/annotation/IOCPriority.java new file mode 100644 index 0000000..ffe3fa5 --- /dev/null +++ b/src/main/java/com/imyeyu/inject/annotation/IOCPriority.java @@ -0,0 +1,27 @@ +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; + +/** + * 控制反转类使用此注解时将会优先执行实例化,对不存在异步控制反转的类使用不会提升效率。 + *

使用此注解后,{@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; +} diff --git a/src/main/java/com/imyeyu/inject/annotation/IOCReturn.java b/src/main/java/com/imyeyu/inject/annotation/IOCReturn.java new file mode 100644 index 0000000..df40411 --- /dev/null +++ b/src/main/java/com/imyeyu/inject/annotation/IOCReturn.java @@ -0,0 +1,30 @@ +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 中并后续执行注入 + * + *

注意:注解参数为空时 {@link Inject} 注入对象的变量名需要和此方法名一致

+ * + * @author 夜雨 + * @version 2022-03-04 23:16 + */ +@Target({ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface IOCReturn { + + /** + * 对象名称,非空时注入需要明确指定 + * + *

为空时注入对象的变量名需要和此方法名一致

+ * + * @return 对象名称 + */ + String value() default ""; +} diff --git a/src/main/java/com/imyeyu/inject/annotation/Inject.java b/src/main/java/com/imyeyu/inject/annotation/Inject.java new file mode 100644 index 0000000..2f00345 --- /dev/null +++ b/src/main/java/com/imyeyu/inject/annotation/Inject.java @@ -0,0 +1,30 @@ +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; + +/** + * 注入,使用在类变量上,该变量的类需要在 TimiInject 控制反转中才能执行对象注入 + * + *

注意 + *

    + *
  • 如果该类使用 {@link StaticInject} 静态注入,此变量必须为静态变量
  • + *
  • 如果是 {@link IOCReturn} 控制反转的对象,并且控制反转时没有指定名称,此变量名称需要和控制反转方法名一致
  • + *
+ * + * @author 夜雨 + * @version 2022-03-04 23:15 + */ +@Target({ElementType.FIELD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface Inject { + + /** + * 注入名称,控制反转明确指定名称时此属性也需要明确指定 + * + * @return 注入名称 + */ + String value() default ""; +} diff --git a/src/main/java/com/imyeyu/inject/annotation/InvokeForInjected.java b/src/main/java/com/imyeyu/inject/annotation/InvokeForInjected.java new file mode 100644 index 0000000..4227cdb --- /dev/null +++ b/src/main/java/com/imyeyu/inject/annotation/InvokeForInjected.java @@ -0,0 +1,43 @@ +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} 有效,方法访问权限无限制。 + *

注意:{@link StaticInject} 注解类的方法使用此注解时,该方法也必须是静态方法 + * + *

+ *     @Component
+ *     public class Demo {
+ *
+ *         @InvokeForInjected
+ *         public void hello() {
+ *             System.out.println("hello");
+ *         }
+ *
+ *         @InvokeForInjected
+ *         public void timiInject() {
+ *             System.out.println("timi-inject");
+ *         }
+ *     }
+ * 
+ * + * @author 夜雨 + * @version 2022-09-23 22:25 + */ +@Target({ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface InvokeForInjected { + + /** + * 执行顺序,默认 0,数值小的先执行 + * + * @return 执行顺序 + */ + int value() default 0; +} diff --git a/src/main/java/com/imyeyu/inject/annotation/Resources.java b/src/main/java/com/imyeyu/inject/annotation/Resources.java new file mode 100644 index 0000000..07369fd --- /dev/null +++ b/src/main/java/com/imyeyu/inject/annotation/Resources.java @@ -0,0 +1,26 @@ +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}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface Resources { + + /** + * 资源名称,非空时注入需要明确执行 + * + * @return 资源名称 + */ + String value() default ""; +} diff --git a/src/main/java/com/imyeyu/inject/annotation/Service.java b/src/main/java/com/imyeyu/inject/annotation/Service.java new file mode 100644 index 0000000..a8b2e0a --- /dev/null +++ b/src/main/java/com/imyeyu/inject/annotation/Service.java @@ -0,0 +1,26 @@ +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}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface Service { + + /** + * 服务名称,非空时注入需要明确执行 + * + * @return 服务名称 + */ + String value() default ""; +} diff --git a/src/main/java/com/imyeyu/inject/annotation/StaticInject.java b/src/main/java/com/imyeyu/inject/annotation/StaticInject.java new file mode 100644 index 0000000..4cf489b --- /dev/null +++ b/src/main/java/com/imyeyu/inject/annotation/StaticInject.java @@ -0,0 +1,27 @@ +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 InvokeForInjected} 对本注解有效,但必须是静态方法
  • + *
+ *
+ *     @InvokeForInjected
+ *     public static void injected() {
+ *     }
+ * 
+ * + * @author 夜雨 + * @version 2022-06-24 11:29 + */ +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface StaticInject { +} diff --git a/src/main/java/com/imyeyu/inject/annotation/SuperIOC.java b/src/main/java/com/imyeyu/inject/annotation/SuperIOC.java new file mode 100644 index 0000000..206872c --- /dev/null +++ b/src/main/java/com/imyeyu/inject/annotation/SuperIOC.java @@ -0,0 +1,19 @@ +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}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface SuperIOC { +} diff --git a/src/main/java/com/imyeyu/inject/annotation/SuperInject.java b/src/main/java/com/imyeyu/inject/annotation/SuperInject.java new file mode 100644 index 0000000..a0ffb73 --- /dev/null +++ b/src/main/java/com/imyeyu/inject/annotation/SuperInject.java @@ -0,0 +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; + +/** + * 父级注入,用在控制反转的类上,对控制反转的对象父级的变量也执行注入对象,父级也可以继续使用此注解 + * + *

*对 {@link StaticInject} 无效 + * + * @author 夜雨 + * @version 2022-03-04 23:16 + */ +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface SuperInject { +} diff --git a/src/main/java/com/imyeyu/inject/annotation/TimiInjectApplication.java b/src/main/java/com/imyeyu/inject/annotation/TimiInjectApplication.java new file mode 100644 index 0000000..8723aa1 --- /dev/null +++ b/src/main/java/com/imyeyu/inject/annotation/TimiInjectApplication.java @@ -0,0 +1,29 @@ +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}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface TimiInjectApplication { + + /** + * 扫描包位置(如:com.imyeyu.inject),此包下的所有类将检测控制反转和注入,留空则使用注解的类所在包作为扫描位置。 + *

TimiInject 的所有注解在此包扫描范围内有效

+ * + * @return 扫描包位置 + */ + String[] value() default {}; +} diff --git a/src/main/java/com/imyeyu/inject/annotation/Util.java b/src/main/java/com/imyeyu/inject/annotation/Util.java new file mode 100644 index 0000000..612b188 --- /dev/null +++ b/src/main/java/com/imyeyu/inject/annotation/Util.java @@ -0,0 +1,26 @@ +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}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface Util { + + /** + * 工具名称,非空时注入需要明确执行 + * + * @return 工具名称 + */ + String value() default ""; +} \ No newline at end of file diff --git a/src/main/java/com/imyeyu/inject/annotation/package-info.java b/src/main/java/com/imyeyu/inject/annotation/package-info.java new file mode 100644 index 0000000..ee93d1e --- /dev/null +++ b/src/main/java/com/imyeyu/inject/annotation/package-info.java @@ -0,0 +1,2 @@ +/** 控制反转和注入注解 */ +package com.imyeyu.inject.annotation; diff --git a/src/main/java/com/imyeyu/inject/package-info.java b/src/main/java/com/imyeyu/inject/package-info.java new file mode 100644 index 0000000..d29a267 --- /dev/null +++ b/src/main/java/com/imyeyu/inject/package-info.java @@ -0,0 +1,2 @@ +/** 主程序 */ +package com.imyeyu.inject; \ No newline at end of file diff --git a/src/main/resources/defBanner.txt b/src/main/resources/defBanner.txt new file mode 100644 index 0000000..3a3c594 --- /dev/null +++ b/src/main/resources/defBanner.txt @@ -0,0 +1,9 @@ + + _____ _ _ ___ _ _ __ _ _ + |_ _(_)_ __ ___ (_) |_ _|_ __ (_) ___ ___| |_ \ \ \ \ + | | | | '_ ` _ \| | | || '_ \ | |/ _ \/ __| __| \ \ \ \ + | | | | | | | | | | | || | | || | __/ (__| |_ ) ) ) ) + |_| |_|_| |_| |_|_| |___|_| |_|/ |\___|\___|\__| / / / / +---------------------------------|__/---------------/_/_/_/ + +Banner drawn by https://patorjk.com/software/taag for Ivrit diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml new file mode 100644 index 0000000..fb496b6 --- /dev/null +++ b/src/main/resources/logback.xml @@ -0,0 +1,29 @@ + + + + UTF-8 + [%d{HH:mm:ss.SSS}][%-5level][%-54logger{54}] %msg%n + + + INFO + + + + ./logs/debug.log + + UTF-8 + [%d{HH:mm:ss.SSS}][%-5level][%-54logger{54}] %msg%n + + + ./logs/debug/debug.%d{yyyy-MM-dd}.log + 30 + + + INFO + + + + + + +