Java DI依赖案例怎么注入实现

wen java案例 30

深入解析Java DI依赖注入:实战案例与多种实现方式

📖 目录导读

  • 什么是依赖注入(DI)?为什么需要它?

    Java DI依赖案例怎么注入实现

  • Java DI的三种核心注入方式(含代码案例)

  • 主流框架实现对比:Spring、Guice、CDI

  • 手写一个简易DI容器(模拟注入原理)

  • 常见问题问答(Q&A)

  • 总结与最佳实践


什么是依赖注入(DI)?为什么需要它?

依赖注入(Dependency Injection,简称DI)是控制反转(IoC)的一种实现方式,核心思想是:将对象的依赖关系从“内部创建”转移到“外部注入”,简单说,就是对象不再自己new依赖类,而是由容器负责创建并传给对象。

为什么需要DI?

  • 解耦:类不再直接依赖具体实现,而是依赖接口。
  • 可测试性:方便Mock替换真实依赖。
  • 灵活性:运行时可以切换不同实现。

💡 举个反例:传统代码中UserService直接new UserDao(),耦合性强;引入DI后,UserService只需声明需要UserDao接口,具体实现由容器注入。


Java DI的三种核心注入方式(含代码案例)

1 构造器注入(推荐)

public class UserService {
    private final UserDao userDao;
    // 通过构造器注入依赖
    public UserService(UserDao userDao) {
        this.userDao = userDao;
    }
}
// 使用(模拟容器)
UserDao dao = new UserDaoImpl();
UserService service = new UserService(dao);

2 Setter注入

public class OrderService {
    private PaymentService paymentService;
    @Autowired  // Spring中通过注解注入
    public void setPaymentService(PaymentService paymentService) {
        this.paymentService = paymentService;
    }
}

3 接口注入(较少用)

public interface ServiceInjector {
    void inject(OrderService service);
}
public class OrderService {
    public void init(ServiceInjector injector) {
        injector.inject(this);
    }
}

对比表格: | 注入方式 | 优点 | 缺点 | 推荐场景 | |---------|------|------|----------| | 构造器 | 不可变、明确依赖 | 参数过多时冗长 | 必须依赖 | | Setter | 可选依赖、灵活 | 可变性、易漏注入 | 非必须依赖 | | 接口 | 标准化 | 侵入性强 | 框架底层 |


主流框架实现对比:Spring、Guice、CDI

Spring DI(最常用)

@Component
public class NotificationService {
    @Autowired
    private EmailSender emailSender;  // Spring自动注入
    // 或构造器注入(推荐)
    public NotificationService(EmailSender emailSender) {
        this.emailSender = emailSender;
    }
}

配置:使用@Configuration+@Bean或XML。

Google Guice

public class BillingModule extends AbstractModule {
    @Override
    protected void configure() {
        bind(TransactionLog.class).to(DatabaseTransactionLog.class);
    }
}
Injector injector = Guice.createInjector(new BillingModule());
BillingService billingService = injector.getInstance(BillingService.class);

CDI(Java EE标准)

@Inject
private UserRepository userRepo;  // 除Spring外另一套规范

手写一个简易DI容器(模拟注入原理)

为深入理解,我们实现一个简单DI框架:

public class SimpleContainer {
    private Map<Class<?>, Object> instances = new HashMap<>();
    public <T> void register(Class<T> clazz, T instance) {
        instances.put(clazz, instance);
    }
    public <T> T resolve(Class<T> clazz) {
        // 1. 获取类所有构造器
        Constructor<?>[] constructors = clazz.getDeclaredConstructors();
        for (Constructor<?> constructor : constructors) {
            if (constructor.getParameterCount() == 0) continue;
            // 2. 递归解析参数依赖
            Class<?>[] paramTypes = constructor.getParameterTypes();
            Object[] params = Arrays.stream(paramTypes)
                    .map(type -> instances.get(type) != null ? instances.get(type) : resolve(type))
                    .toArray();
            try {
                return clazz.cast(constructor.newInstance(params));
            } catch (Exception e) { throw new RuntimeException(e); }
        }
        // 3. 无参构造直接创建
        return clazz.cast(clazz.getDeclaredConstructor().newInstance());
    }
}
// 使用示例
SimpleContainer container = new SimpleContainer();
container.register(UserDao.class, new UserDaoImpl());
UserService userService = container.resolve(UserService.class);  // 自动注入

这个例子展示了:容器如何自动查找依赖、递归创建对象,与Spring的核心逻辑类似。


常见问题问答(Q&A)

Q1:构造器注入和字段注入(@Autowired直接写在字段上)哪个好? A:构造器注入更推荐,因为:

  • 保证依赖不可变(final)
  • 便于测试(无需反射)
  • 明确依赖关系,避免循环引用隐患

Q2:Spring中@Autowired和@Resource区别? A:

  • @Autowired:按类型注入,若多个类型匹配需配合@Qualifier
  • @Resource:按名称注入(默认取字段名),属于Java EE标准

Q3:DI容器会不会带来性能问题? A:生产环境几乎无影响,依赖注入仅在启动时解析一次,运行时性能与手动创建无差异,Spring Boot启动虽慢,但非DI本身导致。

Q4:能否不用框架实现DI? A:完全可以,例如上述手写容器,或使用工厂模式手动实例化,但规模变大后,框架管理生命周期、AOP等优势明显。

Q5:循环依赖如何解决? A:Spring通过三级缓存(早期暴露半成品对象)解决构造器注入外的循环依赖,但构造器注入的循环依赖无法自动解决,需重构代码。


总结与最佳实践

  • 优先使用构造器注入,避免字段注入产生不可测试的代码。
  • 面向接口编程:注入时依赖接口而非具体类。
  • 合理配置作用域:Spring中单例(默认)可复用,原型(Prototype)每次获取新实例。
  • 使用框架但不迷信框架:理解底层原理(如手写容器),遇到问题能快速定位。
  • 注意依赖数量:当一个类有超过5个依赖时,考虑是否违反单一职责原则。

依赖注入让Java代码更优雅、可维护,掌握其核心实现方式,无论是Spring还是其他框架,都能游刃有余。

抱歉,评论功能暂时关闭!