前言
前面两天我们用 XML 配置文件扎实掌握了 Spring 的 IoC 和 DI。但配置写多了总感觉繁琐,Spring 早就想到了这一点,从 2.5 版开始全面支持注解,3.0 版更是实现了纯注解开发,让我们彻底告别 XML。
今天我要带你跟上时代的步伐,用注解重新定义 Bean、注入依赖,还会把第三方的 Druid 数据源、MyBatis 甚至 Junit 都整合进 Spring 容器。学完这一章,你就能体会到 Spring Boot 自动配置的雏形了。
💬课前唠一唠:前两天写 XML 写到手酸了吗?今天换成注解,你最想先“删除”哪段 XML 配置?评论区说说你的怨念,看看有没有同病相怜的伙伴。
一、IOC/DI 配置管理第三方 Bean
之前我们把自家写的类交给 Spring 管,那第三方的 jar 包里的类(比如 Druid 连接池)怎么管?
1.1 案例:管理 Druid 数据源
需求:把 Druid 连接池对象交给 Spring 的 IoC 容器。
步骤:
- 导入依赖
<dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.16</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.47</version></dependency>- 在
applicationContext.xml中配置 Bean,注入数据库四要素
<beanid="dataSource"class="com.alibaba.druid.pool.DruidDataSource"><propertyname="driverClassName"value="com.mysql.jdbc.Driver"/><propertyname="url"value="jdbc:mysql://localhost:3306/spring_db"/><propertyname="username"value="root"/><propertyname="password"value="root"/></bean>- 从容器获取并打印
DataSourcedataSource=(DataSource)ctx.getBean("dataSource");System.out.println(dataSource);🧪动手尝试:换成 C3P0 试试?管理它的类是
ComboPooledDataSource,注意属性名不一样(driverClass,jdbcUrl等)。你会发现 C3P0 初始化就要求驱动,需要额外引入mysql-connector-java。
1.2 加载 properties 文件
把数据库连接信息硬编码在 XML 里不利于维护,咱们把它提到jdbc.properties里。
步骤:
- 创建
jdbc.properties
jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://127.0.0.1:3306/spring_db jdbc.username=root jdbc.password=root- 在 XML 中开启
context命名空间,并加载文件
<beans...xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="... spring-context.xsd"><!-- 加载 properties,system-properties-mode="NEVER" 防止系统环境变量覆盖 --><context:property-placeholderlocation="jdbc.properties"system-properties-mode="NEVER"/></beans>- 用
${key}引用值
<propertyname="driverClassName"value="${jdbc.driver}"/><propertyname="url"value="${jdbc.url}"/><propertyname="username"value="${jdbc.username}"/><propertyname="password"value="${jdbc.password}"/>多个配置文件加载方式:
<!-- 方式一:用逗号分隔 --><context:property-placeholderlocation="jdbc.properties,jdbc2.properties"/><!-- 方式二:通配符 *.properties(不建议) --><!-- 方式三:标准写法,classpath: 开头 --><context:property-placeholderlocation="classpath:*.properties"/>⚠️注意:
username这个 key 会跟系统环境变量冲突,最好用jdbc.username这类带前缀的名字,或加上system-properties-mode="NEVER"。
二、核心容器
我们已经用过ApplicationContext,这里系统梳理一下容器的创建和获取 Bean 的方式。
2.1 容器的创建方式
- 推荐:
ClassPathXmlApplicationContext("applicationContext.xml")– 从类路径加载 - 了解:
FileSystemXmlApplicationContext("绝对路径")– 从文件系统加载,路径写死不推荐
2.2 获取 Bean 的三种方式
// 方式1:按名称,需要强转BookDaobookDao=(BookDao)ctx.getBean("bookDao");// 方式2:按名称 + 类型,省去强转BookDaobookDao=ctx.getBean("bookDao",BookDao.class);// 方式3:按类型,容器中该类型 Bean 必须唯一BookDaobookDao=ctx.getBean(BookDao.class);2.3 容器层次与 BeanFactory
BeanFactory:IoC 容器的顶层接口,延迟加载(获取 bean 时才创建)ApplicationContext:是BeanFactory的子接口,立即加载(初始化容器时就创建所有单例 bean)
若想让ApplicationContext也延迟加载某个 bean,可在配置中加lazy-init="true"。
💡小结:日常开发多采用
ClassPathXmlApplicationContext和按类型获取 bean 的方式。
三、IOC/DI 注解开发
终于不用写 XML 里的<bean>了!Spring 提供了几个注解帮我们自动注册 Bean。
3.1 注解定义 Bean
- 在类上添加
@Component("bookDao"),相当于<bean id="bookDao" class="..."> - 在 xml 中配置包扫描,让 Spring 能发现这些注解
<context:component-scanbase-package="com.itheima"/>- 以后直接在类上写注解即可。
常用分层注解(功能同@Component,只是语义更明确):
@Controller:表现层@Service:业务层@Repository:数据层
如果不指定名称,默认是类名首字母小写。
3.2 纯注解开发(告别 XML)
用配置类完全替代 XML。
@Configuration// 标志这是一个配置类,相当于 applicationContext.xml@ComponentScan("com.itheima")// 包扫描publicclassSpringConfig{}启动容器改用:
ApplicationContextctx=newAnnotationConfigApplicationContext(SpringConfig.class);🔄对比:XML →
ClassPathXmlApplicationContext;配置类 →AnnotationConfigApplicationContext。
3.3 bean 作用范围与生命周期
作用范围:默认单例,用@Scope("prototype")改为非单例。
生命周期:
- 初始化:
@PostConstruct标注的方法(构造后执行) - 销毁前:
@PreDestroy标注的方法(容器关闭前执行)
注意:从 JDK9 开始需要额外引入
javax.annotation-api包,否则这两个注解找不到。
3.4 注解依赖注入
自动装配(按类型注入)
@ServicepublicclassBookServiceImplimplementsBookService{@AutowiredprivateBookDaobookDao;// 按类型自动注入,setter 方法都可以删掉}原理:@Autowired基于暴力反射,可以直接给私有属性赋值。
有多个同类型 Bean 时:@Autowired 会先按类型找,如果找到多个,再根据变量名匹配 bean 名称。若还找不到,就需要配合@Qualifier("bookDao1")指定名称。
注入简单类型 + 读取 properties
@Repository("bookDao")publicclassBookDaoImplimplementsBookDao{@Value("itheima")privateStringname;}配合@PropertySource读取配置文件:
@Configuration@ComponentScan("com.itheima")@PropertySource("classpath:jdbc.properties")publicclassSpringConfig{}// 使用@Value("${name}")// 对应 jdbc.properties 中的 name=itheima888privateStringname;四、注解开发管理第三方 Bean
第三方 jar 包里的类我们不能直接加@Component,需要用@Bean注解。
方法:在配置类中写一个方法,返回要管理的对象,并标注@Bean。
publicclassJdbcConfig{@BeanpublicDataSourcedataSource(){DruidDataSourceds=newDruidDataSource();ds.setDriverClassName("com.mysql.jdbc.Driver");ds.setUrl("jdbc:mysql://localhost:3306/spring_db");ds.setUsername("root");ds.setPassword("root");returnds;}}引入这个配置类:在主配置类上用@Import(JdbcConfig.class)。
@Configuration@ComponentScan("com.itheima")@Import(JdbcConfig.class)publicclassSpringConfig{}🎯最佳实践:把不同类型的第三方 Bean 分开放到各自的配置类(如
JdbcConfig、MyBatisConfig),再用@Import统一引入,结构清晰。
为第三方 Bean 注入资源
方法参数中直接写要注入的对象,Spring 会自动装配。
@BeanpublicDataSourcedataSource(BookDaobookDao){// 自动注入 bookDaoSystem.out.println(bookDao);...}简单类型还是用@Value注入,也可以配合@PropertySource从配置文件读取。
五、XML 配置 vs 注解开发 对照表
| 功能 | XML 配置 | 注解开发 |
|---|---|---|
| 定义 bean | <bean id="" class=""> | @Component/@Controller/@Service/@Repository+@ComponentScan |
| 注入依赖(引用) | <property name="" ref=""> | @Autowired+@Qualifier |
| 注入依赖(简单) | <property name="" value=""> | @Value |
| 配置第三方 bean | <bean>配合工厂方法 | @Bean标注在方法上 |
| 作用范围 | scope属性 | @Scope |
| 生命周期 | init-method/destroy-method | @PostConstruct/@PreDestroy |
| 加载 properties | <context:property-placeholder> | @PropertySource+@Value("${}") |
六、Spring 整合 MyBatis
现在要把 MyBatis 的核心对象也交给 Spring 管,让容器帮我们创建SqlSessionFactory和 Mapper 代理对象。
6.1 整合思路
回顾原生 MyBatis 开发核心对象:
SqlSessionFactory需要数据源、配置等环境SqlSession由SqlSessionFactory产生- Mapper 接口通过
SqlSession.getMapper()获得
Spring 整合后的目标:
DataSource→ Spring 管理(Druid)SqlSessionFactory→ Spring 管理(用SqlSessionFactoryBean)- Mapper 接口 → Spring 通过
MapperScannerConfigurer自动扫描并创建代理对象,放入容器
6.2 具体步骤
- 引入整合包
<dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>5.2.10.RELEASE</version></dependency><dependency><groupId>org.mybatis</groupId><artifactId>mybatis-spring</artifactId><version>1.3.0</version></dependency>- 创建 MyBatis 配置类
publicclassMyBatisConfig{@BeanpublicSqlSessionFactoryBeansqlSessionFactory(DataSourcedataSource){SqlSessionFactoryBeanfactoryBean=newSqlSessionFactoryBean();factoryBean.setDataSource(dataSource);// 数据源factoryBean.setTypeAliasesPackage("com.itheima.domain");// 别名扫描returnfactoryBean;}@BeanpublicMapperScannerConfigurermapperScannerConfigurer(){MapperScannerConfigurerscanner=newMapperScannerConfigurer();scanner.setBasePackage("com.itheima.dao");// Mapper 接口所在包returnscanner;}}- 主配置类引入
@Configuration@ComponentScan("com.itheima")@PropertySource("classpath:jdbc.properties")@Import({JdbcConfig.class,MyBatisConfig.class})publicclassSpringConfig{}现在就可以直接注入 Service 使用了,完全不用手动操作 MyBatis 的 SqlSession 了。
七、Spring 整合 JUnit
测试类里也可以让 Spring 环境自动开启,通过简单注解就能注入需要的 Bean。
步骤:
- 引入
spring-test依赖 - 测试类上添加:
@RunWith(SpringJUnit4ClassRunner.class):指定 Spring 的运行器@ContextConfiguration(classes = SpringConfig.class):指定配置类(或locations指定 xml)
- 直接
@Autowired注入要测试的 Bean,编写@Test方法
@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration(classes=SpringConfig.class)publicclassAccountServiceTest{@AutowiredprivateAccountServiceaccountService;@TestpublicvoidtestFindById(){System.out.println(accountService.findById(1));}}🧪挑战任务:用注解方式重写前两天所有的 XML 配置,再整合 MyBatis 和 JUnit,跑通一个从 Controller 到 Service 到 Dao 再到数据库的全链调用。遇到
NoUniqueBeanDefinitionException不要慌,先用@Qualifier指定,再回来优化你的设计。
八、总结
今天我们从 XML 成功过渡到了注解驱动开发,并完成了 Spring 与 MyBatis、JUnit 的整合。核心能力矩阵如下:
| 技能 | 关键注解/类 |
|---|---|
| 定义 Bean | @Component,@Service,@Repository,@Configuration+@Bean |
| 包扫描 | @ComponentScan |
| 依赖注入(引用/简单) | @Autowired,@Qualifier,@Value |
| 加载 properties | @PropertySource |
| 生命周期/作用范围 | @PostConstruct,@PreDestroy,@Scope |
| 整合 MyBatis | SqlSessionFactoryBean,MapperScannerConfigurer |
| 整合 JUnit | @RunWith,@ContextConfiguration |
学到这里,你会发现 Spring Boot 的@SpringBootApplication其实就是@Configuration+@ComponentScan+ 一堆自动配置的封装。地基已打牢,后续学习将势如破竹。
🎤结课小调查:今天的内容中,让你感觉最“爽”的点是哪个?
A. 终于不用写 XML 了
B.@Autowired自动注入
C.MapperScannerConfigurer自动扫描 DAO
D. Junit 测试也能用@Autowired评论区告诉我,看看大家是不是都选了 A!有任何疑问或整合时遇到的错误,也欢迎贴出来,我们一起排查。
本文为 Spring Framework 第三天授课内容整理,至此我们已经完成了 Spring 核心原理与注解开发的学习。下期我们将进入 AOP 的学习,敬请期待!如果觉得有帮助,别忘了点赞、收藏、关注,你的支持是我最大的动力。