当前位置: 首页 > news >正文

40岁开始学Java:如何实现依赖注入?

在这里插入图片描述

在 Java 中实现依赖注入(Dependency Injection, DI)的核心思想是将对象的依赖关系从代码内部转移到外部容器或框架管理。以下是常见的实现方式及示例:


一、手动实现依赖注入(不依赖框架)

1. 构造器注入

通过类的构造函数传递依赖对象:

// 接口定义
public interface Logger {
    void log(String message);
}

// 具体实现
public class ConsoleLogger implements Logger {
    @Override
    public void log(String message) {
        System.out.println("Console: " + message);
    }
}

public class FileLogger implements Logger {
    @Override
    public void log(String message) {
        System.out.println("File: " + message);
    }
}

// 使用依赖注入的类
public class UserService {
    private final Logger logger;

    // 构造器注入依赖
    public UserService(Logger logger) {
        this.logger = logger;
    }

    public void createUser(String name) {
        logger.log("Creating user: " + name);
    }
}

// 手动创建对象并注入依赖
public class Main {
    public static void main(String[] args) {
        // 选择具体实现
        Logger logger = new ConsoleLogger();
        UserService userService = new UserService(logger);
        userService.createUser("Alice");
    }
}
2. Setter 注入

通过 setter 方法设置依赖对象:

public class UserService {
    private Logger logger;

    public void setLogger(Logger logger) {
        this.logger = logger;
    }

    public void createUser(String name) {
        if (logger == null) {
            throw new IllegalStateException("Logger not initialized");
        }
        logger.log("Creating user: " + name);
    }
}

public class Main {
    public static void main(String[] args) {
        UserService userService = new UserService();
        userService.setLogger(new FileLogger());
        userService.createUser("Bob");
    }
}

在这里插入图片描述

3. 工厂模式

通过工厂类动态创建对象并注入依赖:

public class LoggerFactory {
    public static Logger getLogger(String type) {
        switch (type) {
            case "console":
                return new ConsoleLogger();
            case "file":
                return new FileLogger();
            default:
                throw new IllegalArgumentException("Invalid logger type");
        }
    }
}

public class Main {
    public static void main(String[] args) {
        Logger logger = LoggerFactory.getLogger("console");
        UserService userService = new UserService(logger);
        userService.createUser("Charlie");
    }
}

在这里插入图片描述

二、使用框架实现依赖注入(推荐)

1. Spring 框架
(1) 构造器注入

步骤

  1. 定义接口和实现类。
  2. 使用 @Component 标记组件。
  3. 在目标类中使用 @Autowired 注解注入依赖。

示例

// 定义接口
public interface DatabaseService {
    String query(String sql);
}

// 实现类
@Component
public class MySQLService implements DatabaseService {
    @Override
    public String query(String sql) {
        return "MySQL Result: " + sql;
    }
}

// 目标类
@Component
public class UserService {
    private final DatabaseService databaseService;

    // 构造器注入
    @Autowired
    public UserService(DatabaseService databaseService) {
        this.databaseService = databaseService;
    }

    public String getUserData() {
        return databaseService.query("SELECT * FROM users");
    }
}

// 启动类
@SpringBootApplication
public class App {
    public static void main(String[] args) {
        ApplicationContext context = SpringApplication.run(App.class, args);
        UserService userService = context.getBean(UserService.class);
        System.out.println(userService.getUserData());
    }
}
(2) Setter 注入
@Component
public class UserService {
    private DatabaseService databaseService;

    @Autowired
    public void setDatabaseService(DatabaseService databaseService) {
        this.databaseService = databaseService;
    }
}
(3) 字段注入(不推荐)
@Component
public class UserService {
    @Autowired
    private DatabaseService databaseService;
}
2. Google Guice 框架
(1) 绑定依赖

通过模块(Module)绑定接口到实现类:

public class AppModule extends AbstractModule {
    @Override
    protected void configure() {
        bind(DatabaseService.class).to(MySQLService.class);
    }
}
(2) 注入依赖
public class UserService {
    private final DatabaseService databaseService;

    @Inject
    public UserService(DatabaseService databaseService) {
        this.databaseService = databaseService;
    }
}

// 使用 Guice 创建对象
public class Main {
    public static void main(String[] args) {
        Injector injector = Guice.createInjector(new AppModule());
        UserService userService = injector.getInstance(UserService.class);
        System.out.println(userService.getUserData());
    }
}

三、测试环境中的依赖注入

使用 Mockito 注入模拟对象进行单元测试:

public class UserServiceTest {
    @Test
    void testGetUser() {
        // 创建模拟依赖
        DatabaseService mockDb = Mockito.mock(DatabaseService.class);
        when(mockDb.query(1)).thenReturn("Alice");

        // 构造目标类并注入依赖
        UserService userService = new UserService(mockDb);

        // 执行方法并验证结果
        assertEquals("Alice", userService.getUser(1).getName());
    }
}

四、依赖注入的核心模式

模式描述
控制反转(IoC)由容器(如 Spring/IoC 容器)控制对象的创建和管理,而非代码自身控制。
服务定位器通过统一接口(如 ServiceLocator)动态获取依赖对象,常见于手动实现。
依赖倒置高层模块不依赖低层模块,二者都依赖抽象接口(如 Logger 接口)。

在这里插入图片描述

五、最佳实践

  1. 优先构造器注入:确保对象在创建时依赖已满足,避免 NullPointerException
  2. 避免字段注入:破坏封装性,难以追踪依赖来源。
  3. 使用接口抽象:隔离具体实现,提高代码灵活性。
  4. 结合单元测试:通过模拟依赖快速验证功能。

通过以上方式,可以灵活实现依赖注入,降低代码耦合度,提升可维护性和可测试性。

相关文章:

  • vue+element-plus简洁完美实现古诗文网
  • VSCode配置优化指南:构建高效工程级开发环境
  • Linux操作系统6- 线程1(线程基础,调用接口,线程优缺点)
  • 学习笔记:利用OpenAI实现阅卷智能体
  • AcWing 5438. 密接牛追踪2 区间覆盖问题的详细解释
  • 关闭Windows安全中心,解析与实操指南
  • Git基础之工作原理
  • Spark 3.0核心新特性解析与行业应用展望
  • Docker部署Ragflow(完美解决502 bad gateway)
  • 【RocketMQ 存储】- 异常退出恢复逻辑 recoverAbnormally
  • 机器学习数学基础:44.多元线性回归
  • VTK笔记- 3D Widget类 vtkSplineWidget 样条部件
  • Hadoop项目中的问题(1)——NetworkManager 和 network 服务冲突
  • mysql经典试题共34题
  • fastjson漏洞#不出网#原理#流量特征
  • CD10.【C++ Dev】类和对象(1)
  • 用户仿真任务调度管理平台数据库设计
  • 使用Qt调用HslCommunication(C++调用C#库)
  • 微服务拆分-远程调用
  • 电子学会—2024年12月青少年软件编程(图形化)级等级考试真题——猜年龄互动小游戏
  • 快评|印巴为何停火?已达成“一场胜利,各自表述”的效果
  • 泰特现代美术馆25年:那些瞬间,让艺术面向所有人
  • 中国象棋协会坚决支持司法机关依法打击涉象棋行业的违法行为
  • 异域拾异|大脚怪的形状:一项神秘社会学研究
  • 欧洲承诺投资6亿欧元吸引外国科学家
  • 司法部:加快研究制定行政执法监督条例,建立完善涉企行政执法监督长效机制