Spring Framework IoC依赖注入-按Bean类型注入

Spring Framework 作为一个领先的企业级开发框架,以其强大的依赖注入(Dependency Injection,DI)机制而闻名。DI使得开发者可以更加灵活地管理对象之间的关系,而不必过多关注对象的创建和组装。在Spring Framework中,依赖注入可以分为两种类型:根据Bean名称注入、根据Bean类型注入,在本文中,我们将聚焦于 Spring 中的一种依赖注入方式------按类型注入,并通过一个简单的示例来说明其使用和优势。

什么是依赖注入?

don't call us, we'll call you

依赖注入是一种先进的设计思想,它通过外部注入依赖对象来实现组件之间的松耦合。在 Spring 框架中,依赖注入的核心实现便是通过控制反转(Inversion of Control,IoC)容器。IoC 容器负责实例化、配置和组装应用中的对象,并在需要时将它们智能地注入到其他对象中。相较于传统的面向对象思想,当业务代码变得复杂时,通过直接使用 new 进行对象构造对象间的关系,容易导致代码耦合度的上升。Spring 通过控制反转巧妙地解决了这一问题,运用了好莱坞原则的理念:不要给我们打电话,我们会给你打电话。这种思想使得代码更加灵活、可维护,并促使了更优雅的代码结构。


Spring 中的依赖注入

在 Spring 中,依赖注入有多种方式,包括构造函数注入、Setter 方法注入、接口注入等。示例中展示的是一种基于 XML 配置的 Setter 方法注入。

构造函数注入的案例:

java 复制代码
public class UserService {

    private final EmailService emailService;

    // 构造函数注入
    public UserService(EmailService emailService) {
        this.emailService = emailService;
    }

    public void sendWelcomeEmail(String username) {
        String message = "Welcome, " + username + "!";
        emailService.sendEmail("welcome@example.com", message);
    }
}

Setter 方法注入的案例:

java 复制代码
public class NotificationServiceClient {

    private NotificationService notificationService;

    // Setter 方法注入
    public void setNotificationService(NotificationService notificationService) {
        this.notificationService = notificationService;
    }

    public void sendNotification(String message) {
        notificationService.notifyUser(message);
    }
}
```java

### 接口注入的案例:

public class LoggingService {

typescript 复制代码
private Logger logger;

// 接口注入
public void setLogger(Logger logger) {
    this.logger = logger;
}

public void logMessage(String message) {
    logger.log(message);
}

}

xml 复制代码
在本文中以知名IP:小马哥在《小马哥讲 Spring 核心编程思想》中使用的代码案例展开。

Github源码:

[GeekTime](https://gitee.com/geektime-geekbang/geekbang-lessons.git)

dependency-injection-context.xml

```xml
<!-- 通过导入复用 dependency-lookup-context.xml -->
<import resource="dependency-lookup-context.xml"/>

<!-- Auto-Wiring: 按类型注入 -->
<bean id="userRepository" class="org.thinging.in.spring.ioc.overview.repository.UserRepository" autowire="byType">
</bean>

dependency-lookup-context.xml:

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<beans
        xmlns="http://www.springframework.org/schema/beans"
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">

<!--    <context:annotation-config/>-->

<!--    <context:component-scan base-package="org.acme" />-->

    <!-- Root BeanDefinition 不需要合并,不存在 parent -->
    <!-- 普通 beanDefinition GenericBeanDefinition -->
    <!-- 经过合并后 GenericBeanDefinition 变成 RootBeanDefinition -->
    <bean id="user" class="org.geekbang.thinking.in.spring.ioc.overview.domain.User">
        <property name="id" value="1"/>
        <property name="name" value="小马哥"/>
        <property name="city" value="HANGZHOU"/>
        <property name="workCities" value="BEIJING,HANGZHOU"/>
        <property name="lifeCities">
            <list>
                <value>BEIJING</value>
                <value>SHANGHAI</value>
            </list>
        </property>
        <property name="configFileLocation" value="classpath:/META-INF/user-config.properties"/>
    </bean>

    <!-- 普通 beanDefinition GenericBeanDefinition -->
    <!-- 合并后 GenericBeanDefinition 变成 RootBeanDefinition,并且覆盖 parent 相关配置-->
    <!-- primary = true , 增加了一个 address 属性 -->
    <bean id="superUser" class="org.geekbang.thinking.in.spring.ioc.overview.domain.SuperUser" parent="user"
          primary="true">
        <property name="address" value="杭州"/>
    </bean>

    <bean id="objectFactory" class="org.springframework.beans.factory.config.ObjectFactoryCreatingFactoryBean">
        <property name="targetBeanName" value="user"/>
    </bean>
</beans>

在这个例子中,我们通过 XML 配置文件导入了 dependency-lookup-context.xml ,并配置了一个名为 userRepositoryUserRepository Bean,并通过 autowire="byType" 实现了自动按类型注入。这样,Spring 容器会在运行时查找并注入与 UserRepository 类型匹配的 User 对象。

UserRepository 类

java 复制代码
public class UserRepository {

    /**
     * 自定义Bean
     */
    private Collection<User> users;

    public Collection<User> getUsers() {
        return users;
    }

    public void setUsers(Collection<User> users) {
        this.users = users;
    }
}

UserRepository 类中定义了一个名为 users 的集合属性,并提供了相应的 Getter 和 Setter 方法。通过 Setter 方法,我们可以在 Spring 容器中配置的时候注入一组 User 对象。

主程序

java 复制代码
public static void main(String[] args) {
    // 配置 XML 配置文件
    // 启动 Spring 应用上下文
    BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath:/META-INF/dependency-injection-context.xml");
    UserRepository userRepository = beanFactory.getBean("userRepository", UserRepository.class);
    System.out.println(userRepository.getUsers());
}

在主程序中,我们通过 ClassPathXmlApplicationContext 加载了 XML 配置文件,获取了名为 userRepositoryUserRepository Bean,并输出了其中包含的 User 对象集合。

结论

通过这个简单的示例,我们了解了 Spring Framework 中依赖注入的基本原理。依赖注入通过控制反转容器实现,使得应用程序中的对象不再负责管理自己的依赖关系,而是由 IoC 容器负责。这种设计模式降低了组件之间的耦合度,提高了代码的可测试性和可维护性,是 Spring 成功的关键之一。在实际项目中,我们可以根据需求选择适合的依赖注入方式,使代码更加清晰、灵活且易于维护。

后续内容文章持续更新中...

近期发布。


关于我

👋🏻你好,我是Debug.c。微信公众号:种颗代码技术树 的维护者,一个跨专业自学Java,对技术保持热爱的bug猿,同样也是在某二线城市打拼四年余的Java Coder。

🏆在掘金、CSDN、公众号我将分享我最近学习的内容、踩过的坑以及自己对技术的理解。

📞如果您对我感兴趣,请联系我。

若有收获,就点个赞吧,喜欢原图请私信我。

相关推荐
爱勇宝3 小时前
第 1 章:别把“需求文档”当成真正的需求
前端·后端·程序员
IT_陈寒8 小时前
闭包陷阱让我加了两天班,JavaScript你真行
前端·人工智能·后端
易协同低代码9 小时前
通达OA核心类库TD类深度解析
后端
Gopher_HBo9 小时前
Go语言学习笔记(十八)Gin处理Session
后端
谭光志10 小时前
工具塞满上下文窗口怎么办?深度拆解 AI Agent Tool Search 按需加载实现原理
前端·后端·ai编程
她说..10 小时前
Java 默认值设置方式
java·开发语言·后端·springboot
foggyprojects10 小时前
从0开始,一句话启动AI DataAgent
后端·数据分析·ai编程
郡杰10 小时前
一些基础和问题解决
后端
陈随易10 小时前
前端项目部署只要30秒
前端·后端·程序员
YIAN10 小时前
从零手写文件读取 MCP 服务:一文吃透 Model Context Protocol 全链路通信原理
前端·后端·mcp