MeteorCat / 依赖注入第三库

Created Fri, 27 Jun 2025 14:12:18 +0800 Modified Wed, 29 Oct 2025 23:25:00 +0800
1577 Words

如果长期使用 spring 全系列的开发者, 对于 依赖注入(Dependency Injection) 能够很明显体现到:

/**
 * RestApi接口类
 */
@RestController
public class ExampleController {

    private final IService service;

    /**
     * 在构造的时候注入 IService 接口实现
     */
    @Autowired
    public ExampleController(IService service) {
        this.service = service;
    }
}

这种很便利把所需的外部编写服务类注入到类内部, 省下了直接实例化new的过程尽可能把功能做好可控性.

实际工作中发现在项目之中可以随意实例化确实后患无穷, 要么直接用上下文(context)做构建器传递, 要么就直接做依赖注入管理.

但是这种强依赖 spring 在做自己项目集成的时候基本不会考虑而是考虑比较轻量化的方案容易保持项目的简洁, 社区提供的方案如下:

  • Spring: 和 spring 集成绑定最高的依赖注入库
  • Guice: 谷歌推出的轻量级基于模块方式的依赖注入库
  • Dagger/Dagger2: Android 官方推荐的基于 Guice 依赖注入库
  • 其他可以跳过……

这里基本上已经说明了各自所用的场景, 所以最后按轻量化方案采用了 Guice.

如果想尽快上手可以直接引入 spring-core 底层依赖直接使用就行, 毕竟项目规划好实现时间需要尽快搭建好准备对接需求

实际上我基本上对于依赖注入就是把功能句柄注册到全局之中, 起到作为 任务管理器(Task Manager) 作用.

这里基本上引入第三方库, 之后就开始说明一些:

<!-- 第三号库 -->
<dependencies>
    <!-- 依赖注入基础库 -->
    <dependency>
        <groupId>com.google.inject</groupId>
        <artifactId>guice</artifactId>
        <version>{对应版本,目前最新为7.0.0}</version>
    </dependency>

    <!-- 扩展支持 -->
    <dependency>
        <groupId>com.google.inject.extensions</groupId>
        <!-- 官方支持的拓展: 
        ssistedinject, dagger-adapter,grapher, jmx, jndi, persist, spring, testlib or throwingprovider 
        -->
        <artifactId>guice-{extension-name}</artifactId>
        <version>{对应版本,目前最新为7.0.0}</version>
    </dependency>
</dependencies>

需要注意大版本不同各自依赖包名也是会变, 已知官方上面文档说明:

  • 6.0.0: 支持 javax.{inject,servlet,persistence}
  • 7.0.0: 支持 jakarta.{inject,servlet,persistence})

主要问题是后面这些核心注解后续变迁从 javax 包转为 jakarta 管理

官方Wiki 说明了最基础的用法, 主要如果你的项目需要大量实例化和工厂生成的时候才会去考虑使用, 否则仅仅少量实例化对象不需要这种太过复杂的库.

官方里面也说明任何灰使用到 new 的地方就可以利用 @Inject 替代引入:

class RoleService {
    /**
     * 假设我们需要数据库句柄操作
     */
    private Database database;

    /**
     * 构造方法
     */
    RoleService() {
        // 内部初始化, 但是请注意这里没办法做测试单元
        // 而且如果某一天采用 mariadb 数据库, 那么内部又要修改配置
        this.database = new Database("mysql://127.0.0.1:3306/role");
    }
}

所以基于上面就需要采用依赖注入方式让其从外部引入, 我们内部不关心任何配置细节只需要你把类传入进来:

class RoleService {
    private Database database;

    /**
     * 不关系任何配置参数细节, 只要你传入该类对象
     */
    RoleService(Database database) {
        this.database = database;
    }
}

按照上面的说法, 结合 Guice 其实十分简单只需要处理下:

class RoleService {
    private Database database;

    /**
     * 通过 @Inject 声明, 要求 Guice 系统引入
     */
    @Inject
    RoleService(Database database) {
        this.database = database;
    }
}

但是目前是没办法直接从外部引入该工具类的, 引入需要定义全局的模块来声明这些实例化的初始化, 这种就是需要构建 Guice Module 来声明实例化来源:

import com.google.inject.AbstractModule;
import com.google.inject.Provides;

/**
 * 模块自定义, 用于通过模块加载到内部
 */
class RoleModule extends AbstractModule {

    /**
     * 全局声明实例化对象, 等待被 Guice 引入
     */
    @Provides
    static Database provideDatabase() {
        return new Database("mysql://127.0.0.1:3306/role");
    }
}

最后就是构建 Guice 调用句柄:

/**
 * <a href="https://github.com/google/guice/wiki/GettingStarted">官方说明</a>
 */
public final class MyWebServer {
    /**
     * 启动入口
     */
    public static void main(String[] args) {
        Injector injector = Guice.createInjector(
                // 假设引入所有自己定义的实例化模块
                new RequestLoggingModule(),
                new RequestHandlerModule(),
                new AuthenticationModule(),
                new DatabaseModule(),
                new RoleModule()
                // 其他定义模块.....
        );

        // 之后就是把注入对象对应给指定实例注入启动
        // 这里其实就是以上的模块全部注入到这个主类, 后续内部可以直接加载到模块的实例化对象
        injector.getInstance(MyWebServer.class)
                .start();

        // 如果需要其他功能类也被注册, 就需要通过 injector.getInstance(Class<?>) 来做注册区并
    }

    /**
     * 绑定到目前的的类之中
     */
    private final Database database;

    /**
     * 构造方法声明要求引入 Module 注册的实例化对象
     */
    @Inject
    MyWebServer(Database database) {
        this.database = database;
    }

    public void start() {
        // 假设注入成功启动就启动 
        // 这是否就能拿到 module 注入的实例化
    }
}

其他更高级用法可以参照官方的 wiki, 我只需要用到全局模块注册到指定功能类就行了; 其他功能暂时还不怎么需要, 后续需要扩展的时候再补充进来.

这个库可以作为底层依赖注入功能做前期的开发引入, 方便抛弃 spring 之类过于冗余庞大工具链.