507. Java 反射 - 在 BeanFactory 中实现依赖注入

文章目录

  • [507. Java 反射 - 在 BeanFactory 中实现依赖注入](#507. Java 反射 - 在 BeanFactory 中实现依赖注入)
    • [1. 什么是依赖注入?](#1. 什么是依赖注入?)
    • [2. 定义 @Inject 注解](#2. 定义 @Inject 注解)
    • [3. 示例:应用类依赖 DBService](#3. 示例:应用类依赖 DBService)
    • [4. BeanFactory 的依赖注入逻辑](#4. BeanFactory 的依赖注入逻辑)
    • [5. 使用效果](#5. 使用效果)
    • [6. 要点 🎓](#6. 要点 🎓)

507. Java 反射 - 在 BeanFactory 中实现依赖注入

1. 什么是依赖注入?

在企业应用中,类往往不是孤立的,它们依赖于其他服务。例如:

  • MyApplication 依赖于 DBService。
  • 一个业务逻辑类可能依赖多个工具类(如日志、消息队列、配置服务)。

👉 如果由开发者手动 new 出这些依赖对象,代码会非常耦合 ,难以维护和测试。

👉 依赖注入 (DI) 的思想就是:对象不自己创建依赖,而是由 容器/工厂 负责初始化并注入。

这就是 Spring 框架的核心思想之一 ,我们这里通过 BeanFactory 实现一个简化版。


2. 定义 @Inject 注解

我们先定义一个标记注解,用来告诉工厂哪些字段需要被注入:

java 复制代码
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface Inject {
}

这样工厂在扫描类时,就知道该字段需要自动赋值。


3. 示例:应用类依赖 DBService

java 复制代码
@Singleton
public class MyApplication {
    @Inject
    private DBService dbService;

    public boolean isDBServiceSet() {
        return dbService != null;
    }
}

说明:

  • MyApplication 标记为 单例。
  • dbService 字段使用 @Inject 标记,意味着 由工厂负责赋值。
  • 应用启动时,工厂会帮我们把 DBService 实例注入到 dbService 中。

4. BeanFactory 的依赖注入逻辑

在 instantiateBeanClass() 方法中,添加了对字段的扫描和赋值逻辑:

java 复制代码
private <T> T instantiateBeanClass(Class<T> beanClass, Object[] arguments)
        throws NoSuchMethodException, InstantiationException,
        IllegalAccessException, InvocationTargetException {

    // 1. 通过构造器创建 bean 实例
    Class<?>[] argumentsClasses =
            Arrays.stream(arguments).map(Object::getClass).toArray(Class<?>[]::new);
    Constructor<T> beanConstructor = beanClass.getConstructor(argumentsClasses);
    T bean = beanConstructor.newInstance(arguments);

    // 2. 获取所有字段,筛选出带有 @Inject 的字段
    Field[] injectableFields = Arrays.stream(beanClass.getDeclaredFields())
            .filter(field -> field.isAnnotationPresent(Inject.class))
            .toArray(Field[]::new);

    // 3. 为每个需要注入的字段创建实例并赋值
    for (Field injectableField : injectableFields) {
        Class<?> fieldClass = injectableField.getType();
        Object fieldValue = BeanFactory.INSTANCE.getInstanceOf(fieldClass);

        injectableField.setAccessible(true);  // 无视 private 修饰符
        injectableField.set(bean, fieldValue);
    }
    return bean;
}

5. 使用效果

java 复制代码
void main() {
    BeanFactory beanFactory = BeanFactory.INSTANCE;

    MyApplication app = beanFactory.getInstanceOf(MyApplication.class);
    IO.println("App has been created with a DBService: " + app.isDBServiceSet());
}

运行结果:

java 复制代码
App has been created with a DBService: true

说明:

  • BeanFactory 自动创建了 DBService 实例。
  • 通过 @Inject,它被赋值到 MyApplication 的 dbService 字段。
  • 我们无需手动 new DBService(),解耦了应用类和依赖对象的关系。

6. 要点 🎓

  • 解耦 :MyApplication 不关心 DBService 如何创建,只关心它能否被注入。
  • 自动化 :以后要替换 DBService 实现,只需修改工厂逻辑或注解,不影响应用类。
  • 对比 Spring :Spring 的 @Autowired 与我们自定义的 @Inject 类似,只是功能更强大(支持构造函数注入、方法注入、Qualifier 等)。
  • 局限性 :目前的实现只支持 字段注入,不支持构造函数或方法注入,但已经能展示 DI 的核心思想。
相关推荐
LHICOD10 分钟前
Linux-2
java·linux·后端·jar·vmware·svm·vm
香蕉大香蕉(06敬畏市场)11 分钟前
MySQL 单点详解(03)
java·数据库·mysql·面试·八股·大厂实习
4ever.ov015 分钟前
精学C++第一站 从命名空间到引用:C++入门核心语法全梳理(13000字长文)
开发语言·c++·clion
小杨互联网15 分钟前
Go 现代 CLI 工具离线镜像合集
开发语言·后端·golang·开源·go·命令行
揽星河..23 分钟前
【C语言】数组
c语言·开发语言·算法
FRing0326 分钟前
JavaScript入门到精通三十三(异常处理、call方法、apply方法、bind方法、防抖、节流)
开发语言·前端·javascript
呆呆敲代码的小Y31 分钟前
【实战精选面试题】C# 面试题详细解答,按难易程度排序
android·java·游戏·c#·游戏开发·unity面试题·c#面试题
geovindu32 分钟前
rust: Semaphore Pattern
开发语言·后端·设计模式·rust·代理模式·企业级信号量模式·同步型模式
水无痕simon1 小时前
3、工程重构
java·spring cloud·重构
阿里云基础软件1 小时前
Agent 任务提速 10.71%,内存占用降低 21.60%|Alibaba Cloud Linux 4 重大发布
java·linux·人工智能·操作系统·agent