Spring框架核心(上)——IoC控制反转与DI依赖注入详解

前言

Spring框架自2003年兴起以来,已成为Java企业级开发的事实标准。它由Rod Johnson在其著作《Expert One-On-One J2EE Development and Design》中阐述的理念衍生而来,解决了企业应用开发的复杂性。本系列分为上下两篇,上篇聚焦于Spring最核心的控制反转(IoC)依赖注入(DI),涵盖XML配置、注解开发以及纯注解方式,并配有大量实战代码。


一、Spring框架概述

1.1 什么是Spring?

Spring是一个开放源代码的设计层面框架,它解决的是业务逻辑层和其他各层的松耦合问题,将面向接口的编程思想贯穿整个系统。Spring是一个轻量级一站式(JavaSE/EE full-stack)的开源框架。

1.2 Spring的核心架构

Spring的核心是 IoC(控制反转)AOP(面向切面编程),构建了一个分层架构,允许开发者按需选择组件。

复制代码
┌─────────────────────────────────────────┐
│           表现层(Web层)                  │
├─────────────────────────────────────────┤
│           业务层(Service层)              │
├─────────────────────────────────────────┤
│           持久层(DAO层)                  │
├─────────────────────────────────────────┤
│         Spring核心容器(IoC/AOP)          │
└─────────────────────────────────────────┘

下图展示了Spring框架的经典分层架构,IoC和AOP作为底层基石,向上支撑着数据访问、Web层和业务逻辑层。

1.3 Spring的主要优点

  1. 解耦简化开发:Spring作为大工厂,管理对象创建和依赖关系。

  2. AOP编程支持:方便实现权限拦截、运行监控等。

  3. 声明式事务:通过配置管理事务,无需手动编码。

  4. 方便测试:与JUnit整合,轻松测试。

  5. 集成优秀框架:支持Struts2、Hibernate、MyBatis等。

  6. 降低API难度:对JDBC、JavaMail等提供封装。


二、IoC控制反转核心技术

2.1 什么是IoC?

IoC(Inversion of Control)即控制反转,将对象的创建权力反转给Spring框架。它是一种设计原则,用于降低代码之间的耦合度。

  • 传统方式UserService service = new UserServiceImpl();(主动创建)

  • IoC方式:由Spring工厂创建对象,我们被动获取。

复制代码

2.2 IoC快速入门

步骤1:创建Maven工程,导入依赖
XML 复制代码
xml

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.0.2.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>commons-logging</groupId>
        <artifactId>commons-logging</artifactId>
        <version>1.2</version>
    </dependency>
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.12</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
</dependencies>
步骤2:编写接口与实现类
java 复制代码
java

// UserService.java
package com.example.service;
public interface UserService {
    void hello();
}

// UserServiceImpl.java
package com.example.service;
public class UserServiceImpl implements UserService {
    @Override
    public void hello() {
        System.out.println("Hello IOC!!");
    }
}
步骤3:编写Spring配置文件(applicationContext.xml)
XML 复制代码
xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="userService" class="com.example.service.UserServiceImpl"/>
</beans>
步骤4:测试
java 复制代码
java

@Test
public void testIoC() {
    ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    UserService service = (UserService) context.getBean("userService");
    service.hello();  // 输出 Hello IOC!!
}

IoC容器获取Bean的流程如下图所示:

2.3 ApplicationContext工厂接口

  • ClassPathXmlApplicationContext:加载类路径下的配置文件。

  • FileSystemXmlApplicationContext:加载磁盘路径下的配置文件。

2.4 Bean管理的XML配置

属性 说明
id Bean的唯一标识(命名规范)
class 类的全路径
scope 作用范围:singleton(默认单例)、prototype(多例)、request、session
init-method / destroy-method 生命周期回调方法

示例:

XML 复制代码
xml

<bean id="userService" class="com.example.service.UserServiceImpl" scope="singleton" init-method="init" destroy-method="destroy"/>

2.5 实例化Bean的三种方式

2.5.1 默认无参构造(最常用)
XML 复制代码
xml

<bean id="us" class="com.example.service.UserServiceImpl"/>
2.5.2 静态工厂方法
XML 复制代码
java

public class StaticFactory {
    public static UserService createUs() {
        return new UserServiceImpl();
    }
}
xml

<bean id="us" class="com.example.StaticFactory" factory-method="createUs"/>
2.5.3 实例工厂方法
XML 复制代码
java

public class DFactory {
    public UserService createUs() {
        return new UserServiceImpl();
    }
}
xml

<bean id="dfactory" class="com.example.DFactory"/>
<bean id="us" factory-bean="dfactory" factory-method="createUs"/>

三、DI依赖注入

3.1 什么是DI?

DI(Dependency Injection)即依赖注入 ,在Spring创建Bean时,动态地将依赖对象注入到Bean中。IoC是思想,DI是实现方式

3.2 Setter方法注入

XML 复制代码
java

public class OrderServiceImpl implements OrderService {
    private OrderDao orderDao;
    private String msg;
    private int age;
    // 必须提供setter方法
    public void setOrderDao(OrderDao orderDao) { this.orderDao = orderDao; }
    public void setMsg(String msg) { this.msg = msg; }
    public void setAge(int age) { this.age = age; }
    // 业务方法...
}
xml

<bean id="orderService" class="com.example.OrderServiceImpl">
    <property name="orderDao" ref="orderDao"/>
    <property name="msg" value="测试消息"/>
    <property name="age" value="18"/>
</bean>

3.3 构造方法注入

XML 复制代码
java

public class Car {
    private String cname;
    private Double money;
    public Car(String cname, Double money) {
        this.cname = cname;
        this.money = money;
    }
}
xml

<bean id="car" class="com.example.Car">
    <constructor-arg name="cname" value="奔驰"/>
    <constructor-arg name="money" value="400000"/>
</bean>

3.4 集合类型注入

支持数组、List、Set、Map、Properties等。

XML 复制代码
xml

<bean id="collectionBean" class="com.example.CollectionBean">
    <property name="strs">
        <array><value>美美</value><value>小凤</value></array>
    </property>
    <property name="list">
        <list><value>熊大</value><value>熊二</value></list>
    </property>
    <property name="map">
        <map><entry key="aaa" value="老王"/></map>
    </property>
    <property name="properties">
        <props><prop key="username">root</prop></props>
    </property>
</bean>

三种注入方式关系图


四、多配置文件加载

方式一:在主配置文件中导入

xml

复制代码
<import resource="applicationContext2.xml"/>

方式二:工厂同时加载多个

java

复制代码
ApplicationContext ac = new ClassPathXmlApplicationContext("app1.xml", "app2.xml");

五、Spring整合JDBC实战(IoC+DI综合案例)

5.1 数据库准备

sql 复制代码
sql

CREATE DATABASE spring_db;
USE spring_db;
CREATE TABLE account (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(40),
    money DOUBLE
);
INSERT INTO account VALUES (NULL,'aaa',1000),(NULL,'bbb',1000);

5.2 依赖(添加Druid和MySQL驱动)

XML 复制代码
xml

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.10</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.6</version>
</dependency>

5.3 实体类、DAO、Service代码(省略getter/setter)

Account.java

java 复制代码
java

public class Account {
    private Integer id;
    private String name;
    private Double money;
    // 构造、getter、setter、toString
}

AccountDao接口与实现

java 复制代码
java

public interface AccountDao {
    List<Account> findAll();
}
public class AccountDaoImpl implements AccountDao {
    private DataSource dataSource;
    public void setDataSource(DataSource dataSource) { this.dataSource = dataSource; }
    @Override
    public List<Account> findAll() {
        // 使用dataSource获取连接,执行查询,封装返回
        // 代码略(详见PDF)
    }
}

AccountService接口与实现

java 复制代码
java

public interface AccountService {
    List<Account> findAll();
}
public class AccountServiceImpl implements AccountService {
    private AccountDao accountDao;
    public void setAccountDao(AccountDao accountDao) { this.accountDao = accountDao; }
    @Override
    public List<Account> findAll() {
        return accountDao.findAll();
    }
}

5.4 配置文件(整合所有)

XML 复制代码
xml

<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql:///spring_db"/>
    <property name="username" value="root"/>
    <property name="password" value="root"/>
</bean>
<bean id="accountDao" class="com.example.dao.AccountDaoImpl">
    <property name="dataSource" ref="dataSource"/>
</bean>
<bean id="accountService" class="com.example.service.AccountServiceImpl">
    <property name="accountDao" ref="accountDao"/>
</bean>

5.5 测试

java 复制代码
java

@Test
public void testFindAll() {
    ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
    AccountService service = ac.getBean(AccountService.class);
    List<Account> list = service.findAll();
    list.forEach(System.out::println);
}

六、IOC注解方式

6.1 快速入门

在类上添加@Component(或@Service@Repository@Controller),并在配置文件中开启注解扫描。

java 复制代码
java

@Component("us")
public class UserServiceImpl implements UserService {
    public void hello() { System.out.println("Hello IOC注解..."); }
}
xml

<context:component-scan base-package="com.example"/>

测试时直接通过id获取。

6.2 常用注解详解

注解 作用
@Component 普通类标记为Bean
@Controller 表现层
@Service 业务层
@Repository 持久层
@Value 注入基本类型值
@Autowired 按类型自动装配
@Qualifier 与@Autowired配合,按名称注入
@Resource JSR-250,按名称注入
@Scope 作用范围
@PostConstruct 初始化方法
@PreDestroy 销毁方法

示例:

java 复制代码
java

@Service
@Scope("singleton")
public class Car {
    @Value("大奔")
    private String cname;
    @Autowired
    private Person person;
    @PostConstruct
    public void init() { System.out.println("初始化"); }
}

6.3 纯注解方式(无XML)

配置类

java 复制代码
java

@Configuration
@ComponentScan("com.example")
@Import(OtherConfig.class)
public class SpringConfig {
    @Bean
    public DataSource dataSource() {
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName("com.mysql.jdbc.Driver");
        ds.setUrl("jdbc:mysql:///spring_db");
        ds.setUsername("root");
        ds.setPassword("root");
        return ds;
    }
}

测试

java 复制代码
java

ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfig.class);
Order order = ac.getBean(Order.class);

七、Spring整合JUnit单元测试

7.1 配置文件方式

java 复制代码
java

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestDemo {
    @Autowired
    private UserService userService;
    @Test
    public void test() { userService.hello(); }
}

7.2 纯注解方式

java 复制代码
java

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class TestDemo {
    @Autowired
    private UserService userService;
    @Test
    public void test() { userService.hello(); }
}

总结

本篇详细介绍了Spring框架的IoC控制反转DI依赖注入 ,从XML配置到注解再到纯注解,并通过整合JDBC的实战案例加深理解。掌握了这些内容,您就打下了Spring的坚实基础。下篇我们将深入AOP面向切面编程事务管理JDBC模板等高级特性,敬请期待。

相关推荐
IT_陈寒2 小时前
Redis缓存击穿把我坑惨了,原来这样设过期时间才靠谱
前端·人工智能·后端
fb_123452 小时前
Linux磁盘分区从入门到实操:MBR_GPT全解析+分区工具实战指南
java·linux·gpt
妙码生花2 小时前
从 PHP 到 AI + Golang,程序员自救转型手记(五十二):管理员权限检查中间件,AI 随意放行预闯大祸
前端·后端·go
创安电气研究所2 小时前
用 Node.js 将变频器运行数据接入 MQTT
后端
董员外2 小时前
RAG 系统进化论(八):Agentic RAG(智能体式 RAG),从回答问题到完成任务
人工智能·后端·设计模式
码智社3 小时前
JSON 零基础到资深全体系教程
java·json
大勇前进3 小时前
告别 div 满天飞,HTML5 语义化标签真的有必要用吗?
后端
小满zs3 小时前
Go语言第六章(结构体)
后端·go