跳到正文
Joeplover
学习笔记·2025-07-10·约 3 分钟阅读

SSM 框架学习:Spring IoC 容器与依赖注入

Spring/SpringMVC/MyBatis 全栈框架学习项目,从 IoC 容器、DI 注入到 AOP,层层递进掌握 SSM 核心技术。

Spring IoC

SSM 框架学习:Spring IoC 容器与依赖注入

项目背景

SSM 是 Spring + Spring MVC + MyBatis 的缩写,在 Spring Boot 普及之前是 Java Web 开发的标准技术栈。这个项目通过整合 SSM 框架,重点学习了 Spring 最核心的 IoC(控制反转)容器和 DI(依赖注入)机制。

没有 IoC 时的问题

传统开发中,Service 层调用 DAO 层时直接在代码里 new 实现类:

public class UserService {
    private UserDao userDao = new UserDaoImpl();  // 硬编码耦合

    public User getById(Long id) {
        return userDao.findById(id);
    }
}

问题:如果要把 UserDaoImpl 换成另一个实现(比如加缓存的实现),必须修改 UserService 的源码。测试时也很难替换为 Mock 对象。

IoC 容器解决耦合

IoC 把对象的创建和管理交给 Spring 容器。Service 只需要声明"我需要什么":

public class UserService {
    private UserDao userDao;  // 只声明接口,不 new 实现

    // Spring 容器自动注入
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    public User getById(Long id) {
        return userDao.findById(id);
    }
}

三种配置方式

方式一: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 -->
    <bean id="userDao" class="com.example.dao.UserDaoImpl"/>

    <bean id="userService" class="com.example.service.UserService">
        <!-- 注入依赖 -->
        <property name="userDao" ref="userDao"/>
    </bean>

    <bean id="userController" class="com.example.controller.UserController">
        <constructor-arg ref="userService"/>
    </bean>
</beans>

方式二:注解配置(目前主流)

@Repository  // 标记为 DAO 组件
public class UserDaoImpl implements UserDao {
    public User findById(Long id) {
        // JDBC 或 MyBatis 查询
    }
}

@Service  // 标记为 Service 组件
public class UserService {
    @Autowired  // 自动注入 UserDao
    private UserDao userDao;
}

@RestController  // 标记为 Controller 组件
@RequestMapping("/users")
public class UserController {
    @Autowired
    private UserService userService;
}

方式三:Java Config(类型安全)

@Configuration
@ComponentScan("com.example")
public class AppConfig {

    @Bean
    public DataSource dataSource() {
        HikariDataSource ds = new HikariDataSource();
        ds.setJdbcUrl("jdbc:mysql://localhost:3306/db");
        ds.setUsername("root");
        ds.setPassword("password");
        ds.setMaximumPoolSize(10);
        return ds;  // 返回的 Bean 自动由 Spring 管理
    }

    @Bean
    public SqlSessionFactory sqlSessionFactory(DataSource ds) throws Exception {
        SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
        factory.setDataSource(ds);
        factory.setTypeAliasesPackage("com.example.domain");
        return factory.getObject();
    }
}

Java Config 的优势:编译期就能发现配置错误(比如拼错方法名),IDE 自动补全,不需要手写 XML。

SSM 整合关键点

SSM 整合的核心是把 MyBatis 的 SqlSessionFactory 交给 Spring 容器管理:

@Configuration
@MapperScan("com.example.mapper")  // 扫描 Mapper 接口
public class MyBatisConfig {

    @Bean
    public SqlSessionFactory sqlSessionFactory(DataSource ds) throws Exception {
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        factoryBean.setDataSource(ds);
        // 配置 MyBatis 全局属性
        org.apache.ibatis.session.Configuration config =
            new org.apache.ibatis.session.Configuration();
        config.setMapUnderscoreToCamelCase(true);  // user_name → userName
        config.setLogImpl(StdOutImpl.class);        // 打印 SQL
        factoryBean.setConfiguration(config);
        return factoryBean.getObject();
    }

    @Bean
    public DataSourceTransactionManager transactionManager(DataSource ds) {
        return new DataSourceTransactionManager(ds);
    }
}

通过 @MapperScan,MyBatis 的 Mapper 接口自动生成代理实现类并注册为 Spring Bean,Service 层直接 @Autowired 就能使用 Mapper,实现了"无感集成"。

常见踩坑

循环依赖: A Service 注入 B Service,同时 B Service 也注入 A Service。虽然 Spring 通过三级缓存可以解决大部分循环依赖,但这通常是设计上的坏味道,应该把公共依赖抽取到第三个服务类。

Bean 作用域: 默认是 singleton(单例),所有线程共享同一个实例。如果 Service 持有状态变量,在高并发下会出现竞态条件,需要使用 @Scope("prototype") 或 ThreadLocal。

事务失效: @Transactional 在同类内部调用不生效——必须从外部调用才能通过 AOP 代理触发事务。

总结

理解 IoC 是理解 Spring 一切功能的基础。AOP 是 IoC 的扩展应用,事务管理是 AOP 的典型场景,Spring MVC 是 IoC 在 Web 层的实现。SSM 虽然已被 Spring Boot 取代,但理解它的整合过程能更好地理解 Spring Boot"约定优于配置"的设计理念。