一、引言
在Spring框架的学习与实战中,传统的XML配置方式虽然稳定,但代码冗余、配置繁琐的问题始终存在。Spring从2.0开始逐步支持注解开发,3.0版本更是实现了纯注解配置,彻底摆脱了XML文件的依赖。
本文将从环境准备开始,逐步掌握Spring注解开发的核心:@Component系列注解、组件扫描、纯注解配置类,以及@ComponentScan的底层路径逻辑。通过实战代码,清晰对比XML与注解的差异,彻底吃透Spring IOC/DI的注解化开发。
二、环境准备:注解开发基础项目搭建
在学习注解开发前,我们先搭建一个标准的Maven项目,为后续注解开发做好铺垫。
2.1 项目结构
spring_11_annotation_bean
├── src
│ └── main
│ ├── java
│ │ └── com
│ │ └── itheima
│ │ ├── dao
│ │ │ ├── BookDao.java
│ │ │ └── impl
│ │ │ └── BookDaoImpl.java
│ │ └── service
│ │ ├── BookService.java
│ │ └── impl
│ │ └── BookServiceImpl.java
│ └── resources
│ └── applicationContext.xml
└── pom.xml
2.2 核心依赖(pom.xml)
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
</dependencies>
2.3 基础代码编写
-
数据访问层(BookDao & BookDaoImpl)
// BookDao接口
public interface BookDao {
void save();
}// BookDaoImpl实现类
public class BookDaoImpl implements BookDao {
public void save() {
System.out.println("book dao save ...");
}
} -
业务逻辑层(BookService & BookServiceImpl)
// BookService接口
public interface BookService {
void save();
}// BookServiceImpl实现类
public class BookServiceImpl implements BookService {
public void save() {
System.out.println("book service save ...");
}
}
2.4 传统XML配置(applicationContext.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指定名称,class指定全限定类名 -->
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl"/>
</beans>
2.5 运行类(App)
public class App {
public static void main(String[] args) {
// 加载XML配置文件
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
// 获取Bean
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
bookDao.save();
}
}
此时运行正常,控制台输出:book dao save ...。但XML配置文件在项目类增多后会变得极其臃肿,这就是注解开发要解决的核心问题。
三、注解开发定义Bean:替代XML的<bean>标签
我们将逐步用注解替代XML配置,核心步骤如下:
3.1 核心步骤拆解
步骤1:删除XML中的<bean>配置
直接移除applicationContext.xml中<bean>相关配置,保留空文件即可。
步骤2:在实现类上添加@Component注解
@Component注解是Spring中最基础的组件注解,用于标识当前类为Spring管理的Bean(替代XML中的<bean>标签)。
import org.springframework.stereotype.Component;
// @Component("bookDao") 括号内指定bean的id,与xml中的id对应
@Component("bookDao")
public class BookDaoImpl implements BookDao {
public void save() {
System.out.println("book dao save ...");
}
}
// 给BookService也添加注解
@Component("bookService")
public class BookServiceImpl implements BookService {
public void save() {
System.out.println("book service save ...");
}
}
⚠️ 注意: @Component注解不能添加在接口上,因为接口无法实例化对象,注解只能标注在具体的实现类上。
步骤3:配置组件扫描
Spring无法自动扫描项目中的注解,需要通过<context:component-scan>标签指定扫描路径,告知Spring去哪个包下寻找@Component注解。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 组件扫描:base-package指定扫描的基础包 -->
<context:component-scan base-package="com.itheima"/>
</beans>
组件扫描说明:
- component-scan:组件扫描,将Spring管理的Bean视为组件
- base-package:指定Spring扫描的包路径,会扫描该包及其子包下所有类的注解
- 扫描范围越小,速度越快;一般扫描项目根包(如com.itheima)即可。
步骤4:运行验证
public class App {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
// 获取BookDao
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
System.out.println(bookDao);
// 获取BookService
BookService bookService = (BookService) ctx.getBean(BookService.class);
System.out.println(bookService);
}
}
控制台输出:
com.itheima.dao.impl.BookDaoImpl@73d4cc9e
com.itheima.service.impl.BookServiceImpl@48d37741f
Process finished with exit code 0
四、@Component注解的衍生注解(分层开发规范)
通过查看源码可知,@Controller、@Service、@Repository三个注解本质上都继承了@Component,它们是@Component的衍生注解。
4.1 注解源码对比
// @Component 源码
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Component {}
// @Controller 源码
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component // 本质是@Component
public @interface Controller {}
// @Service 源码
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component // 本质是@Component
public @interface Service {}
// @Repository 源码
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component // 本质是@Component
public @interface Repository {}
4.2 衍生注解的作用(分层语义化)
虽然功能与@Component一致,但衍生注解让代码更具语义化,清晰区分代码层级:
| 注解 | 适用层级 | 作用 |
|---|---|---|
| @Controller | 表现层(Web层) | 标识控制层组件,处理请求与响应 |
| @Service | 业务层(Service层) | 标识业务逻辑组件,处理核心业务 |
| @Repository | 数据访问层(DAO层) | 标识数据访问组件,操作数据库 |
| @Component | 通用层 | 通用组件,无明确层级划分时使用 |
4.3 实战改造
// BookDaoImpl 改为 @Repository
@Repository("bookDao")
public class BookDaoImpl implements BookDao { ... }
// BookServiceImpl 改为 @Service
@Service("bookService")
public class BookServiceImpl implements BookService { ... }
五、纯注解开发:彻底替代XML配置文件
Spring 3.0版本引入了纯注解开发模式,使用Java类替代XML配置文件,是Spring开发的主流方式。
5.1 实现思路
- 删除applicationContext.xml配置文件;
- 创建Java配置类,替代XML文件;
- 使用@Configuration标识配置类;
- 使用@ComponentScan替代<context:component-scan>组件扫描;
- 通过AnnotationConfigApplicationContext加载配置类。
5.2 实现步骤
步骤1:创建配置类(SpringConfig)
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
// 1. 标识该类为Spring配置类,替代XML文件
@Configuration
// 2. 组件扫描:指定基础包,替代<context:component-scan base-package="com.itheima"/>
@ComponentScan("com.itheima")
public class SpringConfig {
}
步骤2:修改运行类(AppForAnnotation)
import com.itheima.config.SpringConfig;
import com.itheima.dao.BookDao;
import com.itheima.service.BookService;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class AppForAnnotation {
public static void main(String[] args) {
// 加载纯注解配置类
ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
// 获取Bean
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
System.out.println(bookDao);
BookService bookService = (BookService) ctx.getBean(BookService.class);
System.out.println(bookService);
}
}
六、核心难点:@ComponentScan 路径深度解析
@ComponentScan是纯注解开发的核心,很多开发者对其扫描路径存在误解,这里重点拆解其绝对路径特性。
6.1 核心结论
@ComponentScan("com.itheima")中的包名不是相对路径 ,而是Java类路径下的绝对路径 ,其定位规则与Java类的包结构直接关联,本质是全限定包名。
6.2 详细解读
1. 本质是"全限定包名"
"com.itheima"是从根包开始的完整层级,对应项目中:
- 源码路径:src/main/java/com/itheima
- 编译路径:target/classes/com/itheima(Maven/Gradle项目)
2. 与类路径(classpath)的关系
Spring会从Java类路径(classpath)的根目录开始,按照包名层级查找。
3. 与相对路径的区别
| 路径类型 | 示例 | 特点 |
|---|---|---|
| 相对路径 | ./subpackage | 相对于当前目录,与包名无关 |
| 绝对路径(ComponentScan) | com.itheima | 从类路径根开始,与当前类所在包无关 |
七、SpringConfig.class 查找逻辑(类路径核心)
SpringConfig.class的查找与**类路径(classpath)**密切相关,分为编译期和运行期两个阶段。
7.1 编译期:从类路径加载
项目编译后,SpringConfig.java会被编译为字节码文件,输出到target/classes目录:
- 类路径:target/classes 是类路径的核心;
- 查找规则:JVM和Spring会从类路径根加载SpringConfig.class。
7.2 关键前提:包路径必须一致
SpringConfig.class能被找到的核心是:代码中的package声明与实际文件路径一致。
八、总结与核心知识点回顾
8.1 核心对比
| 配置方式 | 核心注解/标签 | 加载方式 |
|---|---|---|
| 传统XML | <bean>、<context:component-scan> | ClassPathXmlApplicationContext |
| 注解+XML | @Component、@ComponentScan | ClassPathXmlApplicationContext |
| 纯注解 | @Configuration、@ComponentScan | AnnotationConfigApplicationContext |
8.2 核心注解速记
- @Configuration:标识当前类为Spring配置类,替代XML文件;
- @ComponentScan:配置组件扫描路径,指定Bean的扫描范围;
- @Component:通用Bean定义注解,衍生出@Controller/@Service/@Repository;
- AnnotationConfigApplicationContext:加载纯注解配置类的核心容器。
8.3 关键注意事项
- @Component不能用于接口,只能标注在具体实现类上;
- @ComponentScan是绝对路径扫描,覆盖指定包及其所有子包;
- 纯注解开发中,配置类的package必须与文件实际路径一致。